Mendistribusikan n poin secara merata pada sebuah bola


121

Saya memerlukan algoritme yang dapat memberi saya posisi di sekitar bola untuk poin N (kurang dari 20, mungkin) yang menyebarkannya secara samar. Tidak perlu "kesempurnaan", tapi saya hanya membutuhkannya jadi tidak ada yang bisa digabungkan.

  • Pertanyaan ini memberikan kode yang bagus, tetapi saya tidak dapat menemukan cara untuk membuat seragam ini, karena ini tampak 100% acak.
  • Posting blog ini merekomendasikan memiliki dua cara yang memungkinkan input jumlah titik pada bola, tetapi algoritma Saff dan Kuijlaars persis dalam psuedocode yang dapat saya transkripsikan, dan contoh kode yang saya temukan berisi "node [k]", yang tidak dapat saya lihat menjelaskan dan menghancurkan kemungkinan itu. Contoh blog kedua adalah Golden Section Spiral, yang memberi saya hasil yang aneh dan terkumpul, tanpa cara yang jelas untuk menentukan radius konstan.
  • Algoritme dari pertanyaan ini sepertinya dapat berfungsi, tetapi saya tidak dapat mengumpulkan apa yang ada di halaman itu ke dalam psuedocode atau apa pun.

Beberapa utas pertanyaan lain yang saya temukan berbicara tentang distribusi seragam secara acak, yang menambah tingkat kerumitan yang tidak saya khawatirkan. Saya minta maaf karena ini pertanyaan yang konyol, tetapi saya ingin menunjukkan bahwa saya benar-benar berusaha keras dan masih gagal.

Jadi, yang saya cari adalah pseudocode sederhana untuk mendistribusikan titik N secara merata di sekitar bola satuan, yang mengembalikan dalam koordinat bola atau Kartesius. Bahkan lebih baik jika ia bahkan dapat menyebar dengan sedikit pengacakan (pikirkan planet di sekitar bintang, tersebar dengan baik, tetapi dengan ruang untuk kelonggaran).


Apa maksud Anda "dengan sedikit pengacakan"? Apakah maksud Anda gangguan dalam beberapa hal?
ninjagecko

32
OP bingung. Yang dia cari adalah meletakkan titik-n pada sebuah bola, sehingga jarak minimum antara dua titik mana pun adalah sebesar mungkin. Ini akan memberikan poin-poin yang tampak "terdistribusi secara merata" di seluruh bidang. Ini sama sekali tidak terkait dengan pembuatan distribusi acak yang seragam pada sebuah bola, yang merupakan inti dari banyak tautan tersebut, dan apa yang dibicarakan oleh banyak jawaban di bawah ini.
BlueRaja - Danny Pflughoeft

1
Angka 20 bukanlah banyak poin untuk ditempatkan pada sebuah bola jika Anda tidak ingin mereka terlihat acak.
John Alexiou

2
Berikut adalah cara untuk melakukannya (memiliki contoh kode): pdfs.semanticscholar.org/97a6/… (sepertinya menggunakan perhitungan gaya tolak)
trusktr

1
Tentu saja untuk nilai N pada {4, 6, 8, 12, 20} terdapat solusi eksak di mana jarak dari setiap titik ke (masing-masing) tetangga terdekatnya adalah konstanta untuk semua titik dan semua tetangga terdekat.
dmckee --- mantan moderator anak kucing

Jawaban:


13

Dalam contoh kode node[k] ini hanya simpul ke-k. Anda menghasilkan titik array N dan node[k]merupakan kth (dari 0 hingga N-1). Jika hanya itu yang membuat Anda bingung, semoga Anda bisa menggunakannya sekarang.

(dengan kata lain, kadalah larik berukuran N yang ditentukan sebelum fragmen kode dimulai, dan yang berisi daftar poin).

Atau , membangun jawaban lain di sini (dan menggunakan Python):

> 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

Jika Anda memplotnya, Anda akan melihat bahwa jarak vertikal lebih besar di dekat kutub sehingga setiap titik terletak di sekitar total luas ruang yang sama (di dekat kutub ada lebih sedikit ruang "secara horizontal", sehingga memberi lebih banyak "vertikal" ).

Ini tidak sama dengan semua titik yang memiliki jarak yang kira-kira sama ke tetangga mereka (yang menurut saya link Anda bicarakan), tetapi mungkin cukup untuk apa yang Anda inginkan dan meningkatkan hanya dengan membuat kisi lintang / bujur yang seragam .


bagus, ada baiknya melihat solusi matematika. Saya berpikir untuk menggunakan heliks dan pemisahan panjang busur. Saya masih belum yakin bagaimana mendapatkan solusi optimal yang merupakan masalah yang menarik.
robert king

apakah Anda melihat bahwa saya mengedit jawaban saya untuk menyertakan penjelasan node [k] di atas? Saya rasa mungkin hanya itu yang Anda butuhkan ...
andrew cooke

Luar biasa, terima kasih atas penjelasannya. Saya akan mencobanya nanti, karena saya tidak punya waktu saat ini, tapi terima kasih banyak telah membantu saya. Saya akan memberi tahu Anda bagaimana ini akhirnya berhasil untuk tujuan saya. ^^
Befall

Menggunakan metode Spiral sangat sesuai dengan kebutuhan saya, terima kasih banyak atas bantuan dan klarifikasinya. :)
Befall

13
Tautannya sepertinya mati.
Scheintod

140

Algoritma Fibonacci sphere sangat bagus untuk ini. Cepat dan memberikan hasil yang sekilas akan dengan mudah menipu mata manusia. Anda dapat melihat contoh selesai dengan pemrosesan yang akan menunjukkan hasilnya seiring waktu saat poin ditambahkan. Berikut contoh interaktif hebat lainnya yang dibuat oleh @gman. Dan inilah implementasi sederhana di python.

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 sampel memberi Anda ini:

masukkan deskripsi gambar di sini


variabel n dipanggil saat mendefinisikan phi: phi = ((i + rnd)% n) * increment. Apakah n = sampel?
Andrew Staroscik

@AndrewStaroscik ya! Ketika saya pertama kali menulis kode saya menggunakan "n" sebagai variabel dan kemudian mengubah nama tetapi tidak melakukan uji tuntas. Terima kasih sudah menangkapnya!
Fnord


4
@Xarbrough kode memberi Anda poin di sekitar bola unit, jadi kalikan saja setiap poin dengan skalar apa pun yang Anda inginkan untuk radius.
Fnord

2
@Fnord: Bisakah kita melakukan ini untuk dimensi yang lebih tinggi?
pikachuchameleon

108

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))/2dan 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):

masukkan deskripsi gambar di sini

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?

  1. 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 ).
  2. Kemudian hitung fungsi invers CDF F -1 ( z ).
  3. 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: masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

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

  1. "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 zadalah 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.

  2. 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.

  3. 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.


4
Saya tidak yakin mengapa ini sejauh ini, sejauh ini metode cepat terbaik untuk melakukan ini.
whn

2
@snb terima kasih atas kata-katanya yang baik! sebagian karena jauh, jauh lebih muda dari semua jawaban lainnya di sini. Saya terkejut bahwa hal itu bahkan berjalan sebaik yang telah terjadi.
CR Drost

Satu pertanyaan yang tersisa untuk saya adalah: Berapa banyak poin yang harus saya distribusikan untuk jarak maksimum tertentu antara dua titik?
Felix D.

1
@Tokopedia Kedengarannya seperti pertanyaan yang bisa menjadi sangat rumit dengan sangat cepat terutama jika Anda mulai menggunakan, katakanlah, jarak lingkaran-besar daripada jarak Euclidean. Tapi mungkin saya bisa menjawab pertanyaan sederhana, jika seseorang mengubah titik-titik pada bola menjadi diagram Voronoi mereka, seseorang dapat mendeskripsikan setiap sel Voronoi memiliki kira-kira luas area 4π / N dan seseorang dapat mengubahnya menjadi jarak karakteristik dengan berpura-pura itu lingkaran. dari belah ketupat, πr² = 4π / N. Kemudian r = 2 / √ (N).
CR Drost

2
Menggunakan teorema pengambilan sampel dengan input yang benar-benar seragam dan bukan dengan input yang seragam secara acak adalah salah satu hal yang membuat saya berkata "Nah, mengapa # $% & saya tidak memikirkannya?" . Bagus.
dmckee --- mantan moderator anak kucing

86

