diff --git a/src/main/java/_12/_1/lukas/FibonacciDoubling.java b/src/main/java/_12/_1/lukas/FibonacciDoubling.java new file mode 100644 index 0000000..44009d8 --- /dev/null +++ b/src/main/java/_12/_1/lukas/FibonacciDoubling.java @@ -0,0 +1,33 @@ +package _12._1.lukas; + +import provided._12._1.Fibonacci; + +/* + * F(2n) = F(n) * (2*F(n+1) - F(n)). + * F(2n+1) = F(n+1)^2 + F(n)^2. + * + * O(log(n)) + */ +public class FibonacciDoubling extends Fibonacci { + @Override + public long calculate(int n) { + if(n < 0) { + throw new IllegalArgumentException("n must not be negative. Negafibonacci numbers are not supported."); + } + long a = 0L; + long b = 1L; + for(int bit = Integer.highestOneBit(n); bit != 0; bit >>>= 1) { + long aTemp = a * ((2 * b) - a); + long bTemp = a * a + b * b; + a = aTemp; + b = bTemp; + + if((n & bit) != 0) { + long c = a + b; + a = b; + b = c; + } + } + return a; + } +} diff --git a/src/main/java/_12/_1/lukas/FibonacciDynamicIterative.java b/src/main/java/_12/_1/lukas/FibonacciDynamicIterative.java new file mode 100644 index 0000000..f1c4079 --- /dev/null +++ b/src/main/java/_12/_1/lukas/FibonacciDynamicIterative.java @@ -0,0 +1,20 @@ +package _12._1.lukas; + +import provided._12._1.Fibonacci; + +// iterative, dynamic programing with minimal memory usage +public class FibonacciDynamicIterative extends Fibonacci { + @Override + public long calculate(int n) { + if(n < 0) { + throw new IllegalArgumentException("n must not be negative. Negafibonacci numbers are not supported."); + } + long a = 0L, b = 1L, c; + for(int i = 0; i < n; i++) { + c = a + b; + a = b; + b = c; + } + return a; + } +} diff --git a/src/main/java/_12/_1/lukas/FibonacciDynamic.java b/src/main/java/_12/_1/lukas/FibonacciDynamicRecursivePersistent.java similarity index 51% rename from src/main/java/_12/_1/lukas/FibonacciDynamic.java rename to src/main/java/_12/_1/lukas/FibonacciDynamicRecursivePersistent.java index 64ebfa0..e0c1cdb 100644 --- a/src/main/java/_12/_1/lukas/FibonacciDynamic.java +++ b/src/main/java/_12/_1/lukas/FibonacciDynamicRecursivePersistent.java @@ -5,12 +5,13 @@ import provided._12._1.Fibonacci; import java.util.HashMap; import java.util.Map; -public class FibonacciDynamic extends Fibonacci { - private static final Map memory = new HashMap<>(); +// uses recursion and dynamic programing with persistent memory +public class FibonacciDynamicRecursivePersistent extends Fibonacci { + private final Map memory = new HashMap<>(); - static { - memory.put(0, 0L); - memory.put(1, 1L); + { + this.memory.put(0, 0L); + this.memory.put(1, 1L); } @@ -19,12 +20,12 @@ public class FibonacciDynamic extends Fibonacci { if(n < 0) { throw new IllegalArgumentException("n must not be negative. Negafibonacci numbers are not supported."); } - if(memory.containsKey(n)) { - return memory.get(n); + if(this.memory.containsKey(n)) { + return this.memory.get(n); } long result = calculate(n - 1) + calculate(n - 2); - memory.put(n, result); + this.memory.put(n, result); return result; } } diff --git a/src/main/java/_12/_1/lukas/FibonacciParallelDynamic.java b/src/main/java/_12/_1/lukas/FibonacciDynamicRecursivePersistentParallel.java similarity index 69% rename from src/main/java/_12/_1/lukas/FibonacciParallelDynamic.java rename to src/main/java/_12/_1/lukas/FibonacciDynamicRecursivePersistentParallel.java index be2e041..5f3a433 100644 --- a/src/main/java/_12/_1/lukas/FibonacciParallelDynamic.java +++ b/src/main/java/_12/_1/lukas/FibonacciDynamicRecursivePersistentParallel.java @@ -5,12 +5,13 @@ import provided._12._1.Fibonacci; import java.util.HashMap; import java.util.Map; -public class FibonacciParallelDynamic extends Fibonacci { - private static final Map memory = new HashMap<>(); +// eats up the heap +public class FibonacciDynamicRecursivePersistentParallel extends Fibonacci { + private final Map memory = new HashMap<>(); - static { - memory.put(0, 0L); - memory.put(1, 1L); + { + this.memory.put(0, 0L); + this.memory.put(1, 1L); } @Override @@ -18,8 +19,8 @@ public class FibonacciParallelDynamic extends Fibonacci { if(n < 0) { throw new IllegalArgumentException("n must not be negative. Negafibonacci numbers are not supported."); } - if(memory.containsKey(n)) { - return memory.get(n); + if(this.memory.containsKey(n)) { + return this.memory.get(n); } final long result; @@ -39,7 +40,7 @@ public class FibonacciParallelDynamic extends Fibonacci { } } - memory.put(n, result); + this.memory.put(n, result); return result; } } diff --git a/src/main/java/_12/_1/lukas/FibonacciParallelRecursive.java b/src/main/java/_12/_1/lukas/FibonacciRecursiveParallel.java similarity index 88% rename from src/main/java/_12/_1/lukas/FibonacciParallelRecursive.java rename to src/main/java/_12/_1/lukas/FibonacciRecursiveParallel.java index 0b36ca1..c7a17bc 100644 --- a/src/main/java/_12/_1/lukas/FibonacciParallelRecursive.java +++ b/src/main/java/_12/_1/lukas/FibonacciRecursiveParallel.java @@ -2,7 +2,8 @@ package _12._1.lukas; import provided._12._1.Fibonacci; -public class FibonacciParallelRecursive extends Fibonacci { +// rest in peace ram +public class FibonacciRecursiveParallel extends Fibonacci { @Override public long calculate(int n) { if(n < 0) { diff --git a/src/main/java/_12/_1/lukas/FibonacciTiming.java b/src/main/java/_12/_1/lukas/FibonacciTiming.java index 03eb325..ab36a3e 100644 --- a/src/main/java/_12/_1/lukas/FibonacciTiming.java +++ b/src/main/java/_12/_1/lukas/FibonacciTiming.java @@ -6,39 +6,50 @@ public class FibonacciTiming { private static final int[] NUMBERS = new int[]{ 3, 5, 8, 12, 9, 18, 15, 10, 7, 11, 20 }; private static final Fibonacci RECURSIVE = new FibonacciRecursive(); - private static final Fibonacci PARALLEL_RECURSIVE = new FibonacciParallelRecursive(); - private static final Fibonacci DYNAMIC = new FibonacciDynamic(); - private static final Fibonacci PARALLEL_DYNAMIC = new FibonacciParallelDynamic(); + private static final Fibonacci PARALLEL_RECURSIVE = new FibonacciRecursiveParallel(); + private static final Fibonacci DYNAMIC = new FibonacciDynamicRecursivePersistent(); + private static final Fibonacci PARALLEL_DYNAMIC = new FibonacciDynamicRecursivePersistentParallel(); + private static final Fibonacci ITERATIVE = new FibonacciDynamicIterative(); + // can take a few minutes public static void main(String[] args) { + System.out.println("# Sequential"); System.out.println("FibonacciRecursive (first): " + formatTime( - Timing.measureNanos(() -> runOnNumbers(RECURSIVE)) + Timing.measureNanos(() -> generateHeat(RECURSIVE)) )); - System.out.println("FibonacciParallelRecursive (first): " + formatTime( - Timing.measureNanos(() -> runOnNumbers(PARALLEL_RECURSIVE)) + System.out.println("FibonacciDynamicRecursivePersistent (first): " + formatTime( + Timing.measureNanos(() -> generateHeat(DYNAMIC)) )); - System.out.println("FibonacciDynamic (first): " + formatTime( - Timing.measureNanos(() -> runOnNumbers(DYNAMIC)) - )); - System.out.println("FibonacciParallelDynamic (first): " + formatTime( - Timing.measureNanos(() -> runOnNumbers(PARALLEL_DYNAMIC)) + System.out.println("FibonacciDynamicIterative (first): " + formatTime( + Timing.measureNanos(() -> generateHeat(ITERATIVE)) )); System.out.println("FibonacciRecursive (1M iteration average): " + formatTime( - Timing.measureAverageNanos(1000000, () -> runOnNumbers(RECURSIVE)) + Timing.measureAverageNanos(1_000_000, () -> generateHeat(RECURSIVE)) + )); + System.out.println("FibonacciDynamicRecursivePersistent (1M iteration average): " + formatTime( + Timing.measureAverageNanos(1_000_000, () -> generateHeat(DYNAMIC)) + )); + System.out.println("FibonacciDynamicIterative (1M iteration average): " + formatTime( + Timing.measureAverageNanos(1_000_000, () -> generateHeat(ITERATIVE)) + )); + + System.out.println("# Parallel"); + System.out.println("FibonacciRecursiveParallel (first): " + formatTime( + Timing.measureNanos(() -> generateHeat(PARALLEL_RECURSIVE)) )); - System.out.println("FibonacciParallelRecursive (4 iteration average): " + formatTime( - Timing.measureAverageNanos(4, () -> runOnNumbers(PARALLEL_RECURSIVE)) + System.out.println("FibonacciDynamicRecursivePersistentParallel (first): " + formatTime( + Timing.measureNanos(() -> generateHeat(PARALLEL_DYNAMIC)) )); - System.out.println("FibonacciDynamic (1M iteration average): " + formatTime( - Timing.measureAverageNanos(1000000, () -> runOnNumbers(DYNAMIC)) + System.out.println("FibonacciRecursiveParallel (10 iteration average): " + formatTime( + Timing.measureAverageNanos(10, () -> generateHeat(PARALLEL_RECURSIVE)) )); - System.out.println("FibonacciParallelDynamic (1K iteration average): " + formatTime( - Timing.measureAverageNanos(1000, () -> runOnNumbers(PARALLEL_DYNAMIC)) + System.out.println("FibonacciDynamicRecursivePersistentParallel (1M iteration average): " + formatTime( + Timing.measureAverageNanos(1_000_000, () -> generateHeat(PARALLEL_DYNAMIC)) )); } - private static void runOnNumbers(Fibonacci calculator) { + private static void generateHeat(Fibonacci calculator) { for(int n : NUMBERS) { calculator.calculate(n); } @@ -53,7 +64,7 @@ public class FibonacciTiming { } else if(digits > 3) { return Math.round(nanos / 1e1) / 1e2 + " microseconds"; } else { - return Math.round(nanos) + " nanosecond"; + return Math.round(nanos) + " nanoseconds"; } } } diff --git a/src/test/java/_12/_1/lukas/FibonacciTest.java b/src/test/java/_12/_1/lukas/FibonacciTest.java index fd9c889..d7bff3a 100644 --- a/src/test/java/_12/_1/lukas/FibonacciTest.java +++ b/src/test/java/_12/_1/lukas/FibonacciTest.java @@ -28,20 +28,32 @@ public class FibonacciTest { } @Test - void parallelRecursive() { - Fibonacci calculator = new FibonacciParallelRecursive(); + void recursiveParallel() { + Fibonacci calculator = new FibonacciRecursiveParallel(); NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); } @Test - void dynamic() { - Fibonacci calculator = new FibonacciDynamic(); + void dynamicRecursivePersistent() { + Fibonacci calculator = new FibonacciDynamicRecursivePersistent(); NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); } @Test - void parallelDynamic() { - Fibonacci calculator = new FibonacciParallelDynamic(); + void dynamicRecursivePersistentParallel() { + Fibonacci calculator = new FibonacciDynamicRecursivePersistentParallel(); + NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); + } + + @Test + void dynamicIterative() { + Fibonacci calculator = new FibonacciDynamicIterative(); + NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); + } + + @Test + void doubling() { + Fibonacci calculator = new FibonacciDoubling(); NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); } }