开发一种将四个笛卡尔坐标转换为方坐标的算法

时间:2015-08-19 21:31:13

标签: algorithm math computer-science

我正在开发一个项目,其中四个随机放置的机器人各自具有唯一的笛卡尔坐标。我需要找到一种方法将这些坐标转换为由程序用户定义的边长的正方形坐标。

例如,假设我有四个坐标(5,13),(8,17),(13,2)和(6,24)代表四个机器人的坐标。我需要找到一个方形坐标,这样四个机器人最接近这些坐标。

提前致谢。

2 个答案:

答案 0 :(得分:1)

据我了解你的问题,你正在寻找四个点的centroid,这个点与所有点的距离相等 - 因而是最小的。它计算为每个坐标的平均值:

然而,正方形的边长与位置无关。

<强>更新

如果您还想最小化方角到机器人位置的距离,您可以执行以下操作:

  1. 如上所述计算质心 c 并将正方形放在那里。

  2. 想象一个圆心,中心位于 c ,方形边长的直径。

  3. 对于每个机器人位置,计算与机器人距离最短的圆上的点,并将其用作方形的一角。

答案 1 :(得分:0)

原来的海报似乎没有回来在这里分享他的解决方案,所以我会发布我正在处理的内容。

找到四个机器人的中心点然后围绕这个点绘制正方形确实是一个好的开始方式,但它不一定能给出最佳结果。对于问题中给出的示例,中心点是(8,14),总距离是22.688(假设平方大小为10)。

square in center position with vectors

当您从正方形的角落向最近的机器人绘制矢量时,此矢量会向您显示正方形应移动的方向,以减少从该角落到其最近的机器人的距离。如果计算这四个向量的方向之和(通过在向上添加之前将向量更改为大小1),则在结果方向上移动方形将减少总距离。

the four vectors and the resulting direction

我害怕冒险进入微分方程领域,所以我设计了一个简单的算法,它重复计算进入的方向,并以不断递减的步长移动方块,直到达到一定的精度。
对于问题中的示例,它找到的最佳位置是(10,18),总距离是21.814,这比中心位置改进了0.874(假设方形大小为10)。

the optimal position

按“运行代码段”以查看随机生成的位置的算法。散射的绿点是在搜索广场的最佳位置时考虑的中心点。

function positionSquare(points, size) {

    var center = {x: 0, y:0};
    for (var i in points) {
        center.x += points[i].x / points.length;
        center.y += points[i].y / points.length;
    }

    paintSquare(canvas, square(center), 1, "#D0D0D0");
    order(points);
    textOutput("<P>center position: " + center.x.toFixed(3) + "," + center.y.toFixed(3) + "<BR>total distance: " + distance(center, points).toFixed(3) + "</P>");

    for (var step = 1; step > 0.0001; step /= 2)
    {
        var point = center;
        var shortest, dist = distance(center, points);

        do
        {
            center = point;
            shortest = dist;
            var dir = direction();
            paintDot(canvas, center.x, center.y, 1, "green");
            point.x = center.x + Math.cos(dir) * step;
            point.y = center.y + Math.sin(dir) * step;
            dist = distance(point, points);
        }
        while (dist < shortest)
    }
    textOutput("<P>optimal position: " + center.x.toFixed(3) + "," + center.y.toFixed(3) + "<BR>total distance: " + distance(point, points).toFixed(3) + "</P>");
    return square(center);

    function order(points) {
        var clone = [], best = 0;
        for (var i = 0; i < 2; i++) {
            clone[i] = points.slice();
            for (var j in clone[i]) clone[i][j].n = j;

            if (i) {
                clone[i].sort(function(a, b) {return b.y - a.y});
                if (clone[i][0].x > clone[i][1].x) swap(clone[i], 0, 1);
                if (clone[i][2].x < clone[i][3].x) swap(clone[i], 2, 3);
            } else {
                clone[i].sort(function(a, b) {return a.x - b.x});
                swap(clone[i], 1, 3);
                if (clone[i][0].y < clone[i][3].y) swap(clone[i], 0, 3);
                if (clone[i][1].y < clone[i][2].y) swap(clone[i], 1, 2);
            }
        }
        if (distance(center, clone[0]) > distance(center, clone[1])) best = 1;
        for (var i in points) points[i] = {x: clone[best][i].x, y: clone[best][i].y};

        function swap(a, i, j) {
            var temp = a[i]; a[i] = a[j]; a[j] = temp;
        }
    }

    function direction() {
        var d, dx = 0, dy = 0, corners = square(center);
        for (var i in points) {
            d = Math.atan2(points[i].y - corners[i].y, points[i].x - corners[i].x);
            dx += Math.cos(d);
            dy += Math.sin(d);
        }
        return Math.atan2(dy, dx);
    }

    function distance(center, points) {
        var d = 0, corners = square(center);
        for (var i in points) {
            var dx = points[i].x - corners[i].x;
            var dy = points[i].y - corners[i].y;
            d += Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
        }
        return d;
    }

    function square(center) {
        return [{x: center.x - size / 2, y: center.y + size / 2},
                {x: center.x + size / 2, y: center.y + size / 2},
                {x: center.x + size / 2, y: center.y - size / 2},
                {x: center.x - size / 2, y: center.y - size / 2}];
    }
}

// PREPARE CANVAS
var canvas = document.getElementById("canvas");
canvas.width = 200; canvas.height = 200;
canvas = canvas.getContext("2d");

// GENERATE TEST DATA AND RUN FUNCTION
var points = [{x:5, y:13}, {x:8, y:17}, {x:13, y:2}, {x:6, y:24}];
for (var i = 0; i < 4; i++) {
    points[i].x = 1 + 23 * Math.random(); points[i].y = 1 + 23 * Math.random();
}
for (var i in points) textOutput("point: " + points[i].x.toFixed(3) + "," + points[i].y.toFixed(3) + "<BR>");
var size = 10;
var square = positionSquare(points, size);

// SHOW RESULT ON CANVAS
for (var i in points) {
    paintDot(canvas, points[i].x, points[i].y, 5, "red");
    paintLine(canvas, points[i].x, points[i].y, square[i].x, square[i].y, 1, "blue");
}
paintSquare(canvas, square, 1, "green");

function paintDot(canvas, x, y, size, color) {
canvas.beginPath();
canvas.arc(8 * x, 200 - 8 * y, size, 0, 6.2831853);
canvas.closePath();
canvas.fillStyle = color;
canvas.fill();
}
function paintLine(canvas, x1, y1, x2, y2, width, color) {
canvas.beginPath();
canvas.moveTo(8 * x1, 200 - 8 * y1);
canvas.lineTo(8 * x2, 200 - 8 * y2);
canvas.strokeStyle = color;
canvas.stroke();
}
function paintSquare(canvas, square, width, color) {
    canvas.rect(8 * square[0].x , 200 - 8 * square[0].y, 8 * size, 8 * size);
canvas.strokeStyle = color;
canvas.stroke();
}

// TEXT OUTPUT
function textOutput(t) {
    var output = document.getElementById("output");
    output.innerHTML += t;
}
<BODY STYLE="margin: 0; border: 0; padding: 0;">
<CANVAS ID="canvas" STYLE="width: 200px; height: 200px; float: left; background-color: #F8F8F8;"></CANVAS>
<DIV ID="output" STYLE="width: 400px; height: 200px; float: left; margin-left: 10px;"></DIV>
</BODY>

进一步的改进:我还没有考虑当角落和机器人在同一个位置时会发生什么,但总体位置不是最佳的。由于从角落到机器人的方向是不确定的,所以它应该暂时从等式中取出。