Genetic algorithm 为什么我的遗传算法很糟糕(为什么它不收敛)?

Genetic algorithm 为什么我的遗传算法很糟糕(为什么它不收敛)?,genetic-algorithm,Genetic Algorithm,我用遗传算法写了一个快速实验。它只需要一个方格,并试图改变它们的颜色,使它们都变成黄色。它失败得很惨,我似乎不明白为什么。我已经包含了一个指向JSFIDLE的链接,该链接演示了工作代码,以及完整的代码副本 随机闪烁的方块应变为黄色 注意,下面的javascript在一些地方依赖于jquery // A bit of code that draws several squares in a canvas // and then attempts to use a genetic algori

我用遗传算法写了一个快速实验。它只需要一个方格,并试图改变它们的颜色,使它们都变成黄色。它失败得很惨,我似乎不明白为什么。我已经包含了一个指向JSFIDLE的链接,该链接演示了工作代码,以及完整的代码副本


随机闪烁的方块应变为黄色
注意,下面的javascript在一些地方依赖于jquery

// A bit of code that draws several squares in a canvas
// and then attempts to use a genetic algorithm to slowly
// make those squares all yellow.

// Knobs that can be tweaked
var mutation_rate = 0.1; // how often should we mutate something
var crossover_rate = 0.6; // how often should we crossover two parents
var fitness_influence = 1; // affects the fitness's influence over mutation
var elitism = 1; // how many of the parent's generation to carry over
var num_offspring = 20; // how many spawn's per generation
var use_rank_selection = true; // false == roulette_selection

// Global variables for easy tracking
var children = []; // current generation
var best_spawn = null; // keeps track of our best so far
var best_fitness = null; // keeps track of our best so far
var generation = 0; // global generation counter
var clear_color = 'rgb(0,0,0)';

// used for output
var $gen_span = $('#generation');
var $best_fit = $('#best_fitness');
var $avg_fit = $('#avg_fitness');
var $input_canvas = $('#input_canvas');
var input_ctx = $input_canvas[0].getContext('2d');
var $output_canvas = $('#output_canvas');
var output_ctx = $output_canvas[0].getContext('2d');

// A spawn represents a genome - a collection of colored
// squares.
var Spawn = function(nodes) {
    var _fitness = null; // a cache of our fitness
    this.nodes = nodes; // the squares that make up our image

    this.fitness = function() {
        // fitness is simply a function of how close to yellow we are.
        // This is defined through euclidian distance. Smaller fitnesses
        // are better.

        if (_fitness === null) {
            _fitness = 0;
            for (var i = 0; i < nodes.length; i++) {
                _fitness += Math.pow(-nodes[i].color[0], 2) + 
                            Math.pow(255 - nodes[i].color[1], 2) +
                            Math.pow(255 - nodes[i].color[2], 2);
            }
            _fitness /= 255*255*3*nodes.length; // divide by the worst possible distance
        }

        return _fitness;
    };

    this.mutate = function() {
        // reset our cached fitness to unknown
        _fitness = null;

        var health = this.fitness() * fitness_influence;
        var width = $output_canvas[0].width;
        var height = $output_canvas[0].height;

        for (var i = 0; i < nodes.length; i++) {
            // Sometimes (most times) we don't mutate
            if (Math.random() > mutation_rate) {
                continue;
            }

            // Mutate the colors. 
            for (var j = 0; j < 3; j++) {
                // colors can move by up to 32 in either direction
                nodes[i].color[j] += 64 * (.5 - Math.random()) * health;
                // make sure that our colors stay between 0 and 255
                nodes[i].color[j] = Math.max(0, Math.min(255, nodes[i].color[j]));
            }
        }
    };

    this.draw = function(ctx) {
        // This draw function is a little overly generic in that it supports
        // arbitrary polygons.
        ctx.save();
        ctx.fillStyle = clear_color;
        ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        for (var i = 0; i < nodes.length; i++) {
            ctx.fillStyle = 'rgba(' + Math.floor(nodes[i].color[0]) + ',' + Math.floor(nodes[i].color[1]) + ',' + Math.floor(nodes[i].color[2]) + ',' + nodes[i].color[3] + ')';
            ctx.beginPath();
            ctx.moveTo(nodes[i].points[0][0], nodes[i].points[0][1]);
            for (var j = 1; j < nodes[i].points.length; j++) {
                ctx.lineTo(nodes[i].points[j][0], nodes[i].points[j][1]);
            }
            ctx.fill();
            ctx.closePath();
        }
        ctx.restore();
    };
};

