Ich brauche einen Algorithmus, der mir Positionen um eine Kugel für N Punkte (wahrscheinlich weniger als 20) geben kann, die sie vage ausbreiten. Es gibt keine Notwendigkeit für "Perfektion", aber ich brauche es nur, damit keiner von ihnen zusammengeballt ist.
- Diese Frage lieferte guten Code, aber ich konnte keinen Weg finden, diese Uniform zu erstellen, da dies zu 100% zufällig schien.
- Dieser empfohlene Blog-Beitrag hatte zwei Möglichkeiten, die Eingabe der Anzahl der Punkte auf der Kugel zu ermöglichen, aber der Saff- und Kuijlaars- Algorithmus befindet sich genau im Pseudocode, den ich transkribieren konnte, und das Codebeispiel, das ich gefunden habe, enthielt "node [k]", was ich nicht konnte siehe erklärt und ruiniert diese Möglichkeit. Das zweite Blog-Beispiel war die Golden Section Spiral, die mir seltsame, gebündelte Ergebnisse lieferte, ohne dass eine eindeutige Möglichkeit zur Definition eines konstanten Radius bestand.
- Dieser Algorithmus aus dieser Frage scheint möglicherweise zu funktionieren, aber ich kann nicht zusammenfassen, was auf dieser Seite in Pseudocode oder so etwas enthalten ist.
Einige andere Fragethreads, auf die ich gestoßen bin, sprachen von einer zufälligen gleichmäßigen Verteilung, was zu einer Komplexität führt, über die ich mir keine Sorgen mache. Ich entschuldige mich, dass dies eine so dumme Frage ist, aber ich wollte zeigen, dass ich wirklich hart ausgesehen habe und trotzdem zu kurz gekommen bin.
Was ich also suche, ist ein einfacher Pseudocode, um N Punkte gleichmäßig um eine Einheitskugel zu verteilen, die entweder in sphärischen oder kartesischen Koordinaten zurückkehrt. Noch besser, wenn es sich sogar mit ein wenig Randomisierung verteilen kann (denken Sie an Planeten um einen Stern, anständig verteilt, aber mit Raum für Spielraum).
Antworten:
In diesem Beispiel ist Code
node[k]
nur der k-te Knoten. Sie generieren ein Array mit N Punkten undnode[k]
ist das k-te (von 0 bis N-1). Wenn das alles ist, was Sie verwirrt, können Sie das hoffentlich jetzt verwenden.(Mit anderen Worten, es
k
handelt sich um ein Array der Größe N, das vor dem Start des Codefragments definiert wird und eine Liste der Punkte enthält.)Alternativ können Sie hier auf der anderen Antwort aufbauen (und Python verwenden):
Wenn Sie das zeichnen, werden Sie feststellen, dass der vertikale Abstand in der Nähe der Pole größer ist, sodass sich jeder Punkt auf ungefähr derselben Gesamtfläche befindet (in der Nähe der Pole gibt es "horizontal" weniger Platz, sodass mehr "vertikal" entsteht. ).
Dies ist nicht dasselbe wie alle Punkte, die ungefähr den gleichen Abstand zu ihren Nachbarn haben (worüber ich denke, dass Ihre Links sprechen), aber es kann für das, was Sie wollen, ausreichen und verbessert, einfach ein einheitliches Lat / Lon-Gitter zu erstellen .
quelle
Der Fibonacci-Kugelalgorithmus ist dafür großartig. Es ist schnell und liefert Ergebnisse, die auf einen Blick das menschliche Auge leicht täuschen. Sie können ein Beispiel für die Verarbeitung sehen, das das Ergebnis im Laufe der Zeit anzeigt, wenn Punkte hinzugefügt werden. Hier ist ein weiteres großartiges interaktives Beispiel von @gman. Und hier ist eine einfache Implementierung in Python.
1000 Proben geben Ihnen Folgendes:
quelle
Die goldene Spiralmethode
Sie sagten, Sie könnten die Methode der goldenen Spirale nicht zum Laufen bringen, und das ist eine Schande, weil sie wirklich sehr, sehr gut ist. Ich möchte Ihnen ein umfassendes Verständnis davon geben, damit Sie vielleicht verstehen, wie Sie verhindern können, dass dies „zusammengeballt“ wird.
Hier ist eine schnelle, nicht zufällige Methode, um ein Gitter zu erstellen, das ungefähr korrekt ist. Wie oben diskutiert, ist kein Gitter perfekt, aber dies kann gut genug sein. Es wird mit anderen Methoden verglichen, z. B. bei BendWavy.org, aber es hat nur ein schönes und hübsches Aussehen sowie eine Garantie für gleichmäßige Abstände im Limit.
Grundierung: Sonnenblumenspiralen auf der Gerätescheibe
Um diesen Algorithmus zu verstehen, lade ich Sie zunächst ein, sich den 2D-Sonnenblumen-Spiral-Algorithmus anzusehen. Dies basiert auf der Tatsache, dass die irrationalste Zahl der goldene Schnitt ist.
(1 + sqrt(5))/2
Wenn man Punkte durch den Ansatz "in der Mitte stehen, einen goldenen Schnitt ganzer Umdrehungen drehen und dann einen anderen Punkt in diese Richtung emittieren" ausgibt, konstruiert man natürlich a Spirale, die, wenn Sie zu einer immer höheren Anzahl von Punkten gelangen, sich dennoch weigert, genau definierte „Balken“ zu haben, auf denen die Punkte ausgerichtet sind. (Anmerkung 1.)Der Algorithmus für einen gleichmäßigen Abstand auf einer Platte lautet:
und es erzeugt Ergebnisse, die wie folgt aussehen (n = 100 und n = 1000):
Abstand der Punkte radial
Der Schlüssel seltsam ist die Formel
r = sqrt(indices / num_pts)
; Wie bin ich zu diesem gekommen? (Anmerkung 2.)Nun, ich verwende hier die Quadratwurzel, weil ich möchte, dass diese einen gleichmäßigen Flächenabstand um die Platte haben. Das ist das gleiche wie zu sagen, dass in der Grenze von großem N ich möchte, dass ein kleiner Bereich R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) eine Anzahl von Punkten enthält, die proportional zu seiner Fläche sind, das ist r d r d θ . Wenn wir nun so tun, als würden wir hier von einer Zufallsvariablen sprechen, hat dies eine einfache Interpretation, die besagt, dass die gemeinsame Wahrscheinlichkeitsdichte für ( R , Θ ) nur cr istfür eine Konstante c . Eine Normalisierung auf der Einheitsscheibe würde dann c = 1 / π erzwingen .
Lassen Sie mich nun einen Trick vorstellen. Es stammt aus der Wahrscheinlichkeitstheorie, in der es als Abtastung der inversen CDF bekannt ist : Angenommen, Sie wollten eine Zufallsvariable mit einer Wahrscheinlichkeitsdichte f ( z ) erzeugen und haben eine Zufallsvariable U ~ Uniform (0, 1), genau wie aus in den meisten Programmiersprachen. Wie machst Du das?
random()
Jetzt räumt der Spiraltrick mit dem goldenen Schnitt die Punkte in einem schön gleichmäßigen Muster für θ auf, also integrieren wir das heraus; für die Einheitsscheibe bleibt F ( r ) = r 2 übrig . Die Umkehrfunktion ist also F -1 ( u ) = u 1/2 , und daher würden wir zufällige Punkte auf der Scheibe in Polarkoordinaten mit erzeugen
r = sqrt(random()); theta = 2 * pi * random()
.Anstatt diese Umkehrfunktion zufällig abzutasten, werden sie nun einheitlich abgetastet, und das Schöne an der gleichmäßigen Abtastung ist, dass sich unsere Ergebnisse darüber, wie Punkte in der Grenze von großem N verteilt sind, so verhalten, als hätten wir sie zufällig abgetastet. Diese Kombination ist der Trick. Anstelle von verwenden
random()
wir(arange(0, num_pts, dtype=float) + 0.5)/num_pts
, so dass zum Beispiel, wenn wir 10 Punkte abtasten wollen, sie sindr = 0.05, 0.15, 0.25, ... 0.95
. Wir probieren r gleichmäßig ab , um einen gleichmäßigen Abstand zu erhalten, und wir verwenden das Sonnenblumeninkrement, um schreckliche „Balken“ von Punkten in der Ausgabe zu vermeiden.Jetzt mache ich die Sonnenblume auf einer Kugel
Die Änderungen, die wir vornehmen müssen, um die Kugel mit Punkten zu versehen, umfassen lediglich das Vertauschen der Polarkoordinaten gegen Kugelkoordinaten. Die Radialkoordinate geht natürlich nicht ein, weil wir uns auf einer Einheitskugel befinden. Um die Dinge hier ein wenig konsistenter zu halten, verwende ich, obwohl ich als Physiker ausgebildet wurde, die Koordinaten von Mathematikern, wobei 0 ≤ φ ≤ π der vom Pol herabfallende Breitengrad und 0 ≤ θ ≤ 2π der Längengrad ist. So ist der Unterschied von oben ist , dass wir im Prinzip die Variable ersetzen r mit φ .
Unser Flächenelement, das r d r d θ war , wird nun zur nicht viel komplizierteren sin ( φ ) d φ d θ . Unsere Gelenkdichte für einen gleichmäßigen Abstand ist also sin ( φ ) / 4π. Die Integration aus θ finden wir f ( φ ) = sin ( φ ) / 2, so dass F ( φ -) = (cos (1 φ / 2)). Wenn wir dies umkehren, können wir sehen, dass eine einheitliche Zufallsvariable wie acos (1 - 2 u ) aussehen würde , aber wir probieren einheitlich statt zufällig, also verwenden wir stattdessen φ k = acos (1 - 2 ( k)+ 0,5) / N ). Und der Rest des Algorithmus projiziert dies nur auf die x-, y- und z-Koordinaten:
Wiederum für n = 100 und n = 1000 sehen die Ergebnisse so aus:
Weitere Nachforschungen
Ich wollte Martin Roberts Blog einen Gruß aussprechen. Beachten Sie, dass ich oben einen Versatz meiner Indizes erstellt habe, indem ich jedem Index 0,5 hinzugefügt habe. Dies war nur optisch ansprechend für mich, aber es stellt sich heraus, dass die Wahl des Versatzes sehr wichtig ist und über das Intervall nicht konstant ist und bei richtiger Wahl eine um bis zu 8% bessere Genauigkeit beim Verpacken bedeuten kann. Es sollte auch eine Möglichkeit geben, seine R 2 -Sequenz dazu zu bringen, eine Kugel abzudecken, und es wäre interessant zu sehen, ob dies auch eine schöne gleichmäßige Abdeckung hervorbrachte, vielleicht so wie sie ist, aber vielleicht nur von der Hälfte genommen werden muss Das Einheitsquadrat schnitt diagonal oder so und streckte sich, um einen Kreis zu erhalten.
Anmerkungen
Diese „Balken“ werden durch rationale Annäherungen an eine Zahl gebildet, und die besten rationalen Annäherungen an eine Zahl ergeben sich aus ihrem fortgesetzten Bruchausdruck,
z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))
wobeiz
es sich um eine Ganzzahl handelt undn_1, n_2, n_3, ...
es sich entweder um eine endliche oder eine unendliche Folge positiver Ganzzahlen handelt:Da der Bruchteil
1/(...)
immer zwischen Null und Eins liegt, ermöglicht eine große Ganzzahl im fortgesetzten Bruch eine besonders gute rationale Annäherung: "Eins geteilt durch etwas zwischen 100 und 101" ist besser als "Eins geteilt durch etwas zwischen 1 und 2." Die irrationalste Zahl ist daher diejenige, die1 + 1/(1 + 1/(1 + ...))
keine besonders guten rationalen Annäherungen hat und hat; man kann lösen φ = 1 + 1 / φ indem man mit φ multipliziert , um die Formel für den Goldenen Schnitt zu erhalten.Für Leute, die mit NumPy nicht so vertraut sind - alle Funktionen sind "vektorisiert", so dass dies
sqrt(array)
das gleiche ist, was andere Sprachen schreiben könntenmap(sqrt, array)
. Dies ist also eine komponentenweisesqrt
Anwendung. Gleiches gilt auch für die Division durch einen Skalar oder die Addition mit Skalaren - diese gelten für alle Komponenten parallel.Der Beweis ist einfach, sobald Sie wissen, dass dies das Ergebnis ist. Wenn Sie fragen, wie hoch die Wahrscheinlichkeit ist, dass z < Z < z + d z ist , entspricht dies der Wahrscheinlichkeit, dass z < F -1 ( U ) < z + d z ist , und wenden Sie F auf alle drei Ausdrücke an Eine monoton ansteigende Funktion, also F ( z ) < U < F ( z + d z ), erweitert die rechte Seite nach außen, um F ( z ) + f zu finden( z ) d z , und da Uist einheitlich diese Wahrscheinlichkeit ist nur f ( z ) d z wie versprochen.
quelle
Dies wird als Packungspunkte auf einer Kugel bezeichnet, und es gibt keine (bekannte) allgemeine, perfekte Lösung. Es gibt jedoch viele unvollständige Lösungen. Die drei beliebtesten scheinen zu sein:
n
Punkte ) innerhalb des die Kugel umgebenden Würfels aus und lehnen dann die Punkte außerhalb der Kugel ab. Behandle die verbleibenden Punkte als Vektoren und normalisiere sie. Dies sind Ihre "Samples" - wählen Sien
sie mit einer Methode aus (zufällig, gierig usw.).Eine viel mehr Informationen zu diesem Problem finden sich hier
quelle
Was Sie suchen, wird als sphärische Abdeckung bezeichnet . Das Problem der sphärischen Abdeckung ist sehr schwierig und Lösungen sind bis auf eine geringe Anzahl von Punkten unbekannt. Eine Sache, die sicher bekannt ist, ist, dass bei n Punkten auf einer Kugel immer zwei Punkte der Entfernung
d = (4-csc^2(\pi n/6(n-2)))^(1/2)
oder näher existieren.Wenn Sie eine probabilistische Methode zum Generieren von Punkten wünschen, die gleichmäßig auf einer Kugel verteilt sind, ist dies einfach: Generieren Sie Punkte im Raum gleichmäßig durch Gaußsche Verteilung (sie ist in Java integriert, nicht schwer, den Code für andere Sprachen zu finden). Im dreidimensionalen Raum braucht man also so etwas
Projizieren Sie dann den Punkt auf die Kugel, indem Sie den Abstand zum Ursprung normalisieren
Die Gaußsche Verteilung in n Dimensionen ist sphärisch symmetrisch, so dass die Projektion auf die Kugel gleichmäßig ist.
Natürlich gibt es keine Garantie dafür, dass der Abstand zwischen zwei Punkten in einer Sammlung einheitlich generierter Punkte unten begrenzt wird. Sie können also die Ablehnung verwenden, um solche Bedingungen durchzusetzen, die Sie möglicherweise haben: Wahrscheinlich ist es am besten, die gesamte Sammlung zu generieren und dann lehnen Sie gegebenenfalls die gesamte Sammlung ab. (Oder verwenden Sie "vorzeitige Ablehnung", um die gesamte Sammlung abzulehnen, die Sie bisher generiert haben. Behalten Sie nur einige Punkte nicht bei und lassen Sie andere fallen.) Sie können die
d
oben angegebene Formel abzüglich eines gewissen Durchhangs verwenden, um den Mindestabstand zwischen zu bestimmen Punkte, unter denen Sie eine Reihe von Punkten ablehnen. Sie müssen n Entfernungen berechnen und wählen, und die Wahrscheinlichkeit der Ablehnung hängt vom Durchhang ab. Es ist schwer zu sagen, wie. Führen Sie daher eine Simulation durch, um ein Gefühl für die relevanten Statistiken zu bekommen.quelle
Diese Antwort basiert auf der gleichen "Theorie", die in dieser Antwort gut umrissen ist
Ich füge diese Antwort wie folgt hinzu:
- Keine der anderen Optionen entspricht der Anforderung der „Einheitlichkeit“, „genau richtig“ zu sein (oder nicht offensichtlich - eindeutig). (Um den Planeten wie ein verteilungsähnliches Verhalten zu erhalten, das in der ursprünglichen Frage besonders erwünscht ist, lehnen Sie es einfach aus der endlichen Liste der k einheitlich erstellten Punkte nach dem Zufallsprinzip ab (zufällig anhand der Indexanzahl in den k Elementen zurück).) -
Das nächste Ein anderes Gerät hat Sie gezwungen, das 'N' anhand der 'Winkelachse' zu bestimmen, gegenüber nur 'einem Wert von N' über beide Winkelachsenwerte (was bei niedrigen Zählwerten von N sehr schwierig ist zu wissen, was wichtig sein kann oder nicht). zB willst du '5' Punkte - viel Spaß))
- Darüber hinaus ist es sehr schwierig zu verstehen, wie zwischen den anderen Optionen ohne Bildmaterial unterschieden werden kann. So sieht diese Option aus (siehe unten) und die dazugehörige sofort einsatzbereite Implementierung.
mit N bei 20:
und dann N bei 80:
Hier ist der sofort einsatzbereite Python3-Code, bei dem die Emulation dieselbe Quelle ist: " http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere ", die von anderen gefunden wurde . (Die von mir beigefügte Darstellung, die ausgelöst wird, wenn sie als "Haupt" ausgeführt wird, stammt von: http://www.scipy.org/Cookbook/Matplotlib/mplot3D )
bei niedrigen Zählwerten getestet (N in 2, 5, 7, 13 usw.) und scheint "gut" zu funktionieren.
quelle
Versuchen:
Die obige Funktion sollte in einer Schleife mit N Schleifengesamt- und k Schleifenstromiteration ausgeführt werden.
Es basiert auf einem Sonnenblumenkernmuster, außer dass die Sonnenblumenkerne zu einer halben Kuppel und wieder zu einer Kugel gebogen sind.
Hier ist ein Bild, außer dass ich die Kamera in der Mitte der Kugel platziert habe, sodass sie 2d statt 3d aussieht, da die Kamera von allen Punkten den gleichen Abstand hat. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg
quelle
Healpix löst ein eng verwandtes Problem (Pixelierung der Kugel mit Pixeln gleicher Fläche):
http://healpix.sourceforge.net/
Es ist wahrscheinlich übertrieben, aber vielleicht werden Sie nach dem Betrachten feststellen, dass einige der anderen schönen Eigenschaften für Sie interessant sind. Es ist weit mehr als nur eine Funktion, die eine Punktwolke ausgibt.
Ich bin hier gelandet und habe versucht, es wieder zu finden. Der Name "Healpix" ruft nicht gerade Sphären hervor ...
quelle
Mit einer kleinen Anzahl von Punkten können Sie eine Simulation ausführen:
quelle
Nehmen Sie die zwei größten Faktoren von Ihnen
N
, wennN==20
dann die zwei größten Faktoren sind{5,4}
oder allgemeiner{a,b}
. BerechnungSetzen Sie Ihren ersten Punkt auf
{90-dlat/2,(dlong/2)-180}
, Ihren zweiten auf{90-dlat/2,(3*dlong/2)-180}
, Ihren dritten auf{90-dlat/2,(5*dlong/2)-180}
, bis Sie einmal um die Welt gestolpert sind. Zu diesem Zeitpunkt müssen Sie ungefähr,{75,150}
wann Sie nebenan gehen{90-3*dlat/2,(dlong/2)-180}
.Offensichtlich arbeite ich dies in Grad auf der Oberfläche der kugelförmigen Erde mit den üblichen Konventionen für die Übersetzung von +/- in N / S oder E / W. Und dies ergibt natürlich eine völlig nicht zufällige Verteilung, aber sie ist gleichmäßig und die Punkte sind nicht gebündelt.
Um ein gewisses Maß an Zufälligkeit hinzuzufügen, können Sie 2 normalverteilte Punkte (mit dem Mittelwert 0 und dem Standardabweichungswert von {dlat / 3, dlong / 3}) generieren und diese zu Ihren gleichmäßig verteilten Punkten hinzufügen.
quelle
edit: Dies beantwortet nicht die Frage, die das OP stellen wollte, und lässt sie hier, falls die Leute sie irgendwie nützlich finden.
Wir verwenden die Multiplikationsregel der Wahrscheinlichkeit, kombiniert mit Infinitessimalen. Dies führt zu 2 Codezeilen, um das gewünschte Ergebnis zu erzielen:
(definiert im folgenden Koordinatensystem :)
Ihre Sprache hat normalerweise ein einheitliches Zufallszahlenprimitiv. In Python können Sie beispielsweise
random.random()
eine Zahl im Bereich zurückgeben[0,1)
. Sie können diese Zahl mit k multiplizieren, um eine Zufallszahl im Bereich zu erhalten[0,k)
. Also in Pythonuniform([0,2pi))
würde bedeutenrandom.random()*2*math.pi
.Beweis
Jetzt können wir θ nicht einheitlich zuweisen, sonst würden wir an den Polen verklumpen. Wir möchten Wahrscheinlichkeiten proportional zur Oberfläche des Kugelkeils zuweisen (das θ in diesem Diagramm ist tatsächlich φ):
Eine Winkelverschiebung dφ am Äquator führt zu einer Verschiebung von dφ * r. Wie wird diese Verschiebung bei einem beliebigen Azimut θ sein? Nun, der Radius von der z-Achse ist
r*sin(θ)
, so dass die Bogenlänge des „Breite“ sich schneidende, den Keil istdφ * r*sin(θ)
. Daher berechnen wir die kumulative Verteilung der zu untersuchenden Fläche, indem wir die Fläche der Scheibe vom Südpol zum Nordpol integrieren.(wo Zeug =
dφ*r
)Wir werden nun versuchen, die Umkehrung der CDF zum Abtasten daraus zu machen: http://en.wikipedia.org/wiki/Inverse_transform_sampling
Zuerst normalisieren wir, indem wir unseren Fast-CDF durch seinen Maximalwert dividieren. Dies hat den Nebeneffekt, dass dφ und r aufgehoben werden.
So:
quelle
ODER ... um 20 Punkte zu platzieren, berechnen Sie die Zentren der Ikosaederflächen. Finden Sie für 12 Punkte die Eckpunkte des Ikosaeders. Für 30 Punkte der Mittelpunkt der Kanten des Ikosaeders. Sie können dasselbe mit Tetraedern, Würfeln, Dodekaedern und Oktaedern tun: Ein Satz von Punkten befindet sich auf den Eckpunkten, ein anderer in der Mitte des Gesichts und ein anderer in der Mitte der Kanten. Sie können jedoch nicht gemischt werden.
quelle
quelle
@ Robert King Es ist eine wirklich schöne Lösung, hat aber einige schlampige Fehler. Ich weiß, dass es mir sehr geholfen hat, also macht mir die Schlamperei nichts aus. :) Hier ist eine aufgeräumte Version ....
quelle
Das funktioniert und es ist tödlich einfach. So viele Punkte wie Sie möchten:
quelle