Bir küre üzerinde n noktayı eşit olarak dağıtma


121

Bana bir küre etrafında, onları belli belirsiz bir şekilde yayan N nokta (muhtemelen 20'den az) için konumlar verebilecek bir algoritmaya ihtiyacım var. "Mükemmelliğe" gerek yok, ama ona ihtiyacım var, bu yüzden hiçbiri bir araya toplanmasın.

  • Bu soru iyi bir kod sağladı, ancak bu üniformayı yapmanın bir yolunu bulamadım, çünkü bu% 100 rastgele görünüyordu.
  • Önerilen bu blog gönderisinde , küredeki nokta sayısının girilmesine izin veren iki yol vardı, ancak Saff ve Kuijlaars algoritması tam olarak kopyalayabileceğim psuedocode içindedir ve bulduğum kod örneği , yapamadığım "node [k]" içeriyordu bu olasılığı açıkladı ve mahvetti. İkinci blog örneği, sabit bir yarıçapı tanımlamanın net bir yolu olmayan, garip, toplanmış sonuçlar veren Altın Bölüm Spirali idi.
  • Bu algoritma gelen bu soruya o işi belki olabilir, ama ben psuedocode içine girmesine ya da o sayfada var arada neler parçaları bir olamaz gibi görünüyor.

Karşılaştığım diğer birkaç soru dizisi, endişelenmediğim bir karmaşıklık düzeyi ekleyen rastgele tek tip dağılımdan bahsetti. Bunun çok aptalca bir soru olduğu için özür dilerim, ancak gerçekten dikkatli baktığımı ve yine de yetersiz kaldığımı göstermek istedim.

Yani, aradığım şey, küresel veya Kartezyen koordinatlarda dönen bir birim küre etrafına N noktayı eşit olarak dağıtmak için basit sözde koddur. Hatta biraz rasgele dağıtabilirse daha da iyi (bir yıldızın etrafındaki gezegenleri, düzgün bir şekilde dağılmış halde, ancak rüzgar boşluğu bırakarak düşünün).


"Biraz rastgele" ne demek istiyorsun? Bir anlamda tedirginlik mi demek istiyorsun?
ninjagecko

32
OP kafası karışık. Aradığı şey, herhangi iki nokta arasındaki minimum mesafenin mümkün olduğu kadar büyük olması için bir küreye n-nokta koymaktır. Bu, noktalara tüm küre üzerinde "eşit olarak dağılmış" görünümü verecektir. Bu, bir küre üzerinde tekdüze bir rastgele dağılım oluşturmakla tamamen ilgisizdir, bu bağlantıların çoğunun ve aşağıdaki cevapların çoğunun bahsettiği şey budur.
BlueRaja - Dany Pflughoeft

1
20, rastgele görünmelerini istemiyorsanız, bir küre üzerine yerleştirilecek çok fazla nokta değildir.
John Alexiou

2
İşte bunu yapmanın bir yolu (kod örnekleri var): pdfs.semanticscholar.org/97a6/… (itme kuvveti hesaplamaları kullanıyor gibi görünüyor)
trusktr

1
Elbette, {4, 6, 8, 12, 20} 'de N üzerindeki değerler için, her noktadan (her biri) en yakın komşularına olan mesafenin tüm noktalar ve tüm en yakın komşular için sabit olduğu kesin çözümler vardır.
dmckee --- eski moderatör kedicik

Yanıtlar:


13

In bu örnek kod node[k] sadece kth düğümdür. Bir dizi N noktaları oluşturuyorsunuz ve node[k]k'inci (0'dan N-1'e). Kafanızı karıştıran tek şey buysa, umarım bunu şimdi kullanabilirsiniz.

(başka bir deyişle, kkod parçası başlamadan önce tanımlanan ve noktaların bir listesini içeren N boyutunda bir dizidir).

Alternatif olarak , buradaki diğer cevaba dayanarak (ve Python kullanarak):

> 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

Bunu çizerseniz, dikey boşluğun kutupların yakınında daha büyük olduğunu ve böylece her noktanın yaklaşık aynı toplam alan içinde yer aldığını görürsünüz (kutupların yakınında "yatay olarak" daha az yer vardır, bu nedenle "dikey" olarak daha fazlasını verir. ).

Bu, komşularına yaklaşık olarak aynı mesafeye sahip tüm noktalar ile aynı değildir (ki bence bağlantılarınızın bahsettiği budur), ancak istediğiniz şey için yeterli olabilir ve basitçe tek tip bir enlem / boylam ızgarası oluşturmayı geliştirir. .


güzel, matematiksel bir çözüm görmek güzel. Bir sarmal ve yay uzunluğu ayrımı kullanmayı düşünüyordum. İlginç bir sorun olan en uygun çözümü nasıl elde edeceğime hâlâ emin değilim.
robert king

Cevabımı üstte [k] düğümünün bir açıklamasını içerecek şekilde düzenlediğimi gördünüz mü? Sanırım tek ihtiyacın olan bu olabilir ...
Andrew cooke

Harika, açıklama için teşekkürler. Şu anda zamanım olmadığı için daha sonra deneyeceğim, ama bana yardım ettiğiniz için çok teşekkür ederim. Benim amaçlarım için nasıl sonuç verdiğini size bildireceğim. ^^
befall

Spiral yöntemini kullanmak ihtiyaçlarıma mükemmel bir şekilde uyuyor, yardım ve açıklama için çok teşekkürler. :)
2012'de 1

13
Bağlantı kesik görünüyor.
Scheintod

140

Fibonacci küre algoritması bunun için harika. Hızlıdır ve bir bakışta insan gözünü kolayca kandıracak sonuçlar verir. Noktalar eklendikçe zaman içinde sonucu gösterecek olan işlemle yapılan bir örnek görebilirsiniz . İşte @gman tarafından yapılan başka bir harika etkileşimli örnek . Ve işte python'da basit bir uygulama.

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 numune size şunu verir:

görüntü açıklamasını buraya girin


phi'yi tanımlarken bir n değişkeni çağrılır: phi = ((i + rnd)% n) * artış. N = örnekler mi?
Andrew Staroscik

@AndrewStaroscik evet! Kodu ilk yazdığımda değişken olarak "n" kullandım ve daha sonra adını değiştirdim ancak durum tespiti yapmadım. Bunu yakaladığın için teşekkürler!
Fnord


4
@Xarbrough kodu size bir birim küre etrafında puan verir, bu yüzden her noktayı yarıçap için istediğiniz skaler ile çarpın.
Fnord

2
@Fnord: Bunu daha yüksek boyutlar için yapabilir miyiz?
pikachuchameleon

108

Altın sarmal yöntemi

Altın spiral yönteminin işe yaramayacağını söyledin ve bu utanç verici çünkü gerçekten çok iyi. Bunu "toparlanmaktan" nasıl uzak tutabileceğinizi anlayabilmeniz için size tam bir anlayış vermek istiyorum.

İşte yaklaşık olarak doğru olan bir kafes oluşturmanın hızlı ve rastgele olmayan bir yolu; Yukarıda tartışıldığı gibi, hiçbir kafes mükemmel olmayacaktır, ancak bu yeterince iyi olabilir. BendWavy.org gibi diğer yöntemlerle karşılaştırılır, ancak güzel ve hoş bir görünüme sahip olduğu gibi, sınırda eşit boşluk bırakma garantisi de vardır.

Astar: ünite diskindeki ayçiçeği spiralleri

Bu algoritmayı anlamak için öncelikle sizi 2D ayçiçeği spiral algoritmasına bakmaya davet ediyorum. Bu, en irrasyonel sayının altın oran olduğu gerçeğine dayanmaktadır (1 + sqrt(5))/2ve eğer biri "merkezde durun, tam dönüşlerin altın oranını çevirin, sonra o yönde başka bir nokta yayınlayın" yaklaşımıyla puan yayarsa, doğal olarak bir gittikçe artan sayıda noktaya ulaştığınızda, yine de noktaların sıralandığı iyi tanımlanmış 'çubuklara' sahip olmayı reddeden spiral. (Not 1.)

Bir diskte eşit aralık bırakma algoritması,

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

ve şuna benzeyen sonuçlar üretir (n = 100 ve n = 1000):

görüntü açıklamasını buraya girin

Noktaları radyal olarak aralıklandırma

Anahtar tuhaf olan şey formüldür r = sqrt(indices / num_pts); buna nasıl geldim (Not 2.)

Burada karekökü kullanıyorum çünkü bunların diskin etrafında eşit alan aralıklarına sahip olmasını istiyorum. Bu, büyük N'nin sınırında küçük bir R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) bölgesinin kendi alanına orantılı bir dizi nokta içermesini istediğimi söylemekle aynıdır , olan r d r d θ . Şimdi, burada rastgele bir değişkenden bahsettiğimizi varsayarsak, bu, ( R , Θ ) için ortak olasılık yoğunluğunun sadece cr olduğu şeklinde basit bir yorumu vardırbazı sabitler için c . Birim diskteki normalizasyon, c = 1 / π zorlar .

Şimdi bir numara göstermeme izin verin. Ters CDF'yi örnekleme olarak bilindiği olasılık teorisinden gelir : Olasılık yoğunluğu f ( z ) olan bir rastgele değişken oluşturmak istediğinizi varsayalım ve Rastgele değişken U ~ Tekdüzen (0, 1) 'den çıktığı gibi çoğu programlama dilinde. Bunu nasıl yapıyorsun?random()

  1. Öncelikle yoğunluğunuzu F ( z ) olarak adlandıracağımız kümülatif dağılım işlevine veya CDF'ye çevirin . Bir CDF, unutmayın, monoton olarak 0'dan 1'e, türev f ( z ) ile artar .
  2. Daha sonra CDF'nin ters fonksiyonu F -1 ( z ) hesaplayın .
  3. Z = F -1 ( U ) 'nun hedef yoğunluğa göre dağıldığını göreceksiniz . (Not 3).

Şimdi altın oran sarmal numarası, noktaları θ için güzel bir şekilde eşit bir düzende yerleştirir, öyleyse bunu bütünleştirelim; birim disk için F ( r ) = r 2 ile kaldık . Yani ters fonksiyon F -1 ( u ) = u 1/2 ve bu nedenle diskte kutupsal koordinatlarda rastgele noktalar oluşturabiliriz r = sqrt(random()); theta = 2 * pi * random().

Şimdi, bu ters işlevi rastgele örneklemek yerine, onu tekdüze örnekliyoruz ve tek tip örneklemenin güzel yanı, büyük N sınırında noktaların nasıl yayıldığına ilişkin sonuçlarımızın, onu rastgele örneklemiş gibi davranmasıdır. Bu kombinasyon işin püf noktası. Yerine random()kullandığımız (arange(0, num_pts, dtype=float) + 0.5)/num_ptsbiz onlar 10 puan örnek isterseniz, böylece, demek r = 0.05, 0.15, 0.25, ... 0.95. Eşit alan aralığı elde etmek için r'yi tek tip olarak örnekliyoruz ve çıktıdaki korkunç nokta “çubuklarını” önlemek için ayçiçeği artışını kullanıyoruz.

Şimdi bir küre üzerinde ayçiçeği yapıyor

Küreyi noktalarla noktalamak için yapmamız gereken değişiklikler, yalnızca küresel koordinatlar için kutupsal koordinatların değiştirilmesini içerir. Elbette radyal koordinat buna girmiyor çünkü bir birim kürenin üzerindeyiz. Burada işleri biraz daha tutarlı tutmak için, fizikçi olarak eğitilmiş olmama rağmen matematikçilerin koordinatlarını kullanacağım, burada 0 ≤ φ ≤ π kutuptan gelen enlem ve 0 ≤ θ ≤ 2≤ boylamdır. Yani yukarıdan fark, temelde r değişkenini φ ile değiştirmemizdir .

R d r d θ olan alan öğemiz , artık çok daha karmaşık olmayan günah ( φ ) d φ d θ olur . Dolayısıyla, düzgün aralık için eklem yoğunluğumuz günah ( φ ) / 4π'dir. Θ ile integral alırsak, f ( φ ) = sin ( φ ) / 2, dolayısıyla F ( φ ) = (1 - cos ( φ )) / 2 buluruz . Bunu tersine çevirdiğimizde, tek tip bir rastgele değişkenin acos (1 - 2 u ) gibi görüneceğini görebiliriz , ancak rastgele yerine tek tip örnekleme yapıyoruz, bu nedenle φ k = acos (1 - 2 ( k+ 0.5) / N ). Ve algoritmanın geri kalanı sadece bunu x, y ve z koordinatlarına yansıtıyor:

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

Yine n = 100 ve n = 1000 için sonuçlar şöyle görünür: görüntü açıklamasını buraya girin görüntü açıklamasını buraya girin

Daha fazla araştırma

Martin Roberts'ın bloguna bir haykırmak istedim. Yukarıda, her bir dizine 0,5 ekleyerek endekslerimin bir ofsetini oluşturduğuma dikkat edin. Bu sadece görsel olarak bana çekici geldi, ancak ofset seçiminin çok önemli olduğu ve aralık boyunca sabit olmadığı ve doğru seçilirse ambalajda% 8'e kadar daha iyi doğruluk elde edilmesi anlamına gelebileceği ortaya çıktı. Aynı zamanda onun R 2 dizisinin bir küreyi örtmesini sağlamanın bir yolu olmalı ve bunun aynı zamanda güzel ve eşit bir örtü oluşturup oluşturmadığını görmek ilginç olurdu, belki olduğu gibi ama belki de, örneğin, sadece yarısından alınması gerekiyor mu? birim kare çapraz olarak kesilir ve bir daire elde etmek için gerilir.

notlar

  1. Bu "çubuklar", bir sayıya rasyonel yaklaşımlarla oluşturulur ve bir sayıya yönelik en iyi rasyonel yaklaşımlar , bir tam sayı olan ve pozitif tam sayıların sonlu veya sonsuz bir dizisi z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))olduğu sürekli kesir ifadesinden gelir :zn_1, n_2, n_3, ...

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

    Kesir bölümü 1/(...)her zaman sıfır ile bir arasında olduğu için , sürekli kesirdeki büyük bir tam sayı özellikle iyi bir rasyonel yaklaşıma olanak tanır: "100 ile 101 arasında bir bölü bir", "bir bölü 1 ile 2" den daha iyidir. Dolayısıyla en irrasyonel sayı, 1 + 1/(1 + 1/(1 + ...))özellikle iyi rasyonel yaklaşımları olan ve olmayan sayıdır ; Bir çözebilir φ = 1 + 1 / φ göre yoluyla çarparak cp altın oranı için, aşağıdaki formüle alır.

  2. NumPy'ye pek aşina olmayan kişiler için - tüm işlevler "vektörleştirilmiştir", bu nedenle bu sqrt(array), diğer dillerin yazabileceği ile aynıdır map(sqrt, array). Yani bu, bileşen bileşen bir sqrtuygulamadır. Aynı şey, bir skaler ile bölme veya skaler ile toplama için de geçerlidir - bunlar paralel olarak tüm bileşenler için geçerlidir.

  3. Bunun sonucun olduğunu öğrendikten sonra kanıt basittir. O olasılık ne sorarsanız z < Z < z + d z , bu olasılığı ne soran aynıdır z < F -1 ( U ) < z + d z , uygulamak F o olduğuna dikkat çeken üç ifadelere monoton olarak artan bir fonksiyon, dolayısıyla F ( z ) < U < F ( z + d z ), F ( z ) + f'yi bulmak için sağ tarafı genişletin( Z ) d z ve o zamandan beri , U , bu olasılık sadece bir üniform f ( Z ) d z söz olarak.


4
Bunun neden bu kadar aşağıda olduğundan emin değilim, bu bunu yapmak için açık ara en hızlı yöntem.
whn

2
@snb nazik sözler için teşekkür ederim! Kısmen çok aşağı çünkü buradaki diğer tüm cevaplardan çok, çok daha genç. Olduğu kadar iyi yapmasına bile şaşırdım.
CR Drost

Bana kalan bir soru şudur: Herhangi iki nokta arasında belirli bir maksimum mesafe için kaç noktayı dağıtmam gerekir?
Felix D.

1
@FelixD. Bu, özellikle Öklid mesafeleri yerine örneğin büyük daire mesafelerini kullanmaya başlarsanız, çok hızlı bir şekilde karmaşıklaşabilecek bir soru gibi geliyor. Ama belki basit bir soruya cevap verebilirim, eğer küre üzerindeki noktaları Voronoi diyagramına dönüştürürse, her bir Voronoi hücresini yaklaşık 4π / N alanına sahip olarak tanımlayabilirim ve bunu bir daire yerine bir çembermiş gibi davranarak karakteristik bir mesafeye çevirebilirim. eşkenar dörtgenden, πr² = 4π / N. O zaman r = 2 / √ (N).
CR Drost

2
Örnekleme teoremini rastgele tek tip girdi yerine tek tip girdi ile kullanmak, bana "Peki, neden # $% & bunu düşünmedim?" Dememe neden olan şeylerden biri. . Güzel.
dmckee --- eski moderatör yavru kedi

86

Bu, bir küre üzerindeki paketleme noktaları olarak bilinir ve (bilinen) genel, mükemmel bir çözüm yoktur. Bununla birlikte, pek çok kusurlu çözüm vardır. En popüler üç tanesi şöyle görünüyor:

  1. Bir simülasyon oluşturun . Her noktayı bir küreyle sınırlı bir elektron olarak ele alın, ardından belirli sayıda adım için bir simülasyon çalıştırın. Elektronların itilmesi doğal olarak sistemi, noktaların birbirlerinden alabildikleri kadar uzakta olduğu daha kararlı bir duruma yöneltir.
  2. Hypercube reddi . Kulağa hoş gelen bu yöntem aslında gerçekten çok basittir: Küreyi çevreleyen küpün içindeki noktaları ( nbunlardan çok daha fazlasını ) tekdüze olarak seçersiniz , sonra kürenin dışındaki noktaları reddedersiniz. Kalan noktaları vektör olarak değerlendirin ve normalleştirin. Bunlar sizin "örneklerinizdir" - nbir yöntem kullanarak onları seçin (rastgele, açgözlü, vb.).
  3. Spiral yaklaşımlar . Bir kürenin etrafında bir spiral çizersiniz ve spiralin etrafındaki noktaları eşit olarak dağıtırsınız. İşin içerdiği matematik nedeniyle, bunların anlaşılması simülasyondan daha karmaşıktır, ancak çok daha hızlıdır (ve muhtemelen daha az kod içerir). En popüler olanı Saff ve ark .

Bir sürü bu sorun hakkında daha fazla bilgi bulunabilir burada


Aşağıda Andrew cooke'un yayınladığı spiral taktiğe bakacağım, ancak benim ne istediğim ile "tek tip rastgele dağılım" arasındaki farkı açıklayabilir misiniz? Bu, tekdüze olarak yerleştirilmeleri için bir küre üzerine noktaların% 100 rastgele yerleştirilmesi mi? Yardım için teşekkürler. :)
Befall

4
@Befall: "düzgün rastgele dağılım", olasılık dağılımının tekdüze olmasını ifade eder - bu, küre üzerinde rastgele bir nokta seçerken, her noktanın eşit seçilme olasılığına sahip olduğu anlamına gelir. Bunun, noktaların son mekansal dağılımı ile hiçbir ilgisi yoktur ve bu nedenle sorunuzla hiçbir ilgisi yoktur.
BlueRaja - Danny Pflughoeft

Ahhh, tamam, çok teşekkürler. Sorumu aramak, her ikisi için de bir ton cevapla sonuçlandı ve benim için anlamsız olanı gerçekten anlayamadım.
59'da düşüş

Açık olmak gerekirse, her noktanın seçilme olasılığı sıfırdır. Kürenin yüzeyinde noktanın herhangi iki alana ait olma olasılıklarının oranı yüzeylerin oranına eşittir.
AturSams

2
Son halka artık öldü
Felix D.

10

Aradığınız şeye küresel kaplama denir . Küresel örtme problemi çok zordur ve az sayıdaki nokta dışında çözümleri bilinmemektedir. Kesin olarak bilinen bir şey, bir küre üzerinde n nokta verildiğinde, her zaman iki d = (4-csc^2(\pi n/6(n-2)))^(1/2)veya daha yakın mesafe noktası vardır .

Bir küre üzerinde tekdüze olarak dağıtılmış noktalar oluşturmak için olasılıklı bir yöntem istiyorsanız, bu kolaydır: Gauss dağılımıyla uzayda tekdüze olarak noktalar oluşturun (Java'da yerleşiktir, diğer diller için kodu bulmak zor değildir). Dolayısıyla, 3 boyutlu uzayda şunun gibi

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

Ardından noktayı başlangıç ​​noktasına olan mesafesini normalleştirerek küreye yansıtın

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

N boyutlu Gauss dağılımı küresel olarak simetriktir, bu nedenle küre üzerindeki izdüşüm üniformdur.

Elbette, tek tip olarak oluşturulmuş noktalar koleksiyonundaki herhangi iki nokta arasındaki mesafenin aşağıda sınırlandırılacağına dair bir garanti yoktur, bu nedenle sahip olabileceğiniz bu tür koşulları uygulamak için reddetmeyi kullanabilirsiniz: muhtemelen tüm koleksiyonu oluşturmak en iyisidir ve sonra gerekirse tüm koleksiyonu reddedin. (Veya şimdiye kadar oluşturduğunuz tüm koleksiyonu reddetmek için "erken reddetme" yi kullanın; sadece bazı noktaları saklamayın ve diğerlerini bırakmayın.) dAradaki minimum mesafeyi belirlemek için yukarıda verilen formülü eksi biraz gevşek altında bir dizi noktayı reddedeceğiniz noktalar. 2 mesafe seçip hesaplamanız gerekecek ve reddedilme olasılığı boşluğa bağlı olacaktır; Nasıl olduğunu söylemek zor, bu nedenle ilgili istatistikler hakkında fikir edinmek için bir simülasyon çalıştırın.


Minimum maksimum mesafe ifadeleri için oy verildi. Kullanmak istediğiniz puan sayısına sınır koymak için kullanışlıdır. Yine de bunun için yetkili bir kaynağa atıfta bulunmak güzel olurdu.
dmckee --- eski moderatör yavru kedi

6

Bu cevap, bu cevapla iyi bir şekilde özetlenen aynı 'teoriye' dayanmaktadır.

Bu yanıtı şu şekilde ekliyorum:
- Diğer seçeneklerin hiçbiri 'tekdüzelik' ihtiyacına 'tam olarak uymuyor (ya da açıkça-açıkça öyle değil). (Gezegene, özellikle orijinal sorunda özellikle istenen dağılım görünümlü davranışı elde etmek için rastgele oluşturulmuş k noktalarının sonlu listesinden reddediyorsunuz (k öğesindeki indeks sayımını rastgele geri döndürür).)
- En yakın diğer impl sizi, her iki açısal eksen değerinde sadece bir N değerine karşılık "N" ye "açısal eksen" ile karar vermeye zorladı (bu, N'nin düşük sayımlarında neyin önemli olup olmadığını bilmek çok zordur ( örneğin '5' puan istiyorsunuz - iyi eğlenceler))
- Dahası, herhangi bir görüntü olmadan diğer seçenekleri nasıl ayırt etmek çok zordur, işte bu seçenek (aşağıda) ve bununla birlikte çalışan çalışmaya hazır uygulama.

20'de N ile:

görüntü açıklamasını buraya girin
ve sonra 80'de N: görüntü açıklamasını buraya girin


işte çalıştırmaya hazır python3 kodu, burada öykünme aynı kaynak: " http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere " başkaları tarafından bulundu . (Dahil ettiğim, 'ana' olarak çalıştırıldığında patlayan çizim şu adresten alınmıştır: 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

düşük sayılarda test edildi (2, 5, 7, 13, vb. sayılarda N) ve 'iyi' çalışıyor gibi görünüyor


5

Deneyin:

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

Yukarıdaki fonksiyon, toplam N döngü ve k döngü akımı yinelemeli döngü içinde çalışmalıdır.

Ayçiçeği çekirdeği desenine dayanır, ancak ayçiçeği tohumlarının etrafına yarım kubbe ve tekrar küre şeklinde kıvrılır.

İşte bir resim, ancak kamerayı kürenin yarısına kadar koyduğum için 3d yerine 2d görünüyor çünkü kamera tüm noktalardan aynı uzaklıkta. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg


2

Healpix yakından ilgili bir sorunu çözer (küreyi eşit alan pikselleriyle pikselleştirme):

http://healpix.sourceforge.net/

Muhtemelen abartılıdır, ancak belki ona baktıktan sonra diğer bazı güzel özelliklerinin sizin için ilginç olduğunu fark edeceksiniz. Bir nokta bulutu çıkaran bir işlevden çok daha fazlası.

Onu tekrar bulmaya çalışarak buraya indim; "healpix" adı tam olarak küreleri çağrıştırmıyor ...


1

az sayıda nokta ile bir simülasyon çalıştırabilirsiniz:

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

cevabımı iyileştirmek için en yakın_index = i'yi en yakın_index = randchoice (i, j) olarak değiştirmelisiniz
robert king

1

Sizin en büyük iki faktörünüzü alın N, eğer N==20o zaman en büyük iki faktör {5,4}veya daha genel olarak {a,b}. Hesaplamak

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

İçin ilk noktayı koyun {90-dlat/2,(dlong/2)-180}, at ikinci {90-dlat/2,(3*dlong/2)-180}, adresinde 3rd {90-dlat/2,(5*dlong/2)-180}size yaklaşık lazım hangi zaman bir kez dünyaya, yuvarlak takıldı bulana kadar, {75,150}bir sonraki gittiğinizde {90-3*dlat/2,(dlong/2)-180}.

Açıkçası bunu küresel dünyanın yüzeyinde derece cinsinden, +/- N / S veya E / W'ye çevirmek için olağan kurallarla çalışıyorum. Ve tabii ki bu size tamamen rastgele olmayan bir dağılım sağlar, ancak tek tiptir ve noktalar bir araya toplanmaz.

Bir dereceye kadar rastgelelik eklemek için, 2 tane normal dağıtılmış (uygun şekilde ortalama 0 ve std dev {dlat / 3, dlong / 3} ile) oluşturabilir ve bunları düzgün dağılmış noktalarınıza ekleyebilirsiniz.


5
Enlem yerine günah (enlem) olarak çalışırsanız bu çok daha iyi görünür. olduğu gibi, kutupların yakınında çok fazla toplanma elde edeceksiniz.
andrew cooke

1

düzenleme: Bu, OP'nin sormak istediği soruyu yanıtlamaz, insanların bir şekilde yararlı bulması ihtimaline karşı burada bırakır.

Olasılık çarpım kuralını sonsuz simitlerle birlikte kullanıyoruz. Bu, istediğiniz sonucu elde etmek için 2 satır kodla sonuçlanır:

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

(aşağıdaki koordinat sisteminde tanımlanmıştır :)

görüntü açıklamasını buraya girin

Diliniz tipik olarak tek tip rasgele sayı ilkeline sahiptir. Örneğin python'da random.random()aralıktaki bir sayıyı döndürmek için kullanabilirsiniz [0,1). Aralıkta rastgele bir sayı elde etmek için bu sayıyı k ile çarpabilirsiniz [0,k). Böylece python'da uniform([0,2pi))demek olur random.random()*2*math.pi.


Kanıt

Şimdi tekdüze olarak θ atayamayız, aksi takdirde kutuplarda topaklanırız. Küresel kamanın yüzey alanına orantılı olasılıklar atamak istiyoruz (bu diyagramdaki θ aslında φ'dur):

görüntü açıklamasını buraya girin

Ekvatorda açısal bir yer değiştirme dφ, dφ * r'nin yer değiştirmesine neden olacaktır. Bu yer değiştirme keyfi bir azimutta θ ne olacak? Pekala, z ekseninin yarıçapı olduğu r*sin(θ)için kama ile kesişen "enlemin" yay uzunluğu dφ * r*sin(θ). Böylece , dilimin alanını güney kutbundan kuzey kutbuna entegre ederek, ondan örneklenecek alanın kümülatif dağılımını hesaplıyoruz .

görüntü açıklamasını buraya girin(burada şeyler = dφ*r)

Şimdi ondan örneklemek için CDF'nin tersini almaya çalışacağız: http://en.wikipedia.org/wiki/Inverse_transform_sampling

İlk önce neredeyse CDF'mizi maksimum değerine bölerek normalize ediyoruz. Bu, dφ ve r'yi iptal etme yan etkisine sahiptir.

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

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

Böylece:

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

bu, "% 100 rastgele" olarak attığı seçeneğe eşdeğer değil mi? Anladığım kadarıyla, tek tip rastgele dağılımdan daha eşit aralıklı olmalarını istiyor.
andrew cooke

@ BlueRaja-DannyPflughoeft: Hmm, yeterince adil. Sanırım soruyu olması gerektiği kadar dikkatli okumadım. Başkalarının yararlı bulması durumunda bunu burada bırakıyorum. Bunu işaret ettiğiniz için teşekkürler.
ninjagecko

1

VEYA ... 20 nokta yerleştirmek için ikosahedronal yüzlerin merkezlerini hesaplayın. 12 nokta için icosahedron'un köşelerini bulun. 30 nokta için, ikosahedronun kenarlarının orta noktası. aynı şeyi tetrahedron, küp, oniki yüzlü ve oktahedronlar için de yapabilirsiniz: bir nokta kümesi köşelerde, diğeri yüzün merkezinde ve diğeri de kenarların ortasında. Ancak karıştırılamazlar.


İyi bir fikir, ancak yalnızca 4, 6, 8, 12, 20, 24 veya 30 puan için işe yarar.
Şapkalı Adam

Hile yapmak istiyorsanız, yüzlerin ve tepe noktalarının merkezini kullanabilirsiniz. Onlar edecek değil eşit aralıklı ama iyi bir yaklaşım olabilir. Bu güzel çünkü determinist.
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
Bunun ne anlama geldiğini açıklayan bir metin yazmanız yararlı olacaktır, böylece OP'nin sadece işe yarayacağına inanması gerekmez.
hcarver

0

@robert king Bu gerçekten güzel bir çözüm ama içinde bazı özensiz hatalar var. Yine de bana çok yardımcı olduğunu biliyorum, bu yüzden saçmalığa aldırma. :) İşte temizlenmiş bir versiyon ...

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

Bu işe yarıyor ve son derece basit. İstediğiniz kadar puan:

    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());
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.