web-dev-qa-db-fra.com

Répartir uniformément n points sur une sphère

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 étend vaguement. Il n'y a pas besoin de "perfection", mais j'en ai juste besoin pour qu'aucun d'entre eux ne soit groupé.

  • Cette question a fourni un bon code, mais je ne pouvais pas trouver un moyen de faire cet uniforme, car cela semblait 100% aléatoire.
  • Ce billet de blog recommandé avait deux manières permettant de saisir le nombre de points sur la sphère, mais l'algorithme Saff et Kuijlaars est exactement en psuedocode que je pourrais transcrire, et le exemple de code J'ai trouvé contenu "noeud [k]", ce que je ne pouvais pas voir expliqué et ruiné cette possibilité. Le deuxième exemple de blog était la spirale de la section d’or, ce qui m’a donné d’étranges résultats, sans aucun moyen clair de définir un rayon constant.
  • Cet algorithme de cette question semble pouvoir fonctionner, mais je ne peux pas reconstituer le contenu de cette page en psuedocode ni quoi que ce soit.

Quelques autres questions que j'ai rencontrées ont parlé de la distribution uniforme randomisée, ce qui ajoute un niveau de complexité qui ne m'inquiète pas. Je suis désolé, mais c’est une question aussi stupide, mais je voulais montrer que j’ai vraiment regardé haut et que j’ai encore fait court.

Donc, ce que je recherche, c’est un pseudocode simple pour répartir uniformément N points autour d’une unité de sphère, qui retourne en coordonnées sphériques ou cartésiennes. Encore mieux s’il peut même se distribuer avec un peu de randomisation (imaginez des planètes autour d’une étoile, bien réparties, mais avec une marge de liberté).

104
Befall

Dans cet exemple de codenode[k] n'est que le kème nœud. Vous générez un tableau N points et node[k] est le kième (de 0 à N-1). Si c’est tout ce qui vous dérange, espérons que vous pourrez l’utiliser maintenant.

(en d'autres termes, k est un tableau de taille N défini avant le début du fragment de code et contenant une liste des points).

Alternativement , construisant sur l'autre réponse ici (et en utilisant 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

Si vous tracez cela, vous verrez que l'espacement vertical est plus grand près des pôles, de sorte que chaque point se situe dans à peu près la même superficie totale (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 de leurs voisins (c'est ce dont vos liens parlent, je pense), mais cela peut suffire à ce que vous voulez et améliore simplement la création d'une grille uniforme lat/lon .

11
andrew cooke

L'algorithme de sphère de Fibonacci est idéal pour cela. C'est rapide et donne des résultats qui en un coup d'oeil tromperont facilement l'oeil humain. Vous pouvez voir un exemple de 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 rapide python avec une option de randomisation simple:

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 échantillons vous donnent ceci:

enter image description here

113
Fnord

C’est ce que l’on appelle des points d’emballage sur une sphère et il n’existe pas de solution générale (connue) parfaite. Cependant, il existe de nombreuses solutions imparfaites. Les trois plus populaires semblent être:

  1. Créez une simulation . Traitez chaque point comme un électron contraint à une sphère, puis exécutez une simulation pour un certain nombre d'étapes. La répulsion des électrons tendra naturellement le système à un état plus stable, où les points sont à peu près aussi éloignés les uns des autres que possible.
  2. Rejet de l'hypercube . En réalité, cette méthode est très simple: vous choisissez uniformément des points (beaucoup plus que n d’eux) à l’intérieur du cube entourant la sphère, puis vous les rejetez en dehors de la sphère. Traitez les points restants comme des vecteurs et normalisez-les. Ce sont vos "échantillons" - choisissez n en utilisant une méthode (aléatoire, gourmande, etc.).
  3. Approximations en spirale . Vous tracez une spirale autour d'une sphère et répartissez uniformément les points autour de la spirale. En raison des mathématiques impliquées, elles sont plus compliquées à comprendre que la simulation, mais beaucoup plus rapide (et impliquant probablement moins de code). Le plus populaire semble être par Saff, et al .

A beaucoup plus d'informations sur ce problème peuvent être trouvées ici

La méthode de la spirale d'or

Vous avez dit que vous ne pouviez pas utiliser la méthode de la spirale dorée et c'est dommage car c'est vraiment très bon. Je voudrais vous en donner une compréhension complète afin que vous puissiez peut-être comprendre comment éviter que cela ne soit "groupé".