Ini dikenal sebagai titik pengepakan pada bola, dan tidak ada solusi umum yang sempurna (diketahui). Namun, ada banyak solusi yang tidak sempurna. Tiga yang paling populer tampaknya:

  1. Buat simulasi . Perlakukan setiap titik sebagai elektron yang dibatasi pada bola, lalu jalankan simulasi untuk sejumlah langkah tertentu. Tolakan elektron secara alami akan cenderung sistem ke keadaan yang lebih stabil, di mana titik-titik tersebut berjarak sejauh mungkin dari satu sama lain.
  2. Penolakan hypercube . Metode yang terdengar mewah ini sebenarnya sangat sederhana: Anda memilih titik-titik secara seragam (lebih ndari mereka) di dalam kubus yang mengelilingi bola, lalu menolak titik-titik di luar bola. Perlakukan titik yang tersisa sebagai vektor, dan normalkan. Ini adalah "sampel" Anda - pilih ndari mereka menggunakan beberapa metode (secara acak, serakah, dll).
  3. Perkiraan spiral . Anda menelusuri spiral di sekitar bola, dan mendistribusikan titik-titik di sekitar spiral secara merata. Karena matematika terlibat, ini lebih rumit untuk dipahami daripada simulasi, tetapi jauh lebih cepat (dan mungkin melibatkan lebih sedikit kode). Yang paling populer tampaknya oleh Saff, et al .

Lebih banyak informasi tentang masalah ini dapat ditemukan di sini


Saya akan melihat ke dalam taktik spiral andrew cooke yang diposting di bawah ini, namun, bisakah Anda menjelaskan perbedaan antara apa yang saya inginkan dan apa itu "distribusi acak seragam"? Apakah itu hanya penempatan titik-titik secara acak 100% pada sebuah bola sehingga ditempatkan secara seragam? Terima kasih untuk bantuannya. :)
Befall

4
@Befall: "distribusi acak seragam" mengacu pada distribusi probabilitas yang seragam - artinya, ketika memilih titik acak pada bola, setiap titik memiliki kemungkinan yang sama untuk dipilih. Ini tidak ada hubungannya dengan distribusi spasial akhir dari titik-titik, dan dengan demikian tidak ada hubungannya dengan pertanyaan Anda.
BlueRaja - Danny Pflughoeft

Ahhh, oke, terima kasih banyak. Mencari pertanyaan saya menghasilkan banyak jawaban untuk keduanya, dan saya tidak dapat benar-benar memahami mana yang tidak ada gunanya bagi saya.
Befall

Untuk lebih jelasnya, setiap titik memiliki probabilitas nol untuk dipilih. Rasio probabilitas titik tersebut akan dimiliki oleh dua area mana pun di permukaan bola, sama dengan rasio permukaan.
AturSams

2
Tautan terakhir sekarang sudah mati
Felix D.

10

Apa yang Anda cari disebut penutup bola . Masalah penutup bola sangat sulit dan solusinya tidak diketahui kecuali untuk sejumlah kecil poin. Satu hal yang diketahui dengan pasti adalah bahwa dengan n titik pada sebuah bola, selalu ada dua titik jarakd = (4-csc^2(\pi n/6(n-2)))^(1/2) atau lebih dekat.

Jika Anda menginginkan metode probabilistik untuk menghasilkan titik-titik yang didistribusikan secara seragam pada sebuah bola, itu mudah: menghasilkan titik-titik dalam ruang secara seragam dengan distribusi Gaussian (ini dibangun ke dalam Java, tidak sulit untuk menemukan kode untuk bahasa lain). Jadi dalam ruang 3 dimensi, Anda membutuhkan sesuatu seperti

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

Kemudian proyeksikan titik tersebut pada bola dengan menormalkan jaraknya dari asalnya

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

Distribusi Gaussian dalam n dimensi simetris bola sehingga proyeksi ke bola seragam.

Tentu saja, tidak ada jaminan bahwa jarak antara dua titik mana pun dalam kumpulan titik yang dihasilkan secara seragam akan dibatasi di bawah, jadi Anda dapat menggunakan penolakan untuk menerapkan ketentuan apa pun yang mungkin Anda miliki: mungkin yang terbaik adalah menghasilkan seluruh koleksi lalu tolak seluruh koleksi jika perlu. (Atau gunakan "penolakan awal" untuk menolak seluruh koleksi yang telah Anda hasilkan sejauh ini; hanya saja, jangan simpan beberapa poin dan jatuhkan yang lain.) Anda dapat menggunakan rumus yang ddiberikan di atas, dikurangi beberapa kelonggaran, untuk menentukan jarak min antara poin di bawah ini yang akan Anda tolak serangkaian poin. Anda harus menghitung n memilih 2 jarak, dan kemungkinan penolakan akan bergantung pada kelonggaran; sulit untuk mengatakan caranya, jadi jalankan simulasi untuk mengetahui statistik yang relevan.


