page contents

开发票 拆单需求求解合适的算法

Pack 发布于 2020-01-14 16:39
阅读 528
收藏 0

attachments-2020-01-znPiU1dK5e1d7e5370aa2.png目前情况有几种列举出一种例如:图片所示 有没有什么合适的算法 可以达到拆解的目的

最后理想的组合 例如
{A:11} 一张
{A:4,B:2}一张


194
Pack
Pack

1、首先算法的目的 是否是开票数最少?
若是则初步想法如下
现有 Product:A11 B10 C2 D23 E1 F3 G6
则总票数可以算出来
总金额/11 = (11 + 10 + 2 +23 + 1 + 3 + 6) /11 = 56/11 = 5 余 1
则总票数 = 5+ 1 = 6

则准备 6张发票 Invoices{i0,i1,i2,i3,i4,i5}

//发票
Invoice {
List item; //包含条目
int totalPrice; //总金额==11 标识已经满了
}

//发票条目
InvoiceItem {
productName //商品名称
productPrice //商品数量
}

用 TreeSet 保存商品信息 可以省去自行进行排序的麻烦(若发生商品分割还要放回集合 重新排序,用TreeSet自动保证有序了)
productSet{D23 A11 B10 G6 F3 C2 E1}

然后开始依次填充发票
1、取一张发票
2、循环遍历商品(上面预准备的TreeSet金额从大到小的顺序,可以尽量避免切割)进行填充

填充规则大致如下
//从set中取出商品(需要iterater.remove())
//若当前产品价格大于最大值 说明可以直接填满一张发票 则填满本张发票 
	已填满,直接跳出商品循环。
	且若本产品金额还有剩余,则将剩余的金额商品 重新构造放回到treeSet中
//若当前产品价格小于最大值
	小于当前发票剩余值,说明也可以直接放进当前发票 放入,
			若没填满继续遍历(循环下一商品进行填充),若填满,跳出商品循环
			
	大于当前发票剩余值,说明放不下,跳过本产品,遍历下一产品

3、遍历完所有产品后
//若本张发票满了 完成本次发票 填充下张发票
//若仍然没满,说明需要切割才能填满,这里从最小值开始切割,尽量保证产品信息不回切的太碎
//这里还要想一下,注意一下,有可能不一定填满,
比如 本张发票还剩1才能满, 最小产品是2,但是实际上需要一张票可以填,那就没必要切割了,完全可以把2直接放到那张票上
类似{A10}{B2} 优于{A10,B1}{B1}
总结出一个算法:(剩余空票总钱数 - 剩余未开票钱数)< 0 才说明需要切割一部分放入本张票,否则剩余票够了,不着急切分填充本张半票。

可能说的不够具体:
参考代码如下:

package com;

import java.util.*;

