383 lines
8.4 KiB
JavaScript
383 lines
8.4 KiB
JavaScript
/*var seedSlider = document.getElementById('sliderRange')
|
|
seedSlider.onChange = function(){
|
|
return this.value;
|
|
}
|
|
|
|
var citiesSlider = document.getElementById('nRange')
|
|
citiesSlider.onChange = function(){
|
|
return this.value;
|
|
}*/
|
|
|
|
var Seed = 21//seedChange(11);
|
|
var totalCities = 25//citiesChange(9);
|
|
// Sketch One
|
|
var randomSearch = function( p ) { // p could be any variable name
|
|
var cities = [];
|
|
var recordDistance;
|
|
var bestEver;
|
|
var localSeed = Seed;
|
|
var localCities = totalCities;
|
|
p.setup = function() {
|
|
p.createCanvas(400, 400);
|
|
p.setupPoints(Seed, totalCities);
|
|
}
|
|
|
|
p.setupPoints = function(rngSeed, numCities){
|
|
p.randomSeed(rngSeed);
|
|
for (var i = 0; i < numCities; i++) {
|
|
var v = p.createVector(p.random(p.width), p.random(p.height/2));
|
|
cities[i] = v;
|
|
}
|
|
|
|
var d = p.calcDistance(cities);
|
|
recordDistance = d;
|
|
bestEver = cities.slice();
|
|
}
|
|
|
|
|
|
p.draw = function() {
|
|
if(localSeed != Seed){
|
|
p.setupPoints(Seed, localCities);
|
|
localSeed = Seed;
|
|
}
|
|
if(localCities != totalCities){
|
|
setupPoints(localSeed, totalCities);
|
|
localCities = totalCities;
|
|
}
|
|
|
|
p.background(0);
|
|
|
|
p.fill(255);
|
|
p.stroke(255);
|
|
for (var i = 0; i < cities.length; i++) {
|
|
p.ellipse(cities[i].x, cities[i].y, 8, 8);
|
|
}
|
|
|
|
p.stroke(255, 0, 255);
|
|
p.strokeWeight(4);
|
|
p.noFill();
|
|
p.beginShape();
|
|
for (var i = 0; i < cities.length; i++) {
|
|
p.vertex(bestEver[i].x, bestEver[i].y);
|
|
}
|
|
p.endShape();
|
|
|
|
p.stroke(255);
|
|
p.translate(0 , p.height/2);
|
|
p.strokeWeight(1);
|
|
p.noFill();
|
|
p.beginShape();
|
|
for (var i = 0; i < cities.length; i++) {
|
|
p.vertex(cities[i].x, cities[i].y);
|
|
}
|
|
p.endShape();
|
|
|
|
var i = p.floor(p.random(cities.length));
|
|
var j = p.floor(p.random(cities.length));
|
|
p.swap(cities, i, j);
|
|
|
|
var d = p.calcDistance(cities);
|
|
if (d < recordDistance) {
|
|
recordDistance = d;
|
|
bestEver = cities.slice();
|
|
}
|
|
}
|
|
|
|
p.swap = function(a, i, j) {
|
|
var temp = a[i];
|
|
a[i] = a[j];
|
|
a[j] = temp;
|
|
}
|
|
|
|
|
|
p.calcDistance = function(points) {
|
|
var sum = 0;
|
|
for (var i = 0; i < points.length - 1; i++) {
|
|
var d = p.dist(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
|
|
sum += d;
|
|
}
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
var myp5 = new p5(randomSearch, 'c1');
|
|
|
|
// Sketch Two
|
|
var lexicographicOrder = function( q ) {
|
|
var cities = [];
|
|
var order = [];
|
|
var totalPermutations;
|
|
var count = 0;
|
|
var recordDistance;
|
|
var bestEver;
|
|
|
|
q.setup = function() {
|
|
q.randomSeed(Seed);
|
|
q.createCanvas(400, 400);
|
|
for (var i = 0; i < totalCities; i++) {
|
|
var v = q.createVector(q.random(q.width), q.random(q.height / 2));
|
|
cities[i] = v;
|
|
order[i] = i;
|
|
}
|
|
|
|
var d = q.calcDistance(cities, order);
|
|
recordDistance = d;
|
|
bestEver = order.slice();
|
|
}
|
|
|
|
q.draw = function() {
|
|
q.background(0);
|
|
|
|
q.fill(255);
|
|
for (var i = 0; i < cities.length; i++) {
|
|
q.ellipse(cities[i].x, cities[i].y, 8, 8);
|
|
}
|
|
|
|
q.stroke(255, 0, 255);
|
|
q.strokeWeight(4);
|
|
q.noFill();
|
|
q.beginShape();
|
|
for (var i = 0; i < order.length; i++) {
|
|
var n = bestEver[i];
|
|
q.vertex(cities[n].x, cities[n].y);
|
|
}
|
|
q.endShape();
|
|
|
|
|
|
q.translate(0, q.height / 2);
|
|
q.stroke(255);
|
|
q.strokeWeight(1);
|
|
q.noFill();
|
|
q.beginShape();
|
|
for (var i = 0; i < order.length; i++) {
|
|
var n = order[i];
|
|
q.vertex(cities[n].x, cities[n].y);
|
|
}
|
|
q.endShape();
|
|
|
|
var d = q.calcDistance(cities, order);
|
|
if (d < recordDistance) {
|
|
recordDistance = d;
|
|
bestEver = order.slice();
|
|
}
|
|
|
|
q.nextOrder();
|
|
}
|
|
|
|
q.swap = function(a, i, j) {
|
|
var temp = a[i];
|
|
a[i] = a[j];
|
|
a[j] = temp;
|
|
}
|
|
|
|
|
|
q.calcDistance = function(points, order) {
|
|
var sum = 0;
|
|
for (var i = 0; i < order.length - 1; i++) {
|
|
var cityAIndex = order[i];
|
|
var cityA = points[cityAIndex];
|
|
var cityBIndex = order[i + 1];
|
|
var cityB = points[cityBIndex];
|
|
var d = q.dist(cityA.x, cityA.y, cityB.x, cityB.y);
|
|
sum += d;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
// This is my lexical order algorithm
|
|
|
|
q.nextOrder = function() {
|
|
|
|
// STEP 1 of the algorithm
|
|
// https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering
|
|
var largestI = -1;
|
|
for (var i = 0; i < order.length - 1; i++) {
|
|
if (order[i] < order[i + 1]) {
|
|
largestI = i;
|
|
}
|
|
}
|
|
if (largestI == -1) {
|
|
q.noLoop();
|
|
}
|
|
|
|
// STEP 2
|
|
var largestJ = -1;
|
|
for (var j = 0; j < order.length; j++) {
|
|
if (order[largestI] < order[j]) {
|
|
largestJ = j;
|
|
}
|
|
}
|
|
|
|
// STEP 3
|
|
q.swap(order, largestI, largestJ);
|
|
|
|
// STEP 4: reverse from largestI + 1 to the end
|
|
var endArray = order.splice(largestI + 1);
|
|
endArray.reverse();
|
|
order = order.concat(endArray);
|
|
}
|
|
|
|
};
|
|
var myp5 = new p5(lexicographicOrder, 'c2');
|
|
|
|
var geneticSearch = function( p ) {
|
|
var cities = [];
|
|
var popSize = 500;
|
|
var population = [];
|
|
var fitness = [];
|
|
var recordDistance = Infinity;
|
|
var bestEver;
|
|
var currentBest;
|
|
var statusP;
|
|
|
|
p.setup = function(){
|
|
p.randomSeed(Seed);
|
|
p.createCanvas(400, 400);
|
|
var order = [];
|
|
for (var i = 0; i < totalCities; i++) {
|
|
var v = p.createVector(p.random(p.width), p.random(p.height / 2));
|
|
cities[i] = v;
|
|
order[i] = i;
|
|
}
|
|
|
|
for (var i = 0; i < popSize; i++) {
|
|
population[i] = p.shuffle(order);
|
|
}
|
|
statusP = p.createP('').style('font-size', '32pt');
|
|
}
|
|
|
|
p.draw = function() {
|
|
p.background(0);
|
|
p.stroke(255);
|
|
p.fill(255);
|
|
for (var i = 0; i < cities.length; i++) {
|
|
p.ellipse(cities[i].x, cities[i].y, 8, 8);
|
|
}
|
|
|
|
p.calculateFitness();
|
|
p.normalizeFitness();
|
|
p.nextGeneration();
|
|
|
|
p.noFill();
|
|
p.strokeWeight(4);
|
|
p.beginShape();
|
|
for (var i = 0; i < bestEver.length; i++) {
|
|
var n = bestEver[i];
|
|
p.stroke(255, 0, 255);
|
|
p.vertex(cities[n].x, cities[n].y);
|
|
//p.stroke(255);
|
|
//p.ellipse(cities[n].x, cities[n].y, 8, 8);
|
|
}
|
|
p.endShape();
|
|
|
|
p.translate(0, p.height / 2);
|
|
p.stroke(255);
|
|
p.strokeWeight(1);
|
|
p.noFill();
|
|
p.beginShape();
|
|
for (var i = 0; i < currentBest.length; i++) {
|
|
var n = currentBest[i];
|
|
p.vertex(cities[n].x, cities[n].y);
|
|
//p.ellipse(cities[n].x, cities[n].y, 8, 8);
|
|
}
|
|
p.endShape();
|
|
}
|
|
|
|
p.swap = function(a, i, j) {
|
|
var temp = a[i];
|
|
a[i] = a[j];
|
|
a[j] = temp;
|
|
}
|
|
|
|
p.calcDistance = function(points, order) {
|
|
var sum = 0;
|
|
for (var i = 0; i < order.length - 1; i++) {
|
|
var cityAIndex = order[i];
|
|
var cityA = points[cityAIndex];
|
|
var cityBIndex = order[i + 1];
|
|
var cityB = points[cityBIndex];
|
|
var d = p.dist(cityA.x, cityA.y, cityB.x, cityB.y);
|
|
sum += d;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
p.calculateFitness = function() {
|
|
var currentRecord = Infinity;
|
|
for (var i = 0; i < population.length; i++) {
|
|
var d = p.calcDistance(cities, population[i]);
|
|
if (d < recordDistance) {
|
|
recordDistance = d;
|
|
bestEver = population[i];
|
|
}
|
|
if (d < currentRecord) {
|
|
currentRecord = d;
|
|
currentBest = population[i];
|
|
}
|
|
|
|
fitness[i] = 1 / (p.pow(d, 8) + 1);
|
|
}
|
|
}
|
|
|
|
p.normalizeFitness = function() {
|
|
var sum = 0;
|
|
for (var i = 0; i < fitness.length; i++) {
|
|
sum += fitness[i];
|
|
}
|
|
for (var i = 0; i < fitness.length; i++) {
|
|
fitness[i] = fitness[i] / sum;;
|
|
}
|
|
}
|
|
|
|
p.nextGeneration = function() {
|
|
var newPopulation = [];
|
|
for (var i = 0; i < population.length; i++) {
|
|
var orderA = p.pickOne(population, fitness);
|
|
var orderB = p.pickOne(population, fitness);
|
|
var order = p.crossOver(orderA, orderB);
|
|
p.mutate(order, 0.01);
|
|
newPopulation[i] = order;
|
|
}
|
|
population = newPopulation;
|
|
|
|
}
|
|
|
|
p.pickOne = function(list, prob) {
|
|
var index = 0;
|
|
var r = p.random(1);
|
|
|
|
while (r > 0) {
|
|
r = r - prob[index];
|
|
index++;
|
|
}
|
|
index--;
|
|
return list[index].slice();
|
|
}
|
|
|
|
p.crossOver = function(orderA, orderB) {
|
|
var start = p.floor(p.random(orderA.length));
|
|
var end = p.floor(p.random(start + 1, orderA.length));
|
|
var neworder = orderA.slice(start, end);
|
|
// var left = totalCities - neworder.length;
|
|
for (var i = 0; i < orderB.length; i++) {
|
|
var city = orderB[i];
|
|
if (!neworder.includes(city)) {
|
|
neworder.push(city);
|
|
}
|
|
}
|
|
return neworder;
|
|
}
|
|
|
|
|
|
p.mutate = function(order, mutationRate) {
|
|
for (var i = 0; i < totalCities; i++) {
|
|
if (p.random(1) < mutationRate) {
|
|
var indexA = p.floor(p.random(order.length));
|
|
var indexB = (indexA + 1) % totalCities;
|
|
p.swap(order, indexA, indexB);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var myp5 = new p5(geneticSearch, 'c3');
|