p5js是一个javascript库,用来在浏览器上画图。具体关于p5js的使用方法请下载以前我在群文件里上传的p5js.pptx来复习一下,在sketch.js 里编写代码,在index.html里查看结果。sketch.js里必须包含两个函数,setup函数是初始化函数,只在网页打开时执行一次,draw函数是一个死循环,只要网页不关闭draw函数里的内容被反复执行。具体例子如下:
1 function setup() { 2 3 } 4 5 function draw() { 6 7 }
在本次实验中所有的代码都写在setup里面,draw里面空着即可。随机搜索算法是基于暴力法的简化算法,假设有四座城市ABCD,要想找到遍历这四座城市的最短路径,暴力法的思路是列举所有可能的访问路线,例如ABCD,ACBD,ADCB等共有4的阶乘个访问路线,暴力法把所有可能的路线都计算一次距离,再从中找出距离最短的那条。
为了节约时间,随机搜索算法并不像暴力法那样计算所有可能的访问路线,而是随机生成n个访问路线(n由你自己指定),从这随机生成的n条路线中挑出最短的那条作为结果返回,本次实验用的正是随机搜索算法来解决旅行商问题。首先,创建一个宽400像素,高300像素的画布,画布的背景设置为黑色。
1 function setup(){ 2 3 createCanvas(400, 300); 4 background(0); 5 6 } 7 8 function draw(){ 9 10 }
在sketch.js的开头要先定义四个变量,cities变量是一个空列表,存放各个城市的横纵座标,totalCities是城市的个数,recordDistance存放目前最短随机路径的总长度,bestEver存放当前找到的最短随机路径。
var cities = []; var totalCities = 4; var recordDistance; var bestEver;
下面这段代码是随机生成一个包含totalCities个城市的随机路径数组cities,random(width)和random(height)表示在画布上随机产生一个坐标点,cities数组的每个元素都代表一个城市的坐标点,cities的下标代表城市访问的先后顺序。
1 for (var i = 0; i < totalCities; i++) { 2 var v = createVector(random(width), random(height)); 3 cities[i] = v; 4 }
calcDistance这个函数接受一个代表访问路径(包含各个城市座标点)的数组,然后计算出这个访问路径的总长度。
function calcDistance(points) { var sum = 0; for (var i = 0; i < points.length - 1; i++) { var d = dist(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y); sum += d; } return sum; }
下面这段代码就利用了上面的calcDistance函数来计算出当前随机路径数组cities的总长度,将这个长度存在recordDistance整型变量(目前最短路径长度)里面,同时利用slice()函数把cities数组拷贝到存放当前最佳路径的bestEver数组里面。
var d = calcDistance(cities); recordDistance = d; bestEver = cities.slice();
在随机搜索算法里如何生成随机路径是一个问题,我们采用的是在当前路径里随机挑选两个城市(PS:即随机生成两个cities数组的下标),然后交换这两个城市在cities里面的位置即可。比如原来的路径是ABCD, 随机生成两个下标0,1,交换后得到的新路径是BACD。生成随机两个城市下标的代码如下:
var i = floor(random(cities.length)); var j = floor(random(cities.length));
给出两个下标位置,交换它们在路径数组的对应元素的swap函数如下,a是路径数组,i,j是待交换元素的下标:
function swap(a, i, j) { var temp = a[i]; a[i] = a[j]; a[j] = temp; }
利用calcDistance来计算cities路径数组中存放的当前路径长度,如果当前路径长度d小于目前找到的最短路径长度recordDistance,则把当前路径长度赋值给recordDistance变量, 把当前路径cities拷贝赋值给存放当前最短路径的bestEver数组。
var d = calcDistance(cities); if (d < recordDistance) { recordDistance = d; bestEver = cities.slice(); }
随机搜索可以指定搜索路径的条数,在下面的代码里我们指定了搜索条数为20条,从这20条里面找到最短的那条路径。
for (let k = 0;k < 20; k++){ var i = floor(random(cities.length)); var j = floor(random(cities.length)); swap(cities, i, j); var d = calcDistance(cities); if (d < recordDistance) { recordDistance = d; bestEver = cities.slice(); } }
最后我们利用p5js的绘图功能将最短路径在浏览器里面画出来(具体的绘图方法见QQ群文件里的p5js.pptx文件)
stroke(255, 0, 255); strokeWeight(4); noFill(); beginShape(); for (var i = 0; i < cities.length; i++) { vertex(bestEver[i].x, bestEver[i].y); } endShape();
var cities = []; var totalCities = 4; var recordDistance; var bestEver; function setup() { createCanvas(400, 300); background(0); for (var i = 0; i < totalCities; i++) { var v = createVector(random(width), random(height)); cities[i] = v; } var d = calcDistance(cities); recordDistance = d; bestEver = cities.slice(); for (let k = 0;k < 20; k++){ var i = floor(random(cities.length)); var j = floor(random(cities.length)); swap(cities, i, j); var d = calcDistance(cities); if (d < recordDistance) { recordDistance = d; bestEver = cities.slice(); } } stroke(255, 0, 255); strokeWeight(4); noFill(); beginShape(); for (var i = 0; i < cities.length; i++) { vertex(bestEver[i].x, bestEver[i].y); } endShape(); } function draw() { } function swap(a, i, j) { var temp = a[i]; a[i] = a[j]; a[j] = temp; } function calcDistance(points) { var sum = 0; for (var i = 0; i < points.length - 1; i++) { var d = dist(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y); sum += d; } return sum; }
下面是效果截屏: