diff --git a/src/Main.java b/src/Main.java index a89d176..807414e 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,14 +1,19 @@ import excel.CSVCommodityParserImpl; +import knapsack.GreedyBoundedKnapsackSolver; import model.Purchase; import model.Purchases; -import service.JackSparrowHelperImpl; +import service.JackSparrowHelper; +import service.JackSparrowHelperGreedy; +import service.JackSparrowHelperWithSolver; /** * Created by SBT-Kovalev-DA on 24.02.2016. */ public class Main { public static void main(String[] args) { - JackSparrowHelperImpl jackSparrowHelper = new JackSparrowHelperImpl(new CSVCommodityParserImpl(";")); + //JackSparrowHelper jackSparrowHelper = new JackSparrowHelperGreedy(new CSVCommodityParserImpl(";")); + JackSparrowHelper jackSparrowHelper = new JackSparrowHelperWithSolver(new CSVCommodityParserImpl(";"), new GreedyBoundedKnapsackSolver()); + Purchases abr = jackSparrowHelper.helpJackSparrow("C:\\Users\\SBT-Kovalev-DA\\Downloads\\pirates\\sources.csv", 500); System.out.println(abr.calculateAveragePrice()); diff --git a/src/excel/CSVCommodityParserImpl.java b/src/excel/CSVCommodityParserImpl.java index 0e46a1e..8b0481b 100644 --- a/src/excel/CSVCommodityParserImpl.java +++ b/src/excel/CSVCommodityParserImpl.java @@ -9,6 +9,7 @@ import java.util.List; import static java.lang.Integer.valueOf; +import static model.Commodity.init; /** * Created by SBT-Kovalev-DA on 25.02.2016. @@ -32,7 +33,7 @@ public List parseFile(String fileName) { br.readLine(); //avoid headers while ((line = br.readLine()) != null) { String[] commodity = line.split(separator); - commodities.add(new Commodity(commodity[0], + commodities.add(init(commodity[0], valueOf(commodity[1]), Double.valueOf(commodity[2]), valueOf(commodity[3]), valueOf(commodity[4]))); } diff --git a/src/knapsack/BoundedKnapsackSolver.java b/src/knapsack/BoundedKnapsackSolver.java new file mode 100644 index 0000000..0098e28 --- /dev/null +++ b/src/knapsack/BoundedKnapsackSolver.java @@ -0,0 +1,14 @@ +package knapsack; + +import model.Commodity; +import model.Purchase; + +import java.util.List; +import java.util.Set; + +/** + * Created by SBT-Kovalev-DA on 01.03.2016. + */ +public interface BoundedKnapsackSolver { + Set solveKnapsack(int capacity, List commodities); +} diff --git a/src/knapsack/GreedyBoundedKnapsackSolver.java b/src/knapsack/GreedyBoundedKnapsackSolver.java new file mode 100644 index 0000000..69e84d2 --- /dev/null +++ b/src/knapsack/GreedyBoundedKnapsackSolver.java @@ -0,0 +1,53 @@ +package knapsack; + +import model.Commodity; +import model.Purchase; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static java.lang.Math.min; +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; + +/** + * Created by SBT-Kovalev-DA on 01.03.2016. + */ +public class GreedyBoundedKnapsackSolver implements BoundedKnapsackSolver { + @Override + public Set solveKnapsack(int capacity, List commodities) { + List cmdtDescendingPrice = commodities.stream().sorted(comparing(Commodity::getAvgPrice).reversed()).collect(toList()); + + int gotWeight = 0; + Set purchases = new LinkedHashSet<>(); + while (gotWeight < capacity) { + Purchase purchase = decidePurchase(capacity - gotWeight, cmdtDescendingPrice); + if (purchase == null || gotWeight + purchase.getNumberOfGallons() > capacity) break; + + purchases.add(purchase); + gotWeight += purchase.getNumberOfGallons(); + } + return purchases; + } + + private Purchase decidePurchase(int needGallons, List commodities) { + List cmdtsLeft = commodities.stream() + .filter(c -> c.getAmountLeft() != 0) + .collect(toList()); + for (Commodity commodity : cmdtsLeft) { + Purchase purchase = bestForCommodity(commodity, needGallons); + if (purchase != null) return purchase; + } + return null; + } + + private Purchase bestForCommodity(Commodity cmdty, int needGallons) { + int canTake = min(cmdty.getAmountLeft(), needGallons); + int canTakeByServings = (canTake / cmdty.getServingSize()) * cmdty.getServingSize(); + if (canTake < cmdty.getMinSize() || canTakeByServings == 0) return null; + + cmdty.decreaseAmount(canTakeByServings); + return new Purchase(cmdty.getSource(), canTakeByServings, cmdty.getAvgPrice()); + } +} diff --git a/src/model/Commodity.java b/src/model/Commodity.java index 7b6a07c..214db93 100644 --- a/src/model/Commodity.java +++ b/src/model/Commodity.java @@ -5,23 +5,33 @@ */ public class Commodity { private final String source; + private final int totalAmount; private int amountLeft; private final double avgPrice; private final int minSize; private final int servingSize; - public Commodity(String source, int amount, double avgPrice, int minSize, int servingSize) { + private Commodity(String source, int totalAmount, int amount, double avgPrice, int minSize, int servingSize) { this.source = source; this.amountLeft = amount; + this.totalAmount = totalAmount; this.avgPrice = avgPrice; this.minSize = minSize; this.servingSize = servingSize; } + public static Commodity init(String source, int amount, double avgPrice, int minSize, int servingSize) { + return new Commodity(source, amount, amount, avgPrice, minSize, servingSize); + } + public String getSource() { return source; } + public int getTotalAmount() { + return totalAmount; + } + public int getAmountLeft() { return amountLeft; } diff --git a/src/service/AbstractJackSparrowHelper.java b/src/service/AbstractJackSparrowHelper.java new file mode 100644 index 0000000..4dad44f --- /dev/null +++ b/src/service/AbstractJackSparrowHelper.java @@ -0,0 +1,38 @@ +package service; + +import excel.CommodityParser; +import model.Commodity; +import model.Purchase; +import model.Purchases; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static java.lang.Math.min; +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; + +public abstract class AbstractJackSparrowHelper implements JackSparrowHelper { + private final CommodityParser parser; + + public AbstractJackSparrowHelper(CommodityParser parser) { + this.parser = parser; + } + + @Override + public Purchases helpJackSparrow(String pathToPrices, int numberOfGallons) { + List commodities = getCommodities(pathToPrices); + + Set purchases = getPurchases(numberOfGallons, commodities); + + return new Purchases(numberOfGallons, purchases); + } + + protected abstract Set getPurchases(int numberOfGallons, List commodities); + + private List getCommodities(String pathToPrices) { + return parser.parseFile(pathToPrices) + .stream().sorted(comparing(Commodity::getAvgPrice)).collect(toList()); + } +} diff --git a/src/service/JackSparrowHelperImpl.java b/src/service/JackSparrowHelperGreedy.java similarity index 69% rename from src/service/JackSparrowHelperImpl.java rename to src/service/JackSparrowHelperGreedy.java index ad30232..073c215 100644 --- a/src/service/JackSparrowHelperImpl.java +++ b/src/service/JackSparrowHelperGreedy.java @@ -13,26 +13,22 @@ import static java.util.Comparator.comparing; import static java.util.stream.Collectors.toList; -public class JackSparrowHelperImpl implements JackSparrowHelper { - private final CommodityParser parser; - - public JackSparrowHelperImpl(CommodityParser parser) { - this.parser = parser; +@Deprecated +public class JackSparrowHelperGreedy extends AbstractJackSparrowHelper { + public JackSparrowHelperGreedy(CommodityParser parser) { + super(parser); } @Override - public Purchases helpJackSparrow(String pathToPrices, int numberOfGallons) { - List commodities = getCommodities(pathToPrices); - + protected Set getPurchases(int numberOfGallons, List commodities) { int gotGallons = 0; Set purchases = new LinkedHashSet<>(); while (gotGallons < numberOfGallons) { Purchase purchase = decidePurchase(numberOfGallons - gotGallons, commodities); purchases.add(purchase); - gotGallons+=purchase.getNumberOfGallons(); + gotGallons += purchase.getNumberOfGallons(); } - - return new Purchases(numberOfGallons, purchases); + return purchases; } private Purchase decidePurchase(int needGallons, List commodities) { @@ -46,7 +42,6 @@ private Purchase decidePurchase(int needGallons, List commodities) { return null; } - private Purchase bestForCommodity(Commodity cmdty, int needGallons) { int canTake = min(cmdty.getAmountLeft(), needGallons); int canTakeByServings = (canTake / cmdty.getServingSize()) * cmdty.getServingSize(); @@ -55,9 +50,4 @@ private Purchase bestForCommodity(Commodity cmdty, int needGallons) { cmdty.decreaseAmount(canTakeByServings); return new Purchase(cmdty.getSource(), canTakeByServings, cmdty.getAvgPrice()); } - - private List getCommodities(String pathToPrices) { - return parser.parseFile(pathToPrices) - .stream().sorted(comparing(Commodity::getAvgPrice)).collect(toList()); - } } diff --git a/src/service/JackSparrowHelperWithSolver.java b/src/service/JackSparrowHelperWithSolver.java new file mode 100644 index 0000000..5cac0df --- /dev/null +++ b/src/service/JackSparrowHelperWithSolver.java @@ -0,0 +1,59 @@ +package service; + +import excel.CommodityParser; +import knapsack.BoundedKnapsackSolver; +import model.Commodity; +import model.Purchase; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static java.util.stream.Collectors.toSet; + +public class JackSparrowHelperWithSolver extends AbstractJackSparrowHelper { + private final BoundedKnapsackSolver knapsackSolver; + + public JackSparrowHelperWithSolver(CommodityParser parser, BoundedKnapsackSolver knapsackSolver) { + super(parser); + this.knapsackSolver = knapsackSolver; + } + + @Override + protected Set getPurchases(int numberOfGallons, List commodities) { + int knapsackSize = getKnapsackSize(commodities, numberOfGallons); + Set excludePurchases = fillKnapsack(knapsackSize, commodities); + return findNeededPurchases(commodities, excludePurchases); + } + + private Set findNeededPurchases(List commodities, Set excludePurchases) { + return commodities.stream() + .map(c -> findNeeded(c, excludePurchases)) + .filter(p -> p.getNumberOfGallons()!=0) + .collect(toSet()); + } + + private Purchase findNeeded(Commodity commodity, Set excludePurchases) { + Optional excludeFromCmdty = excludePurchases.stream() + .filter(p -> commodity.getSource().equals(p.getSourceName())).findFirst(); + return new Purchase(commodity.getSource(), + getAmount(commodity, excludeFromCmdty), + commodity.getAvgPrice()); + } + + private Integer getAmount(Commodity commodity, Optional excludeFromCmdty) { + return commodity.getTotalAmount() - (excludeFromCmdty.isPresent() ? excludeFromCmdty.get().getNumberOfGallons() : 0); + } + + private int getKnapsackSize(List commodities, int numberOfGallons) { + return calculateTotalPurchaseSize(commodities) - numberOfGallons; + } + + private int calculateTotalPurchaseSize(List commodities) { + return commodities.stream().mapToInt(Commodity::getAmountLeft).sum(); + } + + private Set fillKnapsack(int knapsackSize, List commodities) { + return knapsackSolver.solveKnapsack(knapsackSize, commodities); + } +}