Il existe donc un moyen rapide et non aléatoire de créer un réseau approximativement correct. comme discuté ci-dessus, aucun réseau ne sera parfait, mais cela peut être "assez bon". Il est comparé à d’autres méthodes, par ex. at BendWavy.org mais il a juste un joli et joli look ainsi qu'une garantie d'un espacement uniforme dans la limite.

Primer: spirales de tournesol sur le disque de l'unité

Pour comprendre cet algorithme, je vous invite tout d'abord à examiner l'algorithme 2D en spirale du 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 "placez-vous au centre, tournez un nombre d'or de tours entiers, puis émettez un autre point dans cette direction. , "on construit naturellement une spirale qui, quand on arrive à des nombres de points de plus en plus élevés, refuse néanmoins de disposer de" barres "bien définies sur lesquelles les points s'alignent.(Note 1.)

L'algorithme pour l'espacement uniforme sur un disque est,

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

et cela donne des résultats qui ressemblent à (n = 100 et n = 1000):

enter image description here

Espacer les points radialement

La chose étrange clé 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 ceux-ci aient un espacement uniforme autour de la sphère. C’est la même chose que de dire que dans la limite des grands [~ # ~] n [~ # ~] je veux une petite région [~ # ~] r [~ # ~] ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) contient un nombre de points proportionnel à sa surface, ce qui correspond à r d r d θ . Maintenant, si nous prétendons que nous parlons d’une variable aléatoire ici, cela a une interprétation directe qui dit que la densité de probabilité conjointe pour ( [~ # ~] r [~ # ~] , Θ ) est simplement cr pour une constante c . La normalisation sur le disque de l'unité force alors c = 1/π.

Maintenant, laissez-moi vous présenter un truc. Il provient de la théorie des probabilités où il est connu sous le nom échantillonnage du CDF inverse : supposons que vous vouliez générer une variable aléatoire avec une densité de probabilité f ( z ) et vous avez une variable aléatoire [~ # ~] u [~ # ~] ~ Uniforme (0, 1), comme dans random() dans la plupart des langages de programmation. Comment est-ce que tu fais ça?

  1. Commencez par transformer votre densité en un fonction de distribution cumulative [~ # ~] f [~ # ~] ( z ), qui, rappelons-le, augmente monotonement de 0 à 1 avec dérivée f ( z ).
  2. Calculez ensuite la fonction inverse du CDF [~ # ~] f [~ # ~] -1( z ).
  3. Vous constaterez que [~ # ~] z [~ # ~] = [~ # ~] f [~ # ~] -1( [~ # ~] u [~ # ~] ) est distribué en fonction de la densité cible. (Note 3).

Maintenant, le tour en spirale du nombre d'or espacera les points dans un motif bien uniforme pour θ alors intégrons cela à l'extérieur; pour le cercle unitaire, il nous reste [~ # ~] 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 la sphère en coordonnées polaires avec r = sqrt(random()); theta = 2 * pi * random().

Maintenant au lieu de au hasard échantillonner cette fonction inverse, nous sommes uniformément l'échantillonner, 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 de grande [~ # ~] n [~ # ~] == se comportera comme si nous l'avions échantillonné de manière aléatoire. Cette combinaison est le truc. Au lieu de random(), nous utilisons (arange(0, num_pts, dtype=float) + 0.5)/num_pts, De sorte que, par exemple, si nous voulons échantillonner 10 points, ils sont r = 0.05, 0.15, 0.25, ... 0.95. Nous échantillonnons uniformément r pour obtenir un espacement égal des surfaces, et nous utilisons l'incrément de tournesol pour éviter de terribles "barres" de points dans la sortie.

Maintenant faire le tournesol sur une sphère

Les changements que nous devons faire pour doter la sphère de points consistent simplement à désactiver les coordonnées polaires pour les coordonnées sphériques. Bien entendu, la coordonnée radiale n’entre pas dans ceci parce que nous sommes sur une sphère unité. Pour garder les choses un peu plus cohérentes ici, même si j'ai été formé en physicien, j'utiliserai les coordonnées des mathématiciens où 0 ≤ φ ≤ π est la latitude descendant du pôle et 0 ≤ θ ≤ 2π est la longitude. La différence avec ce qui précède est donc que nous remplaçons la variable r par φ .

Notre élément de surface, qui était r d r d θ , devient maintenant le péché pas beaucoup plus compliqué ( φ ) d φ d θ . Donc, notre densité de joint pour un espacement uniforme est sin ( φ )/4π. Intégrant dehors θ , 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 de manière uniforme au lieu d’aléatoire, nous utilisons donc φ k  = acos (1 - 2 ( k + 0,5)/ [~ # ~] n [~ # ~] ). Et le reste de l'algorithme ne fait que projeter cela sur les coordonnées x, y et 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()

Encore une fois pour n = 100 et n = 1000, les résultats sont les suivants: enter image description hereenter image description here

Remarques

  1. 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 + ...)))z est un entier et n_1, n_2, n_3, ... est une suite finie ou infinie d’entiers positifs:

    def continued_fraction(r):
        while r != 0:
            n = floor(r)
            yield n
            r = 1/(r - n)
    

    Puisque 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 est 1 + 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.

    1. Pour ceux qui ne connaissent pas bien NumPy, toutes les fonctions sont "vectorisées", de sorte que sqrt(array) est identique à ce que les autres langages peuvent écrire map(sqrt, array). Il s’agit donc d’une application composant par composant sqrt. Il en va de même pour la division par un scalaire ou l'addition de scalaires - ceux-ci s'appliquent à tous les composants en parallèle.

    2. 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 , applique [~ # ~] f [~ # ~] == == ==) == aux trois expressions, notant qu'il s'agit d'une fonction à croissance monotone, d'où [ ~ # ~] f [~ # ~] ( z ) < [~ # ~] u [~ # ~] < [~ # ~] f [~ # ~] ( z + d z ), développez le côté droit pour trouver [~ # ~] f [~ # ~] ( z ) + f ( z ) d z , et depuis [~ # ~] u [~ # ~] est uniforme, cette probabilité est juste f ( z ) d z comme promis.

68
CR Drost

Ce que vous recherchez s'appelle un revêtement sphérique. Le problème de recouvrement sphérique est très difficile et les solutions sont inconnues, à l'exception d'un petit nombre de points. Ce qui est certain, c’est que pour n points d’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, rien de plus simple: générez des points dans l’espace uniformément par distribution gaussienne (il est intégré à Java, il n’est pas difficile de trouver le code pour d’autres langues). Donc, dans un espace tridimensionnel, vous avez besoin de quelque chose comme

Random r = new Random();
double[] p = { r.nextGaussian(), r.nextGaussian(), r.nextGaussian() };

Puis projetez le point sur la sphère en normalisant sa distance à l’origine

double norm = Math.sqrt( (p[0])^2 + (p[1])^2 + (p[2])^2 ); 
double[] sphereRandomPoint = { p[0]/norm, p[1]/norm, p[2]/norm };

La distribution gaussienne dans n dimensions est symétrique sphérique, de sorte que la projection sur la sphère est uniforme.

Bien entendu, rien ne garantit que la distance entre deux points d'une collection de points générés uniformément sera délimitée ci-dessous. Vous pouvez donc utiliser le rejet pour imposer les conditions que vous pourriez avoir: il est probablement préférable de générer l'ensemble de la collection, puis rejeter toute la collection si nécessaire. (Ou utilisez "rejet précoce" pour rejeter toute la collection que vous avez générée jusqu'à présent; ne gardez pas certains points et n'en déposez pas d'autres.) Vous pouvez utiliser la formule pour d donnée ci-dessus, moins le temps mort, pour déterminer la distance minimale entre les points en dessous de laquelle vous rejetterez un ensemble de points. Vous devrez calculer n choisissez 2 distances et la probabilité de rejet dépendra du jeu; il est difficile de dire comment, alors lancez une simulation pour avoir une idée des statistiques pertinentes.

7
Edward Doolittle

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:
- Aucune des autres options ne correspond au besoin d'uniformité (ou pas manifestement). (En notant que pour que la planète ressemble à un comportement de distribution particulièrement voulu dans la demande initiale, vous rejetez simplement de la liste finie des k points uniformément créés aléatoirement (au hasard le nombre d'index dans les k éléments de retour).)
- L’autre impl. Le plus proche vous a forcé à choisir le "N" par "axe angulaire", par opposition à "une seule valeur de N" entre les deux angular axes (qui avec un faible nombre de N, il est très difficile de savoir ce qui est important ou non (par exemple, vous voulez des "5 points - amusez-vous))
- En outre, il est très difficile de comprendre comment différencier les autres options sans aucune image. Voici donc à quoi ressemble cette option (ci-dessous) et son implémentation prête à l'emploi. .

avec N à 20:

enter image description here
puis N en 80:  enter image description here


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 "principal", provient de: 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 __== '__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

testé à faible taux (N dans 2, 5, 7, 13, etc.) et semble fonctionner 'Nice'

6
Matt S.

Essayer:

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

La fonction ci-dessus doit être exécutée en boucle avec le nombre total de boucles et l'itération en cours de la boucle N.

Il est basé sur un motif de graines de tournesol, sauf que les graines de tournesol sont incurvées dans un demi-dôme, puis dans une sphère.

Voici une photo, sauf que je place la caméra à mi-chemin à l'intérieur de la sphère, de sorte qu'elle a l'air 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

4
com.prehensible

Healpix résout un problème étroitement lié (pixelisation 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 examiné, vous vous rendrez compte que certaines des autres propriétés de Nice vous intéressent. C'est beaucoup plus qu'une simple fonction qui génère un nuage de points.

J'ai atterri ici pour essayer de le retrouver; le nom "healpix" n'évoque pas exactement les sphères ...

2
Andrew Wagner

OU ... pour placer 20 points, calculez les centres des faces icosahédronales. Pour 12 points, trouvez les sommets de l'icosaèdre. Pour 30 points, le milieu des bords 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 pas être mélangés, cependant.

1
user19371

edit: Ceci ne répond pas à la question que le PO voulait poser, le laissant ici au cas où les gens le jugeraient utile.

Nous utilisons la règle de probabilité de multiplication, combinée à des infinis-minimaux. Cela donne 2 lignes de code pour obtenir le résultat souhaité:

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

(défini dans le système de coordonnées suivant :)

enter image description here

Votre langue a généralement une primitive de nombre aléatoire uniforme. Par exemple, dans python, vous pouvez utiliser random.random() pour renvoyer un nombre compris dans l'intervalle [0,1). Vous pouvez multiplier ce nombre par k pour obtenir un nombre aléatoire. la plage [0,k). Ainsi, en python, uniform([0,2pi)) signifierait random.random()*2*math.pi.


Preuve

Maintenant, nous ne pouvons pas attribuer θ de manière uniforme, sinon nous nous regrouperions aux pôles. Nous souhaitons attribuer des probabilités proportionnelles à la surface du coin sphérique (le θ dans ce diagramme est en fait):

enter image description here

Un angular déplacement dφ à l'équateur entraînera 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 de l'arc de cette "latitude" coupant le coin est dφ * 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.

enter image description here (où choses = dφ*r)

Nous allons maintenant essayer d’obtenir l’inverse du CDF: http://en.wikipedia.org/wiki/Inverse_transform_sampling

Nous normalisons d’abord en divisant notre quasi-CDF par sa valeur maximale. Cela a pour effet d'annuler les d et r.

azimuthalCDF: cumProb = (sin(θ)+1)/2 from -pi/2 to pi/2

inverseCDF: θ = -sin^(-1)(1 - 2*cumProb)

Ainsi:

let x by a random float in range [0,1]
θ = -arcsin(1-2*x)
1
ninjagecko

avec un petit nombre de points, vous pouvez exécuter une simulation:

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)]
1
robert king

Prenez les deux facteurs les plus importants de votre N, si N==20, Les deux facteurs les plus importants sont {5,4} Ou plus généralement {a,b}. Calculer

dlat  = 180/(a+1)
dlong = 360/(b+1})

Placez votre premier point sur {90-dlat/2,(dlong/2)-180}, votre second sur {90-dlat/2,(3*dlong/2)-180}, votre 3ème sur {90-dlat/2,(5*dlong/2)-180}, jusqu'à ce que vous ayez fait le tour du monde une fois. Nous en arrivons à environ {75,150} lorsque vous passez à côté de {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 bien évidemment, cela vous donne une distribution totalement 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 0 et un dév std de {dlat/3, dlong/3}) et les ajouter à vos points uniformément distribués.

1
# 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)])
0
ksmith