Suara positif untuk ekspresi jarak maksimum minimum. Berguna untuk membatasi jumlah poin yang ingin Anda gunakan. Namun, referensi ke sumber otoritatif untuk itu akan menyenangkan.
dmckee --- mantan moderator anak kucing

6

Jawaban ini didasarkan pada 'teori' yang sama yang diuraikan dengan baik oleh jawaban ini

Saya menambahkan jawaban ini sebagai:
- Tidak ada pilihan lain yang sesuai dengan kebutuhan 'keseragaman' 'tepat' (atau tidak jelas-jelas begitu). (Memperhatikan untuk mendapatkan planet seperti distribusi perilaku mencari yang secara khusus diinginkan dalam permintaan awal, Anda hanya menolak dari daftar terbatas dari k titik-titik yang dibuat secara seragam secara acak (tuliskan secara acak jumlah indeks dalam k item kembali).)
- impl lainnya memaksa Anda untuk memutuskan 'N' dengan 'sumbu sudut', vs. hanya 'satu nilai N' di kedua nilai sumbu sudut (yang pada jumlah rendah N sangat sulit untuk mengetahui apa yang mungkin, atau mungkin tidak penting ( misalnya Anda ingin poin '5' - bersenang-senanglah))
--Selanjutnya, sangat sulit untuk 'memahami' cara membedakan antara opsi lain tanpa citra apa pun, jadi inilah tampilan opsi ini (di bawah), dan implementasi yang siap dijalankan yang menyertainya.

dengan N pada 20:

masukkan deskripsi gambar di sini
dan kemudian N pada 80: masukkan deskripsi gambar di sini


berikut kode python3 yang siap dijalankan, dengan emulasinya dari sumber yang sama: " http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere " ditemukan oleh orang lain . (Plotting yang saya sertakan, yang menyala ketika dijalankan sebagai 'utama,' diambil dari: 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

diuji pada hitungan rendah (N dalam 2, 5, 7, 13, dll) dan tampaknya berfungsi 'bagus'


5

Mencoba:

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

Fungsi di atas harus berjalan dalam loop dengan N loop total dan k loop iterasi saat ini.

Ini didasarkan pada pola biji bunga matahari, kecuali biji bunga matahari yang melengkung menjadi setengah kubah, dan sekali lagi menjadi bulatan.

Ini adalah fotonya, kecuali saya meletakkan kamera setengah jalan di dalam bola sehingga terlihat 2d bukan 3d karena jarak kamera sama dari semua titik. http://3.bp.blogspot.com/-9lbPHLccQHA/USXf88_bvVI/AAAAAAAAADY/j7qhQsSZsA8/s640/sphere.jpg


2

Healpix memecahkan masalah yang terkait erat (membuat piksel bola dengan piksel area yang sama):

http://healpix.sourceforge.net/

Ini mungkin berlebihan, tapi mungkin setelah melihatnya Anda akan menyadari beberapa properti bagus lainnya yang menarik bagi Anda. Ini lebih dari sekedar fungsi yang menghasilkan point cloud.

Saya mendarat di sini mencoba menemukannya lagi; nama "healpix" tidak benar-benar membangkitkan bola ...


1

dengan sejumlah kecil poin, Anda dapat menjalankan simulasi:

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

untuk meningkatkan jawaban saya Anda harus mengubah terdekat_index = i menjadi terdekat_index = randchoice (i, j)
robert king

1

Ambillah dua faktor terbesar dari Anda N, jika N==20maka dua faktor terbesar adalah {5,4}, atau, secara lebih umum {a,b}. Menghitung

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

Tempatkan poin pertama Anda di {90-dlat/2,(dlong/2)-180}, poin kedua di {90-dlat/2,(3*dlong/2)-180}, poin ketiga di {90-dlat/2,(5*dlong/2)-180}, hingga Anda pernah melakukan perjalanan keliling dunia satu kali, yang pada saat itu Anda harus melakukannya {75,150}saat berikutnya {90-3*dlat/2,(dlong/2)-180}.

Jelas saya mengerjakan ini dalam derajat di permukaan bumi bulat, dengan konvensi biasa untuk menerjemahkan +/- ke N / S atau E / W. Dan jelas ini memberi Anda distribusi yang sama sekali tidak acak, tetapi seragam dan poinnya tidak digabungkan bersama.

Untuk menambahkan beberapa derajat keacakan, Anda dapat membuat 2 yang terdistribusi normal (dengan mean 0 dan std dev dari {dlat / 3, dlong / 3} yang sesuai) dan menambahkannya ke titik terdistribusi seragam Anda.


5
itu akan terlihat jauh lebih baik jika Anda bekerja di sin (lat) daripada lat. sebagaimana adanya, Anda akan mendapatkan banyak tumpukan di dekat kutub.
andrew cooke

1

edit: Ini tidak menjawab pertanyaan OP yang dimaksudkan untuk ditanyakan, meninggalkannya di sini jika orang merasa berguna.

Kami menggunakan aturan perkalian probabilitas, dikombinasikan dengan infinitessimals. Ini menghasilkan 2 baris kode untuk mencapai hasil yang Anda inginkan:

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

(ditentukan dalam sistem koordinat berikut :)

masukkan deskripsi gambar di sini

Bahasa Anda biasanya memiliki primitif nomor acak yang seragam. Misalnya di python Anda dapat menggunakan random.random()untuk mengembalikan angka dalam kisaran [0,1). Anda bisa mengalikan angka ini dengan k untuk mendapatkan angka acak dalam kisaran tersebut [0,k). Jadi dalam python, uniform([0,2pi))artinya random.random()*2*math.pi.


Bukti

Sekarang kita tidak dapat menetapkan θ secara seragam, jika tidak kita akan menggumpal di kutub. Kami ingin menetapkan probabilitas yang proporsional dengan luas permukaan irisan bola (θ dalam diagram ini sebenarnya adalah φ):

masukkan deskripsi gambar di sini

Perpindahan sudut dφ di ekuator akan menghasilkan perpindahan dφ * r. Apa perpindahan itu pada azimuth sewenang-wenang θ? Nah, jari-jari dari sumbu z adalah r*sin(θ), jadi panjang ar dari "garis lintang" yang memotong irisan tersebut adalah dφ * r*sin(θ). Jadi kami menghitung distribusi kumulatif area untuk mengambil sampel darinya, dengan mengintegrasikan area potongan dari kutub selatan ke kutub utara.

masukkan deskripsi gambar di sini(dimana barang = dφ*r)

Kami sekarang akan mencoba untuk mendapatkan kebalikan dari CDF untuk mengambil sampel darinya: http://en.wikipedia.org/wiki/Inverse_transform_sampling

Pertama kita normalisasi dengan membagi hampir CDF kita dengan nilai maksimumnya. Ini memiliki efek samping menghilangkan dφ dan r.

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

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

Jadi:

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

Bukankah ini sama dengan opsi yang dia buang sebagai "100% acak"? pemahaman saya adalah bahwa dia ingin mereka memiliki jarak yang lebih merata daripada distribusi acak yang seragam.
andrew cooke

@ BlueRaja-DannyPflughoeft: Hmm, cukup adil. Saya kira saya tidak membaca pertanyaan itu dengan hati-hati sebagaimana seharusnya. Saya tinggalkan ini di sini kalau-kalau orang lain menganggapnya berguna. Terima kasih telah menunjukkan hal ini.
ninjagecko

1

ATAU ... untuk menempatkan 20 poin, hitung pusat-pusat permukaan ikosahedronal. Untuk 12 poin, temukan simpul dari ikosahedron. Untuk 30 poin, titik tengah dari tepi ikosahedron. Anda dapat melakukan hal yang sama dengan tetrahedron, kubus, dodecahedron, dan oktahedron: satu set titik ada di simpul, satu lagi di tengah muka dan satu lagi di tengah tepi. Namun, mereka tidak dapat dicampur.


Ide bagus, tetapi hanya berfungsi untuk 4, 6, 8, 12, 20, 24, atau 30 poin.
The Guy with The Hat

Jika Anda ingin melakukan cheat, Anda dapat menggunakan bagian tengah wajah dan sudut. Mereka tidak akan memiliki spasi yang sama tetapi perkiraan yang layak. Ini bagus karena itu deterministik.
caturofnerd

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
Akan sangat membantu jika Anda menulis beberapa teks yang menjelaskan maksud dari hal ini, jadi OP tidak harus percaya bahwa ini akan berhasil.
hcarver

0

@robert king Ini adalah solusi yang sangat bagus tetapi memiliki beberapa bug di dalamnya. Saya tahu itu sangat membantu saya, jadi tidak masalah kecerobohannya. :) Ini adalah versi yang sudah dibersihkan ....

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

Ini berhasil dan sangat sederhana. Poin sebanyak yang Anda inginkan:

    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());
    }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.