/**

  • Description

  • @author : lxl

  • @version : 1.0

  • @date : 2019/11/21
    */
    public class TestInvo {

    public static void main(String[] args) {
    //随机初始化数据
    TreeSet products = new TreeSet(new Comparator() {
    @Override
    public int compare(Product o1, Product o2) {
    int result = o2.getProductPrice().compareTo(o1.getProductPrice());
    if(result == 0){
    result = 1;
    }
    return result;
    }
    });

     int totalPrice = 0;
     for (int i = 0; i < 10; i++) {
         int tp = 1 + (int) (Math.random() * 30);
         totalPrice += tp;
         Product p = new Product("NAME_" + i,tp);
         products.add(p);
     }
    
     System.out.println("原始数据 -- " + products);
    
     //计算总票数
     int maxPrice = 11;
     int totalInvoiceNum = totalPrice / maxPrice;
     if(totalPrice % maxPrice > 0){
         totalInvoiceNum++;
     }
    
    
     //初始化 6个票据 依次填充
     List<Invoice> invoices = new ArrayList<>(totalInvoiceNum);
     for (int i = 0; i < totalInvoiceNum; i++) {
    
         //初始化发票
         Invoice invoice = new Invoice();
         invoices.add(invoice);
    
         int remaining = maxPrice;
    
         Iterator<Product> productIter = products.iterator();
         for(int j=0; j<products.size(); j++){
    
             Product current = productIter.next();
    
             if(current.getProductPrice() > maxPrice){
    
                 //当前价格大于最大值 直接填满本张发票
                 //fullFix   //快速填充
                 invoice.addItem(new InvoiceItem(current.getProductName(),maxPrice));
                 remaining = 0;
                 //移除本产品
                 productIter.remove();
                 j--;
    
                 //剩余数量 放回集合
                 int still = current.getProductPrice() - maxPrice;
                 if(still > 0){
                     products.add(new Product(current.getProductName(),still));
                 }
                 break;
             }else if(current.getProductPrice() <= remaining){
                 //当前价格小于最大值 且不大于本张发票剩余空间值 填充剩余空间
                 invoice.addItem(new InvoiceItem(current.getProductName(),current.getProductPrice()));
                 //移除本产品
                 productIter.remove();
                 j--;
    
                 remaining = maxPrice - invoice.getTotalPrice();
                 if(remaining == 0){
                     //正好填满 跳出本次填充
                     break;
                 }
             }
         }
    
         //遍历完所有商品 本张发票无法填满 且products不为空 切分最小值进行填充
         //但是 这里有意外情况还要进行判断
         //剩余总空位 - 剩余未开票钱数
         //是否大于当前remaing
         //没有必要 进行切分
         if(remaining > 0 && products.size() > 0){
             int remainingPrice = 0;
             for (Product p:products) {
                 remainingPrice += p.getProductPrice();
             }
    
             int totalRemaining = maxPrice * (totalInvoiceNum - i - 1);
             int i1 = totalRemaining - remainingPrice;
             if(i1 < 0){
                 Product min = products.last();
                 invoice.addItem(new InvoiceItem(min.getProductName(),remaining));
                 products.remove(min);
                 products.add(new Product(min.getProductName(),min.getProductPrice() - remaining));
             }
         }
     }
    
     System.out.println("总开票数:" + totalInvoiceNum);
     System.out.println("总价格:" + totalPrice);
     invoices.forEach(item->{
         System.out.println(item.toString());
     });
    

    }

    //发票
    static class Invoice {
    List items; //包含条目
    int totalPrice = 0; //总金额==11 标识已经满了

     public int getTotalPrice() {
         return totalPrice;
     }
    
     //添加item
     public void addItem(InvoiceItem invoiceItem) {
         if(this.items == null){
             this.items = new ArrayList<>();
         }
         this.items.add(invoiceItem);
         this.totalPrice += invoiceItem.getProductPrice();
     }
    
     @Override
     public String toString() {
         return this.items.toString();
     }
    

    }

    //发票item
    static class InvoiceItem {
    private String productName; //商品名称
    private Integer productPrice; //商品价格

     public InvoiceItem(String productName, Integer productPrice) {
         this.productName = productName;
         this.productPrice = productPrice;
     }
    
     public String getProductName() {
         return productName;
     }
    
     public void setProductName(String productName) {
         this.productName = productName;
     }
    
     public Integer getProductPrice() {
         return productPrice;
     }
    
     public void setProductPrice(Integer productPrice) {
         this.productPrice = productPrice;
     }
    
     @Override
     public String toString() {
         return  "product='" + productName + '\'' +
                 ", price=" + productPrice ;
     }
    

    }

    //产品
    static class Product{
    String productName;
    Integer productPrice;

     public Product(String productName, Integer productPrice) {
         this.productName = productName;
         this.productPrice = productPrice;
     }
    
     public String getProductName() {
         return productName;
     }
    
     public void setProductName(String productName) {
         this.productName = productName;
     }
    
     public Integer getProductPrice() {
         return productPrice;
     }
    
     public void setProductPrice(Integer productPrice) {
         this.productPrice = productPrice;
     }
    
     @Override
     public String toString() {
         return "{" +
                 "productName='" + productName + '\'' +
                 ", productPrice=" + productPrice +
                 '}';
     }
    

    }
    }

请先 登录 后评论