遗传算法初始种子多样性采用C#值编码



我想知道以下内容:如何使用值编码有效地使具有高多样性的染色体初始生成?一种方法是网格初始化,但速度太慢。

到目前为止,我一直在使用来自的Random类。NET用于在值编码中选择随机值,但是,尽管值是均匀分布的,但从这样的染色体计算的适应度函数值不是。这是染色体初始化的代码:

public Chromosome(Random rand) 
{
Alele = new List<double>();
for (int i = 0; i < ChromosomeLength; i++)
{
Alele.Add(rand.NextDouble() * 2000 - 1000);
}
}

因此,我开发了一个函数,根据新的随机制作的染色体(上码)计算适合度,如果适合度与染色体列表中的任何其他染色体相似,则随机制作一条新染色体,计算他的适合度,并重复这个过程,直到他的适合度与列表中的适合度相差不大。

这是这个部分的代码:

private bool CheckSimilarFitnes(List<Chromosome> chromosome, Chromosome newCandidate) 
{
Boolean flag=false;
double fitFromList, fitFromCandidate;
double fitBigger,fitSmaller;
foreach (var listElement in chromosome)
{  
fitFromList = listElement.CalculateChromosomeFitness(listElement.Alele);
fitFromCandidate = newCandidate.CalculateChromosomeFitness(newCandidate.Alele);
fitBigger = fitFromList >= fitFromCandidate ? fitFromList : fitFromCandidate;
fitSmaller =  fitFromList < fitFromCandidate ? fitFromList : fitFromCandidate;
if ((fitFromList / fitFromCandidate) < 1.5) 
return false
}
else return true;
}

但是,我在列表中的染色体越多,添加一条新染色体所需的时间就越长,而且与其他染色体的适应度已经足够不同了。

那么,有没有办法让网格初始化更快,像这样需要几天时间才能生成80条染色体?

这里有一些代码可能会有所帮助(我刚刚写的):GA,用于对10个值进行排序,间距为1.0。它从100个完全随机的等位基因群体开始,这正是你的代码开始的方式。

我给GA求解的目标是以1.0的间隔按递增顺序排列值。它在适应度函数CCD_ 1中通过从1.0计算每对样本的标准偏差来实现这一点。当适应度趋于0.0时,等位基因应该开始按顺序出现。

0代最适者的染色体是完全随机的,其余的染色体也是如此。你可以看到适应度值非常高(即坏):

GEN: fitness   (allele, ...)
0: 375.47460 (583.640, -4.215, -78.418, 164.228, -243.982, -250.237, 354.559, 374.306, 709.859, 115.323) 

随着世代的持续,适应度(1.0的标准偏差)会降低,直到100000世代接近完美:

100: 68.11683 (-154.818, -173.378, -170.846, -193.750, -198.722, -396.502, -464.710, -450.014, -422.194, -407.162)
...
10000:  6.01724 (-269.681, -267.947, -273.282, -281.582, -287.407, -293.622, -302.050, -307.582, -308.198, -308.648)
...
99999:  0.67262 (-294.746, -293.906, -293.114, -292.632, -292.596, -292.911, -292.808, -292.039, -291.112, -290.928)

代码中有趣的部分是适应度函数:

// try to pack the aleles together spaced apart by 1.0
// returns the standard deviation of the samples from 1.0
static float Eval_OrderedDistance(Chromosome c) {
float sum = 0;
int n = c.alele.Length;
for(int i=1; i<n; i++) {
float diff = (c.alele[i] - c.alele[i-1]) - 1.0f; 
sum += diff*diff; // variance from 1.0
}
return (float)Math.Sqrt(sum/n);
}

还有突变。我使用了一个简单的交叉和"完全突变一个等位基因":

Chromosome ChangeOne(Chromosome c) {
Chromosome d = c.Clone();
int i = rand.Next() % d.alele.Length;
d.alele[i] = (float)(rand.NextDouble()*2000-1000);
return d;
}

我用精英主义总是保留一份最好的染色体的精确拷贝。然后通过突变和交叉产生了100个新的染色体。

听起来你真的在计算适合度的方差,这当然会告诉你,你的人群中的适合度都差不多。我发现如何定义你的健身功能非常重要。适应度函数越精细,你就越能区分你的染色体。显然,您的适应度函数为完全不同的染色体返回相似的值,因为您的0代返回68e-19的适应度方差。

你能分享你的健身计算吗?或者你要求GA解决什么问题?我想这可能有助于我们帮助你。

[编辑:添加显式健身共享/小生境]

我重新思考了一下,并更新了我的代码。如果你试图维护独特的染色体,你必须比较它们的内容(正如其他人所提到的)。一种方法是计算它们之间的标准偏差。如果它小于某个阈值,你可以认为它们是一样的。来自分类染色体:

// compute the population standard deviation
public float StdDev(Chromosome other) {
float sum = 0.0f;
for(int i=0; i<alele.Length; i++) {
float diff = other.alele[i] - alele[i];
sum += diff*diff;
}
return (float)Math.Sqrt(sum);
}

我想尼辛会给你想要的。它比较群体中的所有染色体以确定它们的相似性,并为每个染色体分配一个"小生境"值。然后,使用一种名为"显式适合度共享"的技术,染色体因属于某个生态位而受到"惩罚"。适合度值除以每个生态位中染色体的数量。因此,如果你有三个利基组A(A,A,A),而不是那个利基被选择的可能性的3倍,它被视为一个单一的实体。

我将我的样本与Explicit Fitness Sharing进行了比较。在STDDEV最大值为500且Niching关闭的情况下,大约有18-20个壁龛(因此基本上100个群体中每个项目有5个重复)。开启壁龛后,大约有85个壁龛。这是种群中85%的独特染色体。在我的测试结果中,你可以看到17000代人之后的多样性。

这是niching代码:

// returns: total number of niches in this population
// max_stddev -- any two chromosomes with population stddev less than this max
//               will be grouped together
int ComputeNiches(float max_stddev) {
List<int> niches = new List<int>();
// clear niches
foreach(var c in population) {
c.niche = -1;
}
// calculate niches
for(int i=0; i<population.Count; i++) {
var c = population[i];
if( c.niche != -1) continue; // niche already set
// compute the niche by finding the stddev between the two chromosomes 
c.niche = niches.Count;
int count_in_niche = 1; // includes the curent Chromosome
for(int j=i+1; j<population.Count; j++) {
var d = population[j];
float stddev = c.StdDev(d);
if(stddev < max_stddev) {
d.niche = c.niche; // same niche
++count_in_niche;
}
}
niches.Add(count_in_niche);
}
// penalize Chromosomes by their niche size
foreach(var c in population) {
c.niche_scaled_fitness = c.scaled_fitness / niches[c.niche];
}
return niches.Count;
}

[编辑:安东代码的后期分析和更新]

我知道这可能不是解决家庭作业问题的合适论坛,但由于我在知道这一点之前就已经做了努力,而且我做这件事很有趣,我认为这只会对安东有所帮助。

Genotip.cs、Kromosom.cs、KromMain.cs

这个代码保持了良好的多样性,我在一次运行中就将"原始适应度"降到了47,在您的情况下,这就是均方误差。太接近了!

正如我在评论中所指出的,我想试着帮助你编程,而不仅仅是帮助你做家庭作业。请阅读这些对你作品的分析。

  1. 正如我们所预期的,没有必要从一开始就让人口"更加多样化"。只需生成一些完全随机的Kromosomes。

  2. 你的突变和交叉是极具破坏性的,而你只有其中的几个。我添加了几个新的运算符,它们似乎能更好地解决这个问题。

  3. 你扔掉了最好的解决方案。当我只使用锦标赛选择运行你的代码时,会有一个Kromo比其他所有代码都好99%。在锦标赛选择中,这个最佳值很可能会被遗忘。我添加了一点"精英主义",为下一代保留了这种价值观的副本。

  4. 考虑一下面向对象的技术。将我发给你的重写代码与我的原始代码进行比较。

  5. 不要重复代码。您将采样参数分为两个不同的类。

  6. 保持代码整洁。代码中有几个未使用的部分。尤其是在向SO提交问题时,请尝试缩小范围,删除未使用的代码,并进行一些清理。

  7. 评论你的代码!我对这部作品评价很高。我知道这是塞尔维亚语,但即使是一些评论也会帮助别人理解你在做什么和你打算做什么

  8. 总的来说,很好地实现了一些更复杂的东西,比如锦标赛选择

  9. 首选双[]数组而不是List。开销更少。此外,您的一些List临时变量甚至都不需要。您的结构

    List-temp=new List();对于(…){temp.add(值);}for(每个温度值){sum+=值}平均值=总和/温度计数

可以很容易地写成:

