python --货车装厢问题

python 复制代码
import matplotlib

matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from typing import List, Tuple
import numpy as np


class Box:
    """表示长方体(货车或货物)"""

    def __init__(self, length: float, width: float, height: float, position: Tuple[float, float, float] = (0, 0, 0)):
        self.length = length  # x轴(车厢长度)
        self.width = width  # y轴(车厢宽度)
        self.height = height  # z轴(车厢高度)
        self.position = position

    def get_corners(self) -> List[Tuple[float, float, float]]:
        x, y, z = self.position
        return [
            (x, y, z), (x + self.length, y, z),
            (x + self.length, y + self.width, z), (x, y + self.width, z),
            (x, y, z + self.height), (x + self.length, y, z + self.height),
            (x + self.length, y + self.width, z + self.height), (x, y + self.width, z + self.height)
        ]

    def rotate(self, rotation_type: int) -> None:
        dims = [self.length, self.width, self.height]
        if rotation_type == 0:  # LWH
            self.length, self.width, self.height = dims[0], dims[1], dims[2]
        elif rotation_type == 1:  # LHW
            self.length, self.width, self.height = dims[0], dims[2], dims[1]
        elif rotation_type == 2:  # WLH
            self.length, self.width, self.height = dims[1], dims[0], dims[2]
        elif rotation_type == 3:  # WHW
            self.length, self.width, self.height = dims[1], dims[2], dims[0]
        elif rotation_type == 4:  # HLW
            self.length, self.width, self.height = dims[2], dims[0], dims[1]
        elif rotation_type == 5:  # HWL
            self.length, self.width, self.height = dims[2], dims[1], dims[0]

    def can_fit_in(self, other: 'Box') -> bool:
        return (self.length <= other.length and
                self.width <= other.width and
                self.height <= other.height)


class BinPacking3D:
    """3D装箱求解器(优化空间分割逻辑)"""

    def __init__(self, truck: Box):
        self.truck = truck
        self.bins = [truck]  # 可用空间
        self.placed_boxes = []  # 已放置货物

    def add_box(self, box: Box) -> bool:
        """尝试放置箱子(按空间效率排序)"""
        # 优先选择利用率最高的空间放置
        for i in sorted(range(len(self.bins)),
                        key=lambda x: (self.bins[x].length * self.bins[x].width * self.bins[x].height),
                        reverse=False):
            bin_box = self.bins[i]
            if box.can_fit_in(bin_box):
                box.position = bin_box.position
                self.placed_boxes.append(box)
                self._split_bin(i, box)  # 优化的空间分割
                return True
        return False

    def _split_bin(self, bin_index: int, placed_box: Box) -> None:
        """
        优化的空间分割逻辑:
        1. 右侧剩余空间(沿长度方向)
        2. 后方剩余空间(沿宽度方向)→ 关键:确保宽度方向能放下第二排
        3. 上方剩余空间(沿高度方向)
        """
        bin_box = self.bins[bin_index]
        x, y, z = bin_box.position  # 原始空间起点
        px, py, pz = placed_box.position  # 放置的箱子起点
        pl, pw, ph = placed_box.length, placed_box.width, placed_box.height  # 放置的箱子尺寸

        new_bins = []

        # 1. 右侧空间(长度方向剩余)
        if px + pl < x + bin_box.length:
            new_bins.append(Box(
                length=(x + bin_box.length) - (px + pl),
                width=pw,  # 保持与放置箱子相同的宽度
                height=ph,
                position=(px + pl, py, pz)
            ))

        # 2. 后方空间(宽度方向剩余 → 关键修复:确保能放第二排)
        if py + pw < y + bin_box.width:
            new_bins.append(Box(
                length=bin_box.length,  # 保持原始空间的全部长度
                width=(y + bin_box.width) - (py + pw),  # 宽度方向剩余
                height=ph,
                position=(x, py + pw, pz)  # 从原始空间起点开始(而非放置箱子的起点)
            ))

        # 3. 上方空间(高度方向剩余)
        if pz + ph < z + bin_box.height:
            new_bins.append(Box(
                length=bin_box.length,  # 保持原始空间的全部长度
                width=bin_box.width,  # 保持原始空间的全部宽度
                height=(z + bin_box.height) - (pz + ph),
                position=(x, y, pz + ph)
            ))

        # 替换原始空间为新分割的空间
        self.bins.pop(bin_index)
        self.bins.extend(new_bins)

    def optimize_placement(self, boxes: List[Box]) -> None:
        """优化放置顺序和旋转方式"""
        # 按体积排序(大的先放)
        boxes_sorted = sorted(boxes, key=lambda b: b.length * b.width * b.height, reverse=True)

        for box in boxes_sorted:
            best_rotation = 0
            best_fit = False
            # 尝试所有旋转方式
            for rotation in range(6):
                box_copy = Box(box.length, box.width, box.height)
                box_copy.rotate(rotation)
                temp_solver = BinPacking3D(Box(self.truck.length, self.truck.width, self.truck.height))
                temp_solver.placed_boxes = [b for b in self.placed_boxes]
                temp_solver.bins = [b for b in self.bins]

                if temp_solver.add_box(box_copy):
                    # 计算利用率
                    used = sum(b.length * b.width * b.height for b in temp_solver.placed_boxes)
                    total = self.truck.length * self.truck.width * self.truck.height
                    if (used / total > best_fit) or not best_fit:
                        best_rotation = rotation
                        best_fit = True

            box.rotate(best_rotation)
            if not self.add_box(box):
                print(f"无法放入箱子: {box.length}x{box.width}x{box.height}")

    def visualize(self, title: str = "货车装载可视化(修复宽度方向)") -> None:
        """可视化保持不变"""
        fig = plt.figure(figsize=(12, 8))
        ax = fig.add_subplot(111, projection='3d')

        # 绘制货车轮廓
        self._plot_box_outline(ax, self.truck, color='black', linewidth=1)

        # 绘制货物
        colors = ['#FF5733', '#33FF57', '#3357FF', '#F3FF33', '#FF33F3', '#33FFF3', '#F333FF']
        for i, box in enumerate(self.placed_boxes):
            self._plot_box(ax, box, color=colors[i % len(colors)], alpha=0.8)

        # 真实比例设置
        max_dim = max(self.truck.length, self.truck.width, self.truck.height)
        x_scale = self.truck.length / max_dim
        y_scale = self.truck.width / max_dim
        z_scale = self.truck.height / max_dim
        ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([x_scale, y_scale, z_scale, 1]))

        # 视角
        ax.view_init(elev=20, azim=45)  # 降低仰角,更易看到宽度方向的两排
        ax.axis('off')
        fig.patch.set_facecolor('white')
        plt.tight_layout()
        plt.show()

    def _plot_box(self, ax: Axes3D, box: Box, color: str, alpha: float) -> None:
        corners = box.get_corners()
        faces = [
            [corners[0], corners[1], corners[2], corners[3]],
            [corners[4], corners[5], corners[6], corners[7]],
            [corners[0], corners[1], corners[5], corners[4]],
            [corners[2], corners[3], corners[7], corners[6]],
            [corners[0], corners[3], corners[7], corners[4]],
            [corners[1], corners[2], corners[6], corners[5]]
        ]
        ax.add_collection3d(Poly3DCollection(faces, facecolors=color, edgecolors='black', alpha=alpha))

    def _plot_box_outline(self, ax: Axes3D, box: Box, color: str, linewidth: float) -> None:
        corners = box.get_corners()
        edges = [
            [corners[0], corners[1]], [corners[1], corners[2]], [corners[2], corners[3]], [corners[3], corners[0]],
            [corners[4], corners[5]], [corners[5], corners[6]], [corners[6], corners[7]], [corners[7], corners[4]],
            [corners[0], corners[4]], [corners[1], corners[5]], [corners[2], corners[6]], [corners[3], corners[7]]
        ]
        for edge in edges:
            x, y, z = zip(*edge)
            ax.plot(x, y, z, color=color, linewidth=linewidth)


