Равномерно распределяя n точек на сфере


121

Мне нужен алгоритм, который может дать мне позиции вокруг сферы для N точек (менее 20, вероятно), который расплывчат их. Нет необходимости в «совершенстве», но мне это просто нужно, чтобы ни одно из них не было сгруппировано вместе.

  • В этом вопросе был хороший код, но я не смог найти способ сделать эту униформу, так как она казалась на 100% случайной.
  • В этом сообщении в блоге было рекомендовано два способа ввода количества точек на сфере, но алгоритм Саффа и Куйлаарса находится именно в псевдокоде, который я мог расшифровать, а найденный мной пример кода содержал «узел [k]», который я не мог смотри объяснил и разрушил эту возможность. Второй пример блога - спираль золотого сечения, которая дала мне странные, сгруппированные результаты, без четкого способа определения постоянного радиуса.
  • Этот алгоритм из этого вопроса кажется, что он может работать, но я не могу объединить то, что на этой странице, в псевдокод или что-то еще.

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

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


Что вы имеете в виду «с небольшой рандомизацией»? Вы имеете в виду возмущения в каком-то смысле?
ninjagecko

32
OP сбит с толку. Он хочет нанести на сферу n точек, чтобы минимальное расстояние между любыми двумя точками было как можно большим. Это придаст точкам вид «равномерно распределенных» по всей сфере. Это совершенно не связано с созданием равномерного случайного распределения на сфере, о чем говорят многие из этих ссылок и о чем говорится во многих ответах ниже.
BlueRaja - Дэнни Пфлугхофт,

1
20 - не так уж много точек для размещения на сфере, если вы не хотите, чтобы они выглядели просто случайными.
John Alexiou

2
Вот способ сделать это (у него есть примеры кода): pdfs.semanticscholar.org/97a6/… (похоже, он использует вычисления силы отталкивания)
trusktr 07

1
Конечно, для значений N в {4, 6, 8, 12, 20} существуют точные решения, в которых расстояние от каждой точки до (каждого из) ее ближайших соседей является константой для всех точек и всех ближайших соседей.
dmckee --- котенок экс-модератора

Ответы:


13

В этом примере код node[k] - это только k-й узел. Вы создаете массив из N точек и node[k]является k-м (от 0 до N-1). Если это все, что вас смущает, надеюсь, вы сможете использовать это сейчас.

(другими словами, kэто массив размера N, который определяется до начала фрагмента кода и который содержит список точек).

В качестве альтернативы , опираясь на другой ответ здесь (и используя Python):

> cat ll.py
from math import asin
nx = 4; ny = 5
for x in range(nx):
    lon = 360 * ((x+0.5) / nx)
    for y in range(ny):                                                         
        midpt = (y+0.5) / ny                                                    
        lat = 180 * asin(2*((y+0.5)/ny-0.5))                                    
        print lon,lat                                                           
> python2.7 ll.py                                                      
45.0 -166.91313924                                                              
45.0 -74.0730322921                                                             
45.0 0.0                                                                        
45.0 74.0730322921                                                              
45.0 166.91313924                                                               
135.0 -166.91313924                                                             
135.0 -74.0730322921                                                            
135.0 0.0                                                                       
135.0 74.0730322921                                                             
135.0 166.91313924                                                              
225.0 -166.91313924                                                             
225.0 -74.0730322921                                                            
225.0 0.0                                                                       
225.0 74.0730322921                                                             
225.0 166.91313924
315.0 -166.91313924
315.0 -74.0730322921
315.0 0.0
315.0 74.0730322921
315.0 166.91313924

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

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


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

Вы видели, что я отредактировал свой ответ, включив в начало объяснение узла [k]? Думаю, это может быть все, что вам нужно ...
Эндрю Кук

Замечательно, спасибо за объяснение. Я попробую позже, так как сейчас у меня нет времени, но большое спасибо за помощь. Я дам вам знать, как это работает в моих целях. ^^
Befall

Использование метода спирали идеально соответствует моим потребностям, большое спасибо за помощь и разъяснения. :)
Befall 08

13
Ссылка кажется мертвой.
Scheintod

140

Для этого отлично подходит алгоритм сферы Фибоначчи. Это быстро и дает результаты, которые легко обмануть человеческий глаз. Вы можете увидеть пример, выполненный с обработкой, которая покажет результат с течением времени по мере добавления точек. Вот еще один отличный интерактивный пример, сделанный @gman. А вот простая реализация на Python.

