diff --git a/src/main/java/_12/_1/lukas/FibonacciDynamic.java b/src/main/java/_12/_1/lukas/FibonacciDynamic.java new file mode 100644 index 0000000..643c0b9 --- /dev/null +++ b/src/main/java/_12/_1/lukas/FibonacciDynamic.java @@ -0,0 +1,27 @@ +package _12._1.lukas; + +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<>(); + + static { + memory.put(0, 0L); + memory.put(1, 1L); + } + + + @Override + public long calculate(int n) { + if(memory.containsKey(n)) { + return memory.get(n); + } + + long result = calculate(n - 1) + calculate(n - 2); + memory.put(n, result); + return result; + } +} diff --git a/src/main/java/_12/_1/lukas/FibonacciParallelDynamic.java b/src/main/java/_12/_1/lukas/FibonacciParallelDynamic.java new file mode 100644 index 0000000..ca8994b --- /dev/null +++ b/src/main/java/_12/_1/lukas/FibonacciParallelDynamic.java @@ -0,0 +1,42 @@ +package _12._1.lukas; + +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<>(); + + static { + memory.put(0, 0L); + memory.put(1, 1L); + } + + @Override + public long calculate(int n) { + if(memory.containsKey(n)) { + return memory.get(n); + } + + final long result; + { + final long[] parts = new long[2]; + Thread big = new Thread(() -> parts[0] = calculate(n - 1)); + Thread small = new Thread(() -> parts[1] = calculate(n - 2)); + big.start(); + small.start(); + + try { + big.join(); + small.join(); + result = parts[0] + parts[1]; + } catch(InterruptedException e) { + throw new IllegalStateException(e); + } + } + + memory.put(n, result); + return result; + } +} diff --git a/src/main/java/_12/_1/lukas/FibonacciParallelRecursive.java b/src/main/java/_12/_1/lukas/FibonacciParallelRecursive.java new file mode 100644 index 0000000..704034f --- /dev/null +++ b/src/main/java/_12/_1/lukas/FibonacciParallelRecursive.java @@ -0,0 +1,26 @@ +package _12._1.lukas; + +import provided._12._1.Fibonacci; + +public class FibonacciParallelRecursive extends Fibonacci { + @Override + public long calculate(int n) { + if(n == 0 || n == 1) { + return n; + } + + final long[] parts = new long[2]; + Thread big = new Thread(() -> parts[0] = calculate(n - 1)); + Thread small = new Thread(() -> parts[1] = calculate(n - 2)); + big.start(); + small.start(); + + try { + big.join(); + small.join(); + return parts[0] + parts[1]; + } catch(InterruptedException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/src/main/java/_12/_1/lukas/FibonacciRecursive.java b/src/main/java/_12/_1/lukas/FibonacciRecursive.java new file mode 100644 index 0000000..2d3c382 --- /dev/null +++ b/src/main/java/_12/_1/lukas/FibonacciRecursive.java @@ -0,0 +1,13 @@ +package _12._1.lukas; + +import provided._12._1.Fibonacci; + +public class FibonacciRecursive extends Fibonacci { + @Override + public long calculate(int n) { + if(n == 0 || n == 1) { + return n; + } + return calculate(n - 1) + calculate(n - 2); + } +} diff --git a/src/main/java/_12/_1/lukas/FibonacciTiming.java b/src/main/java/_12/_1/lukas/FibonacciTiming.java new file mode 100644 index 0000000..03eb325 --- /dev/null +++ b/src/main/java/_12/_1/lukas/FibonacciTiming.java @@ -0,0 +1,59 @@ +package _12._1.lukas; + +import provided._12._1.Fibonacci; + +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(); + + public static void main(String[] args) { + System.out.println("FibonacciRecursive (first): " + formatTime( + Timing.measureNanos(() -> runOnNumbers(RECURSIVE)) + )); + System.out.println("FibonacciParallelRecursive (first): " + formatTime( + Timing.measureNanos(() -> runOnNumbers(PARALLEL_RECURSIVE)) + )); + System.out.println("FibonacciDynamic (first): " + formatTime( + Timing.measureNanos(() -> runOnNumbers(DYNAMIC)) + )); + System.out.println("FibonacciParallelDynamic (first): " + formatTime( + Timing.measureNanos(() -> runOnNumbers(PARALLEL_DYNAMIC)) + )); + + System.out.println("FibonacciRecursive (1M iteration average): " + formatTime( + Timing.measureAverageNanos(1000000, () -> runOnNumbers(RECURSIVE)) + )); + System.out.println("FibonacciParallelRecursive (4 iteration average): " + formatTime( + Timing.measureAverageNanos(4, () -> runOnNumbers(PARALLEL_RECURSIVE)) + )); + System.out.println("FibonacciDynamic (1M iteration average): " + formatTime( + Timing.measureAverageNanos(1000000, () -> runOnNumbers(DYNAMIC)) + )); + System.out.println("FibonacciParallelDynamic (1K iteration average): " + formatTime( + Timing.measureAverageNanos(1000, () -> runOnNumbers(PARALLEL_DYNAMIC)) + )); + } + + private static void runOnNumbers(Fibonacci calculator) { + for(int n : NUMBERS) { + calculator.calculate(n); + } + } + + private static String formatTime(double nanos) { + double digits = Math.log10(nanos); + if(digits > 9) { + return Math.round(nanos / 1e7) / 1e2 + " seconds"; + } else if(digits > 6) { + return Math.round(nanos / 1e4) / 1e2 + " milliseconds"; + } else if(digits > 3) { + return Math.round(nanos / 1e1) / 1e2 + " microseconds"; + } else { + return Math.round(nanos) + " nanosecond"; + } + } +} diff --git a/src/main/java/_12/_1/lukas/Timing.java b/src/main/java/_12/_1/lukas/Timing.java new file mode 100644 index 0000000..ef37eb0 --- /dev/null +++ b/src/main/java/_12/_1/lukas/Timing.java @@ -0,0 +1,18 @@ +package _12._1.lukas; + +public class Timing { + + public static long measureNanos(Runnable runnable) { + long start = System.nanoTime(); + runnable.run(); + return System.nanoTime() - start; + } + + public static double measureAverageNanos(int iterations, Runnable runnable) { + long start = System.nanoTime(); + for(int i = 0; i < iterations; i++) { + runnable.run(); + } + return (System.nanoTime() - start) / (double) iterations; + } +} diff --git a/src/test/java/_12/_1/lukas/FibonacciTest.java b/src/test/java/_12/_1/lukas/FibonacciTest.java new file mode 100644 index 0000000..fd9c889 --- /dev/null +++ b/src/test/java/_12/_1/lukas/FibonacciTest.java @@ -0,0 +1,47 @@ +package _12._1.lukas; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import provided._12._1.Fibonacci; + +import java.util.Map; + +public class FibonacciTest { + private static final Map NUMBERS = Map.ofEntries( + Map.entry(3, 2L), + Map.entry(5, 5L), + Map.entry(8, 21L), + Map.entry(12, 144L), + Map.entry(9, 34L), + Map.entry(18, 2584L), + Map.entry(15, 610L), + Map.entry(10, 55L), + Map.entry(7, 13L), + Map.entry(11, 89L), + Map.entry(20, 6765L) + ); + + @Test + void recursive() { + Fibonacci calculator = new FibonacciRecursive(); + NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); + } + + @Test + void parallelRecursive() { + Fibonacci calculator = new FibonacciParallelRecursive(); + NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); + } + + @Test + void dynamic() { + Fibonacci calculator = new FibonacciDynamic(); + NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); + } + + @Test + void parallelDynamic() { + Fibonacci calculator = new FibonacciParallelDynamic(); + NUMBERS.forEach((n, fn) -> Assertions.assertEquals(fn.longValue(), calculator.calculate(n))); + } +}