Ограничивающий круг множества кругов - PullRequest
2 голосов
/ 08 августа 2011

Я пытаюсь реализовать следующее в Java.

Учитывая список кругов разных размеров (возможно) и положений, определите большой круг (положение и размер), который точно охватывает все круги.

public class Circle {
    public int x, y, radius;
}

Есть идеи?

Ответы [ 11 ]

3 голосов
/ 25 марта 2013

Задача минибола шариков была изучена в «Наименьший вмещающий шарик шариков: комбинаторная структура и алгоритмы», например, . Одним из результатов этого исследования было то, что, по крайней мере, некоторые алгоритмы для задачи о мини-точках (такие как алгоритм Вельцля ) не могут быть легко обобщены от точек к шарам.

В приведенном выше документе представлен O (n) -алгоритм для вычисления минибола набора шаров ( n - количество входных шаров, то есть окружностей в 2D) , Их реализация на C ++ доступна в библиотеке алгоритмов вычислительной геометрии (CGAL) . (Вам не нужно использовать весь CGAL; просто извлеките требуемый заголовок и исходные файлы.)

Примечание: Я являюсь соавтором вышеуказанного документа и автором пакета CGAL Min_sphere_of_spheres.

1 голос
/ 04 января 2013

У меня есть примерно O (n 4 ) истинное решение, которое я реализую для продукта на JavaScript:

  • Вам нужна функция, чтобы определить, является ли решение верным: если быть точным, функция, которая проверит, все ли круги лежат в предложенном супер-круге. Это довольно тривиально: для каждого круга C i требуется, чтобы расстояние от центра суперкруга до центра C i плюс радиус C i меньше или равно радиусу суперкруга.

  • Затем создайте супер-круг из каждой пары и каждой тройки кругов.

    • Для пары нарисуйте линию от центра C i до центра C j . Удлините линию на каждом конце радиусом соответствующего круга. Середина линии является центром суперкруга, а его радиус равен половине длины линии.

    • Это проблема Аполлония для 3 кругов: http://mathworld.wolfram.com/ApolloniusProblem.html;, отметив, что вам нужно получить правильные знаки, чтобы получить один, который будет включать все три круга.

  • Правильным решением является действительный супер-круг с наименьшим радиусом.

Вот мой код:

'use strict';

/**
 * Epsilon value for floating point equality.
 * @const
 */
var EPSILON = 1E-6;

/**
 * Calculates the minimum bounding circle for a set of circles.
 * O(n^4)
 *
 * @param {Array.<Object.<string, number>>} circles A list of 2+ circles.
 * @return {Object.<string, number>} {cx, cy, radius} of the circle.
 */
function minimumBoundingCircleForCircles(circles) {

    var areAllCirclesInOrOnCircle = function(circle) {
        for (var i = 0; i < circles.length; i++) {
            if (!isCircleInOrOnCircle(circles[i], circle)) return false;
        }
        return true;
    };

    // try every pair and triple
    var best = {radius: 9E9};

    for (var i = 0; i < circles.length; i++) {
        for (var j = i + 1; j < circles.length; j++) {
            var circle = circleFrom2Circles(circles[i], circles[j]);
            if (areAllCirclesInOrOnCircle(circle) &&
                circle.radius < best.radius) {
                best.cx = circle.cx; best.cy = circle.cy;
                best.radius = circle.radius;
            }

            for (var k = j + 1; k < circles.length; k++) {
                var signs = [-1, 1, 1, 1];
                circle = apollonius(circles[i], circles[j], circles[k],
                                    signs);
                if (areAllCirclesInOrOnCircle(circle) &&
                    circle.radius < best.radius) {
                    best.cx = circle.cx; best.cy = circle.cy;
                    best.radius = circle.radius;
                }
            }
        }
    }

    return best;
}


/**
 * Calculates a circle from 2 circles.
 *
 * @param {Object.<string, number>} circle1 The first circle.
 * @param {Object.<string, number>} circle2 The second circle.
 * @return {Object.<string, number>} cx, cy, radius of the circle.
 */
