Je suis intéressé à distribuer uniformément n points sur la surface des sphères dans les dimensions 3 et supérieures.
Pour être plus précis:
Je ne suis pas intéressé par:
Une méthode qui satisfait que ces critères s'appelle le réseau Fibonacci, mais je n'ai pu trouver que des implémentations de code pour cela en 2D et 3D.
La méthode derrière le réseau Fibonacci (également appelée la spirale Fibonacci) consiste à générer une ligne 1D que les spirales autour de la surface de la sphère de sorte que la surface couverte par la ligne soit à peu près la même à chaque tour. Vous pouvez ensuite déposer n points également répartis sur la spirale et ils seront à peu près répartis de manière uniforme à la surface de la sphère.
Dans cette réponse Il y a une implémentation python pour 3 dimensions qui génère ce qui suit:
Je voulais savoir si la spirale de Fibonacci pourrait être étendue aux dimensions supérieures à 3 et a posté une question sur l'échange de pile de mathématiques. À ma grande surprise, j'ai reçu deux réponses étonnantes qui aussi loin que je peux dire (parce que je ne comprends pas complètement les mathématiques montrées) montre qu'il est en effet possible d'étendre cette méthode à n dimensions.
Malheureusement, je ne comprends pas assez des mathématiques montrés pour pouvoir transformer soit la réponse à (pseudo). Je suis un programmeur informatique expérimenté, mais mon fond de mathématiques ne va que jusqu'à présent.
Je vais copier dans ce que je crois être la partie la plus importante de l'une des réponses ci-dessous (malheureusement SO ne prend pas en charge Mathjax, donc je devais copier comme une image)
Difficultés présentées par ce qui précède que je lutte avec:
Quiconque comprendrait-il que les mathématiques impliquées puissent progresser vers une implémentation pseudo-code de la seule réponse à la question liée au réseau de fibonacci? Je comprends une mise en œuvre complète peut être assez difficile, donc je serais heureux d'une mise en œuvre de la partie qui me conduit assez loin pour pouvoir compléter le reste moi-même.
Pour faciliter la tâche, j'ai déjà codé une fonction qui prend des coordonnées sphériques en n dimensions et les transforme en coordonnées cartésiennes, la mise en œuvre peut donc générer l'un ou l'autre comme je peux facilement convertir.
De plus, je vois qu'une réponse utilise le prochain numéro principal pour chaque dimension supplémentaire. Je peux facilement coder une fonction qui génère chaque prime successive, vous pouvez donc supposer que cela est déjà implémenté.
À défaut d'une mise en œuvre du réseau Fibonacci dans N Dimensions, je serais heureux d'accepter une méthode différente qui répond aux contraintes ci-dessus.
Toute la réponse précédente a fonctionné, mais manquait toujours de code réel. Il manquait deux vrais morceaux, ce qui implémente en général.
sin^(d-2)(x)
. Cela a une forme fermée si vous intégrez une intégration récursive par des pièces. Ici, je le met en œuvre à la mode récursive, bien que pour la dimension ~> 100 J'ai trouvé une intégration numérique de sin^d
être plus rapidesin^d
, d > 1
n'a pas de forme de fermeture. Ici, je le calculer à l'aide de la recherche binaire, même s'il y a probablement de meilleures façons, comme indiqué dans d'autres réponses.Ces deux combinés avec un moyen de générer des premiers résultats dans l'algorithme complet:
from itertools import count, islice
from math import cos, gamma, pi, sin, sqrt
from typing import Callable, Iterator, List
def int_sin_m(x: float, m: int) -> float:
"""Computes the integral of sin^m(t) dt from 0 to x recursively"""
if m == 0:
return x
Elif m == 1:
return 1 - cos(x)
else:
return (m - 1) / m * int_sin_m(x, m - 2) - cos(x) * sin(x) ** (
m - 1
) / m
def primes() -> Iterator[int]:
"""Returns an infinite generator of prime numbers"""
yield from (2, 3, 5, 7)
composites = {}
ps = primes()
next(ps)
p = next(ps)
assert p == 3
psq = p * p
for i in count(9, 2):
if i in composites: # composite
step = composites.pop(i)
Elif i < psq: # prime
yield i
continue
else: # composite, = p*p
assert i == psq
step = 2 * p
p = next(ps)
psq = p * p
i += step
while i in composites:
i += step
composites[i] = step
def inverse_increasing(
func: Callable[[float], float],
target: float,
lower: float,
upper: float,
atol: float = 1e-10,
) -> float:
"""Returns func inverse of target between lower and upper
inverse is accurate to an absolute tolerance of atol, and
must be monotonically increasing over the interval lower
to upper
"""
mid = (lower + upper) / 2
approx = func(mid)
while abs(approx - target) > atol:
if approx > target:
upper = mid
else:
lower = mid
mid = (upper + lower) / 2
approx = func(mid)
return mid
def uniform_hypersphere(d: int, n: int) -> List[List[float]]:
"""Generate n points over the d dimensional hypersphere"""
assert d > 1
assert n > 0
points = [[1 for _ in range(d)] for _ in range(n)]
for i in range(n):
t = 2 * pi * i / n
points[i][0] *= sin(t)
points[i][1] *= cos(t)
for dim, prime in Zip(range(2, d), primes()):
offset = sqrt(prime)
mult = gamma(dim / 2 + 0.5) / gamma(dim / 2) / sqrt(pi)
def dim_func(y):
return mult * int_sin_m(y, dim - 1)
for i in range(n):
deg = inverse_increasing(dim_func, i * offset % 1, 0, pi)
for j in range(dim):
points[i][j] *= sin(deg)
points[i][dim] *= cos(deg)
return points
Qui produit l'image suivante pour 200 points sur une sphère: