Рівномірно розподіляючи n точок на кулі


121

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

  • Це питання надало хороший код, але я не зміг знайти спосіб зробити цю форму, оскільки це здавалося 100% рандомізованим.
  • У цій публікації в блозі було рекомендовано два способи, що дозволяють вводити кількість точок на сферу, але алгоритм Саффа і Куджлаарса саме в psuedocode, який я міг би переписати, і приклад коду, який я знайшов, містив "вузол [k]", який я не міг див. пояснив та зруйнував цю можливість. Другим прикладом блогу була спіраль Золотого перерізу, яка дала мені дивні, згуртовані результати, не маючи чіткого способу визначення постійного радіуса.
  • Цей алгоритм із цього запитання здається, що він може працювати, але я не можу зібрати те, що на цій сторінці, у psuedocode чи що-небудь інше.

Кілька інших тем питань, на які я натрапив, говорили про рандомізований рівномірний розподіл, що додає рівня складності, який мене не хвилює. Прошу вибачення, що це таке нерозумне запитання, але я хотів показати, що я справді важко виглядав і все-таки прийшов коротко.

Отже, те, що я шукаю, - це простий псевдокод для рівномірного розподілу N точок навколо одиничної сфери, який або повертається в сферичних, або декартових координатах. Ще краще, якщо він навіть може поширюватися з невеликою кількістю рандомізації (подумайте, планети навколо зірки, пристойно розкинуті, але з простором для свободи).


Що ви маєте на увазі "з невеликою кількістю рандомізації"? Ви маєте на увазі збурення в якомусь сенсі?
ninjagecko

32
ОП заплутаний. Що він шукає, це поставити n-точок на сферу, щоб мінімальна відстань між будь-якими двома точками була якомога більшою. Це додасть очкам вигляд "рівномірного розподілу" по всій сфері. Це абсолютно не пов’язано із створенням рівномірного випадкового розподілу на сферу, про що йдеться у багатьох із цих посилань та про які багато відповідей нижче.
BlueRaja - Danny Pflughoeft

1
20 не так багато очок, які потрібно розмістити на кулі, якщо ви не хочете, щоб вони виглядали просто випадковими.
Джон Алексьоу

2
Ось спосіб це зробити (має приклади коду): pdfs.semanticscholar.org/97a6/… (схоже, він використовує обчислення сили відштовхування)
trusktr

1
Звичайно, для значень на N в {4, 6, 8, 12, 20} існують точні рішення, в яких відстань від кожної точки до (кожного) до найближчих сусідів є постійною для всіх точок і всіх найближчих сусідів.
dmckee --- кошеня колишнього модератора

Відповіді:


13

У цьому прикладі код node[k] є лише kth вузлом. Ви генеруєте масив 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

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

Це не те саме, що всі точки, що мають приблизно однакову відстань до своїх сусідів (про що я думаю, що ваші посилання говорять), але це може бути достатньо для того, що ви хочете, і вдосконалює просто створення рівномірної сітки lat / lon .


приємно, добре бачити математичне рішення. Я думав використати поділ спіралі та дуги. Я досі не впевнений у тому, як отримати оптимальне рішення, яке є цікавою проблемою.
король Роберт

Ви бачили, що я редагував свою відповідь, щоб включити пояснення вузла [k] вгорі? я думаю, що це може бути все, що вам потрібно ...
andrew cooke

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

Використання методу спіралі ідеально відповідає моїм потребам, дуже дякую за допомогу та уточнення. :)
Befall

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) * приріст. Чи n = зразки?
Андрій Старошик

@AndrewStaroscik так! Коли я вперше написав код, я використав "n" як змінну і пізніше змінив ім'я, але не робив належної ретельності. Дякуємо, що це зробили!
Фнорд


4
@Xarbrough код дає точки навколо одиничної сфери, тому просто помножте кожну точку на будь-який скаляр, який ви хочете за радіусом.
Фнорд

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 = 1 / π.

Тепер дозвольте ввести хитрість. Це випливає з теорії ймовірностей, де вона відома як вибірка зворотного CDF : припустимо, ви хотіли генерувати випадкову величину з щільністю ймовірності f ( z ) і у вас є випадкова величина U ~ Уніформа (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 θ , тепер стає не набагато складнішим гріхом ( φ ) 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. Спіральні наближення . Ви простежите спіраль навколо сфери і рівномірно розподілите точки навколо спіралі. Через математику, що займається, зрозуміти їх складніше, ніж моделювання, але набагато швидше (і, ймовірно, передбачає менше коду). Найпопулярнішими, здається, є Saff et al .

Набагато більше інформації про цю проблему можна знайти тут


Я розглядаю спіральну тактику, яку розмістив Андре Кук, опублікований нижче, однак, чи можете ви уточнити різницю між тим, що я хочу, і що таке "рівномірний випадковий розподіл"? Це лише 100% рандомізоване розміщення очок на кулі, щоб вони були рівномірно розміщені? Дякую за допомогу. :)
Befall

4
@Befall: "рівномірний випадковий розподіл" означає, що розподіл ймовірностей є рівномірним - це означає, що при виборі випадкової точки на кулі кожна точка має рівну ймовірність бути обраною. Це не має нічого спільного з остаточним просторовим розподілом балів, і, отже, не має нічого спільного з вашим запитанням.
BlueRaja - Danny Pflughoeft

Ах, гаразд, дуже дякую. Пошук мого запитання призвів до того, що я отримав безліч відповідей для обох, і я не міг зрозуміти, що для мене було безглуздо.
Припадає

Щоб було зрозуміло, кожен пункт має нульову ймовірність бути обраним. Відношення ймовірностей того, що точка буде належати до будь-яких двох областей на поверхні сфери, дорівнює відношенню поверхонь.
AturSams

2
Останнє посилання тепер мертве
Фелікс Д.

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 рівномірно створених точок навмання (випадковий wrt кількість індексу в k елементів назад).)
- Найближчі інший імпульс змусив вас визначити «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 ", знайдений іншими . (Сюжет, який я включив, що запускається під час запуску як "основний", взято з: 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 циклу та ітерацією струму k.

Він заснований на шаблоні насіння соняшнику, за винятком того, що насіння соняшнику зігнуті навколо в половину купола і знову в кулю.

Ось знімок, за винятком того, що я поставив камеру наполовину всередині сфери, щоб вона виглядала 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)]

щоб покращити мою відповідь, слід змінити najbli_index = i на найближчий_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}.

Очевидно, я працюю це в градусах на поверхні земної кулі, зі звичайними умовами перекладу +/- до N / S або E / W. І очевидно, це дає вам абсолютно невипадковий розподіл, але він рівномірний і точки не з’єднуються разом.

Щоб додати певну ступінь випадковості, ви можете створити 2 нормально розподілених (із середнім значенням 0 та std dev {dlat / 3, dlong / 3}, якщо це доречно) та додати їх до своїх рівномірно розподілених точок.


5
це виглядало б набагато краще, якби ви працювали в гріху (лат.), а не в латі. як це є, ви отримаєте багато купок біля полюсів.
andrew cooke

1

редагувати: Це не відповідає на питання, яке хотів задати ОП, залишаючи його тут на випадок, якщо люди вважають його корисним.

Ми використовуємо правило множення імовірності, поєднане з нескінченнимишвидками. У результаті виходить 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% рандомізований"? моє розуміння полягає в тому, що він хоче, щоб вони були більш рівномірними, ніж рівномірний випадковий розподіл.
andrew cooke

@ BlueRaja-DannyPflughoeft: Хм, досить справедливо. Я думаю, я не прочитав питання так уважно, як повинен був. Я залишаю це тут у будь-якому випадку, якщо інші вважають його корисним. Дякуємо, що вказали на це.
ninjagecko

1

АБО ... щоб розмістити 20 балів, обчислити центри граней ікосаедра. За 12 балів знайдіть вершини ікосаедра. На 30 балів - середня точка країв ікосаедра. ви можете зробити те ж саме з тетраедром, кубом, додекаедром і октаедрами: один набір точок знаходиться на вершинах, інший - по центру обличчя, а інший - по центру ребер. Однак їх не можна змішувати.


Хороша ідея, але вона працює лише на 4, 6, 8, 12, 20, 24 або 30 балів.
Хлопець із

Якщо ви хочете обдурити, можете скористатися центром облич і вершин. Вони не будуть розділені між собою, а гідне наближення. Це приємно, бо це детерміновано.
шахофнерд

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
Було б корисно, якби ви написали текст, в якому пояснювали, що це має бути робити, тож ОП не мусить сприймати це на віру, що це просто спрацює.
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.