function circleFrom2Circles(circle1, circle2) {

    var angle = Math.atan2(circle1.cy - circle2.cy,
                           circle1.cx - circle2.cx);

    var lineBetweenExtrema = [[circle1.cx + circle1.radius * Math.cos(angle),
                               circle1.cy + circle1.radius * Math.sin(angle)],
                              [circle2.cx - circle2.radius * Math.cos(angle),
                               circle2.cy - circle2.radius * Math.sin(angle)]];

    var center = lineMidpoint(lineBetweenExtrema[0], lineBetweenExtrema[1]);
    return { cx: center[0],
             cy: center[1],
             radius: lineLength(lineBetweenExtrema[0], 
                                lineBetweenExtrema[1]) / 2
           };
}

/**
 * Solve the Problem of Apollonius: a circle tangent to all 3 circles.
 * http://mathworld.wolfram.com/ApolloniusProblem.html
 *
 * @param {Object.<string, number>} circle1 The first circle.
 * @param {Object.<string, number>} circle2 The second circle.
 * @param {Object.<string, number>} circle3 The third circle.
 * @param {Array.<number>} signs The array of signs to use.
 *                               [-1, 1, 1, 1] gives max circle.
 * @return {Object.<string, number>} The tangent circle.
 */
function apollonius(circle1, circle2, circle3, signs) {

    var sqr = function(x) { return x * x };

    var a1 = 2 * (circle1.cx - circle2.cx);
    var a2 = 2 * (circle1.cx - circle3.cx);
    var b1 = 2 * (circle1.cy - circle2.cy);
    var b2 = 2 * (circle1.cy - circle3.cy);
    var c1 = 2 * (signs[0] * circle1.radius + signs[1] * circle2.radius);
    var c2 = 2 * (signs[0] * circle1.radius + signs[2] * circle3.radius);
    var d1 = (sqr(circle1.cx) + sqr(circle1.cy) - sqr(circle1.radius)) -
        (sqr(circle2.cx) + sqr(circle2.cy) - sqr(circle2.radius));
    var d2 = (sqr(circle1.cx) + sqr(circle1.cy) - sqr(circle1.radius)) -
        (sqr(circle3.cx) + sqr(circle3.cy) - sqr(circle3.radius));

    // x = (p+q*r)/s; y = (t+u*r)/s

    var p = b2 * d1 - b1 * d2;
    var q = (- b2 * c1) + (b1 * c2);
    var s = a1 * b2 - b1 * a2;
    var t = - a2 * d1 + a1 * d2;
    var u = a2 * c1 - a1 * c2;

    // you are not expected to understand this.
    // It was generated using Mathematica's Solve function.
    var det = (2 * (-sqr(q) + sqr(s) - sqr(u)));
    var r = (1 / det) * 
        (2 * p * q + 2 * circle1.radius * sqr(s) + 2 * t * u -
         2 * q * s * circle1.cx - 2 * s * u * circle1.cy + signs[3] *
         Math.sqrt(sqr(-2 * p * q - 2 * circle1.radius * sqr(s) - 2 * t * u +
                       2 * q * s * circle1.cx + 2 * s * u * circle1.cy) - 
                   4 * (-sqr(q) + sqr(s) - sqr(u)) * 
                   (-sqr(p) + sqr(circle1.radius) * sqr(s) - sqr(t) +
                    2 * p * s * circle1.cx - sqr(s) * sqr(circle1.cx) + 
                    2 * s * t * circle1.cy - sqr(s) * sqr(circle1.cy))))

    //console.log(r);
    r = Math.abs(r);

    var x = (p + q * r) / s;

    var y = (t + u * r) / s;

    //console.log(x); console.log(y);
    return {cx: x, cy: y, radius: r};
}

/**
 * Is the circle inside/on another circle?
 *
 * @param {Object.<string, number>} innerCircle the inner circle.
 * @param {Object.<string, number>} outerCircle the outer circle.
 * @return {boolean} is the circle inside/on the circle?
 */
function isCircleInOrOnCircle(innerCircle, outerCircle) {
    return ((lineLength([innerCircle.cx, innerCircle.cy],
                        [outerCircle.cx, outerCircle.cy]) +
             innerCircle.radius - EPSILON) < outerCircle.radius);
}


/**
 * Calculates the length of a line.
 * @param {Array.<number>} pt1 The first pt, [x, y].
 * @param {Array.<number>} pt2 The second pt, [x, y].
 * @return {number} The length of the line.
 */
