起因:最近公司要發票自動匹配,
比如財務輸入10000W塊,找到發票中能湊10000的。然後可以快速覈銷。
廢話不多,
一 官方文檔
https://developers.google.cn/optimization/pack/knapsack?hl=zh-cn
二 POM文件
<!--google 算法包--> <dependency> <groupId>com.google.ortools</groupId> <artifactId>ortools-java</artifactId> <version>9.9.3963</version> </dependency> <!--google 算法包-->
三 代碼
1 查詢業務數據
說明:根據條件查詢List<FsBill>,
由於發票金額的匹配,只有一個維度,所以設置values=amount,
然後調用knapsackSolver_invoice揹包核心算法。
@Override public List<FsBill> solverBill(CheckingBill_Req req) { List<FsBill> fsBills = findCheckingBill(req); //揹包算法只支持Long,所以amount*1000轉換 //由於不需要考慮價值因素,所以設置values=amount.這樣可以匹配正好的金額。 long[] values = fsBills.stream().mapToLong(x -> x.getTotalRateAmount().multiply(new BigDecimal(1000)).longValue()).toArray(); //金額:小數*1000,作整數處理。 long[][] amount = {values}; //總金額*1000,作整數處理。 long[] capacities = {req.getTotalAmount().multiply(new BigDecimal(1000)).longValue()}; List<Integer> fsBillIndexs = knapsackSolver_invoice(values, amount, capacities); List<FsBill> solverBill = new ArrayList<>(); if (!CollectionUtils.isEmpty(fsBillIndexs)) { for (Integer i : fsBillIndexs) { solverBill.add(fsBills.get(i)); } } return solverBill; }
2 揹包核心算法
說明:
values:代表物品價值(發票只有一個金額維度,所以values=weights)
weights:物品重量(此處可以傳遞發票金額amount)
返回的是List<Integer>數組下標,可以對應到List<FsBill>的對象。
@Override public List<Integer> knapsackSolver_invoice(long[] values, long[][] weights, long[] capacities) { //加載OR-TOOL本地庫 Loader.loadNativeLibraries(); //開始業務 System.out.println("=========Begin : 匹配發票"); KnapsackSolver solver = new KnapsackSolver( KnapsackSolver.SolverType.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, "test"); solver.init(values, weights, capacities); final long computedValue = solver.solve(); ArrayList<Integer> packedItems = new ArrayList<>(); ArrayList<Long> packedWeights = new ArrayList<>(); int totalWeight = 0; for (int i = 0; i < values.length; i++) { if (solver.bestSolutionContains(i)) { packedItems.add(i); packedWeights.add(weights[0][i]); totalWeight = (int) (totalWeight + weights[0][i]); } } //匹配金額 System.out.println("Target amounts: " + capacities[0]); //總價值 System.out.println("Total values: " + computedValue); //總重量 System.out.println("Total amounts: " + totalWeight); //裝載項的下標,可對應List<發票>的下標 System.out.println("Packed items: " + packedItems); //裝載項的重量 System.out.println("Packed amounts: " + packedWeights); System.out.println("=========End : 匹配發票"); //如果沒有完全匹配金額,則清空packedItems if (capacities[0] != totalWeight) { packedItems.clear(); } return packedItems; }