Potrzebuję algorytmu, który może podać mi pozycje wokół kuli dla N punktów (prawdopodobnie mniej niż 20), który rozłoży je niejasno. Nie ma potrzeby „perfekcji”, ale po prostu jej potrzebuję, aby żadne z nich nie były ze sobą połączone.
- To pytanie zawierało dobry kod, ale nie mogłem znaleźć sposobu na zrobienie tego munduru, ponieważ wydawało się, że jest to w 100% losowe.
- Ten post na blogu zalecany miał dwa sposoby pozwalające na wprowadzenie liczby punktów na kuli, ale algorytm Saffa i Kuijlaarsa jest dokładnie w psuedokodzie, który mogłem transkrybować, a przykładowy kod, który znalazłem, zawierał „węzeł [k]”, którego nie mogłem zobacz wyjaśnienie i zrujnowało tę możliwość. Drugim przykładem na blogu była spirala złotego przekroju, która dała mi dziwne, zebrane wyniki, bez jasnego sposobu zdefiniowania stałego promienia.
- Wydaje się, że ten algorytm z tego pytania mógłby prawdopodobnie zadziałać, ale nie mogę poskładać tego, co jest na tej stronie, w psuedokodzie lub cokolwiek.
Kilka innych wątków pytań, na które się natknąłem, dotyczyło losowej dystrybucji jednolitej, która dodaje poziom złożoności, o który się nie martwię. Przepraszam, że to takie głupie pytanie, ale chciałem pokazać, że naprawdę wyglądałem na twardego i nadal nie jestem w stanie.
Tak więc szukam prostego pseudokodu do równomiernego rozłożenia N punktów wokół kuli jednostkowej, która powraca we współrzędnych sferycznych lub kartezjańskich. Jeszcze lepiej, jeśli może nawet rozprowadzać się z odrobiną randomizacji (pomyśl o planetach wokół gwiazdy, przyzwoicie rozłożonych, ale z miejscem na swobodę).
Odpowiedzi:
W tym przykładzie kod
node[k]
jest po prostu k-tym węzłem. Generujesz tablicę N punktów inode[k]
jest to k-ta (od 0 do N-1). Jeśli to wszystko, co cię dezorientuje, miejmy nadzieję, że możesz to teraz wykorzystać.(innymi słowy,
k
jest to tablica o rozmiarze N, która jest zdefiniowana przed rozpoczęciem fragmentu kodu i która zawiera listę punktów).Alternatywnie , opierając się na drugiej odpowiedzi tutaj (i używając Pythona):
Jeśli to narysujesz, zobaczysz, że odstępy w pionie są większe w pobliżu biegunów, dzięki czemu każdy punkt znajduje się mniej więcej na tym samym całkowitym obszarze przestrzeni (w pobliżu biegunów jest mniej miejsca „w poziomie”, więc daje więcej „w pionie” ).
To nie to samo, co wszystkie punkty mające mniej więcej taką samą odległość od swoich sąsiadów (o czym myślę, że mówią o tym twoje linki), ale może wystarczyć do tego, co chcesz i ulepszyć po prostu tworząc jednolitą siatkę długości / długości .
źródło
Algorytm sfery Fibonacciego świetnie się do tego nadaje. Jest szybki i daje rezultaty, które na pierwszy rzut oka łatwo oszukają ludzkie oko. Możesz zobaczyć przykład z przetwarzaniem, który pokaże wynik w czasie po dodaniu punktów. Oto kolejny świetny interaktywny przykład autorstwa @gman. A oto prosta implementacja w Pythonie.
1000 próbek daje to:
źródło
Metoda złotej spirali
Powiedziałeś, że nie możesz uruchomić metody złotej spirali i szkoda, bo jest naprawdę, naprawdę dobra. Chciałbym dać ci pełne zrozumienie tego, żebyś mógł zrozumieć, jak nie dopuścić do tego, by się „zgniatało”.
Oto szybki, nielosowy sposób tworzenia sieci, która jest w przybliżeniu poprawna; jak omówiono powyżej, żadna krata nie będzie idealna, ale może to wystarczyć. Porównuje się go do innych metod, np. Na BendWavy.org, ale ma po prostu ładny i ładny wygląd oraz gwarancję równych odstępów w limicie.
Podkład: spirale słonecznika na dysku jednostkowym
Aby zrozumieć ten algorytm, najpierw zapraszam do przyjrzenia się algorytmowi spirali słonecznika 2D. Jest to oparte na fakcie, że najbardziej irracjonalną liczbą jest złoty podział
(1 + sqrt(5))/2
i jeśli ktoś emituje punkty, stosując podejście „stań w środku, obróć złoty stosunek całych obrotów, a następnie emituj kolejny punkt w tym kierunku”, w naturalny sposób konstruuje się spirala, która w miarę jak dochodzi do coraz większej liczby punktów, niemniej jednak odmawia posiadania dobrze zdefiniowanych „słupków”, na których punkty się pokrywają. (Notatka 1.)Algorytm równych odstępów na dysku to:
i daje wyniki, które wyglądają następująco (n = 100 in = 1000):
Rozmieszczenie punktów promieniowo
Kluczową dziwną rzeczą jest formuła
r = sqrt(indices / num_pts)
; jak doszedłem do tego? (Uwaga 2.)Cóż, używam tutaj pierwiastka kwadratowego, ponieważ chcę, aby miały równe odstępy wokół dysku. To jest to samo, co powiedzieć, że w granicy dużego N chcę, aby mały obszar R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) zawierał liczbę punktów proporcjonalną do jego powierzchni, czyli r d r d θ . Teraz, jeśli udajemy, że mówimy tutaj o zmiennej losowej, ma to prostą interpretację, mówiąc, że łączna gęstość prawdopodobieństwa dla ( R , Θ ) jest po prostu crdla jakiejś stałej c . Normalizacja na dysku jednostkowym wymusiłaby wówczas c = 1 / π.
Pozwólcie, że przedstawię sztuczkę. Pochodzi z teorii prawdopodobieństwa, gdzie jest znane jako próbkowanie odwrotnego CDF : załóżmy, że chcesz wygenerować zmienną losową o gęstości prawdopodobieństwa f ( z ) i masz zmienną losową U ~ Jednolity (0, 1), tak jak wychodzi z
random()
w większości języków programowania. Jak Ty to robisz?Teraz spirala złotego podziału rozdziela punkty w ładnie równy wzór dla θ, więc zintegrujmy to; dla dysku jednostkowego pozostaje F ( r ) = r 2 . Czyli funkcja odwrotna to F -1 ( u ) = u 1/2 , a zatem wygenerowalibyśmy losowe punkty na dysku we współrzędnych biegunowych z
r = sqrt(random()); theta = 2 * pi * random()
.Teraz zamiast losowo próbkować tę funkcję odwrotną, próbkujemy ją równomiernie , a fajną rzeczą w próbkowaniu jednorodnym jest to, że nasze wyniki dotyczące rozłożenia punktów na granicy dużego N będą się zachowywać tak, jakbyśmy próbkowali ją losowo. Ta kombinacja jest sztuczką. Zamiast tego
random()
używamy(arange(0, num_pts, dtype=float) + 0.5)/num_pts
, więc powiedzmy, że jeśli chcemy pobrać próbkę 10 punktów, to one sąr = 0.05, 0.15, 0.25, ... 0.95
. Jednolicie próbujemy r, aby uzyskać równe odstępy, i używamy przyrostu słonecznika, aby uniknąć okropnych „słupków” punktów w wyniku.Teraz robię słonecznik na kuli
Zmiany, które musimy wprowadzić, aby kropkować kulę punktami, polegają jedynie na zmianie współrzędnych biegunowych na współrzędne sferyczne. Współrzędna promieniowa oczywiście nie wchodzi w to, ponieważ jesteśmy na kuli jednostkowej. Aby zachować trochę spójności w tym miejscu, mimo że byłem wyszkolony jako fizyk, użyję współrzędnych matematyków, gdzie 0 ≤ φ ≤ π to szerokość geograficzna schodząca z bieguna, a 0 ≤ θ ≤ 2π to długość geograficzna. Zatem różnica z powyższego polega na tym, że w zasadzie zastępujemy zmienną r przez φ .
Nasz element obszaru, który był r d r d θ , teraz staje się niewiele bardziej skomplikowanym sin ( φ ) d φ d θ . Więc nasza gęstość spoiny dla równomiernych odstępów wynosi sin ( φ ) / 4π. Całkując θ , otrzymujemy f ( φ ) = sin ( φ ) / 2, więc F ( φ ) = (1 - cos ( φ )) / 2. Odwracając to, możemy zobaczyć, że jednolita zmienna losowa wyglądałaby jak acos (1 - 2 u ), ale próbkujemy jednakowo zamiast losowo, więc zamiast tego używamy φ k = acos (1 - 2 ( k+ 0,5) / N ). Reszta algorytmu po prostu rzutuje to na współrzędne x, y i z:
Ponownie dla n = 100 in = 1000 wyniki wyglądają następująco:
Dalsze badania
Chciałem się pochwalić blogiem Martina Robertsa. Zauważ, że powyżej utworzyłem przesunięcie moich indeksów, dodając 0,5 do każdego indeksu. To było dla mnie po prostu atrakcyjne wizualnie, ale okazuje się, że wybór offsetu ma duże znaczenie i nie jest stały w przedziale i może oznaczać nawet o 8% lepszą dokładność pakowania, jeśli zostanie wybrany poprawnie. Powinien również istnieć sposób, aby jego sekwencja R 2 pokryła kulę i byłoby interesujące zobaczyć, czy to również stworzyło ładne, równe pokrycie, być może takie, jakie jest, ale być może powinno być, powiedzmy, wzięte tylko z połowy Kwadrat jednostkowy przeciął lub tak po przekątnej i rozciągnął się, aby uzyskać okrąg.
Uwagi
Te „słupki” są tworzone przez racjonalne przybliżenia liczby, a najlepsze racjonalne przybliżenia liczby pochodzą z jej ciągłego wyrażenia ułamkowego,
z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))
gdziez
jest liczbą całkowitą in_1, n_2, n_3, ...
jest skończoną lub nieskończoną sekwencją dodatnich liczb całkowitych:Ponieważ część ułamkowa
1/(...)
zawsze zawiera się w przedziale od zera do jedynki, duża liczba całkowita w ułamku ciągłym pozwala na szczególnie dobre przybliżenie racjonalne: „jedna podzielona przez coś między 100 a 101” jest lepsza niż „podzielona przez coś między 1 a 2”. Dlatego najbardziej irracjonalna liczba jest liczbą, która jest1 + 1/(1 + 1/(1 + ...))
i nie ma szczególnie dobrych racjonalnych przybliżeń; φ = 1 + 1 / φ można rozwiązać mnożąc przez φ, aby otrzymać wzór na złoty współczynnik.Dla osób, które nie są tak zaznajomione z NumPy - wszystkie funkcje są „wektoryzowane”, więc
sqrt(array)
jest to to samo, co można napisać w innych językachmap(sqrt, array)
. Więc to jest aplikacja komponent po komponenciesqrt
. To samo dotyczy dzielenia przez skalar lub dodawania ze skalarami - te dotyczą wszystkich składników równolegle.Dowód jest prosty, gdy wiesz, że to jest wynik. Jeśli zapytasz, jakie jest prawdopodobieństwo, że z < Z < z + d z , to jest to to samo, co pytanie, jakie jest prawdopodobieństwo, że z < F -1 ( U ) < z + d z , zastosuj F do wszystkich trzech wyrażeń, zauważając, że jest funkcja monotonicznie rosnąca, stąd F ( z ) < U < F ( z + d z ), rozwiń prawą stronę na zewnątrz, aby znaleźć F ( z ) + f( z ) d z , a ponieważ U jest jednorodne, to prawdopodobieństwo wynosi tylko f ( z ) d z, jak obiecano.
źródło
Nazywa się to punktami upakowania kuli i nie ma (znanego) ogólnego, idealnego rozwiązania. Istnieje jednak wiele niedoskonałych rozwiązań. Wydaje się, że trzy najpopularniejsze to:
n
ich) wewnątrz sześcianu otaczającego kulę, a następnie odrzucasz punkty poza kulą. Traktuj pozostałe punkty jako wektory i znormalizuj je. To są twoje "próbki" - wybierzn
je za pomocą jakiejś metody (losowo, chciwie itp.).Dużo więcej informacji na temat tego problemu można znaleźć tutaj
źródło
To, czego szukasz, nazywa się kulistym pokryciem . Problem sferycznego pokrycia jest bardzo trudny, a rozwiązania są nieznane, z wyjątkiem niewielkiej liczby punktów. Wiadomo na pewno, że mając n punktów na kuli, zawsze istnieją dwa punkty odległe
d = (4-csc^2(\pi n/6(n-2)))^(1/2)
lub bliższe.Jeśli potrzebujesz probabilistycznej metody generowania punktów równomiernie rozmieszczonych na sferze, jest to łatwe: generuj punkty w przestrzeni równomiernie według rozkładu Gaussa (jest wbudowany w Javę, nietrudno znaleźć kod dla innych języków). Więc w trójwymiarowej przestrzeni potrzebujesz czegoś takiego
Następnie rzutuj punkt na kulę, normalizując jego odległość od początku
Rozkład Gaussa w n wymiarach jest sferycznie symetryczny, więc rzut na kulę jest jednorodny.
Oczywiście nie ma gwarancji, że odległość między dowolnymi dwoma punktami w zbiorze równomiernie wygenerowanych punktów będzie ograniczona poniżej, więc możesz użyć odrzucenia, aby wymusić wszelkie takie warunki, które możesz mieć: prawdopodobnie najlepiej jest wygenerować całą kolekcję, a następnie w razie potrzeby odrzucić całą kolekcję. (Lub użyj opcji „wczesne odrzucenie”, aby odrzucić całą kolekcję, którą wygenerowałeś do tej pory; po prostu nie zatrzymuj niektórych punktów i pomiń inne). Możesz użyć wzoru
d
podanego powyżej, pomniejszonego o trochę luzu, aby określić minimalną odległość między punktów, poniżej których odrzucisz zestaw punktów. Będziesz musiał obliczyć n wybrać 2 odległości, a prawdopodobieństwo odrzucenia będzie zależeć od luzu; trudno powiedzieć, jak to zrobić, więc przeprowadź symulację, aby poznać odpowiednie statystyki.źródło
Ta odpowiedź jest oparta na tej samej „teorii”, która jest dobrze zarysowana w tej odpowiedzi
Dodaję tę odpowiedź jako:
- Żadna z pozostałych opcji nie pasuje do potrzeby „jednolitości” „trafienia na miejscu” (lub nie jest to oczywiście oczywiste). (Zwracając uwagę na zachowanie wyglądające jak dystrybucja planety, szczególnie pożądane w pierwotnym pytaniu, po prostu odrzucasz losowo ze skończonej listy k równomiernie utworzonych punktów (losowo z liczbą indeksów w k pozycji z powrotem).)
- Najbliższy inny implik zmusił cię do określenia „N” na podstawie „osi kątowej”, zamiast tylko „jednej wartości N” w obu wartościach osi kątowej (co przy małej liczbie N jest bardzo trudne, aby wiedzieć, co może, a co nie może mieć znaczenia ( np. chcesz 5 punktów - baw się dobrze))
- Ponadto bardzo trudno jest `` zrozumieć '', jak rozróżnić inne opcje bez żadnych zdjęć, więc oto, jak wygląda ta opcja (poniżej), i gotowa do uruchomienia implementacja, która z nią idzie.
gdzie N w 20:
a następnie N przy 80:
oto gotowy do uruchomienia kod Python3, gdzie emulacja pochodzi z tego samego źródła: „ http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere ” znalezione przez innych . (Zapisane przeze mnie wykresy, uruchamiane po uruchomieniu jako „main”, pochodzą z: http://www.scipy.org/Cookbook/Matplotlib/mplot3D )
testowane przy niskich liczbach (N w 2, 5, 7, 13 itd.) i wydaje się działać „dobrze”
źródło
Próbować:
Powyższa funkcja powinna działać w pętli z sumą N pętli i iteracją prądu pętli k.
Opiera się na wzorze nasion słonecznika, z wyjątkiem tego, że nasiona słonecznika są zakrzywione dookoła w pół kopuły i ponownie w kulę.
Oto zdjęcie, ale umieściłem kamerę do połowy wewnątrz kuli, więc wygląda na 2d zamiast 3d, ponieważ kamera jest w tej samej odległości od wszystkich punktów. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg
źródło
Healpix rozwiązuje blisko powiązany problem (pikselizacja kuli z pikselami o równej powierzchni):
http://healpix.sourceforge.net/
Prawdopodobnie jest to przesada, ale może po obejrzeniu go zdasz sobie sprawę, że niektóre z jego innych fajnych właściwości są dla Ciebie interesujące. To znacznie więcej niż funkcja generująca chmurę punktów.
Wylądowałem tutaj, próbując go znaleźć; nazwa "healpix" nie przywołuje dokładnie sfer ...
źródło
przy małej liczbie punktów można przeprowadzić symulację:
źródło
Weź dwa największe z twoich współczynników
N
, jeśliN==20
to dwa największe czynniki są{5,4}
, lub bardziej ogólnie{a,b}
. ObliczUmieść swój pierwszy punkt na
{90-dlat/2,(dlong/2)-180}
, drugi na{90-dlat/2,(3*dlong/2)-180}
, trzeci na{90-dlat/2,(5*dlong/2)-180}
, aż raz objechałeś świat, w którym to momencie będziesz miał mniej więcej to,{75,150}
kiedy będziesz obok{90-3*dlat/2,(dlong/2)-180}
.Oczywiście pracuję nad tym w stopniach na powierzchni kulistej ziemi, ze zwykłymi konwencjami tłumaczenia +/- na N / S lub E / W. I oczywiście daje to całkowicie nielosowy rozkład, ale jest jednolity i punkty nie są zebrane razem.
Aby dodać pewien stopień losowości, możesz wygenerować 2 normalne rozłożenie (ze średnią 0 i std dev {dlat / 3, dlong / 3}, jeśli to konieczne) i dodać je do swoich równomiernie rozłożonych punktów.
źródło
edycja: To nie odpowiada na pytanie, które OP miał zadać, pozostawiając je tutaj na wypadek, gdyby ludzie uznali je za przydatne.
Używamy reguły mnożenia prawdopodobieństwa w połączeniu z nieskończonością. W rezultacie powstają 2 wiersze kodu, aby osiągnąć pożądany rezultat:
(zdefiniowane w następującym układzie współrzędnych :)
Twój język ma zwykle jednolity prymityw liczb losowych. Na przykład w Pythonie możesz użyć
random.random()
do zwrócenia liczby z zakresu[0,1)
. Możesz pomnożyć tę liczbę przez k, aby otrzymać liczbę losową z zakresu[0,k)
. Tak więc w Pythonieuniform([0,2pi))
oznaczałobyrandom.random()*2*math.pi
.Dowód
Teraz nie możemy przypisać θ równomiernie, w przeciwnym razie zbrylalibyśmy się na biegunach. Chcemy przypisać prawdopodobieństwa proporcjonalne do pola powierzchni klina kulistego (θ na tym diagramie to w rzeczywistości φ):
Przemieszczenie kątowe dφ na równiku spowoduje przemieszczenie dφ * r. Jakie będzie to przemieszczenie przy dowolnym azymucie θ? Cóż, promień od osi z wynosi
r*sin(θ)
, więc długość łuku tej „szerokości geograficznej” przecinającej klin wynosidφ * r*sin(θ)
. W ten sposób obliczamy skumulowany rozkład obszaru do pobrania z niego, całkując powierzchnię wycinka od bieguna południowego do bieguna północnego.(gdzie rzeczy =
dφ*r
)Spróbujemy teraz uzyskać odwrotność CDF, aby pobrać z niej próbkę: http://en.wikipedia.org/wiki/Inverse_transform_sampling
Najpierw normalizujemy, dzieląc nasz prawie-CDF przez jego maksymalną wartość. Ma to uboczny efekt anulowania dφ i r.
A zatem:
źródło
LUB ... aby umieścić 20 punktów, oblicz środki dwudziestościanów. Za 12 punktów znajdź wierzchołki dwudziestościanu. Za 30 punktów środek krawędzi dwudziestościanu. możesz zrobić to samo z czworościanem, sześcianem, dwunastościanem i ośmiościanem: jeden zestaw punktów znajduje się na wierzchołkach, inny na środku ściany, a drugi na środku krawędzi. Nie można ich jednak mieszać.
źródło
źródło
@robert king To naprawdę fajne rozwiązanie, ale zawiera kilka niechlujnych błędów. Wiem jednak, że bardzo mi to pomogło, więc nieważne z niechlujstwa. :) Oto poprawiona wersja ....
źródło
To działa i jest śmiertelnie proste. Tyle punktów, ile chcesz:
źródło