import math


def fibonacci_sphere(samples=1):

    points = []
    phi = math.pi * (3. - math.sqrt(5.))  # golden angle in radians

    for i in range(samples):
        y = 1 - (i / float(samples - 1)) * 2  # y goes from 1 to -1
        radius = math.sqrt(1 - y * y)  # radius at y

        theta = phi * i  # golden angle increment

        x = math.cos(theta) * radius
        z = math.sin(theta) * radius

        points.append((x, y, z))

    return points

1000 образцов дает вам это:

введите описание изображения здесь


переменная n вызывается при определении phi: phi = ((i + rnd)% n) * increment. N = образцы?
Эндрю Старощик

@AndrewStaroscik да! Когда я впервые написал код, я использовал «n» в качестве переменной и позже изменил имя, но не проявил должной осмотрительности. Спасибо, что заметили это!
Fnord


4
@Xarbrough код дает вам точки вокруг единичной сферы, поэтому просто умножьте каждую точку на любой скаляр для радиуса.
Fnord

2
@Fnord: Можем ли мы сделать это для более высоких измерений?
pikachuchameleon

108

Метод золотой спирали

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

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

Праймер: спирали подсолнечника на агрегатном диске

Чтобы понять этот алгоритм, я сначала предлагаю вам взглянуть на алгоритм 2D спирали подсолнечника. Это основано на том факте, что наиболее иррациональным числом является золотое сечение, (1 + sqrt(5))/2и если кто-то излучает точки по принципу «встаньте в центре, поверните золотое сечение целых витков, а затем испустите еще одну точку в этом направлении», то естественно построить спираль, которая по мере того, как вы набираете все большее и большее количество точек, тем не менее отказывается иметь четко определенные «полосы», по которым выстраиваются точки. (Примечание 1.)

Алгоритм равномерного размещения на диске:

from numpy import pi, cos, sin, sqrt, arange
import matplotlib.pyplot as pp

num_pts = 100
indices = arange(0, num_pts, dtype=float) + 0.5

r = sqrt(indices/num_pts)
theta = pi * (1 + 5**0.5) * indices

pp.scatter(r*cos(theta), r*sin(theta))
pp.show()

и он дает результаты, которые выглядят (n = 100 и n = 1000):

введите описание изображения здесь

Радиальное расположение точек

Ключевая странность - это формула r = sqrt(indices / num_pts); как я пришел к этому? (Заметка 2.)

Что ж, я использую здесь квадратный корень, потому что я хочу, чтобы они имели равномерный интервал вокруг диска. Это то же самое, что сказать, что в пределе большого N я хочу, чтобы небольшая область R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) содержала количество точек, пропорциональное ее площади, что есть r d r d θ . Теперь, если мы сделаем вид, что мы говорим здесь о случайной величине, это имеет прямую интерпретацию как утверждение, что совместная плотность вероятности для ( R , Θ ) равна crдля некоторой постоянной c . Нормализация на единичном круге тогда вынудила бы c = 1 / π.

Теперь позвольте мне представить трюк. Он исходит из теории вероятностей, где он известен как выборка обратного CDF : предположим, вы хотите сгенерировать случайную величину с плотностью вероятности f ( z ), и у вас есть случайная величина U ~ Uniform (0, 1), точно так же, как получается из random()на большинстве языков программирования. Как ты делаешь это?

  1. Сначала превратите вашу плотность в кумулятивную функцию распределения или CDF, которую мы назовем F ( z ). Напомним, что CDF монотонно увеличивается от 0 до 1 с производной f ( z ).
  2. Затем вычислите обратную функцию CDF F -1 ( z ).
  3. Вы обнаружите, что Z = F -1 ( U ) распределяется в соответствии с целевой плотностью. (Заметка 3).

Теперь уловка со спиралью золотого сечения распределяет точки по хорошо равномерному шаблону для θ, так что давайте интегрировать это; для единичного круга остается F ( r ) = r 2 . Таким образом, обратная функция F -1 ( u ) = u 1/2 , и поэтому мы будем генерировать случайные точки на диске в полярных координатах с r = sqrt(random()); theta = 2 * pi * random().