function lineLength(pt1, pt2) {
    return Math.sqrt(Math.pow(pt1[0] - pt2[0], 2) +
                     Math.pow(pt1[1] - pt2[1], 2));
}

/**
 * Calculates the midpoint of a line.
 * @param {Array.<number>} pt1 The first pt, [x, y].
 * @param {Array.<number>} pt2 The second pt, [x, y].
 * @return {Array.<number>} The midpoint of the line, [x, y].
 */
function lineMidpoint(pt1, pt2) {
    return [(pt1[0] + pt2[0]) / 2,
            (pt1[1] + pt2[1]) / 2];
}
0 голосов
/ 09 августа 2011

Я бы попытался найти самую верхнюю западную точку, затем самую южную нижнюю точку, а затем сделать круг с этими точками в качестве диаметра.Чтобы найти эти точки, я бы прошел цикл по центру окружностей и их радиусу.

Таким образом, это заканчивается:

 initiate max object and min object to average center of circle and zero radius
 For every circle object
     calculate topmost western point of the circle
     check if further away than current point
     if further, calculate position of topmost western point required to pass over this new point and set as new min object
     calculate down most eastern point of the circle
     do the same as previous step, only for max object
 make a circle with center equals to middle of min-max line and radius equals to half min-max line length

Если вы тип книжного червя, хорошоВ университетских библиотеках есть: E.Welzl, «Самые маленькие вмещающие диски» (шарики и эллипсоиды), H. Maurer (ред.), «Новые результаты и новые тенденции в информатике», «Лекционные заметки в информатике», том.555, Springer-Verlag, 359–37 (1991)

А если вы хотите прочитать код C ++ и адаптировать его к Java, http://miniball.sourceforge.net/. С кружками, d = 2, конечно.

0 голосов
/ 27 декабря 2011

Статья в Википедии Задача наименьшего круга описывает алгоритм линейного среднего времени для случая, когда размеры начальных кругов равны.Представляется простым обобщить его на начальные круги разного размера, хотя я не уверен, что происходит с анализом сложности.

0 голосов
/ 08 августа 2011

Мой предложенный алгоритм похож на алгоритм Сванте, но с некоторыми отличиями.

Идея состоит в том, чтобы сначала создать круг, который тривиально охватывает все подкруги, а затем сжать его как пузырь, пока он не будет закреплен 1, 2 или 3 окружности.

первое приближение:

окружность с центром 0,0 и радиусом макс. (Расстояние от 0,0 окружности + радиус окружности)

если окружность, определяющая радиус окружности, охватывает все остальные окружности, то это тривиально правильный результат, и его можно вернуть

второе приближение:

уменьшить радиус найденной окружностив первом приближении, сохраняя его касательным к подкругу, к которому он «прикреплен», перемещая центр к закрепленному подкругу, пока он не станет касательным к другому подкругу.

конечный результат:

уменьшите радиус круга снова, сохраняя его касательным к двум подкругам, найденным выше, пока любой другой подкруг не станет коричневымПрилегающий к окружности, или центр круга лежит на линии между центрами двух подкругов, к которым он прикреплен.Это должно быть минимумом, потому что отсюда нет способа уменьшить радиус окружности без «прорыва» одного из подкругов

Часть алгоритма, в которой я не уверен, это «уменьшитьрадиус, пока другой подкруг не станет касательной частью.Очевидно, что бинарный поиск может дать достаточно хорошее приближение за приемлемое время, но я подозреваю, что вы можете свести его к уравнению.

0 голосов
/ 09 августа 2011

Я не думаю, что это проблема упаковки сама по себе.Это больше похоже на выпуклый корпус.Я думаю, что вопрос:

Вам дан набор кругов на плоскости.Найдите центральную точку и радиус наименьшего круга, для которого каждая граничная точка каждого круга лежит внутри или на границе содержащего круга.

Для этого вы можете просто выполнить рекурсивно: найти наименьший круг, содержащийпервые два круга (и центр этого круга лежит на линии, соединяющей два центра, и его радиус также должен быть прост для определения), замените первые два круга новым кругом и повторите.

Правильностьэтого алгоритма принадлежит математике.

0 голосов
/ 08 августа 2011

