于Java简要的箱子放盒子的问题

最近工作中需要处理一个实际问题就是大箱子装小盒子的问题,写这篇文章需要解决的实际问题 就是大容器装小东西的问题。例如仓库中货位装载SKU 车厢里面装载快递包裹。
以下代码实现比较粗糙,实际过程中就是为了计算一个箱子中能装多少东西

前提条件
1、自己校验完 最长比较,即 小东西的 L(length) 、W(width)、 H(height) 最大值不能超过 大容器的LWH, 本文只讨论 立体箱子问题 杠精 读到这里就可以去逛其他帖子了,避免浪费你宝贵的时间。

大箱子 小盒子 基本属性类
CountBox

@Data
@AllArgsConstructor
public class CountBox {

    private Integer length;

    private Integer width;

    private Integer height;

    public Double getVol()
    {
        return (double) this.length*this.width*this.height;
    }
}

对于计算 结合前人的计算方法 计算了一个朴素的算法 得到的结果不一定对 后面读者有更好的方式 ,请留言 让鄙人改进 以更好的适应

具体的方法类(可能个人习惯了Java 或者Spring 的注入特性的习惯,公共的方法就抽象出来做某一类业务或者某一种方法)

@Slf4j
public class CountUtils {

    /**
     * 朴素计算(不计算之前已经装了的体积) 大箱子能装多少个小箱子
     * @param maxBox
     * @param minBox
     * @return
     * 原理是:
     * nx < X
     * ny < Y
     * nz < Z
     * 交换 XYZ 比较顺序来计算各种方法最多能放多少个 取整乘积最大值就是可放置的最多个数(朴素算法)
     */
    public Integer toCount(CountBox maxBox,CountBox minBox)
    {
        int a = (
            (maxBox.getLength() / minBox.getLength()) *
                (maxBox.getWidth() / minBox.getWidth()) *
                (maxBox.getHeight() / minBox.getHeight())
        );

        int b = (
            (maxBox.getLength() / minBox.getLength()) *
               (maxBox.getWidth() / minBox.getHeight()) *
                (maxBox.getHeight() / minBox.getWidth())
        );

        int c = (
            (maxBox.getLength() / minBox.getWidth()) *
                (maxBox.getWidth() / minBox.getLength()) *
                (maxBox.getHeight() / minBox.getHeight())
        );

        int d = (
            (maxBox.getLength() / minBox.getWidth()) *
               (maxBox.getWidth() / minBox.getHeight()) *
              (maxBox.getHeight() / minBox.getLength())
        );

        int e = (
            (maxBox.getLength() / minBox.getHeight()) *
                (maxBox.getWidth() / minBox.getLength()) *
                (maxBox.getHeight() / minBox.getWidth())
        );

        int f = (
            (maxBox.getLength() / minBox.getHeight()) *
               (maxBox.getWidth() / minBox.getWidth()) *
              (maxBox.getHeight() / minBox.getLength())
        );
        List<Integer> capacitiesList = new ArrayList<>();
        Stream.of(a, b, c, d, e, f).forEach(x -> capacitiesList.add(x));
        return PrimeSortUtils.getMaxNumberFromArray(capacitiesList);
    }

    /**
     * 比较适用的现实的常规方法 计算前容器已完成了多少体积
     * @param maxBox 大箱子
     * @param minBox 小盒子
     * @param occupyCapacity 已占用体积
     * @return 所能承载的小盒子
     */
    private Integer toCountComplex(CountBox maxBox,CountBox minBox,Double occupyCapacity)
    {
        Integer a=0,b=0,c=0;
        //剔除所占空间的部分将该部分之外的体积分成若干个小的部分的box 进行朴素计算
        Double totalCapacity = maxBox.getVol();
        Double surplusCapacity = totalCapacity-occupyCapacity;
        Double minNeedVolume = minBox.getVol();
        int minValue = Math.min(minBox.getLength(),Math.min(minBox.getWidth(),minBox.getHeight()));
        if(surplusCapacity>minNeedVolume)
        {
           double maxLength = (surplusCapacity/(maxBox.getWidth()*maxBox.getHeight()));
           if(maxLength>minValue){
               Integer lengthNew = (int)maxLength;
               CountBox  maxBoxNew = new CountBox(lengthNew,maxBox.getWidth(),maxBox.getHeight());
               a = toCount(maxBoxNew,minBox);
           }
           double maxWidth = (surplusCapacity/(maxBox.getLength()*maxBox.getHeight()));
            if(maxWidth>minValue){
                Integer widthNew = (int)maxWidth;
                CountBox  maxBoxNew = new CountBox(maxBox.getLength(),widthNew,maxBox.getHeight());
                b = toCount(maxBoxNew,minBox);
            }

            double maxHeight = (surplusCapacity/(maxBox.getLength()*maxBox.getWidth()));
            if(maxHeight>minValue){
                Integer heightNew = (int)maxHeight;
                CountBox  maxBoxNew = new CountBox(maxBox.getLength(),maxBox.getWidth(),heightNew);
                c = toCount(maxBoxNew,minBox);
            }
        }
        return Math.max(a,Math.max(b,c));
    }

    /**
     * 无视空间放置的计算方法( 限用) 只能针对同一个槽所有规格都一样 并且条件比较苛刻 至于后面怎么改造,后面抽时间再研究
     * @param maxBox 大箱子
     * @param minBox 小盒子
     * @param occupyCapacity 已占用的体积
     * @return 小盒子个数
     */
    public Integer toCountAll(CountBox maxBox,CountBox minBox,Double occupyCapacity)
    {
        Integer qty = 0;
        Integer count = 0;
        Double minBoxVolume = minBox.getVol();
        while(true)
        {
            int tmp = toCountComplex(maxBox,minBox,(double)(occupyCapacity+qty*minBoxVolume));
            if(tmp>0)
            {
                qty +=tmp;
                count ++;
            }
            else
            {
                break;
            }
        }
        
        log.info("First Recycle times is:{}",count);
        
        int minValue = Math.min(minBox.getLength(),Math.min(minBox.getWidth(),minBox.getHeight()));
        if(count>1) {
            Integer a = maxBox.getLength() - (count - 1) * minValue;
            Integer b = maxBox.getWidth() - (count - 1) * minValue;
            Integer c = maxBox.getHeight() - (count - 1) * minValue;
            int minSurplusVal = Math.min(a, Math.min(b, c));
            Integer maxValue = Math.max(minBox.getLength(), Math.max(minBox.getWidth(), minBox.getHeight()));
            if (maxValue <= minSurplusVal)//最长边 还小于最少递减值 即 理论上结合总体积比较还可以放得下
            {
                CountBox maxBoxNew = new CountBox(a, b, c);
                Double theoreticalVol = maxBoxNew.getVol();
                Double surplusVol = maxBox.getVol() - occupyCapacity - qty * minBoxVolume;
                if (theoreticalVol > surplusVol) {
                    maxBoxNew.setLength(Math.max(minBox.getLength(), Math.max(minBox.getWidth(), minBox.getHeight())));
                    Double theoreticalVolNew = maxBoxNew.getVol();
                    if (theoreticalVolNew > surplusVol) {
                        maxBoxNew.setWidth(Math.max(minBox.getLength(), Math.min(minBox.getWidth(), minBox.getHeight())));
                        maxBoxNew.setHeight(Math.max(minBox.getLength(), Math.min(minBox.getWidth(), minBox.getHeight())));
                        Double theoreticalVolNew2 = maxBoxNew.getVol();
                        if (theoreticalVolNew2 > surplusVol) {
                            Double theoreticalVolNew3 = maxBoxNew.getVol();
                            if (theoreticalVolNew3 > surplusVol) {
                                maxBoxNew.setHeight(Math.min(minBox.getLength(), Math.min(minBox.getWidth(), minBox.getHeight())));
                            }
                        }
                    }
                }
                Integer qty2 = toCountComplex(maxBoxNew, minBox, 0D);
                qty += qty2;
            }
       }
        return qty;
    }


    public static void main(String[] args) {
    
        CountUtils countUtils = new CountUtils();
        CountBox maxBox = new CountBox(5,5,5);
        CountBox minBox = new CountBox(1,2,3);

        Integer firstQty = countUtils.toCountComplex(maxBox,minBox,0D);
        System.out.println(firstQty);
        Integer secondQty = countUtils.toCount(maxBox,minBox);
        System.out.println(secondQty);
        Integer thridQty = countUtils.toCountAll(maxBox,minBox2,0D);
        System.out.println(thridQty);

    }
}

自己测试过很多组数据 其中 对于解决实际问题的话 countComplex 话已经是比较好解决实际问题的,原因就是:理论计算方法对于实际生产的出入比较大,简单点说空间全部占满 当手都伸不进去了 小盒子怎么拿出来,当有容器包裹物体,空间装满 物体是无法拿出来的额。
1、toCount方法比较简易 也是默认算法 立体空间放置旋转九十度的原理
2、基于此类问题大都是NPL 问题,比较浪费时间,得出简易结果其实就能解决实际问题了
3、楼主不是专注于算法研究,所以该方法还有得改进,希望遇见贵人,能优化下计算过程。

有兴趣的技术大佬 帮忙想下 123 的盒子 和116 的盒子 在满足能够放下两种规格盒子的箱子里(就比如 654),能够放的数量是一样的吗??简化问题 已占空间 都设置成0 分析吧 。
实际生活中这种大箱子可能装的不是同一种规格的货,所以体积肯定不一样,只能每次减去已占用的体积重新分析计算

有建设性建议还可以留言,谢谢!

某些问题计算逻辑文库(外国仁的维基百科(We need to turn over the wall)):
https://en.wikipedia.org/wiki/Packing_problems

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值