Distribuire uniformemente n punti su una sfera


121

Ho bisogno di un algoritmo che possa darmi posizioni attorno a una sfera per N punti (meno di 20, probabilmente) che le distribuisce vagamente. Non c'è bisogno di "perfezione", ma ne ho solo bisogno in modo che nessuno di loro sia raggruppato insieme.

  • Questa domanda ha fornito un buon codice, ma non sono riuscito a trovare un modo per rendere questa uniforme, poiché sembrava casuale al 100%.
  • Questo post del blog consigliato aveva due modi per consentire l'inserimento del numero di punti sulla sfera, ma l' algoritmo di Saff e Kuijlaars è esattamente in psuedocodice che potrei trascrivere e l' esempio di codice che ho trovato conteneva "nodo [k]", che non potevo vedere spiegato e rovinato questa possibilità. Il secondo esempio del blog è stata la spirale della sezione aurea, che mi ha dato risultati strani e raggruppati, senza un modo chiaro per definire un raggio costante.
  • Questo algoritmo da questa domanda sembra che potrebbe funzionare, ma non riesco a mettere insieme ciò che c'è su quella pagina in psuedocode o altro.

Alcuni altri thread di domande che ho incontrato parlavano di distribuzione uniforme casuale, che aggiunge un livello di complessità che non mi preoccupa. Mi scuso per il fatto che questa sia una domanda così sciocca, ma volevo dimostrare che ho davvero guardato bene e sono ancora in ritardo.

Quindi, quello che sto cercando è un semplice pseudocodice per distribuire uniformemente N punti attorno a una sfera unitaria, che ritorni in coordinate sferiche o cartesiane. Ancora meglio se può anche distribuirsi con un po 'di casualità (pensa ai pianeti attorno a una stella, distribuiti in modo decente, ma con margine di manovra).


Cosa intendi con "con un po 'di randomizzazione"? Intendi in qualche modo perturbazioni?
ninjagecko

32
OP è confuso. Quello che sta cercando è mettere n punti su una sfera, in modo che la distanza minima tra due punti qualsiasi sia la più grande possibile. Ciò darà ai punti l'aspetto di essere "distribuiti uniformemente" su tutta la sfera. Questo è completamente estraneo alla creazione di una distribuzione casuale uniforme su una sfera, che è l'argomento di molti di questi collegamenti e di cui parlano molte delle risposte seguenti.
BlueRaja - Danny Pflughoeft

1
20 non sono molti punti da posizionare su una sfera se non vuoi che sembrino casuali.
John Alexiou

2
Ecco un modo per farlo (ha esempi di codice): pdfs.semanticscholar.org/97a6/… (sembra che usi calcoli della forza di repulsione)
trusktr

1
Ovviamente per i valori su N in {4, 6, 8, 12, 20} esistono soluzioni esatte in cui la distanza da ogni punto a (ciascuno dei) suoi vicini più vicini è una costante per tutti i punti e tutti i vicini più vicini.
dmckee --- gattino ex moderatore

Risposte:


13

In questo esempio il codice node[k] è solo il k-esimo nodo. Stai generando un array N punti enode[k] è il k-esimo (da 0 a N-1). Se questo è tutto ciò che ti confonde, spero che tu possa usarlo ora.

(in altre parole, kè una matrice di dimensione N definita prima dell'inizio del frammento di codice e che contiene un elenco dei punti).

In alternativa , basandosi sull'altra risposta qui (e utilizzando 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

Se lo tracciate, vedrete che la spaziatura verticale è maggiore vicino ai poli in modo che ogni punto si trovi all'incirca nella stessa area totale di spazio (vicino ai poli c'è meno spazio "orizzontalmente", quindi dà più "verticalmente" ).

Questo non è lo stesso di tutti i punti che hanno all'incirca la stessa distanza dai loro vicini (che è ciò di cui penso parlino i tuoi link), ma potrebbe essere sufficiente per quello che vuoi e migliora semplicemente creando una griglia lat / lon uniforme .


bello, è bello vedere una soluzione matematica. Stavo pensando di utilizzare un'elica e una separazione della lunghezza dell'arco. Non sono ancora sicuro di come ottenere la soluzione ottimale che è un problema interessante.
robert king

hai visto che ho modificato la mia risposta per includere una spiegazione del nodo [k] in alto? penso che potrebbe essere tutto ciò di cui hai bisogno ...
andrew cooke

Meraviglioso, grazie per la spiegazione. Lo proverò più tardi, dato che non ho tempo al momento, ma grazie mille per avermi aiutato. Ti farò sapere come va a finire per i miei scopi. ^^
Avvicinarsi il

L'uso del metodo a spirale si adatta perfettamente alle mie esigenze, grazie mille per l'aiuto e il chiarimento. :)
Arrivo l'

13
Il collegamento sembra morto.
Scheintod

140

L'algoritmo della sfera di Fibonacci è ottimo per questo. È veloce e dà risultati che a colpo d'occhio inganneranno facilmente l'occhio umano. Puoi vedere un esempio eseguito con l'elaborazione che mostrerà il risultato nel tempo man mano che vengono aggiunti punti. Ecco un altro ottimo esempio interattivo realizzato da @gman. Ed ecco una semplice implementazione in 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 campioni ti danno questo:

inserisci qui la descrizione dell'immagine


una variabile n viene chiamata quando si definisce phi: phi = ((i + rnd)% n) * incremento. N = campioni?
Andrew Staroscik

@AndrewStaroscik yes! Quando ho scritto il codice per la prima volta ho usato "n" come variabile e ho cambiato il nome in seguito, ma non ho fatto la dovuta diligenza. Grazie per averlo notato!
Fnord


4
@Xarbrough il codice ti dà punti attorno a una sfera unitaria, quindi moltiplica ogni punto per qualsiasi scalare desideri per il raggio.
Fnord

2
@Fnord: Possiamo farlo per dimensioni superiori?
pikachuchameleon

108

Il metodo della spirale aurea

Hai detto che non riuscivi a far funzionare il metodo della spirale aurea ed è un peccato perché è davvero, davvero buono. Vorrei darti una comprensione completa in modo che forse tu possa capire come evitare che questo sia "ammucchiato".

Quindi ecco un modo veloce e non casuale per creare un reticolo approssimativamente corretto; come discusso sopra, nessun reticolo sarà perfetto, ma questo potrebbe essere abbastanza buono. Viene confrontato con altri metodi, ad esempio in BendWavy.org, ma ha solo un aspetto gradevole e carino, oltre a una garanzia di spaziatura uniforme nel limite.

Primer: spirali di girasole sul disco dell'unità

Per comprendere questo algoritmo, ti invito prima a guardare l'algoritmo della spirale di girasole 2D. Ciò si basa sul fatto che il numero più irrazionale è la sezione aurea (1 + sqrt(5))/2e se si emettono punti con l'approccio "stare al centro, girare una sezione aurea di intere curve, quindi emettere un altro punto in quella direzione", si costruisce naturalmente un spirale che, man mano che si arriva a numeri sempre più alti di punti, rifiuta tuttavia di avere 'barre' ben definite su cui i punti si allineano. (Nota 1.)

L'algoritmo per la spaziatura uniforme su un disco è,

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

e produce risultati che assomigliano a (n = 100 en = 1000):

inserisci qui la descrizione dell'immagine

Distanziare i punti radialmente

La cosa strana chiave è la formula r = sqrt(indices / num_pts); come sono arrivato a quello? (Nota 2.)

Bene, sto usando la radice quadrata qui perché voglio che abbiano una spaziatura uniforme attorno al disco. Ciò equivale a dire che nel limite di N grande voglio che una piccola regione R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) contenga un numero di punti proporzionale alla sua area, che è r d r d θ . Ora, se pretendiamo di parlare di una variabile casuale qui, questa ha un'interpretazione semplice come dire che la densità di probabilità congiunta per ( R , Θ ) è semplicemente crper qualche costante c . La normalizzazione sull'unità disco forzerebbe quindi c = 1 / π.

Ora lascia che ti presenti un trucco. Deriva dalla teoria della probabilità in cui è noto come campionamento della CDF inversa : supponiamo di voler generare una variabile casuale con una densità di probabilità f ( z ) e di avere una variabile casuale U ~ Uniforme (0, 1), proprio come esce da random()nella maggior parte dei linguaggi di programmazione. Come fai a fare questo?

  1. Innanzitutto, trasforma la tua densità in una funzione di distribuzione cumulativa o CDF, che chiameremo F ( z ). Un CDF, ricorda, aumenta monotonicamente da 0 a 1 con la derivata f ( z ).
  2. Quindi calcola la funzione inversa del CDF F -1 ( z ).
  3. Scoprirai che Z = F -1 ( U ) è distribuito in base alla densità target. (Nota 3).

Ora il trucco della spirale della sezione aurea distanzia i punti in uno schema piacevolmente uniforme per θ quindi integriamolo; per l'unità disco ci rimane con F ( r ) = r 2 . Quindi la funzione inversa è F -1 ( u ) = u 1/2 , e quindi genereremmo punti casuali sul disco in coordinate polari conr = sqrt(random()); theta = 2 * pi * random() .

Ora invece di campionare casualmente questa funzione inversa, la stiamo campionando uniformemente , e la cosa bella del campionamento uniforme è che i nostri risultati su come i punti sono distribuiti nel limite del grande N si comporteranno come se lo avessimo campionato casualmente. Questa combinazione è il trucco. Invece di random()usiamo (arange(0, num_pts, dtype=float) + 0.5)/num_pts, in modo che, diciamo, se vogliamo campionare 10 punti, sono r = 0.05, 0.15, 0.25, ... 0.95. Campioniamo uniformemente r per ottenere una spaziatura di area uguale e utilizziamo l'incremento del girasole per evitare terribili "barre" di punti nell'output.

Ora facendo il girasole su una sfera

I cambiamenti che dobbiamo fare per punteggiare la sfera di punti implicano semplicemente la sostituzione delle coordinate polari con coordinate sferiche. La coordinata radiale ovviamente non entra in questo perché siamo su una sfera unitaria. Per mantenere le cose un po 'più coerenti qui, anche se sono stato addestrato come fisico, userò le coordinate dei matematici in cui 0 ≤ φ ≤ π è la latitudine che scende dal polo e 0 ≤ θ ≤ 2π è la longitudine. Quindi la differenza da sopra è che stiamo sostanzialmente sostituendo la variabile r con φ .

Il nostro elemento area, che era r d r d θ , ora diventa il non molto più complicato sin ( φ ) d φ d θ . Quindi la nostra densità congiunta per una spaziatura uniforme è sin ( φ ) / 4π. Integrando θ , troviamo f ( φ ) = sin ( φ ) / 2, quindi F ( φ ) = (1 - cos ( φ )) / 2. Invertendo questo possiamo vedere che una variabile casuale uniforme apparirebbe come acos (1 - 2 u ), ma campioniamo in modo uniforme invece che in modo casuale, quindi usiamo invece φ k = acos (1 - 2 ( k+ 0,5) / N ). E il resto dell'algoritmo lo proietta solo sulle coordinate x, yez:

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

Anche in questo caso per n = 100 en = 1000 i risultati sono: inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Ulteriore ricerca

