我(几乎)成功地创建了一个遗传算法来查找给定的字符串,但由于某种原因,我在运行程序时半频繁地出现错误,否则它可以完美运行。
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>
当所有成员的适应度为零时,就会发生此错误,然后natSelection
函数中的循环将循环直到j < this.members[i].fitness*10
为零,因此没有循环,也没有向genePool
添加任何内容。
现在,当发生这种情况时,显然this.genePool[Math.floor(Math.random()*this.genePool.length)]
将是未定义的,当您尝试访问属性dna
时,您会得到错误。
要解决此问题,您必须添加一些验证,以便当所有成员的适应度为零时,您可以对其进行处理。或者,在natSelection
函数结束时,检查genePool.length
,如果它仍然为零,则至少添加一个项目。
首先,让我们确保我们完全理解错误。
您正在尝试读取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读取undefined
的dna
属性。
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
时使用了i < this.size - 1
,或者在其他地方类似的东西(又名逐个错误)。
也许this.genePool
有时是不确定的。
如果仍有问题,请使用一些日志输出更新您的问题。