sat问题求解算法流程图_退火算法-求解TSP问题的前端可视化实现

6fb4070c457563013ba2a35c35844ace.png

原理可参考

MOUSE:助力国赛 | 第7弹 模拟退火算法​zhuanlan.zhihu.com
f38ee792fc327fa787fbc6391cdd41ca.png

看了一下知乎的文章,很少有用js作为语言来进行求解的。考虑到js代码量少,并且可视化容易的有点。所以用js来实现退火算法,求解31城最短路径问题。

简单叙述一下我所认知的退火算法:

由于31城最短路径,没有很好的简单算法可以实现。采用粗暴的穷举法的时间复杂度非常高,是31城的全排列,可以达到n!的复杂度。

因此如果通过有限的计算次数,得到一个可接受的局部最优解,是退火算法解决的问题之一。

核心算法在于根据交换任意两城:

1、如果距离减少,则在该条链路上继续预演

2、根据增加的距离,按照一定概率(负相关于增加的距离,增加的距离越小则概率越大)去进行预演。

tips:浏览器运行,可打开console看到前后的路径长度和链路。上下两个图分别代表随机生成的路径,以及根据随机生成路径优化后的结果。——往往会得到一条较为短的路径

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <canvas id="my_canvas_base"></canvas>
    <canvas id="my_canvas_change"></canvas>
</body>
<script>
    const T0 = 5000.0 //初始温度
    const T_end = 1e-8
    const q = 0.98 //退火系数
    const L = 1000 // 每个温度的迭代次数,链长。
    let city_pos = [[1304,2312],[3639,1315],[4177,2244],[3712,1399],[3488,1535],[3326,1556],[3238,1229],[4196,1004],[4312,790],[4386,570],[3007,1970],[2562,1756],[2788,1491],[2381,1676],[1332,695],[3715,1678],[3918,2179],[4061,2370],[3780,2212],[3676,2578],[4029,2838],[4263,2931],[3429,1908],[3507,2367],[3394,2643],[3439,3201],[2935,3240],[3140,3550],[2545,2357],[2778,2826],[2370,2975]] //初始31市坐标点
    //初始温度越高,退火系数越低,链长越长。————> 得出全局最优解的概率越大,所需时间越多

    function distance(city1,city2){//计算两城距离
        let xPow = Math.pow(city1[0]-city2[0],2)
        let yPow = Math.pow(city1[1]-city2[1],2)
        return Math.sqrt(xPow+yPow)
    }

    function path_len(){//计算31城依次相连的距离
        let path = 0;
        for(let i=0;i<city_pos.length-1;i++){
            let dis = distance(city_pos[i],city_pos[i+1])
            path += dis
        }
        path += distance(city_pos[0],city_pos[city_pos.length-1])
        return path
    }

    function create_new(){//随机交换两城顺序
        let tag1 = ~~(Math.random()*city_pos.length)
        let tag2 = ~~(Math.random()*city_pos.length)
        while(tag1===tag2){
            tag2 = ~~(Math.random()*city_pos.length)
        }
        let temp = city_pos[tag2]
        city_pos[tag2] = city_pos[tag1]
        city_pos[tag1] = temp
    }

    function main(){//主函数
        let count = 0 //记录降温次数
        let t = T0 //初始温度
        let tag // 0-1的随机数 用来决定是否接收新解
        let canvasBase = new CanvasBase({name:"my_canvas_base"}) //初始化canvas
        let canvasChange = new CanvasBase({name:"my_canvas_change"}) //初始化canvas
        city_pos = city_pos.sort(()=>Math.random()-0.5)
        let startResult = [city_pos,path_len()]
        canvasBase.drawLine()
        console.log("初始路径长度为:",startResult[1],",路径为:",city_pos)
        // 算法核心就只有下面几行,Math.exp(-df/t) <= r,代表退火接收概率和相差距离
        while(t > T_end){
            for(let i=0;i<L;i++){
                let list = [...city_pos]
                let f1 = path_len()
                create_new()
                let f2 = path_len()
                let df = f2-f1
                if(df >= 0){
                    let r = Math.random()
                    if(Math.exp(-df/t) <= r){
                        city_pos = list
                    }
                }
            }
            t *= q; //降温
            count++;
        }
        canvasChange.drawLine()
        console.log("退火算法过后路径长度为:",path_len(),",路径为:",city_pos)
    }

    class CanvasBase {//canvas绘制初始链路和最终链路
        constructor(props){
            this.props = props;
            this.canvas = document.getElementById(props.name);
            this.canvas.width = 1000;
            this.canvas.height = 1000;
            if (this.canvas.getContext) {
                this.context = this.canvas.getContext('2d');
            }
        }

        drawLine(){
            this.context.lineWidth = 2;
            this.context.strokeStyle = "#F5270B";
            city_pos.forEach((i,index)=>{
                let startIndex = index;
                let endIndex = index < city_pos.length-1 ? (index + 1) : 0
                let start = [this.transformPointToPixelX(city_pos[startIndex]),this.transformPointToPixelY(city_pos[startIndex])]
                let end = [this.transformPointToPixelX(city_pos[endIndex]),this.transformPointToPixelY(city_pos[endIndex])]
                this.context.moveTo(start[0],start[1]);
                this.context.lineTo(end[0],end[1]);
                this.context.stroke();

                //点
                this.context.beginPath();
                this.context.beginPath();
                this.context.arc(start[0],start[1],10,0,360,false);
                this.context.fillStyle="red";//填充颜色,默认是黑色
                this.context.fill();//画实心圆
                this.context.closePath();
            })
        }

        transformPointToPixelX(point){
            return this.transformPointToPixel(point)[0]
        }

        transformPointToPixelY(point){
            return this.transformPointToPixel(point)[1]
        }

        transformPointToPixel(point){
            // 坐标为0-5000 canvas大小为1000
            return [point[0]/5,point[1]/5]
        }

    }

    main()
    // by:知乎 lee
</script>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值