如何在 C++ 中编写高效的遗传算法

Posted

技术标签:

【中文标题】如何在 C++ 中编写高效的遗传算法【英文标题】:How to write efficient Genetic Algorithms in C++ 【发布时间】:2011-10-25 23:50:09 【问题描述】:

我正在尝试为canonical genetic algorithm 编写一个 C++ 程序,其中有一群长度为 N 的个体(染色体),其中每个元素都是 O 或 1。

我已经开始使用 STL 向量 编写程序,但在我更深入地研究它之前,我想询问您对如何以最有效的方式编写函数和数据结构的意见.

内存占用不是问题,我有大约 100 个个体,每个个体都是 64 个字符长的 0-s 和 1-s 字符串。另一方面,性能非常重要,因为大约有数千代,每代都有数千次操作。

这是我目前的实现(只是最重要的功能和数据结构):

typedef vector<int> chromosome;
typedef vector<chromosome> population;

population popul;
float eval[number];

void cross_chromosomes( const chromosome &parent_a, const chromosome &parent_b, chromosome &child_a, chromosome &child_b )

    int crossing_point = crossing_point_gen( gen );

    child_a.reserve( length );
    child_a.insert( child_a.end(), parent_a.cbegin(), parent_a.cbegin() + crossing_point );
    child_a.insert( child_a.end(), parent_b.cbegin() + crossing_point, parent_b.cend() );

    child_b.reserve( length );
    child_b.insert( child_b.end(), parent_b.cbegin(), parent_b.cbegin() + crossing_point );
    child_b.insert( child_b.end(), parent_a.cbegin() + crossing_point, parent_a.cend() );


void calculate_eval()

    for( int i = 0; i < number; i++ )
    
        eval[i] = evaluate_chromosome( popul[i] );
    

你认为这是实现这个算法的一种有效方式吗?我最初使用向量作为染色体,但我已经阅读了这个问题:C++ Vector vs Array (Time),我将我的代码更新为vector&lt;int&gt; .

您认为我应该对我的代码进行其他优化以使其更高效吗?交叉代码是否像现在这样有效?

【问题讨论】:

我不确定这是解决这个问题的正确方法。如果您运行该程序会更好,发现它确实太慢了(根据您自己对“快速”的定义)。否则,何必担心速度呢? 我同意@chetan。 Sutter 和 Alexandrescu 的 C++ Coding Standards 规则 8:“不要过早优化。” 我一直对遗传算法持批评态度,或者我只是没有欣赏。如果你知道什么是合适的,那么只需构建一个由它组成的有机体并完成它。 :P 【参考方案1】:

如何利用进化算法中令人尴尬的并行性。以及尝试将您的解决方案移植到 GPU 上怎么样。就像 chetan 和 David 所说的那样,使用现有框架可能比编写自己的 fast 框架花费的时间要少得多。

OpenBeagle 和 EO 是众所周知的良好支持且非常高效的框架。

请注意,在进化算法中真正需要快速的唯一部分是评估其他一切通常不耗时。您还可以查找DEAP,它可以非常轻松地在超级计算机上分发评估(以及更多)(我们通过更改串行算法中的一行代码在 Colosse 超级计算机的 1024 个内核上进行了测试)。

【讨论】:

您正在将一个简单的锻炼问题转化为更复杂的问题,但您基本上是对的:如果您需要性能,那么请使用正确的工具。 考虑到并行性,openGA 是另一个灵活的 C++ 库,开销低,可以调整所涉及的 CPU 内核数。【参考方案2】:

对于您尝试对向量执行的操作,交叉代码似乎处于最高效率。根据我在遗传算法方面的经验,适应度函数和选择算子是最耗时的。由于您将对总体样本使用交叉和变异,因此您不必过多担心交叉算子的效率。专注于为您的数据定义良好的表示和最佳的适应度函数实现。

【讨论】:

针对广泛研究的需求的最有趣的可能解决方案。在 google 中查找“C++ 中的高效通用算法”【参考方案3】:

如果您已经知道数组的大小,那么使用数组将比使用向量快得多。向量比数组需要更多的开销。

【讨论】:

这是不正确的。如果您事先知道数组的大小,则可以 vector::reserve 该空间或将大小传递给向量构造函数。假设您为发布版本正确配置了 Visual Studio 的检查迭代器,那么在数组上使用向量应该不会有任何性能损失。 啊,我不知道用向量保留空间。谢谢你澄清这一点。只是出于好奇,如果我不使用 Visual Studio,需要设置哪些编译器设置? 我不知道。我不认为微软的调试迭代器和 g++ 中的安全 scl 有任何类似之处,但我几乎没有使用微软以外的编译器的经验。我相信性能扼杀(但有助于调试)检查的迭代器只出现在 VS >= 2005 的版本中。我提到了这个 Visual Studio 问题,因为这个问题被标记了;它与使用 vector::reserve 提高分配效率没有具体关系。 在这篇文章(MSDN 之外的第一个 Google 结果)中,它说对于 VS2010,默认情况下它不会为发布版本启用。 preshing.com/20110807/the-cost-of-_secure_scl 我认为它的意思正好相反。只要您使用 .reserve(),您几乎可以使用向量而不是数组而不会注意到任何区别【参考方案4】:

你可以稍微压缩你的内存:使用 4 个字节来表示一个二进制值是相当浪费的。我建议将 chromosome 替换为 std::array&lt;unsigned char, 64&gt;std::bitset&lt;64&gt; 以进行更多打包。

尽管您声称不担心内存,但压缩内存会导致 CPU 中更好的缓存使用率,这可能会加速程序。

但是,如果您真的想优化您的程序,唯一明智的方法是使用成熟的分析器。过去我已经连续使用过 callgrind,但还有其他的,具体取决于您的开发环境。

【讨论】:

这里有一个权衡:如果要从每个位中提取信息,打包内存可能会导致一些访问损失。还有对齐注意事项(但可能由您的 std::bitset 实现处理)。最好的办法是同时尝试和配置文件,但不要猜测(但我相信你都知道)。

以上是关于如何在 C++ 中编写高效的遗传算法的主要内容,如果未能解决你的问题,请参考以下文章

遗传算法的中心思想

PSO算法、蚁群算法和遗传算法matlab求解函数最值问题,程序怎么编写?

MATLAB遗传算法

《常用算法之智能计算 》:遗传算法

确定该遗传算法的基因型; GA 初始化神经网络以实现快速高效的学习

C++算法之遗传算法及实现