【发布时间】:2025-12-07 11:20:07
【问题描述】:
我目前正在研究一个 NP 完全问题,并为此实施了个人遗传算法。结果超出了我的预期。凭借精心设计的适应度函数和精心调整的几个种群/突变,我猜 GA 在某些情况下可能是一个出色的工具。
无论如何,我现在正在寻找一种能够产生最佳改组输出的元启发式(GA,模拟退火...)。
我的意思是,在这种情况下,我的意思是有限集的无偏(à la Fisher-Yates)随机排列。就像卡组一样。一个巨大的(〜500!排列)。
这个集合的值都是不同的。预计不会发生碰撞。
由于这种限制,我在实施 GA 解决方案时遇到了一些困难。事实上,洗牌后的值不能用作基因。很容易看出原因:
#include <iostream>
#include <vector>
#define SPLICING 50 // 50|50 one-point crossover
int crossover(int gene, int DNA_length, int A, int B)
{if (gene < (SPLICING*DNA_length)/100) return A; else return B;}
int main() {
std::vector<int> A, B, C;
A = { 3, 4, 8, 12, 2, 0, 9, 7, 10, 20 };
B = { 8, 10, 3, 4, 20, 0, 7, 9, 2, 12 };
int DNA_length = int(A.size());
for (int i=0; i<DNA_length; i++) {
C.push_back(crossover(i, DNA_length, A[i], B[i]));
if (i == DNA_length/2) std::cout << "| ";
std::cout << C[i] << " ";}
}
输出:3 4 8 12 2 | 0 7 9 2 12
有两次碰撞 (2, 12)。
我的预期输出是这样的:3 4 8 12 2 | 0 7 9 10 20(没有碰撞,原始集合的完美洗牌)。
然后,我需要对这些值的顺序进行编码以避免这种困难。
一种天真的方法是使用唯一键标识每个值。但随后创建的集合是一个序数,因为它指的是值的顺序。
我认为交叉函数必须处理父母 DNA 的序数。但我无法解决将序数集(整个 DNA)的两个非线性有序序数子集(父母的 DNA 切片)混合而不会发生碰撞的问题!
也许我只能依靠变异来收敛。没有选择,没有父母/孩子,只有同一集合中的交换功能(个人的 DNA)。简而言之:不是很有说服力。
在唯一的有限集中置换序数确实很容易(例如,平凡的:第一个变成第七个;第二个,第十个等等)。但是我不确定当集合 B 的第二个 变成新集合的第十个时,说集合 A 的第一个 变成第七个是否有意义。
那么,我的问题是:
在您看来,可以在遗传算法的上下文中使用交叉函数来打乱集合的序数吗?如果不是,您能否建议一种比蛮力、爬山技术或遗传算法更有效的元启发式方法?
谢谢。
【问题讨论】:
-
染色体的交配通常会导致后代发生碰撞,从而产生许多不合适的解决方案。您是否考虑过操纵后代(可能在突变之前或之后)以确保解决方案中没有碰撞?也许碰撞基因可以根据具有原始位置的父母随机分配或连续分配。这不是 GA 的理想选择,但它可能有助于删除所有不合适的解决方案。另外,使用变异不是说洗牌时可以用其他牌替换牌组中的某些牌吗?
-
确实有这种可能,但恐怕会大大增加随机性因素。想象一下,您将来自父母 1 [1, 4, 3, 2 |.然后你必须修改父 2 的 DNA 部分 | 2, 7, 5, 0] 以使孩子的 DNA 中的所有值都是唯一的 [1, 4, 3, 2 | 6*、7、5、0]。因此,该 DNA 的第二部分并不能真正反映父母 2 的 DNA 的品质。如果我们将其视为一种突变,那么这种突变就太重要了,在我的例子中,可能 > 整个 DNA 的 5%。所以比遗传选择更具随机性。无论如何感谢您的回答!
标签: c++ genetic-algorithm shuffle heuristics