Теперь вместо случайной выборки этой обратной функции мы ее выбираем равномерно , и хорошая вещь в равномерной выборке заключается в том, что наши результаты о том, как точки распределены в пределах большого N, будут вести себя так, как если бы мы выбрали ее случайным образом. Эта комбинация и есть уловка. Вместо того, random()чтобы использовать (arange(0, num_pts, dtype=float) + 0.5)/num_pts, так что, скажем, если мы хотим выбрать 10 точек, они есть r = 0.05, 0.15, 0.25, ... 0.95. Мы единообразно выбираем r, чтобы получить равные интервалы площадей, и используем приращение подсолнечника, чтобы избежать ужасных «полос» точек на выходе.

Теперь делаем подсолнух на сфере

Изменения, которые нам нужно внести, чтобы расставить точки на сфере, просто включают в себя отключение полярных координат для сферических координат. Радиальная координата, конечно, сюда не входит, потому что мы находимся на единичной сфере. Для большей согласованности здесь, хотя я был физиком по образованию, я буду использовать координаты математиков, где 0 ≤ φ ≤ π - это широта, идущая от полюса, а 0 ≤ θ ≤ 2π - долгота. Таким образом, отличие от приведенного выше в том, что мы в основном заменяем переменную r на φ .

Наш элемент площади, который был r d r d θ , теперь становится не намного более сложным sin ( φ ) d φ d θ . Таким образом, наша плотность стыков для равномерного расстояния равна sin ( φ ) / 4π. Интегрируя θ , находим f ( φ ) = sin ( φ ) / 2, поэтому F ( φ ) = (1 - cos ( φ )) / 2. Обращая это, мы видим, что однородная случайная величина будет выглядеть как acos (1 - 2 u ), но мы делаем выборку равномерно, а не случайным образом, поэтому вместо этого мы используем φ k = acos (1 - 2 ( k+ 0,5) / N ). А остальная часть алгоритма просто проецирует это на координаты x, y и z:

from numpy import pi, cos, sin, arccos, arange
import mpl_toolkits.mplot3d
import matplotlib.pyplot as pp

num_pts = 1000
indices = arange(0, num_pts, dtype=float) + 0.5

phi = arccos(1 - 2*indices/num_pts)
theta = pi * (1 + 5**0.5) * indices

x, y, z = cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi);

pp.figure().add_subplot(111, projection='3d').scatter(x, y, z);
pp.show()

Снова для n = 100 и n = 1000 результаты выглядят так: введите описание изображения здесь введите описание изображения здесь

Дальнейшие исследования

Я хотел отдать должное блогу Мартина Робертса. Обратите внимание, что выше я создал смещение своих индексов, добавив 0,5 к каждому индексу. Это было просто визуально привлекательно для меня, но оказалось, что выбор смещения имеет большое значение и не является постоянным в течение интервала и может означать повышение точности упаковки на 8% при правильном выборе. Также должен быть способ заставить его последовательность R 2 покрывать сферу, и было бы интересно посмотреть, дает ли это также хорошее равномерное покрытие, возможно, как есть, но, возможно, нужно, чтобы быть, например, взято только из половины единичный квадрат разрезают по диагонали или около того и растягиваются, чтобы получить круг.

Ноты

  1. Эти «полосы» образованы рациональными приближениями к числу, а наилучшие рациональные приближения к числу получаются из его выражения непрерывной дроби, z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))где z- целое число и n_1, n_2, n_3, ...представляет собой конечную или бесконечную последовательность положительных целых чисел:

    def continued_fraction(r):
        while r != 0:
            n = floor(r)
            yield n
            r = 1/(r - n)

    Поскольку дробная часть 1/(...)всегда находится между нулем и единицей, большое целое число в непрерывной дроби позволяет получить особенно хорошее рациональное приближение: «один, разделенный на что-то от 100 до 101» лучше, чем «один, разделенный на что-то между 1 и 2». Поэтому наиболее иррациональным числом является то, которое 1 + 1/(1 + 1/(1 + ...))не имеет особенно хороших рациональных приближений; можно решить φ = 1 + 1 / φ , умножив на φ, чтобы получить формулу золотого сечения.

  2. Для людей, которые не так хорошо знакомы с NumPy - все функции «векторизованы», так что sqrt(array)это то же самое, что и другие языки map(sqrt, array). Итак, это приложение для каждого компонента sqrt. То же самое справедливо и для деления на скаляр или сложения со скалярами - они применяются ко всем компонентам параллельно.

  3. Доказательство становится простым, если вы знаете, что это результат. Если вы спросите, какова вероятность того, что z < Z < z + d z , это то же самое, что спросить, какова вероятность того, что z < F -1 ( U ) < z + d z , примените F ко всем трем выражениям, отметив, что это монотонно возрастающая функция, следовательно, F ( z ) < U < F ( z + d z ), разверните правую часть, чтобы найти F ( z ) + f( z ) d z , и поскольку U равномерно, эта вероятность равна f ( z ) d z, как и было обещано.


4
Я не уверен, почему это так далеко, это, безусловно, лучший быстрый способ сделать это.
whn

2
@snb спасибо за добрые слова! он так далеко вниз отчасти потому, что он намного, намного моложе, чем все остальные ответы здесь. Я удивлен, что у него дела обстоят так же хорошо, как раньше.
CR Drost

Мне остается один вопрос: сколько точек n мне нужно распределить для данного максимального расстояния между любыми двумя точками?
Феликс Д.

1
@FelixD. Это похоже на вопрос, который может очень быстро усложниться, особенно если вы начнете использовать, скажем, расстояния большого круга, а не евклидовы расстояния. Но, возможно, я смогу ответить на простой вопрос: если преобразовать точки на сфере в их диаграмму Вороного, можно описать каждую ячейку Вороного как имеющую приблизительно площадь 4π / N, и можно преобразовать это в характеристическое расстояние, представив, что это скорее круг чем ромб, πr² = 4π / N. Тогда r = 2 / √ (N).
CR Drost

2
Использование теоремы выборки с фактически однородным, а не случайным образом однородным вводом - одна из тех вещей, которые заставляют меня сказать: «Ну, а почему # $% и я не подумал об этом?» , Ницца.
dmckee --- котенок экс-модератора

86

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

  1. Создайте симуляцию . Рассматривайте каждую точку как электрон, привязанный к сфере, затем запустите моделирование для определенного количества шагов. Отталкивание электронов естественным образом приведет систему к более стабильному состоянию, когда точки будут удалены друг от друга настолько далеко, насколько это возможно.
  2. Отказ от гиперкуба . Этот фантастически звучащий метод на самом деле очень прост: вы равномерно выбираете точки (гораздо больше, чем nих) внутри куба, окружающего сферу, а затем отклоняете точки вне сферы. Считайте оставшиеся точки векторами и нормализуйте их. Это ваши «образцы» - выбирайте nих каким-либо методом (случайным, жадным и т. Д.).
  3. Спиральные приближения . Вы обводите спиралью вокруг сферы и равномерно распределяете точки по спирали. Из-за задействованной математики их сложнее понять, чем моделирование, но они намного быстрее (и, вероятно, требуют меньше кода). Наиболее популярным, по-видимому, является Сафф и др .

Намного больше информации об этой проблеме можно найти здесь


Я буду изучать спиральную тактику, которую Эндрю Кук опубликовал ниже, однако, не могли бы вы прояснить разницу между тем, что я хочу, и тем, что такое «равномерное случайное распределение»? Это всего лишь 100% случайное размещение точек на сфере, чтобы они были размещены равномерно? Спасибо за помощь. :)
Befall 07

4
@Befall: «равномерное случайное распределение» относится к равномерному распределению вероятностей - это означает, что при выборе случайной точки на сфере каждая точка имеет равную вероятность быть выбранной. Это не имеет ничего общего с окончательным пространственным распределением точек и, следовательно, не имеет ничего общего с вашим вопросом.
BlueRaja - Дэнни Пфлугофт,

Ааа, хорошо, большое спасибо. Поиск моего вопроса привел к тонне ответов на оба вопроса, и я не мог понять, что для меня было бессмысленно.
Befall

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

2
Последняя ссылка теперь мертва
Felix D.

10

То, что вы ищете, называется сферическим покрытием . Задача о сферическом покрытии очень сложна, и решения неизвестны, за исключением небольшого числа точек. Одно известно наверняка: для данных n точек на сфере всегда существуют две точки на расстоянии d = (4-csc^2(\pi n/6(n-2)))^(1/2)или ближе.

Если вам нужен вероятностный метод для создания точек, равномерно распределенных на сфере, это просто: генерировать точки в пространстве равномерно по распределению Гаусса (он встроен в Java, нетрудно найти код для других языков). Итак, в трехмерном пространстве вам нужно что-то вроде

Random r = new Random();
double[] p = { r.nextGaussian(), r.nextGaussian(), r.nextGaussian() };

Затем спроецируйте точку на сферу, нормализуя ее расстояние от начала координат.

