J'ai besoin d'un algorithme qui puisse me donner des positions autour d'une sphère pour N points (moins de 20, probablement) qui les étale vaguement. Il n'y a pas besoin de "perfection", mais j'en ai juste besoin pour qu'aucune d'entre elles ne soit regroupée.
- Cette question a fourni un bon code, mais je n'ai pas trouvé de moyen de créer cet uniforme, car cela semblait aléatoire à 100%.
- Ce billet de blog recommandé avait deux façons permettant la saisie du nombre de points sur la sphère, mais l' algorithme Saff et Kuijlaars est exactement dans psuedocode que je pourrais transcrire, et l' exemple de code que j'ai trouvé contenait "node [k]", que je ne pouvais pas voir expliqué et ruiné cette possibilité. Le deuxième exemple de blog était la spirale de la section dorée, qui m'a donné des résultats étranges et groupés, sans moyen clair de définir un rayon constant.
- Cet algorithme de cette question semble pouvoir fonctionner, mais je ne peux pas reconstituer ce qui se trouve sur cette page en psuedocode ou quoi que ce soit.
Quelques autres fils de questions que j'ai rencontrés parlaient de distribution uniforme aléatoire, ce qui ajoute un niveau de complexité qui ne me préoccupe pas. Je m'excuse que ce soit une question tellement idiote, mais je voulais montrer que j'ai vraiment regardé attentivement et que j'ai toujours échoué.
Donc, ce que je recherche, c'est un pseudocode simple pour répartir uniformément N points autour d'une sphère unitaire, qui retourne en coordonnées sphériques ou cartésiennes. Encore mieux s'il peut même distribuer avec un peu de randomisation (pensez aux planètes autour d'une étoile, décemment réparties, mais avec une marge de manœuvre).
Réponses:
Dans cet exemple de code, il
node[k]
n'y a que le kième nœud. Vous générez un tableau N points etnode[k]
est le kième (de 0 à N-1). Si c'est tout ce qui vous déroute, j'espère que vous pourrez l'utiliser maintenant.(en d'autres termes,
k
est un tableau de taille N qui est défini avant le début du fragment de code et qui contient une liste des points).Alternativement , en s'appuyant sur l'autre réponse ici (et en utilisant Python):
Si vous tracez cela, vous verrez que l'espacement vertical est plus grand près des pôles de sorte que chaque point est situé à peu près dans la même surface totale de l'espace (près des pôles, il y a moins d'espace "horizontalement", donc cela donne plus "verticalement" ).
Ce n'est pas la même chose que tous les points ayant à peu près la même distance par rapport à leurs voisins (ce dont je pense que vos liens parlent), mais cela peut être suffisant pour ce que vous voulez et améliore simplement la création d'une grille de latitude / longitude uniforme .
la source
L'algorithme de la sphère de Fibonacci est idéal pour cela. Il est rapide et donne des résultats qui, en un coup d'œil, tromperont facilement l'œil humain. Vous pouvez voir un exemple réalisé avec le traitement qui affichera le résultat au fil du temps à mesure que des points sont ajoutés. Voici un autre excellent exemple interactif réalisé par @gman. Et voici une implémentation simple en python.
1000 échantillons vous donnent ceci:
la source
La méthode de la spirale d'or
Vous avez dit que vous ne pouviez pas faire fonctionner la méthode de la spirale dorée et c'est dommage parce que c'est vraiment très bon. Je voudrais vous donner une compréhension complète de cela afin que vous puissiez peut-être comprendre comment éviter que cela ne soit «groupé».
Voici donc un moyen rapide et non aléatoire de créer un treillis qui est approximativement correct; comme indiqué ci-dessus, aucun réseau ne sera parfait, mais cela peut être suffisant. Il est comparé à d'autres méthodes, par exemple sur BendWavy.org, mais il a juste un joli et joli look ainsi qu'une garantie d'espacement uniforme dans la limite.
Primer: spirales de tournesol sur le disque de l'unité
Pour comprendre cet algorithme, je vous invite d'abord à vous pencher sur l'algorithme 2D de la spirale de tournesol. Ceci est basé sur le fait que le nombre le plus irrationnel est le nombre d'or
(1 + sqrt(5))/2
et si l'on émet des points par l'approche «se tenir au centre, tourner un nombre d'or de tours entiers, puis émettre un autre point dans cette direction», on construit naturellement un spirale qui, à mesure que vous atteignez des nombres de points de plus en plus élevés, refuse néanmoins d'avoir des «barres» bien définies sur lesquelles les points s'alignent. (Note 1.)L'algorithme pour l'espacement pair sur un disque est,
et il produit des résultats qui ressemblent à (n = 100 et n = 1000):
Espacement radial des points
La principale chose étrange est la formule
r = sqrt(indices / num_pts)
; comment suis-je arrivé à celui-là? (Note 2.)Eh bien, j'utilise la racine carrée ici parce que je veux que celles-ci aient un espacement de zone uniforme autour du disque. Cela revient à dire que dans la limite du grand N je veux qu'une petite région R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) contienne un nombre de points proportionnel à son aire, qui est r d r d θ . Maintenant, si nous prétendons parler ici d'une variable aléatoire, cela a une interprétation simple comme disant que la densité de probabilité conjointe pour ( R , Θ ) est juste crpour une certaine constante c . La normalisation sur le disque unité forcerait alors c = 1 / π.
Maintenant, laissez-moi vous présenter une astuce. Cela vient de la théorie des probabilités où il est connu sous le nom d' échantillonnage du CDF inverse : supposons que vous vouliez générer une variable aléatoire avec une densité de probabilité f ( z ) et que vous ayez une variable aléatoire U ~ Uniform (0, 1), tout comme sort de
random()
dans la plupart des langages de programmation. Comment est-ce que tu fais ça?Maintenant, l'astuce en spirale du nombre d'or espace les points dans un motif bien uniforme pour θ alors intégrons cela; pour le disque unitaire, on se retrouve avec F ( r ) = r 2 . Donc, la fonction inverse est F -1 ( u ) = u 1/2 , et donc nous générerions des points aléatoires sur le disque en coordonnées polaires avec
r = sqrt(random()); theta = 2 * pi * random()
.Maintenant, au lieu d' échantillonner au hasard cette fonction inverse, nous l' échantillonnons uniformément , et la bonne chose à propos de l'échantillonnage uniforme est que nos résultats sur la façon dont les points sont répartis dans la limite d'un grand N se comporteront comme si nous l'avions échantillonné au hasard. Cette combinaison est le truc. Au lieu de
random()
nous utiliser(arange(0, num_pts, dtype=float) + 0.5)/num_pts
, de sorte que, disons, si nous voulons échantillonner 10 points, ils le sontr = 0.05, 0.15, 0.25, ... 0.95
. Nous échantillonnons uniformément r pour obtenir un espacement de surface égale, et nous utilisons l'incrément de tournesol pour éviter de terribles «barres» de points dans la sortie.Maintenant en train de faire le tournesol sur une sphère
Les changements que nous devons apporter pour doter la sphère de points impliquent simplement de remplacer les coordonnées polaires par des coordonnées sphériques. La coordonnée radiale n'entre bien sûr pas dans cela car nous sommes sur une sphère unitaire. Pour garder les choses un peu plus cohérentes ici, même si j'ai été formé en tant que physicien, j'utiliserai les coordonnées des mathématiciens où 0 ≤ φ ≤ π est la latitude descendant du pôle et 0 ≤ θ ≤ 2π est la longitude. Donc, la différence par rapport à ci-dessus est que nous remplaçons fondamentalement la variable r par φ .
Notre élément d'aire, qui était r d r d θ , devient maintenant le sin ( φ ) d φ d θ pas beaucoup plus compliqué . Donc, notre densité conjointe pour un espacement uniforme est sin ( φ ) / 4π. En intégrant θ , nous trouvons f ( φ ) = sin ( φ ) / 2, donc F ( φ ) = (1 - cos ( φ )) / 2. En inversant cela, nous pouvons voir qu'une variable aléatoire uniforme ressemblerait à acos (1 - 2 u ), mais nous échantillonnons uniformément plutôt que de manière aléatoire, donc nous utilisons à la place φ k = acos (1 - 2 ( k+ 0,5) / N ). Et le reste de l'algorithme projette simplement ceci sur les coordonnées x, y et z:
Encore une fois pour n = 100 et n = 1000, les résultats ressemblent à:
De plus amples recherches
Je voulais crier au blog de Martin Roberts. Notez que ci-dessus j'ai créé un offset de mes indices en ajoutant 0,5 à chaque index. C'était juste visuellement attrayant pour moi, mais il s'avère que le choix du décalage compte beaucoup et n'est pas constant sur l'intervalle et peut signifier obtenir jusqu'à 8% de meilleure précision dans l'emballage s'il est choisi correctement. Il devrait également y avoir un moyen de faire en sorte que sa séquence R 2 couvre une sphère et il serait intéressant de voir si cela produisait également une belle couverture uniforme, peut-être telle quelle mais peut-être devant être, par exemple, prise à partir de seulement la moitié de l'unité carrée coupée en diagonale environ et étirée pour former un cercle.
Remarques
Ces «barres» sont formées par des approximations rationnelles d'un nombre, et les meilleures approximations rationnelles d'un nombre proviennent de son expression de fraction continue,
z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))
oùz
est un entier etn_1, n_2, n_3, ...
est une séquence finie ou infinie d'entiers positifs:Comme la partie de fraction
1/(...)
est toujours comprise entre zéro et un, un grand entier dans la fraction continue permet une approximation rationnelle particulièrement bonne: "un divisé par quelque chose entre 100 et 101" est meilleur que "un divisé par quelque chose entre 1 et 2." Le nombre le plus irrationnel est donc celui qui n'est1 + 1/(1 + 1/(1 + ...))
et n'a pas d'approximations rationnelles particulièrement bonnes; on peut résoudre φ = 1 + 1 / φ en multipliant par φ pour obtenir la formule du nombre d'or.Pour les gens qui ne sont pas si familiers avec NumPy - toutes les fonctions sont «vectorisées», donc c'est
sqrt(array)
la même chose que ce que d'autres langages pourraient écriremap(sqrt, array)
. Il s'agit donc d'une application composant par composantsqrt
. Il en va de même pour la division par un scalaire ou l'addition avec des scalaires - ceux-ci s'appliquent à tous les composants en parallèle.La preuve est simple une fois que vous savez que c'est le résultat. Si vous demandez quelle est la probabilité que z < Z < z + d z , cela revient à demander quelle est la probabilité que z < F -1 ( U ) < z + d z , appliquez F aux trois expressions en notant que c'est une fonction monotone croissante, d'où F ( z ) < U < F ( z + d z ), étend le côté droit pour trouver F ( z ) + f( z ) d z , et comme U est uniforme, cette probabilité est juste f ( z ) d z comme promis.
la source
C'est ce qu'on appelle des points d'emballage sur une sphère, et il n'y a pas de solution générale parfaite (connue). Cependant, il existe de nombreuses solutions imparfaites. Les trois plus populaires semblent être:
n
qu'eux) à l' intérieur du cube entourant la sphère, puis vous rejetez les points à l'extérieur de la sphère. Traitez les points restants comme des vecteurs et normalisez-les. Ce sont vos "échantillons" - choisissezn
-les en utilisant une méthode (aléatoire, gourmande, etc.).Un beaucoup plus d' informations sur ce problème se trouve ici
la source
Ce que vous recherchez s'appelle un revêtement sphérique . Le problème de la couverture sphérique est très difficile et les solutions sont inconnues sauf pour un petit nombre de points. Une chose que l'on sait avec certitude est que, étant donné n points sur une sphère, il existe toujours deux points de distance
d = (4-csc^2(\pi n/6(n-2)))^(1/2)
ou plus proches.Si vous voulez une méthode probabiliste pour générer des points uniformément répartis sur une sphère, c'est simple: générer des points dans l'espace uniformément par distribution gaussienne (elle est intégrée à Java, pas difficile de trouver le code pour les autres langages). Donc dans un espace tridimensionnel, vous avez besoin de quelque chose comme
Projetez ensuite le point sur la sphère en normalisant sa distance par rapport à l'origine
La distribution gaussienne en n dimensions est sphérique symétrique donc la projection sur la sphère est uniforme.
Bien sûr, il n'y a aucune garantie que la distance entre deux points quelconques dans une collection de points générés uniformément sera limitée ci-dessous, vous pouvez donc utiliser le rejet pour appliquer toutes les conditions que vous pourriez avoir: il est probablement préférable de générer toute la collection, puis rejeter toute la collection si nécessaire. (Ou utilisez le "rejet anticipé" pour rejeter toute la collection que vous avez générée jusqu'à présent; ne gardez simplement pas certains points et n'en perdez pas d'autres.) Vous pouvez utiliser la formule
d
ci-dessus, moins une certaine marge, pour déterminer la distance minimale entre points en dessous desquels vous rejetterez un ensemble de points. Vous devrez calculer n choisir 2 distances, et la probabilité de rejet dépendra du mou; il est difficile de dire comment, alors lancez une simulation pour avoir une idée des statistiques pertinentes.la source
Cette réponse est basée sur la même `` théorie '' qui est bien décrite par cette réponse
J'ajoute cette réponse comme suit:
- Aucune des autres options ne correspond au besoin «d'uniformité» «sur place» (ou pas de manière évidente). (En notant que la planète ressemble à un comportement de distribution particulièrement recherché dans la demande initiale, vous rejetez simplement de la liste finie des k points créés de manière uniforme au hasard (aléatoire par rapport au nombre d'index dans les k éléments).)
--Le plus proche un autre impl vous a forcé à décider du `` N '' par `` axe angulaire '', contre juste `` une valeur de N '' sur les deux valeurs de l'axe angulaire (ce qui à de faibles comptes de N est très difficile à savoir ce qui peut ou peut ne pas avoir d'importance ( Par exemple, vous voulez «5» points - amusez-vous))
- De plus, il est très difficile de `` comprendre '' comment différencier les autres options sans aucune image, alors voici à quoi ressemble cette option (ci-dessous), et l'implémentation prête à l'emploi qui va avec.
avec N à 20:
puis N à 80:
voici le code python3 prêt à l'emploi, où l'émulation est la même source: " http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere " trouvé par d'autres . (Le tracé que j'ai inclus, qui se déclenche lorsqu'il est exécuté en tant que `` main '', est tiré de: http://www.scipy.org/Cookbook/Matplotlib/mplot3D )
testé à de faibles comptes (N en 2, 5, 7, 13, etc.) et semble fonctionner `` bien ''
la source
Essayer:
La fonction ci-dessus doit fonctionner en boucle avec N boucle totale et k itération de courant de boucle.
Il est basé sur un motif de graines de tournesol, sauf que les graines de tournesol sont incurvées en un demi-dôme, puis à nouveau dans une sphère.
Voici une image, sauf que je mets la caméra à mi-chemin à l'intérieur de la sphère pour qu'elle ressemble à 2d au lieu de 3D car la caméra est à la même distance de tous les points. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg
la source
Healpix résout un problème étroitement lié (pixellisation de la sphère avec des pixels de surface égale):
http://healpix.sourceforge.net/
C'est probablement exagéré, mais peut-être qu'après l'avoir regardé, vous vous rendrez compte que certaines de ses autres belles propriétés vous intéressent. C'est bien plus qu'une simple fonction qui génère un nuage de points.
J'ai atterri ici en essayant de le retrouver; le nom "healpix" n'évoque pas exactement des sphères ...
la source
avec un petit nombre de points, vous pouvez exécuter une simulation:
la source
Prenez les deux plus grands facteurs de votre
N
, siN==20
les deux plus grands facteurs sont{5,4}
, ou, plus généralement{a,b}
. CalculerMettez votre premier point à
{90-dlat/2,(dlong/2)-180}
, votre deuxième à{90-dlat/2,(3*dlong/2)-180}
, votre troisième à{90-dlat/2,(5*dlong/2)-180}
, jusqu'à ce que vous ayez fait le tour du monde une fois, à quel moment vous devez savoir{75,150}
quand vous allez à côté{90-3*dlat/2,(dlong/2)-180}
.Évidemment, je travaille cela en degrés sur la surface de la terre sphérique, avec les conventions habituelles pour traduire +/- en N / S ou E / W. Et évidemment, cela vous donne une distribution complètement non aléatoire, mais elle est uniforme et les points ne sont pas regroupés.
Pour ajouter un certain degré d'aléatoire, vous pouvez générer 2 normalement distribués (avec une moyenne de 0 et un dev standard de {dlat / 3, dlong / 3} selon le cas) et les ajouter à vos points uniformément distribués.
la source
edit: Cela ne répond pas à la question que l'OP avait l'intention de poser, la laissant ici au cas où les gens le trouveraient utile d'une manière ou d'une autre.
Nous utilisons la règle de multiplication de la probabilité, combinée à des nombres infinis. Il en résulte 2 lignes de code pour obtenir le résultat souhaité:
(défini dans le système de coordonnées suivant :)
Votre langue a généralement une primitive de nombre aléatoire uniforme. Par exemple, en python, vous pouvez utiliser
random.random()
pour renvoyer un nombre dans la plage[0,1)
. Vous pouvez multiplier ce nombre par k pour obtenir un nombre aléatoire dans la plage[0,k)
. Ainsi en python,uniform([0,2pi))
signifieraitrandom.random()*2*math.pi
.Preuve
Maintenant, nous ne pouvons pas assigner θ uniformément, sinon nous aurions une agglutination aux pôles. Nous souhaitons attribuer des probabilités proportionnelles à la surface du coin sphérique (le θ dans ce diagramme est en fait φ):
Un déplacement angulaire dφ à l'équateur se traduira par un déplacement de dφ * r. Quel sera ce déplacement à un azimut arbitraire θ? Eh bien, le rayon de l'axe z est
r*sin(θ)
, donc la longueur d'arc de cette "latitude" coupant le coin estdφ * r*sin(θ)
. Ainsi nous calculons la distribution cumulative de la zone à échantillonner, en intégrant la zone de la tranche du pôle sud au pôle nord.(où truc =
dφ*r
)Nous allons maintenant essayer d'obtenir l'inverse du CDF pour en échantillonner: http://en.wikipedia.org/wiki/Inverse_transform_sampling
Tout d'abord, nous normalisons en divisant notre quasi-CDF par sa valeur maximale. Ceci a pour effet secondaire d'annuler les dφ et r.
Donc:
la source
OU ... pour placer 20 points, calculez les centres des faces icosaèdres. Pour 12 points, trouvez les sommets de l'icosaèdre. Pour 30 points, le point médian des arêtes de l'icosaèdre. vous pouvez faire la même chose avec le tétraèdre, le cube, le dodécaèdre et les octaèdres: un ensemble de points se trouve sur les sommets, un autre au centre de la face et un autre au centre des arêtes. Ils ne peuvent cependant pas être mélangés.
la source
la source
@robert king C'est une solution vraiment sympa mais qui contient quelques bugs bâclés. Je sais que cela m'a beaucoup aidé, alors ne faites pas attention à la négligence. :) Voici une version nettoyée ....
la source
Cela fonctionne et c'est d'une simplicité mortelle. Autant de points que vous le souhaitez:
la source