javascript求点的凸集/凸包

最近有一个实际的需求,需要用到求凸集,这次用js实现是为了让自己真正了解此过程,并能检测实现的对不对。

<!--
主要是利用叉积判断方向来决定点是否可选
注意实现的时候去掉了在同一条直线上的点

基本代码借鉴
http://blog.csdn.net/miscclp/article/details/41169837
http://blog.csdn.net/enjoying_science/article/details/47760677
-->
<html>
<head>
 <meta charset = "utf-8" / > 
<title>TSP_demo</title>
</head>
<body>
<div id="outText">
</div>
<canvas id="canvas" height="550px" width="1024px">
</canvas>
<script type="text/javascript">

var canvas = document.getElementById("canvas");
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var context = canvas.getContext("2d");
// 未使用
function initMat(M, N, val) {
  var x = new Array();
  for(var i = 0; i < M; i++) {
    x[i] = new Array();
    for(var j = 0; j < N; j++)
        x[i].push(val);
  }
  return x;
}

function drawPath(x1, y1, x2, y2, color, width) {
    context.beginPath();
    context.fillStyle = color;
    context.strokeStyle = color;
    context.lineWidth = width;
    context.moveTo(x1, y1);
    context.lineTo(x2, y2);
    context.stroke();
}
function drawCities(p) {
    for(var i = 0; i < p.length ; i++) {
        context.beginPath();

        context.fillStyle = "blue";
        context.strokeStyle = "blue";
        context.lineWidth = 1;
        context.font = "normal 16px Arial";

        context.arc(p[i].x, p[i].y, 3, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);
        context.fill();
        context.stroke();
        context.closePath();
        if(p[i].tj==true){
            context.fillStyle = "red";
            context.textAlign = "center";
            context.textBaseline = "middle";
            context.fillText(String(i), p[i].x, p[i].y-8);
        }
    }
}

function output(string){
    var out = document.getElementById("outText");
    out.innerHTML+=string
}
// 可以借助cos a 在0-180之间,单调递减!!!
// 这里用的是叉积,正弦的判断
function multiply(p0,p1,p2){
    return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y)); 
}
function distance_no_sqrt(p1,p2)
{
    //return(sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y))); 
    return((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); 
}
function Graham_scan(pointSet,ch,n){
// 这里会修改pointSet
    var i,j,k=0,top=2;
    var tmp=new Object();
    // 找到一个基点,基本就是保证最下面最左面的点
    for(i=1;i<n;i++){
        if( (pointSet[i].y<pointSet[k].y) || 
            ( (pointSet[i].y==pointSet[k].y) && (pointSet[i].x<pointSet[k].x) ) 
          ){
            k=i;
        }
    }

    tmp=pointSet[0];
    pointSet[0]=pointSet[k];
    pointSet[k]=tmp; 

    use=n;
    for (i=1;i<use-1;i++){
        k=i;
        for (j=i+1;j<use;j++){
            var direct=multiply(pointSet[0],pointSet[k],pointSet[j]);
            if(direct>0){
                k=j;
            }else if(direct==0){
                // k j 同方向
                var dis=distance_no_sqrt(pointSet[0],pointSet[j])-distance_no_sqrt(pointSet[0],pointSet[k]);
                use--; // 也就是不要了
                if(dis>0){
                    // 保留j
                    // 把 k 就不要了
                    pointSet[k]=pointSet[j];
                    pointSet[j]=pointSet[use];
                    j--;
                }else{
                    tmp=pointSet[use];
                    pointSet[use]=pointSet[j];
                    pointSet[j]=tmp;
                }
            }
        }
        tmp=pointSet[i];
        pointSet[i]=pointSet[k];
        pointSet[k]=tmp;
    }

    ch.push(pointSet[0]);
    ch.push(pointSet[1]);
    ch.push(pointSet[2]);
    for (i=3;i<use;i++){
        while ( !(multiply(pointSet[i],ch[top-1],ch[top]) < 0 ) ){
            top--;
            ch.pop();
        }
        top++;
        ch.push(pointSet[i]);
    }
}
// 求凸集的方法
function Graham_example(){
    var n=100; // 用100个例子
    var p=new Array(n);
    //var res=new Array(n);
    var res=new Array();
    // 随机初始化定点
    for(var i = 0; i < n; i++) {
        p[i]=new Object();
        p[i].x = (Math.random() * 32767) % 680 + 20;
        p[i].y = (Math.random() * 32767) % 320 + 20;
        p[i].tj=false

    }
    drawCities(p)
    var t1 = new Date()
    t1.setTime(t1.getTime());
    Graham_scan(p,res,n)
    t2 = new Date();
    var ms = t2.getTime() - t1.getTime();
    output("<br/>用时(毫秒):<br/>" + ms)

    var m=res.length;
    res[0].tj=true;
    for(var i=1;i<m;i++){
        res[i].tj=true;
        drawPath(res[i-1].x, res[i-1].y, res[i].x, res[i].y, "black", 1);
    }
    drawPath(res[0].x, res[0].y, res[m-1].x, res[m-1].y, "black", 1);
    // canvas 的坐标和我们的不一样
    drawCities(res)
}

Graham_example();

</script>
</body>
</html>

运行效果
注意,canvas的原点是在左上角,因此基准点位置看起来与直觉不一样。
这里写图片描述
上图中蓝色的点是随机生成的,上面标红色数字的是凸集的顶点。
另外这次用chrome调试,感觉不比firefox差。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值