double norm = Math.sqrt( (p[0])^2 + (p[1])^2 + (p[2])^2 ); 
double[] sphereRandomPoint = { p[0]/norm, p[1]/norm, p[2]/norm };

Распределение Гаусса в n измерениях сферически симметрично, поэтому проекция на сферу однородна.

Конечно, нет гарантии, что расстояние между любыми двумя точками в коллекции равномерно сгенерированных точек будет ограничено ниже, поэтому вы можете использовать отклонение, чтобы обеспечить выполнение любых таких условий, которые могут у вас возникнуть: вероятно, лучше всего сгенерировать всю коллекцию, а затем при необходимости отклонить всю коллекцию. (Или используйте «раннее отклонение», чтобы отклонить всю коллекцию, которую вы создали до сих пор; просто не оставляйте одни баллы и не отбрасывайте другие.) Вы можете использовать dприведенную выше формулу за вычетом некоторого запаса хода, чтобы определить минимальное расстояние между баллов, ниже которых вы отклоните набор баллов. Вам нужно будет вычислить n выбрать 2 расстояния, и вероятность отказа будет зависеть от слабины; Трудно сказать, как это сделать, поэтому запустите моделирование, чтобы получить представление о соответствующей статистике.


Проголосовали за выражения минимального максимального расстояния. Полезно для ограничения количества баллов, которые вы хотите использовать. Тем не менее, ссылка на авторитетный источник для этого было бы неплохо.
dmckee --- котенок экс-модератора

6

Этот ответ основан на той же «теории», которая хорошо изложена в этом ответе.

Я добавляю этот ответ в следующем виде:
- Ни один из других вариантов не подходит для «единообразия», требующего «точного определения» (или не совсем очевидного). (Отмечая, что поведение планеты, похожее на распределение, особенно желательно в исходном запросе, вы просто отклоняете из конечного списка k равномерно созданных точек случайным образом (случайным образом относительно числа индексов в k элементах назад).)
- Ближайшие другой impl вынудил вас выбрать `` N '' по `` угловой оси '', а не просто `` одно значение N '' по обоим значениям угловой оси (что при малых счетах N очень сложно определить, что может иметь или не иметь значения ( например хочешь 5 баллов - развлекайся))
- Кроме того, очень сложно понять, как отличить другие варианты без каких-либо изображений, поэтому вот как выглядит этот вариант (ниже) и готовая к запуску реализация, которая идет с ним.

с N на 20:

введите описание изображения здесь
а затем N на 80: введите описание изображения здесь


вот готовый к запуску код python3, где эмуляция является тем же источником: " http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere ", найденный другими , (График, который я включил, который срабатывает при запуске как 'main', взят из: http://www.scipy.org/Cookbook/Matplotlib/mplot3D )

from math import cos, sin, pi, sqrt

def GetPointsEquiAngularlyDistancedOnSphere(numberOfPoints=45):
    """ each point you get will be of form 'x, y, z'; in cartesian coordinates
        eg. the 'l2 distance' from the origion [0., 0., 0.] for each point will be 1.0 
        ------------
        converted from:  http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere ) 
    """
    dlong = pi*(3.0-sqrt(5.0))  # ~2.39996323 
    dz   =  2.0/numberOfPoints
    long =  0.0
    z    =  1.0 - dz/2.0
    ptsOnSphere =[]
    for k in range( 0, numberOfPoints): 
        r    = sqrt(1.0-z*z)
        ptNew = (cos(long)*r, sin(long)*r, z)
        ptsOnSphere.append( ptNew )
        z    = z - dz
        long = long + dlong
    return ptsOnSphere

if __name__ == '__main__':                
    ptsOnSphere = GetPointsEquiAngularlyDistancedOnSphere( 80)    

    #toggle True/False to print them
    if( True ):    
        for pt in ptsOnSphere:  print( pt)

    #toggle True/False to plot them
    if(True):
        from numpy import *
        import pylab as p
        import mpl_toolkits.mplot3d.axes3d as p3

        fig=p.figure()
        ax = p3.Axes3D(fig)

        x_s=[];y_s=[]; z_s=[]

        for pt in ptsOnSphere:
            x_s.append( pt[0]); y_s.append( pt[1]); z_s.append( pt[2])

        ax.scatter3D( array( x_s), array( y_s), array( z_s) )                
        ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('Z')
        p.show()
        #end

протестировано при небольшом количестве (N в 2, 5, 7, 13 и т. д.) и, похоже, работает «хорошо»


5

Пытаться:

function sphere ( N:float,k:int):Vector3 {
    var inc =  Mathf.PI  * (3 - Mathf.Sqrt(5));
    var off = 2 / N;
    var y = k * off - 1 + (off / 2);
    var r = Mathf.Sqrt(1 - y*y);
    var phi = k * inc;
    return Vector3((Mathf.Cos(phi)*r), y, Mathf.Sin(phi)*r); 
};

Вышеупомянутая функция должна выполняться в цикле с общим количеством циклов N и текущей итерацией цикла.

Он основан на узоре семян подсолнечника, за исключением того, что семена подсолнечника изогнуты в виде полукупола, а затем снова в сферу.

Вот изображение, за исключением того, что я поместил камеру на полпути внутрь сферы, чтобы она выглядела 2d, а не 3d, потому что камера находится на одинаковом расстоянии от всех точек. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg


2

Healpix решает тесно связанную проблему (пикселирование сферы пикселями равной площади):

http://healpix.sourceforge.net/

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

Я приземлился здесь, пытаясь найти его снова; название "healpix" не совсем похоже на сферы ...


1

с небольшим количеством точек вы можете запустить симуляцию:

from random import random,randint
r = 10
n = 20
best_closest_d = 0
best_points = []
points = [(r,0,0) for i in range(n)]
for simulation in range(10000):
    x = random()*r
    y = random()*r
    z = r-(x**2+y**2)**0.5
    if randint(0,1):
        x = -x
    if randint(0,1):
        y = -y
    if randint(0,1):
        z = -z
    closest_dist = (2*r)**2
    closest_index = None
    for i in range(n):
        for j in range(n):
            if i==j:
                continue
            p1,p2 = points[i],points[j]
            x1,y1,z1 = p1
            x2,y2,z2 = p2
            d = (x1-x2)**2+(y1-y2)**2+(z1-z2)**2
            if d < closest_dist:
                closest_dist = d
                closest_index = i
    if simulation % 100 == 0:
        print simulation,closest_dist
    if closest_dist > best_closest_d:
        best_closest_d = closest_dist
        best_points = points[:]
    points[closest_index]=(x,y,z)


print best_points
>>> best_points
[(9.921692138442777, -9.930808529773849, 4.037839326088124),
 (5.141893371460546, 1.7274947332807744, -4.575674650522637),
 (-4.917695758662436, -1.090127967097737, -4.9629263893193745),
 (3.6164803265540666, 7.004158551438312, -2.1172868271109184),
 (-9.550655088997003, -9.580386054762917, 3.5277052594769422),
 (-0.062238110294250415, 6.803105171979587, 3.1966101417463655),
 (-9.600996012203195, 9.488067284474834, -3.498242301168819),
 (-8.601522086624803, 4.519484132245867, -0.2834204048792728),
 (-1.1198210500791472, -2.2916581379035694, 7.44937337008726),
 (7.981831370440529, 8.539378431788634, 1.6889099589074377),
 (0.513546008372332, -2.974333486904779, -6.981657873262494),
 (-4.13615438946178, -6.707488383678717, 2.1197605651446807),
 (2.2859494919024326, -8.14336582650039, 1.5418694699275672),
 (-7.241410895247996, 9.907335206038226, 2.271647103735541),
 (-9.433349952523232, -7.999106443463781, -2.3682575660694347),
 (3.704772125650199, 1.0526567864085812, 6.148581714099761),
 (-3.5710511242327048, 5.512552040316693, -3.4318468250897647),
 (-7.483466337225052, -1.506434920354559, 2.36641535124918),
 (7.73363824231576, -8.460241422163824, -1.4623228616326003),
 (10, 0, 0)]

чтобы улучшить мой ответ, вы должны изменить closest_index = i на closest_index = randchoice (i, j)
Роберт

1

Возьмите два самых больших ваших фактора N, если N==20тогда два самых больших фактора {5,4}, или, в более общем плане {a,b}. Рассчитать

dlat  = 180/(a+1)
dlong = 360/(b+1})

Ставьте первую точку в точку {90-dlat/2,(dlong/2)-180}, вторую в точку , {90-dlat/2,(3*dlong/2)-180}третью точку в точке {90-dlat/2,(5*dlong/2)-180}, пока вы не совершите кругосветное путешествие один раз, и к этому моменту вы должны примерно {75,150}добраться до точки {90-3*dlat/2,(dlong/2)-180}.

