Necesito un algoritmo que pueda darme posiciones alrededor de una esfera para N puntos (menos de 20, probablemente) que los distribuya vagamente. No hay necesidad de "perfección", pero solo la necesito para que ninguno de ellos esté agrupado.
- Esta pregunta proporcionó un buen código, pero no pude encontrar una manera de hacer este uniforme, ya que parecía 100% aleatorio.
- Esta publicación de blog recomendada tenía dos formas que permitían ingresar el número de puntos en la esfera, pero el algoritmo de Saff y Kuijlaars está exactamente en psuedocode que pude transcribir, y el ejemplo de código que encontré contenía "nodo [k]", que no pude Ver explicado y arruinado esa posibilidad. El segundo ejemplo de blog fue el Golden Section Spiral, que me dio resultados extraños y agrupados, sin una forma clara de definir un radio constante.
- Este algoritmo de esta pregunta parece que podría funcionar, pero no puedo juntar lo que hay en esa página en psuedocode ni nada.
Algunos otros hilos de preguntas que encontré hablaban de distribución uniforme aleatoria, que agrega un nivel de complejidad que no me preocupa. Me disculpo porque esta es una pregunta tan tonta, pero quería mostrar que realmente he buscado mucho y aún me he quedado corto.
Entonces, lo que estoy buscando es un pseudocódigo simple para distribuir uniformemente N puntos alrededor de una esfera unitaria, que regresa en coordenadas esféricas o cartesianas. Aún mejor si se puede distribuir con un poco de aleatorización (piense en planetas alrededor de una estrella, esparcidos decentemente, pero con margen de maniobra).
Respuestas:
En este ejemplo, el código
node[k]
es solo el k-ésimo nodo. Está generando una matriz de N puntos ynode[k]
es el kth (de 0 a N-1). Si eso es todo lo que te confunde, es de esperar que puedas usarlo ahora.(en otras palabras,
k
es una matriz de tamaño N que se define antes de que comience el fragmento de código y que contiene una lista de los puntos).Alternativamente , construyendo sobre la otra respuesta aquí (y usando Python):
Si traza eso, verá que el espaciado vertical es mayor cerca de los polos, de modo que cada punto está situado aproximadamente en la misma área total de espacio (cerca de los polos hay menos espacio "horizontalmente", por lo que da más "verticalmente" ).
Esto no es lo mismo que todos los puntos que tienen aproximadamente la misma distancia a sus vecinos (que es de lo que creo que están hablando sus enlaces), pero puede ser suficiente para lo que desea y mejora simplemente haciendo una cuadrícula uniforme de lat / lon .
fuente
El algoritmo de la esfera de Fibonacci es excelente para esto. Es rápido y da resultados que de un vistazo engañarán fácilmente al ojo humano. Puede ver un ejemplo realizado con procesamiento que mostrará el resultado a lo largo del tiempo a medida que se agregan puntos. Aquí hay otro gran ejemplo interactivo creado por @gman. Y aquí hay una implementación simple en Python.
1000 muestras te dan esto:
fuente
El método de la espiral dorada
Dijiste que no podías conseguir que el método de la espiral dorada funcionara y es una pena porque es realmente bueno. Me gustaría darte una comprensión completa de esto para que tal vez puedas entender cómo evitar que esto se "acumule".
Así que aquí hay una forma rápida y no aleatoria de crear una celosía que sea aproximadamente correcta; como se discutió anteriormente, ninguna celosía será perfecta, pero esto puede ser lo suficientemente bueno. Se compara con otros métodos, por ejemplo, en BendWavy.org, pero tiene un aspecto agradable y bonito, así como una garantía sobre el espaciado uniforme en el límite.
Imprimación: espirales de girasol en el disco unitario
Para comprender este algoritmo, primero lo invito a mirar el algoritmo de espiral de girasol 2D. Esto se basa en el hecho de que el número más irracional es la proporción áurea
(1 + sqrt(5))/2
y si uno emite puntos por el enfoque "párese en el centro, gire una proporción áurea de giros completos, luego emita otro punto en esa dirección", uno naturalmente construye un espiral que, a medida que se llega a un número cada vez mayor de puntos, se niega, sin embargo, a tener "barras" bien definidas en las que se alinean los puntos.(Nota 1.)El algoritmo para el espaciado uniforme en un disco es,
y produce resultados que se parecen a (n = 100 yn = 1000):
Espaciar los puntos radialmente
La clave extraña es la fórmula
r = sqrt(indices / num_pts)
; ¿Cómo llegué a ese? (Nota 2.)Bueno, estoy usando la raíz cuadrada aquí porque quiero que tengan un espacio uniforme alrededor del disco. Eso es lo mismo que decir que en el límite de N grande quiero que una pequeña región R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) contenga un número de puntos proporcional a su área, que es r d r d θ . Ahora, si pretendemos que estamos hablando de una variable aleatoria aquí, esto tiene una interpretación sencilla como decir que la densidad de probabilidad conjunta para ( R , Θ ) es simplemente crpara alguna constante = 1 / π.c . La normalización en el disco unitario forzaría entonces c
Ahora déjame presentarte un truco. Proviene de la teoría de la probabilidad, donde se conoce como muestreo de la CDF inversa : suponga que desea generar una variable aleatoria con una densidad de probabilidad f ( z ) y tiene una variable aleatoria U ~ Uniforme (0, 1), tal como sale de
random()
en la mayoría de los lenguajes de programación. ¿Cómo haces esto?Ahora, el truco de la espiral de la proporción áurea espacia los puntos en un patrón uniforme para θ, así que integremos eso; para el disco unitario nos queda F ( r ) = r 2 . Entonces la función inversa es F -1 ( u ) = u 1/2 , y por lo tanto generaríamos puntos aleatorios en el disco en coordenadas polares con
r = sqrt(random()); theta = 2 * pi * random()
.Ahora, en lugar de muestrear aleatoriamente esta función inversa, la estamos muestreando uniformemente , y lo bueno del muestreo uniforme es que nuestros resultados sobre cómo se distribuyen los puntos en el límite de N grande se comportarán como si lo hubiéramos muestreado aleatoriamente. Esta combinación es el truco. En lugar de
random()
usamos(arange(0, num_pts, dtype=float) + 0.5)/num_pts
, de modo que, digamos, si queremos muestrear 10 puntos, lo sonr = 0.05, 0.15, 0.25, ... 0.95
. Tomamos muestras de r uniformemente para obtener un espaciado de áreas iguales y usamos el incremento de girasol para evitar “barras” de puntos horribles en la salida.Ahora haciendo el girasol en una esfera
Los cambios que debemos hacer para salpicar la esfera con puntos simplemente implican cambiar las coordenadas polares por coordenadas esféricas. La coordenada radial, por supuesto, no entra en esto porque estamos en una esfera unitaria. Para mantener las cosas un poco más consistentes aquí, aunque fui entrenado como físico, usaré las coordenadas de los matemáticos donde 0 ≤ φ ≤ π es la latitud que desciende del polo y 0 ≤ θ ≤ 2π es la longitud. Entonces, la diferencia con respecto a lo anterior es que básicamente estamos reemplazando la variable r con φ .
Nuestro elemento de área, que era r d r d θ , ahora se convierte en el pecado no mucho más complicado ( φ ) d φ d θ . Entonces, nuestra densidad conjunta para un espaciado uniforme es sin ( φ ) / 4π. Integrando θ , encontramos f ( φ ) = sin ( φ ) / 2, entonces F ( φ ) = (1 - cos ( φ )) / 2. Al invertir esto, podemos ver que una variable aleatoria uniforme se vería como acos (1 - 2 u ), pero tomamos muestras de manera uniforme en lugar de aleatoria, por lo que en su lugar usamos φ k = acos (1 - 2 ( k+ 0,5) /N ). Y el resto del algoritmo solo proyecta esto en las coordenadas x, y, z:
Nuevamente, para n = 100 yn = 1000, los resultados se ven así:
Más investigación
Quería dar un saludo al blog de Martin Roberts. Tenga en cuenta que antes creé un desplazamiento de mis índices agregando 0.5 a cada índice. Esto fue visualmente atractivo para mí, pero resulta que la elección de la compensación es muy importante y no es constante durante el intervalo y puede significar obtener hasta un 8% más de precisión en el empaque si se elige correctamente. También debería haber una manera de hacer que su secuencia R 2 cubra una esfera y sería interesante ver si esto también producía una cobertura uniforme y agradable, tal vez tal como está, pero tal vez necesitando ser, digamos, tomado de solo la mitad de el cuadrado de la unidad cortó en diagonal más o menos y se estiró para formar un círculo.
Notas
Esas "barras" están formadas por aproximaciones racionales a un número, y las mejores aproximaciones racionales a un número provienen de su expresión de fracción continua,
z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))
dondez
es un número entero yn_1, n_2, n_3, ...
es una secuencia finita o infinita de enteros positivos:Dado que la parte de la fracción
1/(...)
siempre está entre cero y uno, un número entero grande en la fracción continua permite una aproximación racional particularmente buena: "uno dividido por algo entre 100 y 101" es mejor que "uno dividido por algo entre 1 y 2". El número más irracional es, por tanto, el que es1 + 1/(1 + 1/(1 + ...))
y no tiene aproximaciones racionales particularmente buenas; se puede resolver φ = 1 + 1 / φ multiplicando por φ para obtener la fórmula de la proporción áurea.Para las personas que no están tan familiarizadas con NumPy, todas las funciones están "vectorizadas", por lo que
sqrt(array)
es lo mismo que escribirían otros lenguajesmap(sqrt, array)
. Así que esta es una aplicación componente por componentesqrt
. Lo mismo también se aplica a la división por un escalar o la suma con escalares, que se aplican a todos los componentes en paralelo.La prueba es simple una vez que sabes que este es el resultado. Si pregunta cuál es la probabilidad de que z < Z < z + d z , esto es lo mismo que preguntar cuál es la probabilidad de que z < F -1 ( U ) < z + d z , aplique F a las tres expresiones y observe que es una función que aumenta monótonamente, por lo tanto F ( z ) < U < F ( z + d z ), expanda el lado derecho hacia afuera para encontrar F ( z ) + f( z ) d z , y dado que U es uniforme, esta probabilidad es solo f ( z ) d z como se prometió.
fuente
Esto se conoce como puntos de empaquetamiento en una esfera y no existe una solución general perfecta (conocida). Sin embargo, hay muchas soluciones imperfectas. Los tres más populares parecen ser:
n
ellos) dentro del cubo que rodea la esfera, luego rechaza los puntos fuera de la esfera. Trate los puntos restantes como vectores y normalícelos. Estas son sus "muestras" - elíjalasn
usando algún método (al azar, codicioso, etc.).Puede encontrar mucha más información sobre este problema aquí
fuente
Lo que busca se llama recubrimiento esférico . El problema de la cobertura esférica es muy difícil y se desconocen las soluciones, excepto por un pequeño número de puntos. Una cosa que se sabe con certeza es que dados n puntos en una esfera, siempre existen dos puntos de distancia
d = (4-csc^2(\pi n/6(n-2)))^(1/2)
o más cercanos.Si desea un método probabilístico para generar puntos distribuidos uniformemente en una esfera, es fácil: genere puntos en el espacio uniformemente mediante distribución gaussiana (está integrado en Java, no es difícil encontrar el código para otros lenguajes). Entonces, en el espacio tridimensional, necesitas algo como
Luego proyecta el punto sobre la esfera normalizando su distancia desde el origen
La distribución gaussiana en n dimensiones es esféricamente simétrica, por lo que la proyección sobre la esfera es uniforme.
Por supuesto, no hay garantía de que la distancia entre dos puntos cualesquiera en una colección de puntos generados uniformemente esté delimitada a continuación, por lo que puede usar el rechazo para hacer cumplir las condiciones que pueda tener: probablemente sea mejor generar la colección completa y luego rechazar toda la colección si es necesario. (O utilice "rechazo temprano" para rechazar toda la colección que ha generado hasta ahora; simplemente no guarde algunos puntos y descarte otros). Puede usar la fórmula
d
dada anteriormente, menos cierta holgura, para determinar la distancia mínima entre puntos por debajo de los cuales rechazará un conjunto de puntos. Deberá calcular n elegir 2 distancias, y la probabilidad de rechazo dependerá de la holgura; Es difícil decir cómo, así que ejecute una simulación para familiarizarse con las estadísticas relevantes.fuente
Esta respuesta se basa en la misma 'teoría' que se describe bien en esta respuesta
Estoy agregando esta respuesta como:
- Ninguna de las otras opciones se ajusta a la necesidad de 'uniformidad' (o no obviamente). (Teniendo en cuenta que para obtener el comportamiento de apariencia de distribución similar a un planeta particularmente deseado en la solicitud original, simplemente rechaza de la lista finita de los k puntos creados uniformemente al azar (aleatorio con el recuento del índice en los k elementos)) .Además, Es muy difícil "asimilar" cómo diferenciar entre las otras opciones sin ninguna imagen, así que así es como se ve esta opción (a continuación) y la implementación lista para ejecutar que la acompaña.
--La más cercano otra implicación lo obligó a decidir la 'N' por el 'eje angular', frente a solo 'un valor de N' en ambos valores del eje angular (que en recuentos bajos de N es muy complicado saber qué puede o no importar ( por ejemplo, quieres '5' puntos - diviértete))
con N en 20:
y luego N en 80:
aquí está el código python3 listo para ejecutar, donde la emulación es la misma fuente: " http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere " encontrado por otros . (El trazado que he incluido, que se activa cuando se ejecuta como 'principal', está tomado de: http://www.scipy.org/Cookbook/Matplotlib/mplot3D )
probado en recuentos bajos (N en 2, 5, 7, 13, etc.) y parece funcionar 'bien'
fuente
Tratar:
La función anterior debe ejecutarse en bucle con N bucle total y k iteración actual de bucle.
Se basa en un patrón de semillas de girasol, excepto que las semillas de girasol se curvan en una media cúpula y nuevamente en una esfera.
Aquí hay una imagen, excepto que puse la cámara a medio camino dentro de la esfera para que se vea 2d en lugar de 3d porque la cámara está a la misma distancia de todos los puntos. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg
fuente
Healpix resuelve un problema estrechamente relacionado (pixelar la esfera con píxeles de igual área):
http://healpix.sourceforge.net/
Probablemente sea exagerado, pero tal vez después de mirarlo se dé cuenta de que algunas de sus otras propiedades agradables son interesantes para usted. Es mucho más que una función que genera una nube de puntos.
Aterricé aquí tratando de encontrarlo nuevamente; el nombre "healpix" no evoca exactamente esferas ...
fuente
con una pequeña cantidad de puntos, podría ejecutar una simulación:
fuente
Tome los dos factores más grandes de su
N
, siN==20
entonces los dos factores más grandes son{5,4}
, o, más en general{a,b}
. CalcularPon tu primer punto en
{90-dlat/2,(dlong/2)-180}
, tu segundo en{90-dlat/2,(3*dlong/2)-180}
, tu tercero en{90-dlat/2,(5*dlong/2)-180}
, hasta que hayas dado la vuelta al mundo una vez, momento en el que habrás llegado a aproximadamente{75,150}
cuando vayas a continuación{90-3*dlat/2,(dlong/2)-180}
.Obviamente, estoy trabajando esto en grados en la superficie de la tierra esférica, con las convenciones habituales para traducir +/- a N / S o E / W. Y obviamente esto le brinda una distribución completamente no aleatoria, pero es uniforme y los puntos no están agrupados.
Para agregar algún grado de aleatoriedad, puede generar 2 distribuidos normalmente (con media 0 y std dev de {dlat / 3, dlong / 3} según corresponda) y agregarlos a sus puntos distribuidos uniformemente.
fuente
editar: Esto no responde a la pregunta que el OP quería hacer, dejándolo aquí en caso de que la gente lo encuentre útil de alguna manera.
Usamos la regla de multiplicación de la probabilidad, combinada con infinitesimales. Esto da como resultado 2 líneas de código para lograr el resultado deseado:
(definido en el siguiente sistema de coordenadas :)
Su idioma normalmente tiene una primitiva de número aleatorio uniforme. Por ejemplo, en Python puede usar
random.random()
para devolver un número en el rango[0,1)
. Puede multiplicar este número por k para obtener un número aleatorio en el rango[0,k)
. Por lo tanto, en python,uniform([0,2pi))
significaríarandom.random()*2*math.pi
.Prueba
Ahora no podemos asignar θ uniformemente, de lo contrario nos aglutinaríamos en los polos. Deseamos asignar probabilidades proporcionales al área de la superficie de la cuña esférica (la θ en este diagrama es en realidad φ):
Un desplazamiento angular dφ en el ecuador resultará en un desplazamiento de dφ * r. ¿Cuál será ese desplazamiento en un acimut arbitrario θ? Bueno, el radio del eje z es
r*sin(θ)
, por lo que la longitud de arco de esa "latitud" que cruza la cuña esdφ * r*sin(θ)
. Así calculamos la distribución acumulada del área a muestrear de ella, integrando el área del corte del polo sur al polo norte.(donde cosas =
dφ*r
)Ahora intentaremos obtener el inverso del CDF para muestrearlo: http://en.wikipedia.org/wiki/Inverse_transform_sampling
Primero normalizamos dividiendo nuestro casi-CDF por su valor máximo. Esto tiene el efecto secundario de cancelar dφ y r.
Así:
fuente
O ... para colocar 20 puntos, calcule los centros de las caras del icosaedro. Para 12 puntos, encuentra los vértices del icosaedro. Para 30 puntos, el punto medio de los bordes del icosaedro. puedes hacer lo mismo con el tetraedro, el cubo, el dodecaedro y los octaedros: un conjunto de puntos está en los vértices, otro en el centro de la cara y otro en el centro de las aristas. Sin embargo, no se pueden mezclar.
fuente
fuente
@robert king Es una solución realmente agradable, pero tiene algunos errores descuidados. Sin embargo, sé que me ayudó mucho, así que no importa el descuido. :) Aquí hay una versión limpia ...
fuente
Esto funciona y es mortalmente simple. Tantos puntos como quieras:
fuente