Volevo ringraziare il blog di Martin Roberts. Nota che sopra ho creato un offset dei miei indici aggiungendo 0,5 a ciascun indice. Questo è stato solo visivamente attraente per me, ma si scopre che la scelta dell'offset è molto importante e non è costante nell'intervallo e può significare ottenere fino all'8% di precisione nell'imballaggio se scelto correttamente. Dovrebbe esserci anche un modo per fare in modo che la sua sequenza R 2 ricopra una sfera e sarebbe interessante vedere se anche questo ha prodotto una bella copertura uniforme, forse così com'è ma forse deve essere, diciamo, presa solo da metà l'unità quadrata tagliata in diagonale o giù di lì e si estende per ottenere un cerchio.

Appunti

  1. Quelle "barre" sono formate da approssimazioni razionali a un numero, e le migliori approssimazioni razionali a un numero derivano dalla sua espressione di frazione continua, z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))dove zè un numero intero ed n_1, n_2, n_3, ...è una sequenza finita o infinita di numeri interi positivi:

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

    Poiché la parte della frazione 1/(...)è sempre compresa tra zero e uno, un intero grande nella frazione continua consente un'approssimazione razionale particolarmente buona: "uno diviso per qualcosa tra 100 e 101" è meglio di "uno diviso per qualcosa tra 1 e 2." Il numero più irrazionale è quindi quello che è 1 + 1/(1 + 1/(1 + ...))e non ha approssimazioni razionali particolarmente buone; si può risolvere φ = 1 + 1 / φ moltiplicando per φ per ottenere la formula del rapporto aureo.

  2. Per le persone che non hanno molta familiarità con NumPy, tutte le funzioni sono "vettorializzate", quindi sqrt(array)è lo stesso di quello che potrebbero scrivere gli altri linguaggi map(sqrt, array). Quindi questa è un'applicazione componente per componente sqrt. Lo stesso vale per la divisione per uno scalare o per l'addizione con gli scalari: quelli si applicano a tutti i componenti in parallelo.

  3. La dimostrazione è semplice una volta che sai che questo è il risultato. Se chiedi qual è la probabilità che z < Z < z + d z , equivale a chiedere qual è la probabilità che z < F -1 ( U ) < z + d z , applica F a tutte e tre le espressioni notando che è una funzione monotonicamente crescente, quindi F ( z ) < U < F ( z + d z ), espandere il lato destro verso l'esterno per trovare F ( z ) + f (z ) d z , e poiché U è uniforme questa probabilità è solo f ( z ) d z come promesso.


4
Non sono sicuro del motivo per cui questo è così in basso, questo è di gran lunga il miglior metodo veloce per farlo.
WHN

2
@snb grazie per le belle parole! è così in basso in parte perché è molto, molto più giovane di tutte le altre risposte qui. Sono sorpreso che stia andando così bene come è stato.
CR Drost

Una domanda che mi rimane è: quanti punti n devo distribuire per una data distanza massima tra due punti qualsiasi?
Felix D.

1
@FelixD. Sembra una domanda che potrebbe diventare molto complicata molto velocemente, specialmente se inizi a usare, diciamo, distanze del cerchio grande piuttosto che distanze euclidee. Ma forse posso rispondere a una semplice domanda, se si convertono i punti sulla sfera nel loro diagramma di Voronoi, si può descrivere ciascuna cella di Voronoi come avente approssimativamente un'area 4π / N e si può convertirla in una distanza caratteristica fingendo piuttosto che sia un cerchio di un rombo, πr² = 4π / N. Allora r = 2 / √ (N).
CR Drost

2
Usare il teorema del campionamento con un input effettivamente uniforme invece che casuale è una di quelle cose che mi fa dire "Bene, perché il # $% e non ci ho pensato?" . Bello.
dmckee --- gattino ex moderatore il

86

