Ответ 1
Проблема, с которой вы работаете в
Вы столкнулись с проблемой Купонный сборщик, потому что вы используете технику Отклонение выборки.
Вы также делаете серьезные предположения о том, что такое "случайное заполнение". Ваш алгоритм оставит большие промежутки между кругами; это то, что вы подразумеваете под "случайным"? Тем не менее, это вполне достоверное определение, и я одобряю его.
Решение
Чтобы адаптировать текущее "случайное заполнение", чтобы избежать проблемы отбора купон-выборки, просто разделите пространство, которое вы заполняете в сетку. Например, если ваши круги имеют радиус 1, разделите большой круг на сетку с 1/sqrt (2) -страничными блоками. Когда становится "невозможно" заполнить сетку, игнорируйте этот gridbox при выборе новых точек. Проблема решена!
Возможные опасности
Вы должны быть осторожны, как вы это кодируете! Возможные опасности:
- Если вы делаете что-то вроде
if (random point in invalid grid){ generateAnotherPoint() }
, вы игнорируете идею преимущества/основной идеи этой оптимизации. - Если вы сделаете что-то вроде
pickARandomValidGridbox()
, вы слегка уменьшите вероятность создания кругов рядом с краем большего круга (хотя это может быть хорошо, если вы делаете это для проекта графического искусства, а не для научного или математический проект); однако если вы сделаете размер сетки 1/sqrt (2) раз радиус круга, вы не столкнетесь с этой проблемой, потому что невозможно будет нарисовать блоки на краю большого круга, и, таким образом, вы можете игнорировать все сетчатые ячейки на краю.
Реализация
Таким образом, обобщение вашего метода для предотвращения проблемы с купон-сборщиком заключается в следующем:
Inputs: large circle coordinates/radius(R), small circle radius(r)
Output: set of coordinates of all the small circles
Algorithm:
divide your LargeCircle into a grid of r/sqrt(2)
ValidBoxes = {set of all gridboxes that lie entirely within LargeCircle}
SmallCircles = {empty set}
until ValidBoxes is empty:
pick a random gridbox Box from ValidBoxes
pick a random point inside Box to be center of small circle C
check neighboring gridboxes for other circles which may overlap*
if there is no overlap:
add C to SmallCircles
remove the box from ValidBoxes # possible because grid is small
else if there is an overlap:
increase the Box.failcount
if Box.failcount > MAX_PERGRIDBOX_FAIL_COUNT:
remove the box from ValidBoxes
return SmallCircles
(*) Этот шаг также является важной оптимизацией, о которой я могу только предположить, что у вас ее еще нет. Без него ваша функция doesThisCircleOverlapAnother (...) невероятно неэффективна при O(N)
за запрос, что сделает заполнение кругов почти невозможным для больших отношений R>>r
.
Это точное обобщение вашего алгоритма, чтобы избежать медленности, сохраняя при этом элегантную случайность.
Обобщение на большие нерегулярные функции
edit:. Поскольку вы прокомментировали, что это для игры, и вас интересуют неправильные формы, вы можете обобщить это следующим образом. Для любой маленькой неправильной формы приложите ее к кругу, представляющему, как далеко вы хотите, чтобы это было от вещей. Ваша сетка может быть размером с наименьшую характеристику местности. Более крупные функции могут охватывать непрерывные блоки 1x2 или 2x2 или 3x2 или 3x3 и т.д. Обратите внимание, что многие игры с функциями, которые охватывают большие расстояния (горы) и небольшие расстояния (факелы), часто требуют сетки, которые рекурсивно разделены (т.е. Некоторые блоки разбиваются на дополнительные субблоки 2x2 или 2x2x2), генерируя древовидную структуру. Эта структура с обширной бухгалтерией позволит вам случайным образом разместить смежные блоки, однако для этого требуется много кодирования. Однако вы можете использовать алгоритм grid-grid, чтобы сначала разместить более крупные функции (когда на карте есть много места для работы, и вы можете просто проверить соседние сетки для коллекции, не сталкиваясь с проблемой купон-сборщика) затем поместите меньшие функции. Если вы можете разместить свои функции в этом порядке, это требует почти никакого дополнительного кодирования, кроме проверки соседних gridboxes для коллизий при размещении 1x2/3x3/и т.д. группа.