Очевидно, я работаю над этим в градусах на поверхности сферической Земли, используя обычные условные обозначения для перевода +/- в С / Ю или В / З. И, очевидно, это дает вам совершенно неслучайное распределение, но оно равномерное и точки не сгруппированы вместе.

Чтобы добавить некоторую степень случайности, вы можете сгенерировать 2 нормально распределенных (со средним значением 0 и стандартным отклонением {dlat / 3, dlong / 3}, если необходимо) и добавить их к равномерно распределенным точкам.


5
это выглядело бы намного лучше, если бы вы работали в sin (лат), а не в латах. как бы то ни было, вы получите много скоплений возле полюсов.
Эндрю Кук 07

1

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

Мы используем правило умножения вероятностей в сочетании с бесконечно малыми. В результате получается 2 строки кода для достижения желаемого результата:

longitude: φ = uniform([0,2pi))
azimuth:   θ = -arcsin(1 - 2*uniform([0,1]))

(определено в следующей системе координат :)

введите описание изображения здесь

Ваш язык обычно имеет единый примитив случайных чисел. Например, в python вы можете использовать random.random()для возврата числа в диапазоне [0,1). Вы можете умножить это число на k, чтобы получить случайное число в диапазоне [0,k). Таким образом, в Python uniform([0,2pi))будет означать random.random()*2*math.pi.


доказательство

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

введите описание изображения здесь

Угловое смещение dφ на экваторе приведет к смещению dφ * r. Каким будет это смещение при произвольном азимуте θ? Ну, радиус от оси z равен r*sin(θ), поэтому длина дуги этой «широты», пересекающей клин, равна dφ * r*sin(θ). Таким образом, мы рассчитываем кумулятивное распределение площади для выборки из нее, интегрируя площадь среза от южного полюса до северного полюса.

введите описание изображения здесь(где прочее = dφ*r)

Теперь мы попытаемся получить из него инверсию CDF: http://en.wikipedia.org/wiki/Inverse_transform_sampling

Сначала мы нормализуем, разделив наш почти CDF на максимальное значение. Это имеет побочный эффект отмены dφ и r.

azimuthalCDF: cumProb = (sin(θ)+1)/2 from -pi/2 to pi/2

inverseCDF: θ = -sin^(-1)(1 - 2*cumProb)

Таким образом:

let x by a random float in range [0,1]
θ = -arcsin(1-2*x)

Разве это не эквивалентно варианту, который он отверг как «100% рандомизированный»? Насколько я понимаю, он хочет, чтобы они были более равномерно распределены, чем равномерное случайное распределение.
Эндрю Кук 07

@ BlueRaja-DannyPflughoeft: Хм, достаточно честно. Думаю, я не прочитал вопрос так внимательно, как следовало бы. Я все равно оставляю это здесь на случай, если другие сочтут это полезным. Спасибо за указание на это.
ninjagecko

1

ИЛИ ... чтобы разместить 20 точек, вычислите центры граней икосаэдра. По 12 точкам найдите вершины икосаэдра. Для 30 точек середина ребер икосаэдра. вы можете сделать то же самое с тетраэдром, кубом, додекаэдром и октаэдром: один набор точек находится на вершинах, другой - в центре грани, а третий - в центре ребер. Однако их нельзя смешивать.


Хорошая идея, но она работает только для 4, 6, 8, 12, 20, 24 или 30 точек.
Парень в шляпе

Если хотите схитрить, можете использовать центр граней и вершины. Они не будут равномерно распределены, но в хорошем приближении. Это хорошо, потому что детерминировано.
Chessofnerd

0
# create uniform spiral grid
numOfPoints = varargin[0]
vxyz = zeros((numOfPoints,3),dtype=float)
sq0 = 0.00033333333**2
sq2 = 0.9999998**2
sumsq = 2*sq0 + sq2
vxyz[numOfPoints -1] = array([(sqrt(sq0/sumsq)), 
                              (sqrt(sq0/sumsq)), 
                              (-sqrt(sq2/sumsq))])
