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

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

  • Этот вопрос предоставил хороший код, но я не смог найти способ сделать эту форму, так как это казалось на 100% рандомизированным.
  • Этот комментарий в блоге рекомендовал два пути, позволяющих вводить количество точек в сфере, но алгоритм Saff и Kuijlaars точно в psuedocode, который я мог бы расшифровать, и пример кода, который я нашел, содержал "node [k]", который я не мог видеть, объяснил и испортил эту возможность. Вторым примером блога была "Золотая секция", которая дала мне странные, сгруппированные результаты, без четкого способа определить постоянный радиус.
  • Этот алгоритм из этот вопрос кажется, что он может работать, но я не может объединить то, что на этой странице в psuedocode или что-то еще.

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

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

Ответ 1

В этом примере node[k] кода node[k] - это просто k-й узел. Вы генерируете массив из N точек, а node[k] - это kth (от 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

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

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

Ответ 2

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

import math, random

def fibonacci_sphere(samples=1,randomize=True):
    rnd = 1.
    if randomize:
        rnd = random.random() * samples

    points = []
    offset = 2./samples
    increment = math.pi * (3. - math.sqrt(5.));

    for i in range(samples):
        y = ((i * offset) - 1) + (offset / 2);
        r = math.sqrt(1 - pow(y,2))

        phi = ((i + rnd) % samples) * increment

        x = math.cos(phi) * r
        z = math.sin(phi) * r

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

    return points

1000 образцов дают вам следующее:

enter image description here

Ответ 3

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

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

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

Ответ 4

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

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

Так что здесь быстрый, неслучайный способ создать решетку, которая является приблизительно правильной; как обсуждалось выше, ни одна решетка не будет идеальной, но это может быть "достаточно хорошо". Он сравнивается с другими методами, например, на 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):

enter image description here

Расстояние между точками радиально

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

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

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

  1. Во-первых, превратите вашу плотность в кумулятивную функцию распределения F (z), которая, помните, монотонно увеличивается от 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() r = sqrt(random()); theta = 2 * pi * random().

Теперь вместо случайной выборки этой обратной функции мы равномерно выбираем ее, и хорошая вещь в равномерной выборке состоит в том, что наши результаты о том, как точки распределены в пределе большого N, будут вести себя так, как если бы мы случайным образом выбирали ее. Эта комбинация - хитрость. Вместо random() мы используем (arange(0, num_pts, dtype=float) + 0.5)/num_pts, так что, скажем, если мы хотим (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 dr 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 результаты выглядят так: enter image description here enter image description here

Заметки

  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/φ, умножив на φ, чтобы получить формулу для золотого сечения.

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

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

Ответ 5

Этот ответ основан на той же "теории", которая хорошо описана этим ответом

Я добавляю этот ответ как:
- Ни один из других вариантов не соответствует "однородности" необходимости "пятно" (или явно не ясно). (Отмечая, что планета, как поведение, связанное с распределением, особенно в оригинальном запросе, вы просто отвергаете из конечного списка k равномерно созданных точек в случайном порядке (случайный по счету индекса в элементах k назад).

- Ближайший другой имп заставил вас определить ось "N" на "angular", а только "одно значение N" для обоих значений оси angular (что при низких значениях N очень сложно знать что может, или может не иметь значения (например, вы хотите "5 очков" - получайте удовольствие))
- Кроме того, очень сложно "заглянуть", как различать другие параметры без каких-либо образов, поэтому здесь выглядит эта опция (ниже) и готовая к исполнению реализация, которая идет с ней.

с N в 20:

enter image description here
, а затем N в 80: enter image description here


здесь готовый к запуску код 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 и т.д.) и, похоже, работает "хорошо"

Ответ 6

То, что вы ищете, называется сферическим покрытием. Задача о сферическом покрытии очень сложна, и решения неизвестны, за исключением небольшого числа точек. Единственное, что точно известно, это то, что, учитывая 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 расстояния, и вероятность отклонения будет зависеть от слабины; трудно сказать, как это сделать, поэтому запустите симуляцию, чтобы почувствовать соответствующую статистику.

Ответ 7

Try:

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

Ответ 8

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

http://healpix.sourceforge.net/

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

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

Ответ 9

Возьмите два самых больших фактора вашего 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}.

Очевидно, я работаю в градусах по поверхности сферической земли, с обычными соглашениями для перевода +/- в N/S или E/W. И, очевидно, это дает вам совершенно неслучайное распределение, но оно равномерно и точки не сгруппированы вместе.

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

Ответ 10

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

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)]

Ответ 11

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

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

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

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

enter image description here

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


Proof

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

enter image description here

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

enter image description here (где stuff = 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)

Ответ 12

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

Ответ 13

# 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)])

Ответ 14

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

    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());
    }