Questo è noto come punti di imballaggio su una sfera e non esiste una soluzione generale e perfetta (nota). Tuttavia, ci sono molte soluzioni imperfette. I tre più popolari sembrano essere:

  1. Crea una simulazione . Tratta ogni punto come un elettrone vincolato a una sfera, quindi esegui una simulazione per un certo numero di passaggi. La repulsione degli elettroni porterà naturalmente il sistema a uno stato più stabile, in cui i punti sono il più lontano possibile l'uno dall'altro.
  2. Rigetto dell'ipercubo . Questo metodo dal suono fantasioso è in realtà molto semplice: scegli uniformemente i punti (molto più che ndi loro) all'interno del cubo che circonda la sfera, quindi rifiuti i punti all'esterno della sfera. Tratta i punti rimanenti come vettori e normalizzali. Questi sono i tuoi "campioni": sceglili nusando un metodo (a caso, avido, ecc.).
  3. Approssimazioni a spirale . Tracci una spirale attorno a una sfera e distribuisci uniformemente i punti attorno alla spirale. A causa della matematica coinvolta, questi sono più complicati da capire rispetto alla simulazione, ma molto più veloci (e probabilmente coinvolgono meno codice). Il più popolare sembra essere quello di Saff, et al .

Un sacco informazioni di più su questo problema può essere trovato qui


Prenderò in esame la tattica a spirale che Andrew Cooke ha postato di seguito, tuttavia, potresti chiarire la differenza tra ciò che voglio e cos'è la "distribuzione casuale uniforme"? È solo il posizionamento casuale al 100% di punti su una sfera in modo che siano posizionati uniformemente? Grazie per l'aiuto. :)
Avviene il

4
@Befall: "distribuzione casuale uniforme" si riferisce alla distribuzione di probabilità che è uniforme - significa che, quando si sceglie un punto casuale sulla sfera, ogni punto ha la stessa probabilità di essere scelto. Non ha nulla a che fare con la distribuzione spaziale finale dei punti, e quindi non ha nulla a che fare con la tua domanda.
BlueRaja - Danny Pflughoeft

Ahhh, okay, grazie mille. La ricerca della mia domanda ha portato a un sacco di risposte per entrambi, e non sono riuscito a capire cosa fosse inutile per me.
Arrivo il

Per essere chiari, ogni punto ha zero probabilità di essere scelto. Il rapporto tra le probabilità che il punto apparterrà a due aree qualsiasi sulla superficie della sfera, è uguale al rapporto tra le superfici.
AturSams

2
L'ultimo collegamento è ora morto
Felix D.

10

Quello che stai cercando è chiamato rivestimento sferico . Il problema della copertura sferica è molto difficile e le soluzioni sono sconosciute tranne che per un piccolo numero di punti. Una cosa certa è che dati n punti su una sfera, esistono sempre due punti di distanza d = (4-csc^2(\pi n/6(n-2)))^(1/2)o più vicini.

Se vuoi un metodo probabilistico per generare punti distribuiti uniformemente su una sfera, è facile: genera punti nello spazio in modo uniforme mediante distribuzione gaussiana (è integrato in Java, non è difficile trovare il codice per altri linguaggi). Quindi nello spazio tridimensionale, hai bisogno di qualcosa di simile

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

Quindi proiettare il punto sulla sfera normalizzando la sua distanza dall'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 distribuzione gaussiana in n dimensioni è sfericamente simmetrica, quindi la proiezione sulla sfera è uniforme.

