Preciso de um algoritmo que me dê posições em torno de uma esfera para N pontos (menos de 20, provavelmente) que os espalhe vagamente. Não há necessidade de "perfeição", mas eu só preciso para que nenhum deles fique agrupado.
- Essa pergunta forneceu um bom código, mas não consegui encontrar uma maneira de fazer esse uniforme, pois parecia 100% aleatório.
- Esta postagem de blog recomendada tinha duas maneiras de permitir a entrada de número de pontos na esfera, mas o algoritmo de Saff e Kuijlaars está exatamente em psuedocódigo que eu poderia transcrever e o exemplo de código que encontrei continha "nó [k]", que não pude ver explicado e arruinado essa possibilidade. O segundo exemplo de blog foi o Golden Section Spiral, que me deu resultados estranhos e agrupados, sem uma forma clara de definir um raio constante.
- Este algoritmo de esta questão parece que ele poderia funcionar, mas não posso juntar o que está nessa página em psuedocode ou qualquer coisa.
Alguns outros tópicos de perguntas que encontrei falam de distribuição uniforme aleatória, o que adiciona um nível de complexidade com o qual não estou preocupado. Peço desculpas por esta ser uma pergunta tão boba, mas eu queria mostrar que eu realmente procurei muito e ainda não consegui.
Então, o que estou procurando é um pseudocódigo simples para distribuir uniformemente N pontos em torno de uma esfera unitária, que retorna em coordenadas esféricas ou cartesianas. Melhor ainda se puder distribuir com um pouco de randomização (pense em planetas ao redor de uma estrela, decentemente espalhados, mas com espaço para liberdade).
Respostas:
Em este exemplo de código
node[k]
é apenas o nó k. Você está gerando uma matriz de N pontos enode[k]
é o k-ésimo (de 0 a N-1). Se isso é tudo o que está confundindo você, espero que você possa usá-lo agora.(em outras palavras,
k
é uma matriz de tamanho N que é definida antes do início do fragmento de código e que contém uma lista dos pontos).Como alternativa , com base na outra resposta aqui (e usando Python):
Se você plotar isso, verá que o espaçamento vertical é maior perto dos pólos, de modo que cada ponto está situado aproximadamente na mesma área total do espaço (perto dos pólos há menos espaço "horizontalmente", então dá mais "verticalmente" )
Isso não é o mesmo que todos os pontos tendo aproximadamente a mesma distância de seus vizinhos (que é o que eu acho que seus links estão falando), mas pode ser suficiente para o que você deseja e melhora simplesmente fazendo uma grade lat / lon uniforme .
fonte
O algoritmo da esfera de Fibonacci é ótimo para isso. É rápido e dá resultados que à primeira vista enganam facilmente o olho humano. Você pode ver um exemplo feito com processamento que mostrará o resultado ao longo do tempo conforme os pontos são adicionados. Aqui está outro ótimo exemplo interativo feito por @gman. E aqui está uma implementação simples em python.
1000 amostras fornecem isso:
fonte
O método da espiral dourada
Você disse que não conseguiria fazer o método da espiral dourada funcionar e isso é uma pena, porque é muito, muito bom. Eu gostaria de dar a você um entendimento completo disso, para que talvez você possa entender como evitar que isso seja "amontoado".
Portanto, aqui está uma maneira rápida e não aleatória de criar uma rede que seja aproximadamente correta; como discutido acima, nenhuma rede será perfeita, mas isso pode ser bom o suficiente. É comparado a outros métodos, por exemplo, em BendWavy.org, mas tem uma aparência bonita e bonita, bem como uma garantia de espaçamento uniforme no limite.
Primer: espirais de girassol no disco da unidade
Para entender esse algoritmo, primeiro convido você a examinar o algoritmo 2D da espiral do girassol. Isso se baseia no fato de que o número mais irracional é a proporção áurea
(1 + sqrt(5))/2
e se alguém emitir pontos pela abordagem "fique no centro, gire uma proporção áurea de voltas inteiras, em seguida, emita outro ponto nessa direção", naturalmente se constrói um espiral que, conforme você chega a um número cada vez maior de pontos, se recusa a ter 'barras' bem definidas sobre as quais os pontos se alinham. (Nota 1.)O algoritmo para espaçamento uniforme em um disco é,
e produz resultados semelhantes a (n = 100 e n = 1000):
Espaçando os pontos radialmente
O mais estranho é a fórmula
r = sqrt(indices / num_pts)
; como eu cheguei a esse? (Nota 2.)Bem, estou usando a raiz quadrada aqui porque quero que eles tenham um espaçamento de área uniforme ao redor do disco. Isso é o mesmo que dizer que no limite de N grande eu quero uma pequena região R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) para conter um número de pontos proporcionais à sua área, que é r d r d θ . Agora, se fingirmos que estamos falando sobre uma variável aleatória aqui, isso tem uma interpretação direta, dizendo que a densidade de probabilidade conjunta para ( R , Θ ) é apenas crpara alguma constante c . A normalização no disco unitário então forçaria c = 1 / π.
Agora, deixe-me apresentar um truque. Vem da teoria da probabilidade, onde é conhecido como amostragem do CDF inverso : suponha que você queira gerar uma variável aleatória com uma densidade de probabilidade f ( z ) e você tem uma variável aleatória U ~ Uniforme (0, 1), assim como sai de
random()
na maioria das linguagens de programação. Como você faz isso?Agora, o truque da espiral de proporção áurea espaça os pontos em um padrão bem uniforme para θ, então vamos integrar isso; para o disco unitário, ficamos com F ( r ) = r 2 . Portanto, a função inversa é F -1 ( u ) = u 1/2 e, portanto, geraríamos pontos aleatórios no disco em coordenadas polares com
r = sqrt(random()); theta = 2 * pi * random()
.Agora, em vez de amostrar aleatoriamente essa função inversa, estamos amostrando-a uniformemente , e o bom da amostragem uniforme é que nossos resultados sobre como os pontos são espalhados no limite de N grande se comportarão como se tivéssemos amostrado aleatoriamente. Essa combinação é o truque. Em vez de
random()
usarmos(arange(0, num_pts, dtype=float) + 0.5)/num_pts
, de modo que, digamos, se quisermos amostrar 10 pontos eles sãor = 0.05, 0.15, 0.25, ... 0.95
. Amostramos uniformemente r para obter espaçamento de área igual e usamos o incremento do girassol para evitar horríveis “barras” de pontos na saída.Agora fazendo o girassol em uma esfera
As mudanças que precisamos fazer para pontilhar a esfera com pontos envolvem apenas trocar as coordenadas polares por coordenadas esféricas. A coordenada radial é claro não entra nisto porque estamos em uma esfera unitária. Para manter as coisas um pouco mais consistentes aqui, embora eu tenha sido treinado como um físico, usarei coordenadas matemáticas onde 0 ≤ φ ≤ π é a latitude descendo do pólo e 0 ≤ θ ≤ 2π é a longitude. Portanto, a diferença acima é que basicamente substituímos a variável r por φ .
Nosso elemento de área, que era r d r d θ , agora se torna o não muito mais complicado sen ( φ ) d φ d θ . Portanto, nossa densidade de junta para espaçamento uniforme é sin ( φ ) / 4π. Integrando fora de θ , encontramos f ( φ ) = sin ( φ ) / 2, assim F ( φ ) = (1 - cos ( φ )) / 2. Invertendo isso, podemos ver que uma variável aleatória uniforme se pareceria com acos (1 - 2 u ), mas nós amostramos uniformemente em vez de aleatoriamente, então usamos φ k = acos (1 - 2 ( k+ 0,5) / N ). E o resto do algoritmo está apenas projetando isso nas coordenadas x, y e z:
Novamente, para n = 100 en = 1000, os resultados se parecem com:
Mais pesquisa
Eu queria dar uma mensagem para o blog de Martin Roberts. Observe que acima eu criei um deslocamento dos meus índices adicionando 0,5 a cada índice. Isso era apenas visualmente atraente para mim, mas descobri que a escolha do deslocamento é muito importante e não é constante ao longo do intervalo e pode significar obter até 8% a mais de precisão na embalagem, se escolhido corretamente. Também deve haver uma maneira de fazer com que sua sequência R 2 cubra uma esfera e seria interessante ver se isso também produziu uma boa cobertura uniforme, talvez como está, mas talvez precise ser, digamos, tirada de apenas metade de o quadrado da unidade é cortado na diagonal ou assim e esticado para obter um círculo.
Notas
Essas "barras" são formadas por aproximações racionais de um número, e as melhores aproximações racionais de um número vêm de sua expressão de fração contínua,
z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))
ondez
é um inteiro en_1, n_2, n_3, ...
é uma sequência finita ou infinita de inteiros positivos:Como a parte da fração
1/(...)
está sempre entre zero e um, um grande número inteiro na fração contínua permite uma aproximação racional particularmente boa: "um dividido por algo entre 100 e 101" é melhor do que "um dividido por algo entre 1 e 2." O número mais irracional é, portanto, aquele que é1 + 1/(1 + 1/(1 + ...))
e não tem aproximações racionais particularmente boas; pode-se resolver φ = 1 + 1 / φ multiplicando por φ para obter a fórmula da razão áurea.Para pessoas que não estão tão familiarizadas com o NumPy - todas as funções são "vetorizadas", de modo que
sqrt(array)
é o mesmo que outras linguagens podem escrevermap(sqrt, array)
. Portanto, este é umsqrt
aplicativo componente por componente . O mesmo também se aplica à divisão por um escalar ou adição com escalares - esses se aplicam a todos os componentes em paralelo.A prova é simples quando você sabe que esse é o resultado. Se você perguntar qual é a probabilidade de z < Z < z + d z , isso é o mesmo que perguntar qual é a probabilidade de z < F -1 ( U ) < z + d z , aplicar F a todas as três expressões observando que é uma função monotonicamente crescente, portanto, F ( z ) < U < F ( z + d z ), expanda o lado direito para encontrar F ( z ) + f ( z ) d z , e como U é uniforme, essa probabilidade é apenas f ( z ) d z, conforme prometido.
fonte
Isso é conhecido como pontos de empacotamento em uma esfera, e não há uma solução geral perfeita (conhecida). No entanto, existem muitas soluções imperfeitas. Os três mais populares parecem ser:
n
eles) dentro do cubo ao redor da esfera e, em seguida, rejeita os pontos fora da esfera. Trate os pontos restantes como vetores e normalize-os. Estas são as suas "amostras" - escolhan
-as usando algum método (aleatoriamente, ganancioso, etc.).Um monte mais informações sobre este problema pode ser encontrada aqui
fonte
O que você está procurando é chamado de cobertura esférica . O problema da cobertura esférica é muito difícil e as soluções são desconhecidas, exceto por um pequeno número de pontos. Uma coisa que se sabe com certeza é que dados n pontos em uma esfera, sempre existem dois pontos de distância
d = (4-csc^2(\pi n/6(n-2)))^(1/2)
ou mais próximos.Se você deseja um método probabilístico para gerar pontos uniformemente distribuídos em uma esfera, é fácil: gere pontos no espaço uniformemente pela distribuição Gaussiana (é construído em Java, não é difícil encontrar o código para outras linguagens). Então, no espaço tridimensional, você precisa de algo como
Em seguida, projete o ponto na esfera normalizando sua distância da origem
A distribuição gaussiana em n dimensões é esfericamente simétrica, então a projeção na esfera é uniforme.
Claro, não há garantia de que a distância entre quaisquer dois pontos em uma coleção de pontos gerados uniformemente será limitada abaixo, então você pode usar a rejeição para impor quaisquer condições que você possa ter: provavelmente é melhor gerar toda a coleção e então rejeite a coleção inteira se necessário. (Ou use "rejeição antecipada" para rejeitar toda a coleção que você gerou até agora; apenas não mantenha alguns pontos e descartar outros.) Você pode usar a fórmula
d
dada acima, menos alguma folga, para determinar a distância mínima entre pontos abaixo dos quais você rejeitará um conjunto de pontos. Você terá que calcular n escolher 2 distâncias, e a probabilidade de rejeição dependerá da folga; é difícil dizer como, então execute uma simulação para ter uma ideia das estatísticas relevantes.fonte
Esta resposta é baseada na mesma 'teoria' que é bem delineada por esta resposta
Estou adicionando esta resposta como: - Além disso, é muito difícil 'grocar' como diferenciar entre as outras opções sem qualquer imagem, então aqui está a aparência desta opção (abaixo), e a implementação pronta para execução que vai com isso.
- Nenhuma das outras opções se encaixa na necessidade de 'uniformidade' 'exata' (ou não obviamente). (Observando para obter a distribuição parecida com um planeta parecendo um comportamento particularmente desejado na pergunta original, você apenas rejeita da lista finita dos k pontos criados uniformemente aleatoriamente (aleatoriamente em relação à contagem do índice nos k itens de volta). -
- O mais próximo outra implicação forçou você a decidir o 'N' por 'eixo angular', vs. apenas 'um valor de N' em ambos os valores do eixo angular (que em contagens baixas de N é muito complicado saber o que pode ou não importar ( por exemplo, você quer '5' pontos - divirta-se))
com N em 20:
e então N em 80:
aqui está o código python3 pronto para executar, em que a emulação é a mesma fonte: " http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere " encontrada por outros . (A plotagem que incluí, que dispara quando executada como 'principal', foi retirada de: http://www.scipy.org/Cookbook/Matplotlib/mplot3D )
testado em contagens baixas (N em 2, 5, 7, 13, etc) e parece funcionar 'bem'
fonte
Experimentar:
A função acima deve ser executada em loop com total de N loop e iteração de corrente de loop k.
É baseado em um padrão de sementes de girassol, exceto que as sementes de girassol são curvadas em uma meia cúpula e novamente em uma esfera.
Aqui está uma foto, exceto que coloquei a câmera no meio da esfera para que pareça 2d em vez de 3d porque a câmera está à mesma distância de todos os pontos. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg
fonte
Healpix resolve um problema intimamente relacionado (pixelização da esfera com pixels de área igual):
http://healpix.sourceforge.net/
Provavelmente é um exagero, mas talvez depois de olhar você perceberá que algumas de suas outras propriedades interessantes são interessantes para você. É muito mais do que apenas uma função que gera uma nuvem de pontos.
Aterrissei aqui tentando encontrá-lo novamente; o nome "healpix" não evoca exatamente esferas ...
fonte
com um pequeno número de pontos, você pode executar uma simulação:
fonte
Pegue os dois maiores fatores do seu
N
, seN==20
então os dois maiores fatores são{5,4}
, ou, mais geralmente{a,b}
. CalcularPonha seu primeiro ponto em
{90-dlat/2,(dlong/2)-180}
, seu segundo em{90-dlat/2,(3*dlong/2)-180}
, seu terceiro em{90-dlat/2,(5*dlong/2)-180}
, até que você tenha dado a volta ao mundo uma vez, quando então você tem que fazer{75,150}
quando for ao lado{90-3*dlat/2,(dlong/2)-180}
.Obviamente estou trabalhando isso em graus na superfície da Terra esférica, com as convenções usuais para traduzir +/- para N / S ou E / W. E, obviamente, isso dá a você uma distribuição completamente não aleatória, mas é uniforme e os pontos não estão agrupados.
Para adicionar algum grau de aleatoriedade, você pode gerar 2 normalmente distribuídos (com média 0 e dev padrão de {dlat / 3, dlong / 3} conforme apropriado) e adicioná-los aos seus pontos uniformemente distribuídos.
fonte
editar: Isso não responde à pergunta que o OP pretendia fazer, deixando-o aqui para o caso de as pessoas acharem que é útil de alguma forma.
Usamos a regra de multiplicação da probabilidade, combinada com infinitosimais. Isso resulta em 2 linhas de código para alcançar o resultado desejado:
(definido no seguinte sistema de coordenadas :)
Sua linguagem normalmente tem um primitivo de número aleatório uniforme. Por exemplo, em python, você pode usar
random.random()
para retornar um número no intervalo[0,1)
. Você pode multiplicar esse número por k para obter um número aleatório no intervalo[0,k)
. Assim, em python,uniform([0,2pi))
significariarandom.random()*2*math.pi
.Prova
Agora não podemos atribuir θ uniformemente, caso contrário, ficaríamos aglomerados nos pólos. Queremos atribuir probabilidades proporcionais à área de superfície da cunha esférica (o θ neste diagrama é na verdade φ):
Um deslocamento angular dφ no equador resultará em um deslocamento de dφ * r. Qual será esse deslocamento em um azimute arbitrário θ? Bem, o raio do eixo z é
r*sin(θ)
, então o comprimento de arco dessa "latitude" que cruza a cunha édφ * r*sin(θ)
. Assim, calculamos a distribuição cumulativa da área a ser amostrada, integrando a área da fatia do polo sul ao polo norte.(onde coisas =
dφ*r
)Agora tentaremos obter o inverso do CDF para obter uma amostra dele: http://en.wikipedia.org/wiki/Inverse_transform_sampling
Primeiro, normalizamos dividindo nosso quase-CDF por seu valor máximo. Isso tem o efeito colateral de cancelar dφ e r.
Portanto:
fonte
OU ... para colocar 20 pontos, calcule os centros das faces icosaedronais. Por 12 pontos, encontre os vértices do icosaedro. Para 30 pontos, o ponto médio das bordas do icosaedro. você pode fazer a mesma coisa com o tetraedro, cubo, dodecaedro e octaedro: um conjunto de pontos está nos vértices, outro no centro da face e outro no centro das arestas. Eles não podem ser misturados, no entanto.
fonte
fonte
@robert king É uma solução muito boa, mas tem alguns bugs desleixados. Eu sei que me ajudou muito, então não importa o desleixo. :) Aqui está uma versão limpa ....
fonte
Isso funciona e é extremamente simples. Quantos pontos você quiser:
fonte