def main():
    # 货车尺寸:12m长 x 3m宽 x 3.5m高(宽度足够放两排1.5m的箱子)
    truck = Box(length=12.0, width=3, height=3.5)

    # 货物:18个1.5x1.5x1.5的正方体箱子(理论上可放:12/1.5=8排长度,3/1.5=2排宽度 → 8*2=16个,高度3.5/1.5≈2层 → 32个,所以18个应全部放下)

    boxes = [Box(1.1, 1.5, 1.0) for _ in range(5)]
    boxes.append(Box(1,1,1))
    boxes.append(Box(1,1,1))
    boxes.append(Box(1,1,1))
    boxes.append(Box(1,1,1))
    boxes.append(Box(1,1,1))
    boxes.append(Box(1,1,1))
    boxes.append(Box(1,1,1))
    boxes.append(Box(1,1,1))
    boxes.append(Box(1,1,1))
    boxes.append(Box(2,2,2))
    boxes.append(Box(1.2,1,1))
    boxes.append(Box(1,1.5,1))



    solver = BinPacking3D(truck)
    solver.optimize_placement(boxes)

    # 输出结果
    print(f"货车尺寸: {truck.length}m(长) x {truck.width}m(宽) x {truck.height}m(高)")
    print(f"总货物数量: {len(boxes)} 个(1.5x1.5x1.5m)")
    print(f"成功装载: {len(solver.placed_boxes)} 个")
    print(
        f"空间利用率: {sum(b.length * b.width * b.height for b in solver.placed_boxes) / (truck.length * truck.width * truck.height) * 100:.1f}%")

    # 可视化(可看到宽度方向的两排货物)
    solver.visualize()


if __name__ == "__main__":
    main()
相关推荐
iCxhust38 分钟前
c# U盘映像生成工具
开发语言·单片机·c#
yangzhi_emo1 小时前
ES6笔记2
开发语言·前端·javascript
emplace_back2 小时前
C# 集合表达式和展开运算符 (..) 详解
开发语言·windows·c#
jz_ddk2 小时前
[学习] C语言数学库函数背后的故事:`double erf(double x)`
c语言·开发语言·学习
萧曵 丶2 小时前
Rust 所有权系统:深入浅出指南
开发语言·后端·rust
xiaolang_8616_wjl3 小时前
c++文字游戏_闯关打怪2.0(开源)
开发语言·c++·开源
收破烂的小熊猫~3 小时前
《Java修仙传:从凡胎到码帝》第四章:设计模式破万法
java·开发语言·设计模式
蹦蹦跳跳真可爱5893 小时前
Python----OpenCV(图像増强——高通滤波(索贝尔算子、沙尔算子、拉普拉斯算子),图像浮雕与特效处理)
人工智能·python·opencv·计算机视觉
nananaij3 小时前
【Python进阶篇 面向对象程序设计(3) 继承】
开发语言·python·神经网络·pycharm