Spawn.from_parents = function(parents) {
    // Given two parents, mix them together to get another spawn
    var nodes = [];
    for (var i = 0; i < parents[0].nodes.length; i++) {
        if (Math.random() > 0.5) {
            nodes.push($.extend({}, parents[0].nodes[i]));
        }
        else {
            nodes.push($.extend({}, parents[1].nodes[i]));
        }
    }
    var s = new Spawn(nodes);
    s.mutate();

    return s;
};

Spawn.random = function(width, height) {
    // Return a complete random spawn.
    var nodes = [];
    for (var i = 0; i < width * height; i += 10) {
        var n = {
            color: [Math.random() * 256, Math.random() * 256, Math.random() * 256, 1],
            points: [
                [i % width, Math.floor(i / width) * 10],
                [(i % width) + 10, Math.floor(i / width) * 10],
                [(i % width) + 10, Math.floor(i / width + 1) * 10],
                [i % width, Math.floor(i / width + 1) * 10],
                ]
        };
        nodes.push(n);
    }
    return new Spawn(nodes);
};

var select_parents = function(gene_pool) {
    if (use_rank_selection) {
        return rank_selection(gene_pool);
    }
    return roulette_selection(gene_pool);
};

var roulette_selection = function(gene_pool) {
    var mother = null;
    var father = null;
    gene_pool = gene_pool.slice(0);
    var sum_fitness = 0;
    var i = 0;
    for (i = 0; i < gene_pool.length; i++) {
        sum_fitness += gene_pool[i].fitness();
    }
    var choose = Math.floor(Math.random() * sum_fitness);
    for (i = 0; i < gene_pool.length; i++) {
        if (choose <= gene_pool[i].fitness()) {
            mother = gene_pool[i];
            break;
        }
        choose -= gene_pool[i].fitness();
    }
    // now remove the mother and repeat for the father
    sum_fitness -= mother.fitness();
    gene_pool.splice(i, 1);
    choose = Math.floor(Math.random() * sum_fitness);
    for (i = 0; i < gene_pool.length; i++) {
        if (choose <= gene_pool[i].fitness()) {
            father = gene_pool[i];
            break;
        }
        choose -= gene_pool[i].fitness();
    }
    return [mother, father];
};

var rank_selection = function(gene_pool) {
    gene_pool = gene_pool.slice(0);
    gene_pool.sort(function(a, b) {
        return b.fitness() - a.fitness();
    });

    var choose_one = function() {
        var sum_fitness = (gene_pool.length + 1) * (gene_pool.length / 2);
        var choose = Math.floor(Math.random() * sum_fitness);
        for (var i = 0; i < gene_pool.length; i++) {
            // figure out the sume of the records up to this point. if we exceed
            // our chosen spot, we've found our spawn.
            if ((i + 1) * (i / 2) >= choose) {
                return gene_pool.splice(i, 1)[0];
            }
        }

        return gene_pool.pop(); // last element, if for some reason we get here
    };

    var mother = choose_one();
    var father = choose_one();
    return [mother, father];
};

var start = function() {
    // Initialize our first generation
    var width = $output_canvas[0].width;
    var height = $output_canvas[0].height;

    generation = 0;
    children = [];
    for (var j = 0; j < num_offspring; j++) {
        children.push(Spawn.random(width, height));
    }

    // sort by fitness so that our best comes first
    children.sort(function(a, b) {
        return a.fitness() - b.fitness();
    });
    best_spawn = children[0];
    best_fitness = best_spawn.fitness();
    best_spawn.draw(output_ctx);
};

var generate = function(spawn_pool) {
    // generate a new set of offspring
    var offspring = [];
    for (var i = 0; i < num_offspring; i++) {
        var parents = select_parents(spawn_pool);
        // odds of crossover decrease as we get closer
        if (Math.random() * best_fitness < crossover_rate) {
            var s = Spawn.from_parents(parents);
        }
        else {
            // quick hack to copy our mother, with possible mutation
            var s = Spawn.from_parents([parents[0], parents[0]]);
        }
        offspring.push(s);
    }
    // select a number of best from the parent pool (elitism)
    for (var i = 0; i < elitism; i++) {
        offspring.push(spawn_pool[i]);
    }

    // sort our offspring by fitness (this includes the parents from elitism). Fittest first.
    offspring.sort(function(a, b) {
        return a.fitness() - b.fitness();
    });
    // pick off the number that we want
    offspring = offspring.slice(0, num_offspring);
    best_spawn = offspring[0];
    best_fitness = best_spawn.fitness();
    best_spawn.draw(output_ctx);
    generation++;

    return offspring;
};


var average_fitness = function(generation) {
    debugger;
    var a = 0;
    for (var i = 0; i < generation.length; i++) {
        a += generation[i].fitness();
    }
    return a / generation.length;
};

//Draw yellow and then initialize our first generation
input_ctx.fillStyle = 'yellow';
input_ctx.fillRect(0, 0, input_ctx.canvas.width, input_ctx.canvas.height);
start();

// Our loop function. Use setTimeout to prevent things from freezing
var gen = function() {
    children = generate(children);
    $gen_span.text('Generation: ' + generation);
    $best_fit.text('Best Fitness: ' + best_fitness);
    $avg_fit.text('Avg. Fitness: ' + average_fitness(children));
    if (generation % 100 === 0) {
        console.log('Generation', generation);
        console.log('Fitness', best_fitness);
    }
    setTimeout(gen, 1);
};
gen();​
//在画布上绘制多个正方形的一段代码
//然后尝试用遗传算法慢慢地
//把那些方块都变成黄色。
//可以调整的旋钮
变异率=0.1;//我们应该多久变异一次
var交叉率=0.6;//我们应该多久让两个父母交叉一次
var适应度_影响=1;//影响适应度对突变的影响
var精英主义=1;//父母这一代有多少人要继承
var num_子代=20;//每代有多少个卵
var use_rank_selection=true;//false==轮盘赌选择
//全局变量,便于跟踪
var children=[];//当代
var best_spawn=null;//跟踪到目前为止我们最好的状态
var best_fitness=null;//跟踪到目前为止我们最好的状态
变量生成=0;//全局生成计数器
var clear_color='rgb(0,0,0)';
//用于输出
var$gen_span=$(“#generation”);
变量$best_fit=$('best_fitness');
变量$avg_fit=$('avg_fitness');
var$input_canvas=$('input_canvas');
var input_ctx=$input_canvas[0]。getContext('2d');
var$output_canvas=$(“#output_canvas”);
var output_ctx=$output_canvas[0]。getContext('2d');
//一个卵代表一个基因组——一个有色人种的集合
//正方形。
var Spawn=函数(节点){
var _fitness=null;//适合度的缓存
this.nodes=nodes;//构成图像的正方形
this.fitness=函数(){
//健康只是我们离黄色有多近的函数。
//这是通过欧几里得距离定义的。较小的配合度
//你的情况更好。
如果(_fitness==null){
_适合度=0;
对于(var i=0;i变异率){
继续;
}
//改变颜色。
对于(var j=0;j<3;j++){
//颜色在任一方向上最多可移动32
节点[i].color[j]+=64*(.5-Math.random())*运行状况;
//确保我们的颜色保持在0到255之间
节点[i].color[j]=Math.max(0,Math.min(255,节点[i].color[j]);
}
}
};
this.draw=函数(ctx){
//这个draw函数有点过于通用,因为它支持
//任意多边形。
ctx.save();
ctx.fillStyle=清晰的颜色;
ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);
对于(var i=0;i0.5){
nodes.push($.extend({},父节点[0].nodes[i]);
}
否则{
nodes.push($.extend({},父[1].nodes[i]);
}
}
var s=新的繁殖(节点);
s、 突变();
返回s;
};
Spawn.random=函数(宽度、高度){
//返回一个完整的随机繁殖。
var节点=[];
对于(变量i=0;i// A bit of code that draws several squares in a canvas
// and then attempts to use a genetic algorithm to slowly
// make those squares all yellow.

// Knobs that can be tweaked
var mutation_rate = 0.1; // how often should we mutate something
var crossover_rate = 0.6; // how often should we crossover two parents
var fitness_influence = 1; // affects the fitness's influence over mutation
var elitism = 1; // how many of the parent's generation to carry over
var num_offspring = 20; // how many spawn's per generation
var use_rank_selection = true; // false == roulette_selection

// Global variables for easy tracking
var children = []; // current generation
var best_spawn = null; // keeps track of our best so far
var best_fitness = null; // keeps track of our best so far
var generation = 0; // global generation counter
var clear_color = 'rgb(0,0,0)';

// used for output
var $gen_span = $('#generation');
var $best_fit = $('#best_fitness');
var $avg_fit = $('#avg_fitness');
var $input_canvas = $('#input_canvas');
var input_ctx = $input_canvas[0].getContext('2d');
var $output_canvas = $('#output_canvas');
var output_ctx = $output_canvas[0].getContext('2d');

// A spawn represents a genome - a collection of colored
// squares.
var Spawn = function(nodes) {
    var _fitness = null; // a cache of our fitness
    this.nodes = nodes; // the squares that make up our image

    this.fitness = function() {
        // fitness is simply a function of how close to yellow we are.
        // This is defined through euclidian distance. Smaller fitnesses
        // are better.

        if (_fitness === null) {
            _fitness = 0;
            for (var i = 0; i < nodes.length; i++) {
                _fitness += Math.pow(-nodes[i].color[0], 2) + 
                            Math.pow(255 - nodes[i].color[1], 2) +
                            Math.pow(255 - nodes[i].color[2], 2);
            }
            _fitness /= 255*255*3*nodes.length; // divide by the worst possible distance
        }

        return _fitness;
    };

    this.mutate = function() {
        // reset our cached fitness to unknown
        _fitness = null;

        var health = this.fitness() * fitness_influence;
        var width = $output_canvas[0].width;
        var height = $output_canvas[0].height;

        for (var i = 0; i < nodes.length; i++) {
            // Sometimes (most times) we don't mutate
            if (Math.random() > mutation_rate) {
                continue;
            }

            // Mutate the colors. 
            for (var j = 0; j < 3; j++) {
                // colors can move by up to 32 in either direction
                nodes[i].color[j] += 64 * (.5 - Math.random()) * health;
                // make sure that our colors stay between 0 and 255
                nodes[i].color[j] = Math.max(0, Math.min(255, nodes[i].color[j]));
            }
        }
    };

    this.draw = function(ctx) {
        // This draw function is a little overly generic in that it supports
        // arbitrary polygons.
        ctx.save();
        ctx.fillStyle = clear_color;
        ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        for (var i = 0; i < nodes.length; i++) {
            ctx.fillStyle = 'rgba(' + Math.floor(nodes[i].color[0]) + ',' + Math.floor(nodes[i].color[1]) + ',' + Math.floor(nodes[i].color[2]) + ',' + nodes[i].color[3] + ')';
            ctx.beginPath();
            ctx.moveTo(nodes[i].points[0][0], nodes[i].points[0][1]);
            for (var j = 1; j < nodes[i].points.length; j++) {
                ctx.lineTo(nodes[i].points[j][0], nodes[i].points[j][1]);
            }
            ctx.fill();
            ctx.closePath();
        }
        ctx.restore();
    };
};

Spawn.from_parents = function(parents) {
    // Given two parents, mix them together to get another spawn
    var nodes = [];
    for (var i = 0; i < parents[0].nodes.length; i++) {
        if (Math.random() > 0.5) {
            nodes.push($.extend({}, parents[0].nodes[i]));
        }
        else {
            nodes.push($.extend({}, parents[1].nodes[i]));
        }
    }
    var s = new Spawn(nodes);
    s.mutate();

    return s;
};

Spawn.random = function(width, height) {
    // Return a complete random spawn.
    var nodes = [];
    for (var i = 0; i < width * height; i += 10) {
        var n = {
            color: [Math.random() * 256, Math.random() * 256, Math.random() * 256, 1],
            points: [
                [i % width, Math.floor(i / width) * 10],
                [(i % width) + 10, Math.floor(i / width) * 10],
                [(i % width) + 10, Math.floor(i / width + 1) * 10],
                [i % width, Math.floor(i / width + 1) * 10],
                ]
        };
        nodes.push(n);
    }
    return new Spawn(nodes);
};

var select_parents = function(gene_pool) {
    if (use_rank_selection) {
        return rank_selection(gene_pool);
    }
    return roulette_selection(gene_pool);
};

var roulette_selection = function(gene_pool) {
    var mother = null;
    var father = null;
    gene_pool = gene_pool.slice(0);
    var sum_fitness = 0;
    var i = 0;
    for (i = 0; i < gene_pool.length; i++) {
        sum_fitness += gene_pool[i].fitness();
    }
    var choose = Math.floor(Math.random() * sum_fitness);
    for (i = 0; i < gene_pool.length; i++) {
        if (choose <= gene_pool[i].fitness()) {
            mother = gene_pool[i];
            break;
        }
        choose -= gene_pool[i].fitness();
    }
    // now remove the mother and repeat for the father
    sum_fitness -= mother.fitness();
    gene_pool.splice(i, 1);
    choose = Math.floor(Math.random() * sum_fitness);
    for (i = 0; i < gene_pool.length; i++) {
        if (choose <= gene_pool[i].fitness()) {
            father = gene_pool[i];
            break;
        }
        choose -= gene_pool[i].fitness();
    }
    return [mother, father];
};

var rank_selection = function(gene_pool) {
    gene_pool = gene_pool.slice(0);
    gene_pool.sort(function(a, b) {
        return b.fitness() - a.fitness();
    });

    var choose_one = function() {
        var sum_fitness = (gene_pool.length + 1) * (gene_pool.length / 2);
        var choose = Math.floor(Math.random() * sum_fitness);
        for (var i = 0; i < gene_pool.length; i++) {
            // figure out the sume of the records up to this point. if we exceed
            // our chosen spot, we've found our spawn.
            if ((i + 1) * (i / 2) >= choose) {
                return gene_pool.splice(i, 1)[0];
            }
        }

        return gene_pool.pop(); // last element, if for some reason we get here
    };

    var mother = choose_one();
    var father = choose_one();
    return [mother, father];
};

var start = function() {
    // Initialize our first generation
    var width = $output_canvas[0].width;
    var height = $output_canvas[0].height;

    generation = 0;
    children = [];
    for (var j = 0; j < num_offspring; j++) {
        children.push(Spawn.random(width, height));
    }

    // sort by fitness so that our best comes first
    children.sort(function(a, b) {
        return a.fitness() - b.fitness();
    });
    best_spawn = children[0];
    best_fitness = best_spawn.fitness();
    best_spawn.draw(output_ctx);
};

var generate = function(spawn_pool) {
    // generate a new set of offspring
    var offspring = [];
    for (var i = 0; i < num_offspring; i++) {
        var parents = select_parents(spawn_pool);
        // odds of crossover decrease as we get closer
        if (Math.random() * best_fitness < crossover_rate) {
            var s = Spawn.from_parents(parents);
        }
        else {
            // quick hack to copy our mother, with possible mutation
            var s = Spawn.from_parents([parents[0], parents[0]]);
        }
        offspring.push(s);
    }
    // select a number of best from the parent pool (elitism)
    for (var i = 0; i < elitism; i++) {
        offspring.push(spawn_pool[i]);
    }

    // sort our offspring by fitness (this includes the parents from elitism). Fittest first.
    offspring.sort(function(a, b) {
        return a.fitness() - b.fitness();
    });
    // pick off the number that we want
    offspring = offspring.slice(0, num_offspring);
    best_spawn = offspring[0];
    best_fitness = best_spawn.fitness();
    best_spawn.draw(output_ctx);
    generation++;

    return offspring;
};


var average_fitness = function(generation) {
    debugger;
    var a = 0;
    for (var i = 0; i < generation.length; i++) {
        a += generation[i].fitness();
    }
    return a / generation.length;
};

//Draw yellow and then initialize our first generation
input_ctx.fillStyle = 'yellow';
input_ctx.fillRect(0, 0, input_ctx.canvas.width, input_ctx.canvas.height);
start();

// Our loop function. Use setTimeout to prevent things from freezing
var gen = function() {
    children = generate(children);
    $gen_span.text('Generation: ' + generation);
    $best_fit.text('Best Fitness: ' + best_fitness);
    $avg_fit.text('Avg. Fitness: ' + average_fitness(children));
    if (generation % 100 === 0) {
        console.log('Generation', generation);
        console.log('Fitness', best_fitness);
    }
    setTimeout(gen, 1);
};
gen();​
    if (Math.random() > 0.5) {
        nodes.push($.extend({}, parents[0].nodes[i]));
    }
    else {
        nodes.push($.extend({}, parents[1].nodes[i]));
    }