寻词遗传算法中的偶发错误

Occasional error in word finding genetic algorithm

我(几乎)成功地创建了一个遗传算法来查找给定的字符串,但是由于某种原因,我在 运行 程序时半经常地遇到错误,否则程序运行良好。

 Cannot read property 'dna' of undefined at Population.populate

我似乎只有少数几次无法弄清楚是什么原因导致了这个问题。

<!DOCTYPE html>
<html>
<head>
 <title>Word Search</title>
 <link href='https://fonts.googleapis.com/css?family=Ubuntu' rel='stylesheet'>
</head>
<body style ='font-family: Ubuntu, sans-serif'>
 Target: <input type='text' value='Hello World!'  id='target'> <br>
 Population Size: <input type='number' id='size' min='0' max='100' step='1' value='10'><br>
 Mutation Rate: <input type='number' id='rate' min='0' max='100' step='1' value='10'>%<br>
 <input type='submit' id='submit' onclick='evolution()'>
 <div  style='border-width: 2px; border-style: dashed; width: 250px'>
  <div style='text-align: center;' id='value'>
   <p id='generation';>Generation | 0</p>
   <div id='pop'>
   </div>
  </div>
 </div>

<script type="text/javascript">



window.onload = function() {
 evolution();
}

function evolution() {

 var population = new Population(
  document.getElementById('target').value, 
  document.getElementById('rate').value/100, 
  document.getElementById('size').value);

 var running = setInterval(function() {
  document.getElementById('submit').disabled = true;
  population.natSelection();
  population.populate();
  population.evaluate();
  population.display();
  if(population.completed) {
   clearInterval(running);
   document.getElementById('submit').disabled = false;
  }
 }, 50);
}

function Population(target, mutationRate, size) {
 this.target = target;
 this.mutationRate = mutationRate;
 this.size = size;
 this.members = [];
 this.genePool = [];
 this.completed = false;
 this.generation = 0;

 for(var i = 0; i < this.size; i++)
  this.members.push(new Genome(this.target, this.mutationRate));

 this.natSelection = function() {
  for(var i = 0; i < this.members.length; i++)
   this.members[i].calcFitness();
  
  this.genePool = [];

  for(var i = 0; i < this.members.length; i++)
   for(var j = 0; j < this.members[i].fitness*10; j++)
    this.genePool.push(this.members[i]);
 }

 this.populate = function() {
  this.generation++;
  this.members = [];
  for(var i = 0; i < this.size; i++) {
   var a = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
   var b = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
   this.members.push(new Genome(this.target, this.mutationRate, a, b));
  }
 }

 this.evaluate = function() {
  for(var i = 0; i < this.members.length; i++) {
   if(this.members[i].dna === this.target)
    this.completed = true;
  }
 }

 this.calcMaxFitness = function() {
  var fittest = this.memebers[0].fitness; 
  for(var i = 1; i < this.members.length; i++)
   if(this.memebers[i].fitness > fittest)
    fittest = this.memebers[i].fitness;
  return fittest;
 }

 this.display = function() {
  document.getElementById('generation').innerHTML = 'Generation | '+this.generation;
  var div = document.getElementById('pop');
  div.innerHTML = '';
  for(var i = 0; i < this.members.length; i++) {
   div.innerHTML += this.members[i].dna+'<br>'
  }
 }
}

function Genome(target, mutationRate, parentA, parentB) {
 this.dna = '';
 this.fitness = 0;
 this.target = target;
 this.mutationRate = mutationRate;

 this.mutate = function() {
  for(var i = 0; i < this.target.length; i++) {
   if(this.dna.charCodeAt(i) != this.target.charCodeAt(i))
    if(Math.random() > this.mutationRate)
     this.dna = this.dna.replaceAt(i, String.fromCharCode(Math.floor(Math.random()*94+32)));
  }
 }

 if(!parentA && !parentB) {
  for(var i = 0; i < this.target.length; i++)
   this.dna += String.fromCharCode(Math.floor(Math.random()*94+32));
 } else {
  var mid = Math.floor(Math.random()*this.target.length); 
  this.dna = parentA.substr(0, mid) + parentB.substr(mid, parentB.length);
  this.mutate();
 }
 
 this.calcFitness = function() {
  this.fitness = 0;
  for(var i = 0; i < this.target.length; i++) {
   if(this.dna.charCodeAt(i) === this.target.charCodeAt(i))
    this.fitness++;
  }
 }

}

Number.prototype.map = function (in_min, in_max, out_min, out_max) {
  return (this - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

String.prototype.replaceAt=function(index, replacement) {
    return this.substr(0, index) + replacement+ this.substr(index + replacement.length);
}

</script>

</body>
</html>

首先,让我们确保我们完全理解错误。

您正在尝试读取 populate 方法中未定义的 属性 dna

换句话说:

// Your populate method
this.populate = function() {
    this.generation++;
    this.members = [];
    for(var i = 0; i < this.size; i++) {
        var a = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
        var b = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
        this.members.push(new Genome(this.target, this.mutationRate, a, b));
    }
}

JavasScript 正在出现

this.genePool[Math.floor(Math.random()*this.genePool.length)]

作为undefined

然后,您要求 JavaScript 阅读 undefineddna 属性。

undefined.dna

现在,让我们弄清楚如何调试错误。

就个人而言,我会记录以下内容:

// Your populate method
this.populate = function() {
    this.generation++;
    this.members = [];

    console.log('SIZE', this.size);

    for(var i = 0; i < this.size; i++) {

        console.log('I', i);
        console.log('this.genePool', this.genePool);
        console.log('this.genePool.length', this.genePool.length);

        var a = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
        var b = this.genePool[Math.floor(Math.random()*this.genePool.length)].dna;
        this.members.push(new Genome(this.target, this.mutationRate, a, b));
    }
}

接下来我们来分析一下日志。

也许我们在应该使用 i < this.size - 1 或其他地方类似的东西(也称为差一错误)时使用了 i < this.size

可能 this.genePool 有时未定义。

如果您仍然遇到问题,请使用一些日志输出更新您的问题。

当所有成员的适应度为零时会发生此错误,然后 natSelection 函数中的循环一直循环到 j < this.members[i].fitness*10 为零,因此没有循环,也没有向 [= 添加任何内容12=].

现在当发生这种情况时,显然 this.genePool[Math.floor(Math.random()*this.genePool.length)] 将是未定义的,当您尝试访问 属性 dna 时,您会收到错误消息。

要解决这个问题,您必须添加一些验证,以便当所有成员的适应度都为零时,您可以对此做一些事情。或者,在 natSelection 函数的末尾,检查 genePool.length,如果它仍然为零,则至少添加一项。