我目前正在研究NP完全问题,并为此目的实施了个人遗传算法。结果超出了我的预期。通过精心设计的健身功能和仔细调整的几个人口/突变,我猜GA在某些情况下可以成为一个出色的工具。
无论如何,我现在正在寻找能够产生最佳混洗输出的元启发式(GA,模拟退火......)。
通过shuffle,我的意思是,在这种情况下,一个有限集的无偏(à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 的第一个成为第七个是否有意义。
然后,我的问题是:
在您看来,在遗传算法的上下文中,是否可以使用交叉函数对一组的正常性进行改组?如果不是,你能否建议一种比蛮力,爬山技术或遗传算法更有效的元启发式方法?
谢谢。
答案 0 :(得分:2)
您正在寻找的是基于订单的遗传算法。你有许多基于订单的交叉和变异操作符,可以解决这类问题。最简单的交叉算子的工作原理如下:
你可以在下图中看到我书中的一个例子(对不起,但描述是葡萄牙语 - 请与上面的列表相关):
您可以在网上搜索基于订单的运营商,或者如果您愿意,请查看我在My Geneetic Algorithm book的书中的数据。您感兴趣的数字来自第10章(您可以使用Google翻译来理解传说)。
您不必介意本书使用序号 - 如果您没有重复,所有解释的概念对您的问题都有效。
我希望它有所帮助。