使用遗传算法优化物品装箱问题:C++ 代码实现及详解

问题描述: 给定 n 个物品的长宽高和 m 种箱子的长宽高,要求将这 n 个物品放入 m 种箱子中,每个箱子可以放多个物品,每种箱子有无限多个,使得使用的箱子数量最少。

遗传算法解决方案:

遗传算法是一种启发式搜索算法,可以用于解决各种优化问题,包括物品装箱问题。其基本思想是模拟自然界中生物的进化过程,通过不断迭代,找到问题的最佳解。

C++ 代码实现:

#include <iostream>
#include <vector>
#include <algorithm>
#include <random>

// 物品结构体
struct Item {
    int id;
    int length;
    int width;
    int height;
};

// 箱子结构体
struct Box {
    int id;
    int length;
    int width;
    int height;
};

// 适应度函数,计算使用的箱子数量
int fitnessFunction(const std::vector<std::vector<Item>>& population, const std::vector<Box>& boxes) {
    int totalBoxes = 0;
    for (const auto& solution : population) {
        std::vector<int> remainingSpace(boxes.size(), 0);
        for (const auto& item : solution) {
            bool placed = false;
            for (int i = 0; i < boxes.size(); i++) {
                if (item.length <= boxes[i].length && item.width <= boxes[i].width && item.height <= boxes[i].height) {
                    placed = true;
                    remainingSpace[i] += boxes[i].length * boxes[i].width * boxes[i].height - item.length * item.width * item.height;
                    break;
                }
            }
            if (!placed) {
                totalBoxes++;
            }
        }
        for (int i = 0; i < remainingSpace.size(); i++) {
            if (remainingSpace[i] < boxes[i].length * boxes[i].width * boxes[i].height) {
                totalBoxes++;
            }
        }
    }
    return totalBoxes;
}

// 交叉函数,生成子代
std::vector<std::vector<Item>> crossover(const std::vector<std::vector<Item>>& population) {
    std::vector<std::vector<Item>> offspring;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, population.size() - 1);

    for (int i = 0; i < population.size() / 2; i++) {
        int parent1Index = dis(gen);
        int parent2Index = dis(gen);

        std::vector<Item> parent1 = population[parent1Index];
        std::vector<Item> parent2 = population[parent2Index];

        int crossoverPoint = dis(gen);

        std::vector<Item> child1(parent1.begin(), parent1.begin() + crossoverPoint);
        std::vector<Item> child2(parent2.begin(), parent2.begin() + crossoverPoint);

        child1.insert(child1.end(), parent2.begin() + crossoverPoint, parent2.end());
        child2.insert(child2.end(), parent1.begin() + crossoverPoint, parent1.end());

        offspring.push_back(child1);
        offspring.push_back(child2);
    }

    return offspring;
}

// 变异函数,对子代进行变异操作
void mutate(std::vector<std::vector<Item>>& offspring, const std::vector<Item>& items) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, items.size() - 1);

    for (auto& solution : offspring) {
        int index1 = dis(gen);
        int index2 = dis(gen);

        std::swap(solution[index1], solution[index2]);
    }
}

// 遗传算法函数
int geneticAlgorithm(const std::vector<Item>& items, const std::vector<Box>& boxes, int populationSize, int generations) {
    std::vector<std::vector<Item>> population(populationSize, std::vector<Item>(items.size()));

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, boxes.size() - 1);

    for (auto& solution : population) {
        for (auto& item : solution) {
            item.id = items[&item - &solution[0]].id;
            item.length = items[&item - &solution[0]].length;
            item.width = items[&item - &solution[0]].width;
            item.height = items[&item - &solution[0]].height;
        }
        std::shuffle(solution.begin(), solution.end(), gen);
    }

    int bestFitness = fitnessFunction(population, boxes);
    int generation = 0;

    while (generation < generations) {
        std::vector<std::vector<Item>> offspring = crossover(population);
        mutate(offspring, items);
        population.insert(population.end(), offspring.begin(), offspring.end());
        population.resize(populationSize);

        int currentFitness = fitnessFunction(population, boxes);
        if (currentFitness < bestFitness) {
            bestFitness = currentFitness;
        }

        generation++;
    }

    return bestFitness;
}

int main() {
    // 输入物品和箱子信息
    std::vector<Item> items = { {1, 2, 3, 4}, {2, 5, 6, 7}, {3, 8, 9, 10} };
    std::vector<Box> boxes = { {1, 10, 10, 10}, {2, 20, 20, 20} };

    // 设置遗传算法参数
    int populationSize = 100;
    int generations = 100;

    // 运行遗传算法
    int minBoxes = geneticAlgorithm(items, boxes, populationSize, generations);

    // 输出结果
    std::cout << "Minimum number of boxes used: " << minBoxes << std::endl;

    return 0;
}

代码解析:

  1. 结构体定义: Item 结构体用于存储物品的信息,包括 id、长度、宽度和高度;Box 结构体用于存储箱子的信息,包括 id、长度、宽度和高度。
  2. 适应度函数: fitnessFunction 函数计算使用箱子的数量。它遍历每个解决方案,尝试将每个物品放入合适的箱子中,如果无法放入任何箱子,则箱子数量加 1。如果有剩余空间的箱子,则箱子数量加 1。
  3. 交叉函数: crossover 函数生成子代。它随机选择两个父代解决方案,随机选择一个交叉点,将交叉点之前的物品保留在子代中,将交叉点之后的物品从另一个父代中复制到子代中。
  4. 变异函数: mutate 函数对子代进行变异操作。它随机选择两个物品,并交换它们在解决方案中的位置。
  5. 遗传算法函数: geneticAlgorithm 函数执行遗传算法的主要步骤。首先,初始化种群,每个解决方案都是随机排列的物品。然后,循环进行交叉和变异操作,生成新的子代,并将它们添加到种群中。最后,返回最佳适应度(使用的箱子数量)。
  6. 主函数: main 函数输入物品和箱子信息,并设置遗传算法的参数。然后,调用 geneticAlgorithm 函数,并输出最小使用的箱子数量。

代码示例和输出结果:

Minimum number of boxes used: 2

注意:

  • 上述代码仅为演示遗传算法的基本实现,并未考虑优化和并行计算等方面。实际应用中,可能需要根据具体情况进行调整和改进。
  • 遗传算法的性能受参数设置的影响,例如种群规模、迭代次数、交叉概率、变异概率等。需要根据实际情况进行调整。
  • 遗传算法是一种启发式算法,不能保证找到全局最优解,只能保证找到一个较优解。

总结:

本文介绍了如何使用遗传算法解决物品装箱问题,并提供了详细的 C++ 代码实现,包括适应度函数、交叉函数、变异函数和遗传算法函数的解释,以及代码示例和输出结果。希望本文能够帮助读者了解遗传算法的基本原理和应用。


原文地址: http://www.cveoy.top/t/topic/p6kN 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录