Metode spiral emas
Anda bilang Anda tidak bisa mendapatkan metode spiral emas untuk bekerja dan itu memalukan karena itu benar-benar bagus. Saya ingin memberi Anda pemahaman yang lengkap tentang hal itu sehingga mungkin Anda dapat memahami cara menjaga agar ini tidak "terkumpul".
Jadi, inilah cara cepat dan tidak acak untuk membuat kisi yang kira-kira benar; Seperti dibahas di atas, tidak ada kisi yang sempurna, tetapi ini mungkin cukup baik. Ini dibandingkan dengan metode lain misalnya di BendWavy.org tetapi hanya memiliki tampilan yang bagus dan cantik serta jaminan tentang jarak yang merata dalam batas.
Primer: spiral bunga matahari pada disk unit
Untuk memahami algoritma ini, pertama-tama saya mengundang Anda untuk melihat algoritma spiral bunga matahari 2D. Hal ini didasarkan pada fakta bahwa bilangan paling irasional adalah rasio emas (1 + sqrt(5))/2
dan jika seseorang mengeluarkan poin dengan pendekatan "berdiri di tengah, putar rasio emas dari seluruh belokan, lalu pancarkan titik lain ke arah itu", seseorang secara alami membangun a spiral yang, ketika Anda mencapai jumlah poin yang semakin tinggi, namun menolak untuk memiliki 'batang' yang terdefinisi dengan baik di mana titik-titik tersebut berbaris. (Catatan 1.)
Algoritme untuk spasi genap pada disk adalah,
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()
dan menghasilkan hasil yang terlihat seperti (n = 100 dan n = 1000):
Memberi jarak titik secara radial
Hal aneh utama adalah rumusnya r = sqrt(indices / num_pts)
; bagaimana saya bisa sampai yang itu? (Catatan 2.)
Nah, saya menggunakan akar kuadrat di sini karena saya ingin ini memiliki jarak area yang sama di sekitar disk. Itu sama dengan mengatakan bahwa dalam batas N besar saya ingin sedikit daerah R ∈ ( r , r + d r ), Θ ∈ ( θ , θ + d θ ) berisi sejumlah titik sebanding dengan luasnya, yang merupakan r d r d θ . Sekarang jika kita berpura-pura bahwa kita berbicara tentang variabel acak di sini, ini memiliki interpretasi langsung yang mengatakan bahwa kepadatan probabilitas gabungan untuk ( R , Θ ) hanya cr untuk beberapa konstanta c . Normalisasi pada disk unit kemudian akan memaksa c = 1 / π.
Sekarang izinkan saya memperkenalkan trik. Itu berasal dari teori probabilitas di mana itu dikenal sebagai pengambilan sampel CDF terbalik : misalkan Anda ingin menghasilkan variabel acak dengan kepadatan probabilitas f ( z ) dan Anda memiliki variabel acak U ~ Uniform (0, 1), seperti keluar dari random()
di sebagian besar bahasa pemrograman. Bagaimana kamu melakukan ini?
- Pertama, ubah kepadatan Anda menjadi fungsi distribusi kumulatif atau CDF, yang akan kita sebut F ( z ). Ingat, CDF meningkat secara monoton dari 0 ke 1 dengan turunan f ( z ).
- Kemudian hitung fungsi invers CDF F -1 ( z ).
- Anda akan menemukan bahwa Z = F -1 ( U ) didistribusikan sesuai dengan kepadatan target. (Catatan 3).
Sekarang trik spiral rasio emas memberi jarak pada poin dengan pola yang sama bagusnya untuk θ jadi mari kita integrasikan; untuk disk unit kita mendapatkan F ( r ) = r 2 . Jadi fungsi inversnya adalah F -1 ( u ) = u 1/2 , dan oleh karena itu kita akan menghasilkan titik acak pada piringan dalam koordinat kutub dengan r = sqrt(random()); theta = 2 * pi * random()
.
Sekarang alih-alih mengambil sampel secara acak dari fungsi terbalik ini, kami mengambil sampelnya secara seragam , dan hal yang menyenangkan tentang pengambilan sampel yang seragam adalah bahwa hasil kami tentang bagaimana titik-titik tersebar dalam batas N besar akan berperilaku seolah-olah kami telah mengambil sampelnya secara acak. Kombinasi ini adalah triknya. Alih-alih random()
kita gunakan (arange(0, num_pts, dtype=float) + 0.5)/num_pts
, jadi, katakanlah, jika kita ingin mencicipi 10 poin itu r = 0.05, 0.15, 0.25, ... 0.95
. Kami mencontohkan r secara seragam untuk mendapatkan jarak area yang sama, dan kami menggunakan kenaikan bunga matahari untuk menghindari "batang" titik yang buruk dalam output.
Sekarang melakukan bunga matahari di atas bola
Perubahan yang perlu kita lakukan untuk menandai bola dengan titik hanya melibatkan penggantian koordinat kutub untuk koordinat bola. Koordinat radial tentu saja tidak masuk ke sini karena kita berada di bola satuan. Untuk menjaga hal-hal sedikit lebih konsisten di sini, meskipun saya dilatih sebagai fisikawan, saya akan menggunakan koordinat matematikawan di mana 0 ≤ φ ≤ π adalah garis lintang yang turun dari kutub dan 0 ≤ θ ≤ 2π adalah bujur. Jadi perbedaan dari atas adalah pada dasarnya kita mengganti variabel r dengan φ .
Elemen luas kita, yang tadinya r d r d θ , sekarang menjadi sin ( φ ) d φ d θ yang tidak lebih rumit . Jadi kepadatan gabungan kita untuk jarak seragam adalah sin ( φ ) / 4π. Mengintegrasikan θ , kita menemukan f ( φ ) = sin ( φ ) / 2, jadi F ( φ ) = (1 - cos ( φ )) / 2. Membalik ini kita dapat melihat bahwa variabel acak yang seragam akan terlihat seperti acos (1 - 2 u ), tetapi kita mengambil sampel secara seragam dan bukan secara acak, jadi kita menggunakan φ k = acos (1 - 2 ( k)+ 0,5) / N ). Dan algoritme lainnya hanya memproyeksikan ini ke koordinat x, y, dan z:
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()
Sekali lagi untuk n = 100 dan n = 1000 hasilnya terlihat seperti:
Penelitian lebih lanjut
Saya ingin memberikan teriakan ke blog Martin Roberts. Perhatikan bahwa di atas saya membuat offset indeks saya dengan menambahkan 0,5 ke setiap indeks. Ini hanya menarik secara visual bagi saya, tetapi ternyata pilihan offset sangat penting dan tidak konstan selama interval dan bisa berarti mendapatkan akurasi 8% lebih baik dalam pengemasan jika dipilih dengan benar. Seharusnya juga ada cara agar urutan R 2 -nya menutupi sebuah bola dan akan menarik untuk melihat apakah ini juga menghasilkan penutup yang rata dan bagus, mungkin apa adanya tetapi mungkin perlu, katakanlah, diambil dari hanya setengah dari unit persegi dipotong secara diagonal atau lebih dan diregangkan untuk membentuk lingkaran.
Catatan
"Batang" tersebut dibentuk oleh pendekatan rasional terhadap sebuah angka, dan pendekatan rasional terbaik untuk sebuah angka berasal dari ekspresi pecahan lanjutannya, di z + 1/(n_1 + 1/(n_2 + 1/(n_3 + ...)))
mana z
adalah bilangan bulat dan n_1, n_2, n_3, ...
merupakan urutan bilangan bulat positif terbatas atau tak terbatas:
def continued_fraction(r):
while r != 0:
n = floor(r)
yield n
r = 1/(r - n)
Sejak bagian pecahan 1/(...)
selalu antara nol dan satu, bilangan bulat besar dalam pecahan lanjutan memungkinkan perkiraan rasional yang sangat baik: "satu dibagi sesuatu antara 100 dan 101" lebih baik daripada "satu dibagi sesuatu antara 1 dan 2." Oleh karena itu, bilangan yang paling irasional adalah yang merupakan 1 + 1/(1 + 1/(1 + ...))
dan tidak memiliki perkiraan rasional yang sangat baik; seseorang dapat menyelesaikan φ = 1 + 1 / φ dengan mengalikannya dengan φ untuk mendapatkan rumus rasio emas.
Untuk orang-orang yang tidak begitu akrab dengan NumPy - semua fungsinya “di-vectorisasi”, jadi itu sqrt(array)
sama dengan yang mungkin ditulis oleh bahasa lain map(sqrt, array)
. Jadi ini adalah aplikasi komponen demi komponen sqrt
. Hal yang sama juga berlaku untuk pembagian dengan skalar atau penjumlahan dengan skalar - yang berlaku untuk semua komponen secara paralel.
Buktinya sederhana setelah Anda tahu bahwa inilah hasilnya. Jika Anda bertanya berapa probabilitas z < Z < z + d z , ini sama dengan menanyakan berapa probabilitas z < F -1 ( U ) < z + d z , terapkan F ke ketiga ekspresi dengan memperhatikan bahwa itu adalah fungsi yang meningkat secara monoton, maka F ( z ) < U < F ( z + d z ), perluas sisi kanan keluar untuk mencari F ( z ) + f( z ) d z , dan karena U seragam, probabilitas ini hanya f ( z ) d z seperti yang dijanjikan.