Ovviamente, non vi è alcuna garanzia che la distanza tra due punti qualsiasi in una raccolta di punti generati in modo uniforme sarà delimitata di seguito, quindi puoi utilizzare il rifiuto per applicare tali condizioni che potresti avere: probabilmente è meglio generare l'intera raccolta e poi rifiutare l'intera raccolta, se necessario. (Oppure usa "rifiuto anticipato" per rifiutare l'intera raccolta che hai generato finora; semplicemente non mantenere alcuni punti e rilasciarne altri.) Puoi usare la formula per ddata sopra, meno un po 'di allentamento, per determinare la distanza minima tra punti al di sotto dei quali rifiuterai un insieme di punti. Dovrai calcolare n scegliere 2 distanze e la probabilità di rigetto dipenderà dal gioco; è difficile dire come, quindi esegui una simulazione per avere un'idea delle statistiche pertinenti.


Votato per le espressioni di distanza minima massima. Utile per porre dei limiti al numero di punti che vuoi utilizzare. Un riferimento a una fonte autorevole per questo sarebbe carino, però.
dmckee --- gattino ex moderatore

6

Questa risposta si basa sulla stessa "teoria" che è ben delineata da questa risposta

Aggiungo questa risposta come:
- Nessuna delle altre opzioni si adatta alla necessità di "uniformità" "azzeccata" (o non è chiaramente così). (Tenendo presente che per ottenere un comportamento simile alla distribuzione del pianeta desiderato in modo particolare nella domanda originale, devi semplicemente rifiutare dalla lista finita dei k punti creati in modo uniforme a caso (casuale rispetto al conteggio dell'indice nei k elementi).) - Inoltre, è molto difficile distinguere tra le altre opzioni senza alcuna immagine, quindi ecco come appare questa opzione (sotto) e l'implementazione pronta per l'esecuzione che ne consegue.
- Il più vicino altri impl ti hanno costretto a decidere la 'N' per 'asse angolare', contro solo 'un valore di N' su entrambi i valori dell'asse angolare (che a bassi conteggi di N è molto difficile sapere cosa può, o non può, avere importanza ( ad esempio, vuoi '5' punti - divertiti))

con N a 20:

inserisci qui la descrizione dell'immagine
e poi N a 80: inserisci qui la descrizione dell'immagine


ecco il codice python3 pronto per essere eseguito, in cui l'emulazione è la stessa fonte: " http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere " trovato da altri . (Il grafico che ho incluso, che si attiva quando viene eseguito come 'main', è tratto da: 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

testato con conteggi bassi (N in 2, 5, 7, 13, ecc.) e sembra funzionare "bene"


5

Provare:

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 funzione di cui sopra dovrebbe essere eseguita in loop con N loop totali e k iterazioni di corrente.

Si basa su un motivo a semi di girasole, tranne per il fatto che i semi di girasole sono curvati in una mezza cupola e di nuovo in una sfera.

Ecco un'immagine, tranne per il fatto che ho messo la fotocamera a metà strada all'interno della sfera in modo che appaia 2d anziché 3d perché la fotocamera è alla stessa distanza da tutti i punti. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg


2

Healpix risolve un problema strettamente correlato (pixelizzazione della sfera con pixel di area uguale):

http://healpix.sourceforge.net/

Probabilmente è eccessivo, ma forse dopo averlo guardato ti renderai conto che alcune delle sue altre belle proprietà sono interessanti per te. È molto più di una semplice funzione che genera una nuvola di punti.

Sono atterrato qui cercando di ritrovarlo; il nome "healpix" non evoca esattamente le sfere ...


1

con un numero ridotto di punti potresti eseguire una simulazione:

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

per migliorare la mia risposta dovresti cambiare più vicino_index = i a più vicino_indice = randchoice (i, j)
robert king

1

Prendi i due fattori più grandi del tuo N, se N==20i due fattori più grandi sono {5,4}, o, più in generale {a,b}. Calcolare

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

Metti il ​​tuo primo punto su {90-dlat/2,(dlong/2)-180}, il secondo su {90-dlat/2,(3*dlong/2)-180}, il tuo terzo su {90-dlat/2,(5*dlong/2)-180}, finché non sei inciampato in giro per il mondo una volta, a quel punto devi circa {75,150}quando vai accanto a {90-3*dlat/2,(dlong/2)-180}.

Ovviamente lo sto lavorando in gradi sulla superficie della terra sferica, con le solite convenzioni per la traslazione di +/- in N / S o E / O. E ovviamente questo ti dà una distribuzione completamente non casuale, ma è uniforme ei punti non sono raggruppati insieme.

Per aggiungere un certo grado di casualità, potresti generare 2 distribuiti normalmente (con media 0 e dev std di {dlat / 3, dlong / 3} come appropriato) e aggiungerli ai tuoi punti distribuiti uniformemente.


5
sarebbe molto meglio se lavorassi in sin (lat) piuttosto che in lat. così com'è, otterrai molti raggruppamenti vicino ai poli.
andrew cooke

1

modifica: questo non risponde alla domanda che l'OP intendeva porre, lasciandolo qui nel caso in cui le persone lo trovassero utile in qualche modo.

Usiamo la regola di moltiplicazione della probabilità, combinata con infiniti. Ciò si traduce in 2 righe di codice per ottenere il risultato desiderato:

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

(definito nel seguente sistema di coordinate :)

inserisci qui la descrizione dell'immagine

La tua lingua ha tipicamente una primitiva di numeri casuali uniformi. Ad esempio in Python puoi usare random.random()per restituire un numero nell'intervallo [0,1). Puoi moltiplicare questo numero per k per ottenere un numero casuale nell'intervallo [0,k). Quindi in python, uniform([0,2pi))significherebbe random.random()*2*math.pi.


Prova

Ora non possiamo assegnare θ in modo uniforme, altrimenti ci ammasseremmo ai poli. Vogliamo assegnare probabilità proporzionali alla superficie del cuneo sferico (il θ in questo diagramma è in realtà φ):

inserisci qui la descrizione dell'immagine

Uno spostamento angolare dφ all'equatore risulterà in uno spostamento di dφ * r. Quale sarà lo spostamento a un azimut arbitrario θ? Bene, il raggio dall'asse z è r*sin(θ), quindi la lunghezza dell'arco di quella "latitudine" che interseca il cuneo è dφ * r*sin(θ). Quindi calcoliamo la distribuzione cumulativa dell'area da campionare da essa, integrando l'area della fetta dal polo sud al polo nord.

inserisci qui la descrizione dell'immagine(dove roba = dφ*r)

Tenteremo ora di ottenere l'inverso del CDF per campionarlo: http://en.wikipedia.org/wiki/Inverse_transform_sampling

Per prima cosa normalizziamo dividendo il nostro quasi CDF per il suo valore massimo. Questo ha l'effetto collaterale di cancellare dφ e r.

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

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

Così:

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

non è equivalente all'opzione che ha scartato come "100% randomizzato"? la mia comprensione è che vuole che siano spaziati in modo più uniforme rispetto a una distribuzione casuale uniforme.
andrew cooke

@ BlueRaja-DannyPflughoeft: Hmm, abbastanza giusto. Immagino di non aver letto la domanda attentamente come avrei dovuto. Lo lascio comunque qui nel caso altri lo trovassero utile. Grazie per averlo fatto notare.
ninjagecko

1

OPPURE ... per posizionare 20 punti, calcola i centri delle facce icosaedronali. Per 12 punti, trova i vertici dell'icosaedro. Per 30 punti, il punto medio dei bordi dell'icosaedro. puoi fare la stessa cosa con il tetraedro, il cubo, il dodecaedro e gli ottaedri: un insieme di punti è sui vertici, un altro al centro della faccia e un altro al centro dei bordi. Tuttavia, non possono essere mescolati.


Una buona idea, ma funziona solo per 4, 6, 8, 12, 20, 24 o 30 punti.
Il ragazzo con il cappello

Se vuoi imbrogliare, puoi usare il centro di facce e vertici. Saranno Non essere equi-spaziati, ma un'approssimazione decente. Questo è bello perché deterministico.
chessofnerd

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
Sarebbe utile se scrivessi del testo che spieghi cosa si intende fare, quindi l'OP non deve crederlo nella certezza che funzionerà.
hcarver

0

@robert king È una soluzione davvero carina ma contiene alcuni bug sciatti. So che mi ha aiutato molto, quindi non importa la sciatteria. :) Ecco una versione ripulita ....

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

Funziona ed è semplicissimo. Tutti i punti che vuoi:

    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());
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.