2010-10-03 2 views
27

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

Программа принимает список [r1, r2, ... rn] как входной сигнал и выводит центры окружностей.

  1. Я прошу «маленький», потому что «минимальный» радиус преобразует его в гораздо более сложной проблемой (минимальная версия уже оказалась NP трудно/полная - см сноску ближе к концу вопроса). Нам не нужен минимум. Если форма, сделанная кругами, кажется довольно круговой, это достаточно хорошо.
  2. Вы можете предположить, что Rmax/Rmin < 20, если это помогает.
  3. Низкий приоритет - программа должна иметь возможность обрабатывать более 2000 кругов. В начале, даже 100-200 кругов должно быть хорошо.
  4. Возможно, вы догадались, что круги не должны быть упакованы плотно или даже касаться друг друга.

Цель состоит в том, чтобы придумать визуально приятное расположение заданных кругов, которое может вписаться в большой круг и не оставлять слишком много пустого пространства. (например, круги в color blindness test picture). alt text

Вы можете использовать код Python ниже в качестве отправной точки (вам нужно будет NumPy и Matplotlib для этого кода - "Суда APT-получить установку Numpy Matplotlib" на Linux) ...

import pylab 
from matplotlib.patches import Circle 
from random import gauss, randint 
from colorsys import hsv_to_rgb 

def plotCircles(circles): 
    # input is list of circles 
    # each circle is a tuple of the form (x, y, r) 
    ax = pylab.figure() 
    bx = pylab.gca() 
    rs = [x[2] for x in circles] 
    maxr = max(rs) 
    minr = min(rs) 
    hue = lambda inc: pow(float(inc - minr)/(1.02*(maxr - minr)), 3) 

    for circle in circles: 
     circ = Circle((circle[0], circle[1]), circle[2]) 
     color = hsv_to_rgb(hue(circle[2]), 1, 1) 
     circ.set_color(color) 
     circ.set_edgecolor(color) 
     bx.add_patch(circ) 
    pylab.axis('scaled') 
    pylab.show() 

def positionCircles(rn): 
    # You need rewrite this function 
    # As of now, this is a dummy function 
    # which positions the circles randomly 
    maxr = int(max(rn)/2) 
    numc = len(rn) 
    scale = int(pow(numc, 0.5)) 
    maxr = scale*maxr 

    circles = [(randint(-maxr, maxr), randint(-maxr, maxr), r) 
       for r in rn] 
    return circles 

if __name__ == '__main__': 
    minrad, maxrad = (3, 5) 
    numCircles = 400 

    rn = [((maxrad-minrad)*gauss(0,1) + minrad) for x in range(numCircles)] 

    circles = positionCircles(rn) 
    plotCircles(circles) 

Дополнительная информация: Алгоритм упаковки окружности, обычно упоминаемый в результатах поиска Google, не применим к этой проблеме.

Заявление о проблеме другого «алгоритма упаковки окружности»: Таким образом, для комплекса K (графики в этом контексте называются симплициальными комплексами или сложными краткими) и соответствующими граничными условиями, вычисляют радиусы соответствующего круга упаковка для K ....

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

Другая проблема действительно есть интересное наблюдение (независимо от этой проблемы):

круг упаковки Теорема - Каждый круг упаковки имеет соответствующий плоский граф (это легко/очевидная часть), и каждый плоский график имеет соответствующую упаковку круга (не столь очевидная часть). Графики и упаковки являются дуальными друг от друга и уникальны.

У нас нет планарного графика или тангенциального отношения, чтобы начать с нашей проблемы.

Эта статья - Роберт Дж Фаулер, Майк Патерсон, Стивен Л. Танимото: Оптимальная упаковка и Покрытие на плоскости является NP-Complete - доказывает, что минимальная версия этой проблемы является NP-полной. Однако документ недоступен в Интернете (по крайней мере, нелегко).

+0

Не головоломка ... это проблема, с которой я боролся на работе.У меня есть базовое решение, но я ищу лучшего. – Rajan

+2

Я снова просмотрел свою проблему после вашего комментария, но не смог увидеть часть головоломки. Пожалуйста, дайте мне знать, если проблема будет задана способом, который предполагает его несерьезный вопрос типа головоломки. я бы перефразировал его. Или, вы имеете в виду, что это слишком сложная проблема для переполнения стека? – Rajan

+0

@ Грег: Я думаю, что это очень субъективно. То, что вы называете головоломкой, некоторые люди называют ее программированием, в то время как другие называют ее математической. Я прошел через такие аргументы по одному из моих вопросов. – Pupil

ответ

14

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

alt text

Похоже, что они пересекаются в центре, но они этого не делают. Я украсил функцию размещения вложенным циклом, который проверяет каждый круг на каждый другой круг (дважды) и поднимает AssertionError, если есть пересечение.

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

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