Это очень сложная проблема, я бы просто обозначил возможные пути к ней, вы должны закончить ее самостоятельно.Я предполагаю, что вы хотите найти минимальный ограничивающий круг .Формализуя вашу проблему - имея xi, yi, ri для i = 1..N, вы ищете точку [x, y] такую, что:

max(distance([x, y], [xi, yi]) + ri)

минимально.Это нетривиальная минимаксная задача.Сначала рассмотрим более простую версию этой задачи Задача наименьшего круга , которая относится только к точкам:

max(distance([x, y], [xi, yi]))

Итак, сначала попытайтесь улучшить алгоритмы, предложенные вВыше ссылка, чтобы решить более сложный случай.Если этот путь непроходим, вам, вероятно, придется пойти на квадратичное программирование .Удачи ...

0 голосов
/ 08 августа 2011

Я думаю, что это можно сделать в три этапа:

  • Первый ограничивающий круг c1 :

    • Центр определен x c1 = ( x max + x min ) / 2 и y c1 = ( y max + y min ) / 2.
    • Для каждого круга рассчитайте расстояние от его центра до центра c1 плюс его радиус (я называю это чрезмерным расстоянием).Максимум этих значений - радиус c1 .Соответствующий круг: a .
  • Второй ограничивающий круг c2 : (На этом шаге вы перемещаете центр c1 в направлении a насколько это возможно.)

    • Для каждого круга, кроме a , определите, сколько вам нужно переместитьцентр c1 в направлении a , так что расстояние от него до этого круга такое же, как и a .Минимум этого определяет центр c2 .Соответствующий круг: b .Превышение расстояния до a и b (оба одинаковы) составляет радиус c2 .
  • Третий ограничительный круг c3 : (На этом шаге вы перемещаете центр c2 в направлении между a и b насколько это возможно.)

    • Определите направление v , в котором вы можете двигаться c2 так, чтобы расстояние до a и b остается неизменным.
    • Для каждого круга, кроме a и b , определите, сколько вам нужно переместить в центр c2 в направлении v , так что расстояние от него до этого круга такое же, как для a и b .Минимум этого определяет центр c3 .Радиус - это расстояние до трех найденных окружностей.

Я считаю, что c3 - это решение ( edit ) хорошее первое приближение.Вы можете получить лучшие решения, многократно отбрасывая первый круг и повторяя третий шаг.Если вы получите набор из трех кругов, которые вы уже видели, это должно быть окончательным решением.

0 голосов
/ 08 августа 2011

Упс, следующее не работает, как отмечено в комментариях:

Начните с решения задачи для 3 кругов.В этом случае описанный круг коснется каждого из трех внутренних кругов, и вы можете найти его центр как пересечение двух гипербол.(Локус точек, у которых есть заданная разница в расстоянии до двух фиксированных точек, является гиперболой).После небольшой алгебры это сводится к квадратному уравнению.

Теперь добавьте больше внутренних кругов по индукции.В начале каждого шага вы знаете наименьший круг, который охватывает все старые круги;это коснется трех конкретных старых «угловых» кругов.Если новый круг находится внутри этого, делать нечего.Если нет, объедините новый круг со всеми тремя способами, чтобы выбрать два из старых угловых кругов, и рассчитайте описанный круг для каждой из этих тройок.Один из них должен включать четвертый, который больше не является угловым кругом.

Продолжайте, пока не добавите все круги.

Это дает алгоритм линейного времени с ограниченными ошибками округления (потому что каждый описанный круг вычисляется заново из исходных входных координат).

0 голосов
/ 08 августа 2011

С двумя кругами это легко.Линия, проходящая через оба центра, достигнет периметра, где окружающий круг соприкоснется с ними.

При наличии большего количества кругов вам потребуется применить FEM (анализ методом конечных элементов - http://en.wikipedia.org/wiki/Finite_element_method) на каждую точку периметра с потенциаломбыть точкой соприкосновения с внешним кругом. Это исключает те сегменты, которые обращены, например, к другим кругам. Вычисления все еще довольно велики, так как вы продолжаете применять к своим точкам разные радиусы, пока не найдете самые маленькие, которые пересекают всеостальные в общей точке - центре вашего окружающего круга.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...