sum = 0
for(...) {
sum += value;
}
average = sum / count;
  1. 在一些地方,您忘记初始化循环变量,这可能很容易增加您的问题。这样的事情会导致严重的问题,并且它与一起出现在您的适应度代码中

    二重拟合=0;for(每条染色体){//你应该初始化适合这里内部循环for(每个等位基因){fit+=。。。;}fit/=计数;}

祝编程好运!

这里的基本问题是,大多数随机生成的染色体都具有相似的适应度,对吧?这很好;这个想法并不是让你最初的染色体有非常不同的适合性;这是因为染色体本身是不同的,据推测它们是不同的。事实上,由于您还没有运行算法,您应该预计第一代的大多数初始适应度接近于零。

以下是您的代码如此缓慢的原因。假设第一个候选人很糟糕,基本上是零适应度。如果第二个必须有1.5倍的不同,那就意味着它必须是1.5倍的好,因为它不会真的变得更糟。然后下一个必须比这个好1.5倍,以此类推,直到80。因此,您真正要做的是通过生成完全随机的染色体并将其与现有染色体进行比较,来搜索越来越好的染色体。我敢打赌,如果你记录了进展,你会发现找到后续的候选者需要越来越多的时间,因为真正好的染色体很难找到。但是找到更好的染色体是遗传算法的目的基本上,你所做的是手动优化一些染色体,嗯,实际上是优化它们。

如果你想确保你的染色体是多样的,比较它们的含量,不要比较它们的适合度。比较适合度是算法的工作。

我要快速处理这个问题,但Isaac说得很对。你需要让GA完成它的工作。你有一代人(染色体,不管怎样),他们的健康状况都很好(或者他们都是一样的)。

你选择一些好的突变(自己)和交叉(彼此)。你可能会用前10%来产生另一个完整的种群,然后把后90%扔掉。也许你总是把最顶尖的人留在身边(精英主义)。

你在这方面迭代一段时间,直到你的GA停止改进,因为每个人都非常相似。你的人口最终几乎没有多样性。

可能对你有帮助的是1)让你的突变更有效,2)找到一种更好的方法来选择要突变的个体。在我的评论中,我推荐了游戏程序员的AI技术。这是一本很棒的书。很容易阅读。

要列出这本书的几个标题,你要找的是:

选择轮盘选择(在stackoverlow上)(在维基百科上)和随机通用采样等技术,它们控制如何选择您的个人。我一直喜欢轮盘选择。您设置了个人被选中的概率。这不仅仅是简单的白噪声随机采样。

我在GA之外用这个从罗马字母表中随机选择了4个字母。我为每个字母指定了一个从0.0到1.0的值。每次用户(孩子)正确选择字母时,我都会将该值降低0.1。这将增加其他字母被选中的可能性。如果在10次之后,用户选择了正确的字母,则值将为0.0,并且该字母(几乎)不可能再次出现。

适合度缩放使用秩缩放、西格玛缩放和玻尔兹曼缩放(pdf on ftp!!)等技术,可以修改原始适合度值以得出调整后的适合度值。其中一些是动态的,比如玻尔兹曼标度,它允许你设置一个随时间变化的"压力"或"温度"。增加的"压力"意味着选择了更健康的个人。压力降低意味着可以选择种群中的任何个体。

我是这样想的:你在多维空间中寻找解决方案。你达到了一个"顶峰",然后一路攀升。保持健康的压力很大。你紧贴着局部最大值。现在你的健康状况不会改变。你的突变并没有让你走出巅峰。所以你开始减少压力,只是,哦,随机选择项目。你的体能水平开始下降,这在一段时间内是可以的。然后你开始再次增加压力,并感到惊讶!你跳过了局部极大值,找到了一个可爱的新局部极大值。再次增加压力!

小生境(我从未使用过,但似乎是将相似的个体组合在一起的一种方式)。假设你有两个非常好的人,但他们截然不同。他们不断被选中。它们一直在轻微变异,并没有好转多少。现在,你有一半的人群是A的小变体,一半的群体是B的小变体。这似乎是一种说法,嘿,整个A组的平均适合度是多少?B怎么办?还有你拥有的其他利基市场。然后根据每个利基市场的平均适合度进行选择。选择你的利基市场,然后从这个利基市场中随机选择一个人。也许我会开始使用这个。我喜欢它!

希望你能找到一些有用的东西!

如果您的应用程序需要true随机数,我建议您访问random.org。他们有一个免费的HTTP API,以及几乎所有语言的客户端。

随机性来自大气噪声,在许多方面,它比计算机程序中通常使用的伪随机数算法要好。

(我与Random.org无关,尽管我确实贡献了PHP客户端)。

我认为你的问题在于你的适应度函数和你如何选择候选者,而不是随机值。您的过滤感觉过于严格,甚至可能无法接受足够的元素。

样品

  • 值:随机浮动0-10000
  • 适应度函数平方根(n)
  • 适合度的期望分布-与距离至少为1成线性

有了这个健身功能,你会很快获得大部分1宽的"位置"(因为你最多有100个位置),所以下一个位置都需要更长的时间。在某个时候,会有几个很小的范围,大多数结果都会被拒绝,更糟糕的是,在你得到大约50个数字后,下一个很有可能根本无法匹配。

最新更新