diff --git a/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java b/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java new file mode 100644 index 0000000000..9394bbdbb8 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java @@ -0,0 +1,31 @@ +package com.baeldung.algorithms; + +import java.util.Scanner; + +import com.baeldung.algorithms.annealing.SimulatedAnnealing; +import com.baeldung.algorithms.slope_one.SlopeOne; + +public class RunAlgorithm { + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + System.out.println("Run algorithm:"); + System.out.println("1 - Simulated Annealing"); + System.out.println("2 - Slope One"); + int decision = in.nextInt(); + switch (decision) { + case 1: + System.out.println( + "Optimized distance for travel: " + SimulatedAnnealing.simulateAnnealing(10, 10000, 0.9995)); + break; + case 2: + SlopeOne.slopeOne(3); + break; + default: + System.out.println("Unknown option"); + break; + } + in.close(); + } + +} diff --git a/core-java/src/main/java/com/baeldung/algorithms/City.java b/core-java/src/main/java/com/baeldung/algorithms/annealing/City.java similarity index 89% rename from core-java/src/main/java/com/baeldung/algorithms/City.java rename to core-java/src/main/java/com/baeldung/algorithms/annealing/City.java index 1a96dc759d..0f060c73c2 100644 --- a/core-java/src/main/java/com/baeldung/algorithms/City.java +++ b/core-java/src/main/java/com/baeldung/algorithms/annealing/City.java @@ -1,4 +1,4 @@ -package com.baeldung.algorithms; +package com.baeldung.algorithms.annealing; import lombok.Data; diff --git a/core-java/src/main/java/com/baeldung/algorithms/SimulatedAnnealing.java b/core-java/src/main/java/com/baeldung/algorithms/annealing/SimulatedAnnealing.java similarity index 88% rename from core-java/src/main/java/com/baeldung/algorithms/SimulatedAnnealing.java rename to core-java/src/main/java/com/baeldung/algorithms/annealing/SimulatedAnnealing.java index f2db61c3b1..c02b0b8285 100644 --- a/core-java/src/main/java/com/baeldung/algorithms/SimulatedAnnealing.java +++ b/core-java/src/main/java/com/baeldung/algorithms/annealing/SimulatedAnnealing.java @@ -1,4 +1,4 @@ -package com.baeldung.algorithms; +package com.baeldung.algorithms.annealing; public class SimulatedAnnealing { @@ -33,8 +33,4 @@ public class SimulatedAnnealing { return bestDistance; } - public static void main(String[] args) { - System.out.println("Optimized distance for travel: " + simulateAnnealing(10, 10000, 0.9995)); - } - } diff --git a/core-java/src/main/java/com/baeldung/algorithms/Travel.java b/core-java/src/main/java/com/baeldung/algorithms/annealing/Travel.java similarity index 97% rename from core-java/src/main/java/com/baeldung/algorithms/Travel.java rename to core-java/src/main/java/com/baeldung/algorithms/annealing/Travel.java index af03b0abbb..9bf341fbbe 100644 --- a/core-java/src/main/java/com/baeldung/algorithms/Travel.java +++ b/core-java/src/main/java/com/baeldung/algorithms/annealing/Travel.java @@ -1,4 +1,4 @@ -package com.baeldung.algorithms; +package com.baeldung.algorithms.annealing; import java.util.ArrayList; import java.util.Collections; diff --git a/core-java/src/main/java/com/baeldung/algorithms/slope_one/InputData.java b/core-java/src/main/java/com/baeldung/algorithms/slope_one/InputData.java new file mode 100644 index 0000000000..548f7ae4da --- /dev/null +++ b/core-java/src/main/java/com/baeldung/algorithms/slope_one/InputData.java @@ -0,0 +1,36 @@ +package com.baeldung.algorithms.slope_one; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import lombok.Data; + +@Data +public class InputData { + + protected static List items = Arrays.asList(new Item("Candy"), new Item("Drink"), new Item("Soda"), new Item("Popcorn"), + new Item("Snacks")); + + public static Map> initializeData(int numberOfUsers) { + Map> data = new HashMap<>(); + HashMap newUser; + Set newRecommendationSet; + for (int i = 0; i < numberOfUsers; i++) { + newUser = new HashMap(); + newRecommendationSet = new HashSet<>(); + for (int j = 0; j < 3; j++) { + newRecommendationSet.add(items.get((int) (Math.random() * 5))); + } + for (Item item : newRecommendationSet) { + newUser.put(item, Math.random()); + } + data.put(new User("User " + i), newUser); + } + return data; + } + +} diff --git a/core-java/src/main/java/com/baeldung/algorithms/slope_one/Item.java b/core-java/src/main/java/com/baeldung/algorithms/slope_one/Item.java new file mode 100644 index 0000000000..818a6d4601 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/algorithms/slope_one/Item.java @@ -0,0 +1,13 @@ +package com.baeldung.algorithms.slope_one; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Item { + + private String itemName; +} diff --git a/core-java/src/main/java/com/baeldung/algorithms/slope_one/SlopeOne.java b/core-java/src/main/java/com/baeldung/algorithms/slope_one/SlopeOne.java new file mode 100644 index 0000000000..800a86884f --- /dev/null +++ b/core-java/src/main/java/com/baeldung/algorithms/slope_one/SlopeOne.java @@ -0,0 +1,120 @@ +package com.baeldung.algorithms.slope_one; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Slope One algorithm implementation + */ +public class SlopeOne { + + private static Map> differencesMatrix = new HashMap<>(); + private static Map> frequenciesMatrix = new HashMap<>(); + private static Map> inputData; + private static Map> outputData = new HashMap<>(); + + public static void slopeOne(int numberOfUsers) { + inputData = InputData.initializeData(numberOfUsers); + System.out.println("Slope One - Before the Prediction\n"); + buildDifferencesMatrix(inputData); + System.out.println("\nSlope One - With Predictions\n"); + predict(inputData); + } + + /** + * Based on the available data, calculate the relationships between the + * items and number of occurences + * + * @param data existing user data and their items' ratings + */ + private static void buildDifferencesMatrix(Map> data) { + for (HashMap user : data.values()) { + for (Entry entry : user.entrySet()) { + if (!differencesMatrix.containsKey(entry.getKey())) { + differencesMatrix.put(entry.getKey(), new HashMap()); + frequenciesMatrix.put(entry.getKey(), new HashMap()); + } + for (Entry entry2 : user.entrySet()) { + int oldCount = 0; + if (frequenciesMatrix.get(entry.getKey()).containsKey(entry2.getKey())) + oldCount = frequenciesMatrix.get(entry.getKey()).get(entry2.getKey()).intValue(); + double oldDiff = 0.0; + if (differencesMatrix.get(entry.getKey()).containsKey(entry2.getKey())) + oldDiff = differencesMatrix.get(entry.getKey()).get(entry2.getKey()).doubleValue(); + double observedDiff = entry.getValue() - entry2.getValue(); + frequenciesMatrix.get(entry.getKey()).put(entry2.getKey(), oldCount + 1); + differencesMatrix.get(entry.getKey()).put(entry2.getKey(), oldDiff + observedDiff); + } + } + } + for (Item j : differencesMatrix.keySet()) { + for (Item i : differencesMatrix.get(j).keySet()) { + double oldvalue = differencesMatrix.get(j).get(i).doubleValue(); + int count = frequenciesMatrix.get(j).get(i).intValue(); + differencesMatrix.get(j).put(i, oldvalue / count); + } + } + printData(data); + } + + /** + * Based on existing data predict all missing ratings. If prediction is not + * possible, the value will be equal to -1 + * + * @param data existing user data and their items' ratings + */ + private static void predict(Map> data) { + HashMap predictions = new HashMap(); + HashMap frequencies = new HashMap(); + for (Item j : differencesMatrix.keySet()) { + frequencies.put(j, 0); + predictions.put(j, 0.0); + } + for (Entry> entry : data.entrySet()) { + for (Item j : entry.getValue().keySet()) { + for (Item k : differencesMatrix.keySet()) { + try { + double newValue = (differencesMatrix.get(k).get(j).doubleValue() + + entry.getValue().get(j).doubleValue()) * frequenciesMatrix.get(k).get(j).intValue(); + predictions.put(k, predictions.get(k) + newValue); + frequencies.put(k, frequencies.get(k) + frequenciesMatrix.get(k).get(j).intValue()); + } catch (NullPointerException e) { + } + } + } + HashMap cleanPredictions = new HashMap(); + for (Item j : predictions.keySet()) { + if (frequencies.get(j) > 0) { + cleanPredictions.put(j, predictions.get(j).doubleValue() / frequencies.get(j).intValue()); + } + } + for (Item j : InputData.items) { + if (entry.getValue().containsKey(j)) { + cleanPredictions.put(j, entry.getValue().get(j)); + } else { + cleanPredictions.put(j, -1.0); + } + } + outputData.put(entry.getKey(), cleanPredictions); + } + printData(outputData); + } + + private static void printData(Map> data) { + for (User user : data.keySet()) { + System.out.println(user.getUsername() + ":"); + print(data.get(user)); + } + } + + private static void print(HashMap hashMap) { + NumberFormat formatter = new DecimalFormat("#0.000"); + for (Item j : hashMap.keySet()) { + System.out.println(" " + j.getItemName() + " --> " + formatter.format(hashMap.get(j).doubleValue())); + } + } + +} diff --git a/core-java/src/main/java/com/baeldung/algorithms/slope_one/User.java b/core-java/src/main/java/com/baeldung/algorithms/slope_one/User.java new file mode 100644 index 0000000000..57f16f6748 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/algorithms/slope_one/User.java @@ -0,0 +1,14 @@ +package com.baeldung.algorithms.slope_one; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class User { + + private String username; + +} diff --git a/core-java/src/test/java/com/baeldung/algorithms/SimulatedAnnealingTest.java b/core-java/src/test/java/com/baeldung/algorithms/SimulatedAnnealingTest.java index 3fe3650041..2fc7ea9b92 100644 --- a/core-java/src/test/java/com/baeldung/algorithms/SimulatedAnnealingTest.java +++ b/core-java/src/test/java/com/baeldung/algorithms/SimulatedAnnealingTest.java @@ -3,6 +3,8 @@ package com.baeldung.algorithms; import org.junit.Assert; import org.junit.Test; +import com.baeldung.algorithms.annealing.SimulatedAnnealing; + public class SimulatedAnnealingTest { @Test