OR-TOOL 揹包算法

起因:最近公司要發票自動匹配,

比如財務輸入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;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章