def base_points(radial_res, angular_res): 
    circle_angle = 2 * math.pi 
    r = 0 
    while 1: 
     theta = 0 
     while theta <= circle_angle: 
      yield (r * math.cos(theta), r * math.sin(theta)) 
      r_ = math.sqrt(r) if r > 1 else 1 
      theta += angular_res/r_ 
     r += radial_res 

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

Я положу некоторые идеи, которые у меня есть, и сделайте это mo`bettah. Это может послужить хорошим первым шагом для идеи, основанной на физике, потому что вы начинаете без дублирования. Конечно, он может быть достаточно плотным, чтобы у вас не было много места.

Кроме того, я никогда не играл с numpy или matplotlib, поэтому пишу только ванильный питон. Там может быть что-то, что заставит его работать намного быстрее, мне придется посмотреть.

+0

Это выглядит потрясающе. Это напоминает мне гиперболический диск эшера из-за концентрации больших кругов вблизи центра. Я предполагаю, что это может быть изменено, если это необходимо, без чрезмерного влияния на algo (я все еще пытаюсь понять ваш алгоритм, поэтому я могу ошибаться). Кроме того, я использую numpy и matplotlib только для построения изображения. Они не обрабатывают мою заглушку. – Rajan

+0

Мне любопытно ... Сколько кругов в вашей картине? И сколько времени потребовалось для создания этого выхода? – Rajan

+0

@ Раджан, на картинке 400 кругов. Это занимает несколько минут на моей машине, но моя машина работает медленно, поэтому вам, вероятно, не придется ждать слишком долго. Нехорошо это делать, скажем, в режиме реального времени для веб-службы, к сожалению. Чтобы запустить его, просто скопируйте и вставьте код из ссылки через функцию заглушки, которую вы предоставили, и вам хорошо идти. – aaronasterling

18

Не решение, просто идея мозгового штурма: IIRC - один из распространенных способов получить приблизительные решения для TSP - это начать с произвольной конфигурации, а затем применить локальные операции (например, «обменивать» два ребра на пути), чтобы попробовать и получить более короткие и короткие пути. (Wikipedia link)

Я думаю, что что-то подобное можно было бы здесь:

  1. Старт с произвольным центром позиции
  2. «Оптимизация» эти позиции, так что нет пересекающихся кругов и поэтому круги как можно ближе возможно, увеличивая расстояние между перекрывающимися кругами и уменьшая расстояние между другими кругами, пока они не будут плотно упакованы. Это может быть сделано путем минимизации энергии, или может быть более эффективное жадное решение. alt text
  3. Применить итерационный оператор улучшения центра Позитоны
  4. Goto 2, перерыв после максимального числа итераций или последняя итерация не нашла какие-либо улучшений

Интересный вопрос: какой от «оператора итерационного улучшения», который вы могли бы использовать на шаге 3? Можно предположить, что позиции на этом этапе локально оптимальны, но их можно улучшить, переставив большую часть кругов. Мое предложение состояло в том, чтобы произвольно выбрать линию через круги. Затем возьмите все круги «влево» от линии и поместите их на какую-то ось, перпендикулярную этой линии: alt text Возможно, вы попробуете несколько строк и выберите тот, который приведет к самому компактному решению.

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

Другие возможные операции я мог думать:

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

alt text

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

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

+0

Отражение строк - хорошая идея для глобальных изменений без скремблирования локальных оптимизаций, но может, конечно, создавать перекрытия. –

+0

Некоторые превосходные идеи здесь nikie ... Я постараюсь объединить их (если возможно) с подходом rafe и посмотреть, что это приводит. – Rajan

+0

Красивый ответ. – VividD

6

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

+0

Thats довольно крутая идея. – Rajan

+2

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

+1

Этот ответ является своего рода «минимизацией энергии», который можно использовать в качестве шага 2 ответа ники. –

1

Похоже, проблема Круг упаковки, вот некоторая информация:

+2

Алгоритм упаковки окружения, обычно упоминаемый в результатах поиска Google, не применим к этой проблеме. Этот алгоритм начинается с планарного графа и получает соответствующую «упаковку круга». Это приложение Тернстона «Теорема упаковки кругов». Однако у нас нет планарного графика. Если бы у нас был плоский граф, было бы полиномиальным временным алгоритмом найти упаковку круга. В своей общей форме эта проблема является NP полной. – Rajan

+0

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

-1

Вы можете попробовать использовать 2d библиотеку физики, и просто вылейте 2-мерные круги в более крупный круглый контейнер - и дождитесь их установки на место.

+0

Это будет интенсивно вычислить, и результаты, скорее всего, будут очень субоптимальными. – Mene

1

http://en.wikipedia.org/wiki/Apollonian_gasket

Это кажется несколько соответствует тому, что вы пытаетесь сделать, и может обеспечить некоторые потенциальные ограничения для вас.

+0

Я не уверен, как использовать прокладки, но они прекрасны. Спасибо, что опубликовали его. – Rajan

+0

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

Смежные вопросы