vxyz[0] = -vxyz[numOfPoints -1] 
phi2 = sqrt(5)*0.5 + 2.5
rootCnt = sqrt(numOfPoints)
prevLongitude = 0
for index in arange(1, (numOfPoints -1), 1, dtype=float):
  zInc = (2*index)/(numOfPoints) -1
  radius = sqrt(1-zInc**2)

  longitude = phi2/(rootCnt*radius)
  longitude = longitude + prevLongitude
  while (longitude > 2*pi): 
    longitude = longitude - 2*pi

  prevLongitude = longitude
  if (longitude > pi):
    longitude = longitude - 2*pi

  latitude = arccos(zInc) - pi/2
  vxyz[index] = array([ (cos(latitude) * cos(longitude)) ,
                        (cos(latitude) * sin(longitude)), 
                        sin(latitude)])

4
Было бы полезно, если бы вы написали текст, объясняющий, для чего это предназначено, чтобы OP не принимал это на веру, что он просто будет работать.
hcarver

0

@robert king Это действительно хорошее решение, но в нем есть несколько неряшливых ошибок. Я знаю, что это мне очень помогло, так что не говоря уже о небрежности. :) Вот и исправленная версия ....

from math import pi, asin, sin, degrees
halfpi, twopi = .5 * pi, 2 * pi
sphere_area = lambda R=1.0: 4 * pi * R ** 2

lat_dist = lambda lat, R=1.0: R*(1-sin(lat))

#A = 2*pi*R^2(1-sin(lat))
def sphere_latarea(lat, R=1.0):
    if -halfpi > lat or lat > halfpi:
        raise ValueError("lat must be between -halfpi and halfpi")
    return 2 * pi * R ** 2 * (1-sin(lat))

sphere_lonarea = lambda lon, R=1.0: \
        4 * pi * R ** 2 * lon / twopi

#A = 2*pi*R^2 |sin(lat1)-sin(lat2)| |lon1-lon2|/360
#    = (pi/180)R^2 |sin(lat1)-sin(lat2)| |lon1-lon2|
sphere_rectarea = lambda lat0, lat1, lon0, lon1, R=1.0: \
        (sphere_latarea(lat0, R)-sphere_latarea(lat1, R)) * (lon1-lon0) / twopi


def test_sphere(n_lats=10, n_lons=19, radius=540.0):
    total_area = 0.0
    for i_lons in range(n_lons):
        lon0 = twopi * float(i_lons) / n_lons
        lon1 = twopi * float(i_lons+1) / n_lons
        for i_lats in range(n_lats):
            lat0 = asin(2 * float(i_lats) / n_lats - 1)
            lat1 = asin(2 * float(i_lats+1)/n_lats - 1)
            area = sphere_rectarea(lat0, lat1, lon0, lon1, radius)
            print("{:} {:}: {:9.4f} to  {:9.4f}, {:9.4f} to  {:9.4f} => area {:10.4f}"
                    .format(i_lats, i_lons
                    , degrees(lat0), degrees(lat1)
                    , degrees(lon0), degrees(lon1)
                    , area))
            total_area += area
    print("total_area = {:10.4f} (difference of {:10.4f})"
            .format(total_area, abs(total_area) - sphere_area(radius)))

test_sphere()

-1

Это работает, и это очень просто. Сколько хотите очков:

    private function moveTweets():void {


        var newScale:Number=Scale(meshes.length,50,500,6,2);
        trace("new scale:"+newScale);


        var l:Number=this.meshes.length;
        var tweetMeshInstance:TweetMesh;
        var destx:Number;
        var desty:Number;
        var destz:Number;
        for (var i:Number=0;i<this.meshes.length;i++){

            tweetMeshInstance=meshes[i];

            var phi:Number = Math.acos( -1 + ( 2 * i ) / l );
            var theta:Number = Math.sqrt( l * Math.PI ) * phi;

            tweetMeshInstance.origX = (sphereRadius+5) * Math.cos( theta ) * Math.sin( phi );
            tweetMeshInstance.origY= (sphereRadius+5) * Math.sin( theta ) * Math.sin( phi );
            tweetMeshInstance.origZ = (sphereRadius+5) * Math.cos( phi );

            destx=sphereRadius * Math.cos( theta ) * Math.sin( phi );
            desty=sphereRadius * Math.sin( theta ) * Math.sin( phi );
            destz=sphereRadius * Math.cos( phi );

            tweetMeshInstance.lookAt(new Vector3D());


            TweenMax.to(tweetMeshInstance, 1, {scaleX:newScale,scaleY:newScale,x:destx,y:desty,z:destz,onUpdate:onLookAtTween, onUpdateParams:[tweetMeshInstance]});

        }

    }
    private function onLookAtTween(theMesh:TweetMesh):void {
        theMesh.lookAt(new Vector3D());
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.