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))/2
ve 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):
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()
- Ö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 .
- Daha sonra CDF'nin ters fonksiyonu F -1 ( z ) hesaplayın .
- 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_pts
biz 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:
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
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 :z
n_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.
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 sqrt
uygulamadı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.
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.