C订单到B订单聚合算法

背景:

        目前c端用户下单,对应的单位是件。也就是一个用户A可能购买15件(只)口红,另外一个用户B购买5只口红。

然后后台B端进货的时候,对应的单位是箱,箱规可能是5件/箱,10件/箱,20件/箱。

为了确保用户购买到的商品全部来自原同一个箱规。即用户A的15只口红不可以来自5件/箱,10件/箱 这两种箱规的组合。

又要考虑到价格最低(前提:20件/箱 单规格价 一定低于  5件/箱的单规格价),所以A可以和B用户组成一箱购买20件/箱  这种规格。

 

算法思路:

需要将这一批用户的所有订单想象成一个长度为n的正整数无序列表list,箱规组合则为一个固定数a,

然后找出无序列表中所有的组合,组合中每个数只能用一次,这些组合的和是a的整数倍,并记录这些组合。

 

思路一:

求出无序列表中所有的子集,然后通过判断子集中的数没有被使用过,且子集的和可以整除a,并记录这些数字,以及组合。

求解的方法有:1、递归。2、利用二进制运算

 

优点:可以罗列所有的可能性,并尝试凑整,不会遗漏任何情况

 

弊端:一个列表的所有子集为2^n次方,如果用for循环遍历,当n大于64的时候,已经是一个非常大的数字,非常影响性能。

而且C端用户参与人数超过64这种场景感觉非常多,甚至可能超过1000人。如果超过1000人,系统无法运算。

代码:只展示核心思想,如何获取所有可能组合

public static List<List<Bean>> getAllList(List<Bean> list) {
    List<List<Bean>> result = new ArrayList<>();
    //子集的个数为2的n次
    long n = (long) Math.pow(2, list.size());
    List<Bean> combine;
    for (long l = 0L; l < n; l++) {
        combine = new ArrayList<Bean>();
        for (int i = 0; i < list.size(); i++) {
            if ((l >>> i & 1) == 1)
            combine.add(list.get(i));
        }
            result.add(combine);
    }
    return result;
}

 

 

思路二:

通过取余的思路做。假设规格数为a,那么所有数就是a*n+b,b<a。然后通过b把所有的数放到一个map中。key为b,value为List,list保存商品数量。

1、当key1+key2=a时,让商品数量一一匹配。

2、然后剩余的数,即key相同的数,看他们能都自行匹配。

3、对剩下的数在用思路一解决。这时候数的数量应该是大大降低了

代码:

/**
     *
     * @param userOrderNumList
     * @param specNumList key-规格数,value-库存
     */
    public static void getResult(List<Integer> userOrderNumList, Map<Integer,Integer> specNumList){
        //未完成的订单,浅拷贝
        List<Integer> unFinishList = userOrderNumList;
        //已完成的订单
        List<Integer> finishList = new ArrayList<>();
 
        for (int i = 0; i < specNumList.size()-1; i++) {
            //获取规格数
            Set<Integer> specNum = specNumList.keySet();
            for (int j = 0; j < userOrderNumList.size()-1; j++) {
                if (unFinishList.size()==0){
                    break;
                }
               //整除,两两相加整除
                compareStart(unFinishList, finishList, userOrderNumList.get(j));
            }
        }
 
    }
 
    public static void compareStart(List<Integer> unFinishList,List<Integer> finishList,int specNum){
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < unFinishList.size(); i++) {
            Integer integer = unFinishList.get(i);
            int b = integer % specNum;
            setMap(map, b, integer);
        }
 
        //余数为0的都可以整除
        finishList.addAll(map.get(0));
        //余数不为0的,相互匹配
        int times = (specNum - 1) / 2;
        for (int i = 1; i <= times; i++) {
            compare(map, i, specNum, finishList);
 
        }
        //剩余所有的数据匹配
        compare2(map, specNum, finishList);
        //取出剩余无法匹配的数据,塞到unFinishList
        setUnFinishList(map, unFinishList);
 
    }
 
    public static void setUnFinishList(Map<Integer, List<Integer>> map, List<Integer> unFinishList){
        unFinishList = new ArrayList<Integer>();
        for(Map.Entry<Integer, List<Integer>> entry : map.entrySet()){
            List<Integer> mapValue = entry.getValue();
            unFinishList.addAll(mapValue);
        }
    }
 
 
    //剩余所有的数据匹配
    public static void compare2(Map<Integer, List<Integer>> map,int specNum, List<Integer> finishList){
        for(Map.Entry<Integer, List<Integer>> entry : map.entrySet()){
            Integer mapKey = entry.getKey();
            List<Integer> mapValue = entry.getValue();
            //TODO 对mapValue从大到小排序
 
            int a = mapValue.size() / specNum;
            int b = mapValue.size() % specNum;
 
            if (a >0){
                finishList.addAll(mapValue.subList(0, a*specNum-1));
                if (b>0){
                    map.put(mapKey, mapValue.subList(a*specNum, mapValue.size()));
                }
            }
 
            //保证剩下的数和 余数的乘积 是 规格的倍数
            mapValue = map.get(mapKey);
            a = mapValue.size() * mapKey / specNum;
            b = mapValue.size() * mapKey % specNum;
//            if (a>0 ){
//                if (&& b==0)
//                finishList.addAll(mapValue);
//                map.remove(mapKey);
//            }
//
//            if ()
        }
    }
 
    /**
     * 取出无法匹配的数据
     * @param map
     * @param key
     * @param specNum
     * @param finishList
     */
    public static void compare(Map<Integer, List<Integer>> map, Integer key,int specNum, List<Integer> finishList){
        List<Integer> list1 = map.get(key);
        List<Integer> list2 = map.get(specNum - key);
 
        if (list1.size() == list2.size()){
            finishList.addAll(list1);
            finishList.addAll(list2);
            map.remove(key);
            map.remove(specNum - key);
            return;
        }
        //TODO 对list1,list2做从大到小排序
        if (list1.size() > list2.size()){
            finishList.addAll(list2);
            finishList.addAll(list1.subList(0, list2.size()-1));
 
            map.remove(key);
            map.put(specNum - key, list1.subList(list2.size()-1, list1.size()));
            return;
        }
 
        if (list1.size() < list2.size()){
            finishList.addAll(list1);
            finishList.addAll(list2.subList(0, list1.size()-1));
 
            map.remove(specNum - key);
            map.put(key, list2.subList(list1.size()-1, list2.size()));
            return;
        }
    }
 
    public static void setMap(Map<Integer, List<Integer>> map, Integer value,Integer key){
        if (null == map){
            return;
        }
        List<Integer> list = map.get(key);
        if (CollectionUtils.isEmpty(list)){
            List<Integer> result = new ArrayList();
            result.add(value);
 
            map.put(key, result);
            return;
        }
        List<Integer> result = map.get(key);
        result.add(value);
        map.put(key, result);
        return;
 
    }

 

思路三:

用递归思想,

1、让无序列表从第一个数开始依次加上下一个数,判断数的和能否整除规格a,如果不能整除则继续加下面一个数,如果能整除则在列表中剔除这几个数,然后从头开始循环,因为此时数的长度变动了,组合可能会变动。

2、如果一轮下来没有任何数满足条件,则开始第二轮循环,循环从第二个数开始,依次加上下一个数。判断数的和能否整除规格a,如果不能整除则继续加下面一个数,如果能整除则在列表中剔除这几个数,然后从头开始循环,此时的循环的第一个数重新回到列表的最前面那个数,因为此时列表的长度变动了,组合可能会变动。

3、每次想加前,先判断这个数是否被使用过。

4、最后对剩下的数用思路一解决。这时候数的数量应该是大大降低了

代码:

public List<List<Bean>> combinationSum(List<Bean> listOriginal, int specNum) {
        if (listOriginal == null || listOriginal.size() == 0)
            return list;
        dfs(listOriginal, specNum);
//        System.out.println(Arrays.toString(finishList.toArray()));
//        System.out.println(Arrays.toString(list.toArray()));
        for (int i = 0; i < list.size(); i++) {
            List<Bean> beans = list.get(i);
            System.err.println(Arrays.toString(beans.toArray()));
        }
        System.err.println("-----------");
        for (int i = 0; i < listOriginal.size(); i++) {
            Bean bean = listOriginal.get(i);
            if (!finishList.contains(bean)){
                System.err.println(bean.toString());
            }
        }
//        System.out.println(Arrays.toString(listOriginal.toArray()));
        return list;
    }
 
    private void dfs(List<Bean> listOriginal, int specNum) {
//        int sum = getSum(ls);
//        if (sum / specNum > 1 && sum % specNum == 0) {
//            list.add(ls);
//            return;
//        }
 
        for (int i = 0; i < listOriginal.size(); i++) {
            if (checkExist(finishList, listOriginal.get(i))){
//                System.err.println("1---exist" + listOriginal.get(i).getNum());
 
                continue;
            }
            int index=i;
 
            if (index == listOriginal.size()){
                return;
            }
            boolean result = dfs(listOriginal, new ArrayList<>(), specNum, index);
            while (result){
                if (flag){
                    index = 0;
                    flag=false;
                }
                result = dfs(listOriginal, new ArrayList<>(), specNum, index);
            }
        }
 
 
    }
 
    private boolean dfs(List<Bean> listTemp, List<Bean> lastTotal, int specNum, int index){
        for (int i = index; i < listTemp.size(); i++) {
            if (checkExist(finishList, listTemp.get(i))){
//                System.err.println("exist" + listTemp.get(i).getNum());
                continue;
            }
            int sum = getSum(listTemp.get(i), lastTotal);
            if (sum/specNum>0 && sum%specNum==0){
                List<Bean> finishListList = new ArrayList<>();
                lastTotal.add(listTemp.get(i));
                finishListList.addAll(lastTotal);
                list.add(finishListList);
                finishList.addAll(finishListList);
                flag=true;
                return true;
 
            }else if (sum/specNum == 0 || sum%specNum > 0){
                if (i >= listTemp.size()){
                    return false;
                }
                lastTotal.add(listTemp.get(i));
                return dfs(listTemp, lastTotal, specNum, i+1);
            }
        }
        return false;
    }
 
    private int getSum(Bean now, List<Bean> lastTotal){
        Integer integer = StreamUtils.sumInteger(lastTotal, Bean::getNum);
        return integer+now.getNum();
    }
 
    private boolean checkExist(List<Bean> listTemp, Bean temp){
        int id = temp.getId();
        for (int i = 0; i < listTemp.size(); i++) {
            int id1 = listTemp.get(i).getId();
            if (id == id1){
                return true;
            }
        }
        return false;
    }

bean代码:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Bean {

    private int num;

    private int id;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值