Hasilkan titik acak dalam lingkaran (seragam)


212

Saya perlu untuk menghasilkan titik seragam acak dalam lingkaran radius R .

Saya menyadari bahwa dengan hanya mengambil sudut acak seragam dalam interval [0 ... 2π), dan jari-jari acak seragam dalam interval (0 ... R ) saya akan berakhir dengan lebih banyak poin ke arah tengah, karena untuk dua diberikan jari-jari, titik-titik dalam radius yang lebih kecil akan lebih dekat satu sama lain daripada untuk titik-titik dalam radius yang lebih besar.

Saya menemukan entri blog di sini di sini tapi saya tidak mengerti alasannya. Saya kira itu benar, tetapi saya benar-benar ingin memahami dari mana ia mendapat (2 / R 2 ) × r dan bagaimana ia mendapatkan solusi akhir.


Pembaruan: 7 tahun setelah memposting pertanyaan ini saya masih belum menerima jawaban yang memuaskan atas pertanyaan aktual mengenai matematika di balik algoritma akar kuadrat. Jadi saya menghabiskan satu hari menulis jawaban sendiri. Tautan ke jawaban saya .


18
Apakah kelemahan sampel penolakan benar-benar masalah besar? Jumlah percobaan yang diperlukan adalah 4 / π ≈ 1.27, dan probabilitas bahwa Anda membutuhkan lebih dari k percobaan adalah (1-π / 4) ^ k. Untuk k = 20 , ini adalah ≈ .00000000000000004 dan untuk k = 50 berada di urutan 10 ^ {- 34}. Anda dapat mengambil peluang itu kapan saja; kamu akan baik-baik saja.
ShreevatsaR

3
Sebenarnya, sampel penolakan tidak memberikan jaminan untuk penghentian. Peluangnya sangat rendah (tepatnya, nol) yang tidak akan pernah diakhiri oleh algoritma Anda.
Jared Nielsen

2
Menurut pendapat saya, pentingnya kelemahan sampel penolakan sebanding dengan kemudahan menggunakan metode pengambilan sampel yang menghindari penolakan. Dalam hal ini, kelemahannya penting karena pengambilan sampel tanpa penolakan itu sederhana.
spex

4
@spex Dalam praktiknya, teknik penolakan lebih cepat karena menghindari perlunya evaluasi fungsi transendental.
pjs

2
(lanjutan) penolakan: 0,52s Semua memberikan cara yang identik dan standar deviasi (sampai 3 sig. gbr). Seperti yang diharapkan, sampel penolakan gagal 27% dari waktu (4 / pi-1) sehingga diperlukan angka acak 27% lebih banyak daripada btilly tetapi 15% lebih sedikit dari sigfpe. Ini mengkonfirmasi komentar yang dibuat oleh pjs dan lainnya bahwa penolakan sampel mungkin merupakan pendekatan terbaik, kecuali jika tebusan sangat mahal untuk dihasilkan.
Peter Davidson

Jawaban:


189

Mari kita mendekati ini seperti Archimedes.

Bagaimana kita dapat menghasilkan titik secara seragam dalam segitiga ABC, di mana | AB | = | BC |? Mari kita buat ini lebih mudah dengan memperluas ke AB genjang. Mudah untuk menghasilkan poin secara seragam di ABCD. Kami secara seragam memilih titik acak X pada AB dan Y pada BC dan memilih Z sedemikian rupa sehingga XBYZ adalah genjang. Untuk mendapatkan titik yang dipilih secara seragam dalam segitiga asli kami hanya melipat poin yang muncul di ADC kembali ke ABC sepanjang AC.

Sekarang perhatikan sebuah lingkaran. Dalam batas tersebut kita dapat menganggapnya sebagai tak terhingga banyaknya segitiga isoceles ABC dengan B pada titik asal dan A dan C pada lingkar yang semakin dekat satu sama lain. Kita dapat memilih salah satu dari segitiga ini hanya dengan memilih sudut theta. Jadi kita sekarang perlu membuat jarak dari pusat dengan memilih titik di sliver ABC. Sekali lagi, perpanjang ke ABCD, di mana D sekarang dua kali radius dari pusat lingkaran.

Memilih titik acak dalam ABCD mudah menggunakan metode di atas. Pilih titik acak pada AB. Pilih titik acak pada BC secara seragam. Yaitu. pilih sepasang angka acak x dan y secara seragam pada [0, R] yang memberi jarak dari pusat. Segitiga kami adalah sepotong tipis sehingga AB dan BC pada dasarnya paralel. Jadi titik Z hanyalah jarak x + y dari titik asal. Jika x + y> R kita lipat kembali.

Inilah algoritma lengkap untuk R = 1. Saya harap Anda setuju itu sangat sederhana. Ini menggunakan trigonometri, tetapi Anda dapat memberikan jaminan pada berapa lama, dan berapa banyak random()panggilan yang dibutuhkan, tidak seperti sampel penolakan.

t = 2*pi*random()
u = random()+random()
r = if u>1 then 2-u else u
[r*cos(t), r*sin(t)]

Ini dia di Mathematica.

f[] := Block[{u, t, r},
  u = Random[] + Random[];
  t = Random[] 2 Pi;
  r = If[u > 1, 2 - u, u];
  {r Cos[t], r Sin[t]}
]

ListPlot[Table[f[], {10000}], AspectRatio -> Automatic]

masukkan deskripsi gambar di sini


6
@ Karelzarath Saya suka gagasan berlawanan dari segitiga yang sangat tipis yang masih lebih luas di satu ujung daripada yang lain :-) Itu mendapat jawaban yang tepat.
sigfpe

2
@hammar Tidak yakin itu digeneralisasikan dengan baik ke n dimensi. Tetapi untuk 3d Anda dapat menggunakan hasil lain oleh Archimedes! Gunakan teorema "kotak-topi" untuk menghasilkan titik pada silinder (mudah!) Dan kemudian petakan kembali ke bola. Itu memberi arah. Sekarang gunakan random()+random()+random()dengan lipatan yang lebih kompleks (mis. Lipatan paralel 6-arah yang sangat tipis diipip ke terahedron). Tidak yakin ini adalah metode yang baik.
sigfpe

2
Saya pikir 1 menit untuk mencari tahu perbedaan antara random () + random () dan 2 * random () ... Saya sangat bodoh: /
JiminP

3
@ Talwen Perhatikan bagaimana dalam lingkaran ada lebih banyak titik pada radius 0,9-1,0 daripada pada radius 0,0-0,1. acak () + acak () menghasilkan jari-jari lebih mungkin berada di sekitar 1,0 tetapi terletak pada kisaran 0,0-2,0. Ketika dilipat mereka cenderung berada di sekitar 1,0 dan selalu di kisaran 0,0-1,0. Terlebih lagi, ini adalah proporsi yang dibutuhkan dalam kalimat pertama dari komentar ini. Hanya mengurangi separuh menghasilkan angka lebih banyak di sekitar tanda 0,5 dan itu akan salah.
sigfpe

2
@ Talwen Coba gunakan kedua skema untuk menghasilkan angka acak dan lihat apa yang Anda dapatkan. 2 * acak () memberikan angka yang terdistribusi secara merata di kisaran 0 hingga 2. acak () + acak () memberi Anda angka dalam kisaran 0 hingga 2 tetapi akan (biasanya) ada lebih banyak angka di dekat 1,0 daripada di dekat 0,0 atau 2,0. Ini seperti bagaimana menggulirkan dua dadu dan penjumlahan lebih mungkin untuk memberi 7 daripada angka lainnya.
sigfpe

133

Cara menghasilkan titik acak dalam lingkaran jari-jari R :

r = R * sqrt(random())
theta = random() * 2 * PI

(Dengan asumsi random()memberikan nilai antara 0 dan 1 secara seragam)

Jika Anda ingin mengonversikan ini ke koordinat Cartesian, Anda dapat melakukannya

x = centerX + r * cos(theta)
y = centerY + r * sin(theta)


Mengapa sqrt(random())?

Mari kita lihat matematika yang mengarah ke sqrt(random()). Asumsikan untuk kesederhanaan bahwa kita sedang bekerja dengan lingkaran unit, yaitu R = 1.

Jarak rata-rata antara titik harus sama terlepas dari seberapa jauh dari pusat yang kita lihat. Ini berarti misalnya, bahwa dengan melihat keliling lingkaran dengan keliling 2 kita harus menemukan dua kali lebih banyak poin daripada jumlah poin pada keliling lingkaran dengan keliling 1.


                

Karena keliling lingkaran (2π r ) tumbuh linier dengan r , maka jumlah titik acak harus tumbuh linier dengan r . Dengan kata lain, fungsi probabilitas kerapatan yang diinginkan (PDF) tumbuh secara linear. Karena PDF harus memiliki luas sama dengan 1 dan radius maksimum adalah 1, kami punya


                

Jadi kita tahu bagaimana kepadatan yang diinginkan dari nilai acak kita akan terlihat. Sekarang: Bagaimana kita menghasilkan nilai acak seperti itu ketika semua yang kita miliki adalah nilai acak seragam antara 0 dan 1?

Kami menggunakan trik yang disebut sampling transformasi terbalik

  1. Dari PDF, buat fungsi distribusi kumulatif (CDF)
  2. Cerminkan ini di sepanjang y = x
  3. Terapkan fungsi yang dihasilkan ke nilai seragam antara 0 dan 1.

Kedengarannya rumit? Biarkan saya menyisipkan blockquote dengan trek samping kecil yang menyampaikan intuisi:

Misalkan kita ingin menghasilkan titik acak dengan distribusi berikut:

                

Itu adalah

  • 1/5 poin secara seragam antara 1 dan 2, dan
  • 4/5 poin secara seragam antara 2 dan 3.

CDF adalah, seperti namanya, versi kumulatif dari PDF. Secara intuitif: Sementara PDF ( x ) menjelaskan jumlah nilai acak pada x , CDF ( x ) menjelaskan jumlah nilai acak kurang dari x .

Dalam hal ini CDF akan terlihat seperti:

                

Untuk melihat bagaimana ini berguna, bayangkan kita menembakkan peluru dari kiri ke kanan pada ketinggian yang merata. Ketika peluru mengenai garis, mereka jatuh ke tanah:

                

Lihat bagaimana kepadatan peluru di tanah sesuai dengan distribusi yang kami inginkan! Kita hampir sampai!

Masalahnya adalah bahwa untuk fungsi ini, sumbu y adalah output dan sumbu x adalah input . Kita hanya bisa "menembakkan peluru dari tanah lurus ke atas"! Kami membutuhkan fungsi terbalik!

Inilah sebabnya kami mencerminkan semuanya; x menjadi y dan y menjadi x :

                

Kami menyebutnya CDF -1 . Untuk mendapatkan nilai sesuai dengan distribusi yang diinginkan, kami menggunakan CDF -1 (acak ()).

... jadi, kembali untuk menghasilkan nilai radius acak di mana PDF kami sama dengan 2 x .

Langkah 1: Buat CDF:

Karena kami bekerja dengan real, CDF diekspresikan sebagai bagian integral dari PDF.

CDF ( x ) = ∫ 2 x = x 2

Langkah 2: Mirror CDF sepanjang y = x :

Secara matematis ini bermuara pada bertukar x dan y dan memecahkan untuk y :

CDF :      y = x 2
Tukar:    x = y 2
Selesaikan:    y = √ x
CDF -1 :   y = √ x

Langkah 3: Terapkan fungsi yang dihasilkan ke nilai yang seragam antara 0 dan 1

CDF -1 (acak ()) = andomrandom ()

Apa yang ingin kami peroleh :-)


Algoritma ini dapat digunakan untuk secara efisien menghasilkan titik pada cincin.
Ivan Kovtun

Di atas ring? Suka dengan radius tetap? Tidak yakin apakah saya mengerti pertanyaan Anda, tetapi jika Anda memiliki jari-jari tetap, Anda hanya perlu mengacak sudutnya.
aioobe

2
Saya mencoba menggunakan kata "Ring" yang lebih sederhana daripada Annulus - wilayah yang dibatasi oleh dua lingkaran konsentris. Dalam hal ini algoritma penolakan menjadi tidak efektif dan algoritma top pertama sulit digeneralisasi. Dan kasus sudut dengan satu jari-jari juga ditutupi dengan algoritma Anda. Kami selalu menghasilkan radius sebagai sqrt (acak (min_radius ^ 2, max_radius ^ 2)) bahkan ketika min_radius == max_radius.
Ivan Kovtun

1
Oh bagus! Untuk menjadi jelas, ketika Anda mengatakan random(min_radius², max_radius²), apakah Anda bermaksud sesuatu yang setara dengan random() * (max_radius² - min_radius²) + min_radius², di mana random()mengembalikan nilai yang seragam antara 0 dan 1?
aioobe

ya, itulah yang saya maksud: radius = sqrt (random () * (max_radius² - min_radius²) + min_radius²).
Ivan Kovtun

27

Inilah solusi cepat dan sederhana.

Pilih dua angka acak dalam rentang (0, 1), yaitu adan b. Jika b < a, tukar mereka. Maksud Anda adalah (b*R*cos(2*pi*a/b), b*R*sin(2*pi*a/b)).

Anda dapat memikirkan solusi ini sebagai berikut. Jika Anda mengambil lingkaran, memotongnya, lalu meluruskannya, Anda akan mendapatkan segitiga siku-siku. Skala yang segitiga bawah, dan Anda akan memiliki segitiga dari (0, 0)ke (1, 0)ke (1, 1)dan kembali lagi ke (0, 0). Semua transformasi ini mengubah kerapatan secara seragam. Apa yang telah Anda lakukan adalah memilih titik acak dalam segitiga secara terbalik dan membalikkan proses untuk mendapatkan titik di lingkaran.


Ini, untuk beberapa alasan, memberi saya distribusi yang jauh lebih seragam daripada jawaban yang diterima, meskipun saya memang perlu membagi koordinat dengan jari-jari, kalau tidak itu di dalam lingkaran R ^ 2
Greg Zaal

3
Terima kasih, ini kode Anda di Jawa, mungkin seseorang akan merasakan manfaatnya: float random1 = MathUtils.random (); float random2 = MathUtils.random (); float randomXPoint = random2 * radius MathUtils.cos (MathUtils.PI2 * random1 / random2); float randomYPoint = random2 * radius MathUtils.sin (MathUtils.PI2 * random1 / random2);
Tony Ceralva

Baik sekali! Saya suka ide lebih banyak kemungkinan untuk memusatkan poin, jadi jika kita tidak bertukar ketika b < akita bisa mencapai ini! misalnya dalam javascript jsfiddle.net/b0sb5ogL/1
Guilherme

Saya pikir solusi Anda buruk. Itu tidak memberikan hasil yang seragam. Periksa tangkapan layar ini prntscr.com/fizxgc
bolec_kolec

4
Bisakah Anda menjelaskan sedikit lebih banyak cara memotong lingkaran dan meluruskannya?
kec

21

Perhatikan kerapatan titik secara proporsional dengan kuadrat terbalik dari jari-jari, maka alih-alih memilih rdari [0, r_max], memilih dari [0, r_max^2], lalu hitung koordinat Anda sebagai:

x = sqrt(r) * cos(angle)
y = sqrt(r) * sin(angle)

Ini akan memberi Anda distribusi titik yang seragam pada disk.

http://mathworld.wolfram.com/DiskPointPicking.html


12

Pikirkan seperti ini. Jika Anda memiliki persegi panjang di mana satu sumbu adalah jari-jari dan satu adalah sudut, dan Anda mengambil titik-titik di dalam persegi panjang ini yang berada di dekat jari-jari 0. Ini semua akan jatuh sangat dekat dengan titik asal (yang berdekatan bersama pada lingkaran.) Namun, titik dekat radius R, ini semua akan jatuh di dekat tepi lingkaran (yaitu, berjauhan satu sama lain.)

Ini mungkin memberi Anda ide mengapa Anda mendapatkan perilaku ini.

Faktor yang diturunkan pada tautan itu memberi tahu Anda berapa banyak area yang sesuai dalam persegi panjang perlu disesuaikan agar tidak bergantung pada jari-jari begitu dipetakan ke lingkaran.

Sunting: Jadi yang ia tulis di tautan yang Anda bagikan adalah, "Itu cukup mudah dilakukan dengan menghitung kebalikan dari distribusi kumulatif, dan kami mendapatkan untuk r:".

Premis dasar di sini bahwa Anda dapat membuat variabel dengan distribusi yang diinginkan dari seragam dengan memetakan seragam dengan fungsi terbalik dari fungsi distribusi kumulatif fungsi kepadatan probabilitas yang diinginkan. Mengapa? Terima saja untuk saat ini, tetapi ini adalah fakta.

Ini penjelasan intuitif saya tentang matematika. Fungsi kepadatan f (r) sehubungan dengan r harus proporsional dengan r itu sendiri. Memahami fakta ini adalah bagian dari buku-buku kalkulus dasar. Lihat bagian tentang elemen area kutub. Beberapa poster lain menyebutkan ini.

Jadi kita akan menyebutnya f (r) = C * r;

Ini ternyata sebagian besar pekerjaan. Sekarang, karena f (r) harus menjadi densitas probabilitas, Anda dapat dengan mudah melihat bahwa dengan mengintegrasikan f (r) pada interval (0, R) Anda mendapatkan C = 2 / R ^ 2 (ini adalah latihan untuk pembaca .)

Dengan demikian, f (r) = 2 * r / R ^ 2

OK, jadi begitulah cara Anda mendapatkan rumus di tautan.

Kemudian, bagian terakhir diambil dari variabel acak seragam u dalam (0,1) Anda harus memetakan dengan fungsi kebalikan dari fungsi distribusi kumulatif dari kerapatan yang diinginkan ini f (r). Untuk memahami mengapa hal ini terjadi, Anda mungkin perlu menemukan teks probabilitas lanjutan seperti Papoulis (atau menurunkannya sendiri.)

Mengintegrasikan f (r) Anda mendapatkan F (r) = r ^ 2 / R ^ 2

Untuk menemukan fungsi kebalikan dari ini, Anda mengatur u = r ^ 2 / R ^ 2 dan kemudian menyelesaikan untuk r, yang memberi Anda r = R * sqrt (u)

Ini benar-benar masuk akal juga, u = 0 harus dipetakan ke r = 0. Juga, u = 1 peta shoudl ke r = R. Juga, ia pergi dengan fungsi akar kuadrat, yang masuk akal dan cocok dengan tautan.


10

Alasan mengapa solusi naif tidak berhasil adalah karena memberikan kepadatan probabilitas yang lebih tinggi ke titik yang lebih dekat ke pusat lingkaran. Dengan kata lain lingkaran yang memiliki jari-jari r / 2 memiliki probabilitas r / 2 untuk mendapatkan titik yang dipilih di dalamnya, tetapi memiliki luas (jumlah titik) pi * r ^ 2/4.

Karena itu kami ingin kepadatan probabilitas radius memiliki properti berikut:

Probabilitas memilih radius yang lebih kecil atau sama dengan r yang diberikan harus sebanding dengan luas lingkaran dengan jari-jari r. (karena kami ingin memiliki distribusi yang seragam pada titik dan area yang lebih luas berarti lebih banyak titik)

Dengan kata lain kami ingin probabilitas memilih jari-jari antara [0, r] sama dengan bagiannya dari keseluruhan area lingkaran. Luas lingkaran total adalah pi * R ^ 2, dan luas lingkaran dengan jari-jari r adalah pi * r ^ 2. Dengan demikian kami ingin probabilitas memilih jari-jari antara [0, r] menjadi (pi * r ^ 2) / (pi * R ^ 2) = r ^ 2 / R ^ 2.

Sekarang matematikanya:

Probabilitas memilih jari-jari antara [0, r] adalah bagian integral dari p (r) dr dari 0 hingga r (itu hanya karena kita menambahkan semua probabilitas dari jari-jari yang lebih kecil). Jadi kita ingin integral (p (r) dr) = r ^ 2 / R ^ 2. Kita dapat dengan jelas melihat bahwa R ^ 2 adalah konstanta, jadi yang perlu kita lakukan adalah mencari tahu p (r) mana, ketika terintegrasi akan memberi kita sesuatu seperti r ^ 2. Jawabannya jelas r * konstan. integral (r * konstan dr) = r ^ 2/2 * konstan. Ini harus sama dengan r ^ 2 / R ^ 2, oleh karena itu konstan = 2 / R ^ 2. Dengan demikian Anda memiliki distribusi probabilitas p (r) = r * 2 / R ^ 2

Catatan: Cara lain yang lebih intuitif untuk memikirkan masalah adalah membayangkan bahwa Anda mencoba memberikan setiap lingkaran dengan kepadatan probabilitas yang sama dengan proporsi jumlah titik yang ada pada kelilingnya. Dengan demikian lingkaran yang memiliki jari-jari r akan memiliki 2 * pi * r "titik" pada kelilingnya. Jumlah total poin adalah pi * R ^ 2. Dengan demikian Anda harus memberi lingkaran ra probabilitas sama dengan (2 * pi * r) / (pi * R ^ 2) = 2 * r / R ^ 2. Ini jauh lebih mudah untuk dipahami dan lebih intuitif, tetapi tidak cukup baik secara matematis.


9

Misalkan ρ (jari-jari) dan φ (azimuth) adalah dua variabel acak yang berhubungan dengan koordinat polar dari titik acak di dalam lingkaran. Jika poin-poinnya terdistribusi secara merata, apa fungsi disribusi ρ dan φ?

Untuk setiap r: 0 <r <R probabilitas koordinat jari-jari ρ menjadi kurang dari r adalah

P [ρ <r] = P [titik berada dalam lingkaran jari-jari r] = S1 / S0 = (r / R) 2

Di mana S1 dan S0 adalah area lingkaran jari-jari r dan R masing-masing. Jadi CDF dapat diberikan sebagai:

          0          if r<=0
  CDF =   (r/R)**2   if 0 < r <= R
          1          if r > R

Dan PDF:

PDF = d/dr(CDF) = 2 * (r/R**2) (0 < r <= R).

Perhatikan bahwa untuk R = 1 variabel acak sqrt (X) di mana X seragam pada [0, 1) memiliki CDF yang tepat ini (karena P [sqrt (X) <y] = P [x <y ** 2] = y * * 2 untuk 0 <y <= 1).

Distribusi φ jelas seragam dari 0 hingga 2 * π. Sekarang Anda dapat membuat koordinat kutub acak dan mengonversinya ke Cartesian menggunakan persamaan trigonometri:

x = ρ * cos(φ)
y = ρ * sin(φ)

Tidak dapat menolak memposting kode python untuk R = 1.

from matplotlib import pyplot as plt
import numpy as np

rho = np.sqrt(np.random.uniform(0, 1, 5000))
phi = np.random.uniform(0, 2*np.pi, 5000)

x = rho * np.cos(phi)
y = rho * np.sin(phi)

plt.scatter(x, y, s = 4)

Kamu akan mendapatkan

masukkan deskripsi gambar di sini


7

Itu benar-benar tergantung pada apa yang Anda maksud dengan 'acak seragam'. Ini adalah hal yang halus dan Anda dapat membacanya lebih lanjut di halaman wiki di sini: http://en.wikipedia.org/wiki/Bertrand_paradox_%28probability%29 , di mana masalah yang sama, memberikan interpretasi yang berbeda untuk pemberian 'seragam seragam' jawaban berbeda!

Bergantung pada bagaimana Anda memilih poin, distribusinya dapat bervariasi, meskipun mereka secara acak acak dalam beberapa hal.

Sepertinya entri blog sedang mencoba membuatnya acak secara acak dalam arti berikut: Jika Anda mengambil sub-lingkaran dari lingkaran, dengan pusat yang sama, maka kemungkinan bahwa titik jatuh di wilayah itu sebanding dengan luas wilayah. wilayah. Itu, saya percaya, sedang mencoba untuk mengikuti interpretasi standar sekarang 'acak seragam' untuk daerah 2D dengan daerah yang ditentukan pada mereka : probabilitas titik jatuh di wilayah mana pun (dengan luas yang didefinisikan dengan baik) sebanding dengan luas wilayah itu.


5
Atau lebih tepatnya, probabilitas bahwa titik tersebut jatuh di wilayah sewenang - wenang adalah proporsional dengan wilayah wilayah tersebut - dengan asumsi bahwa wilayah tersebut memiliki wilayah .
ShreevatsaR

@Shree: Benar, yang saya maksudkan dengan pernyataan saya dalam tanda kurung. Saya akan membuatnya lebih jelas, terima kasih. btw, tentang blog, tidak ada bukti nyata bahwa area arbitrer memberikan probabilitas proporsional, maka saya memilih untuk mengutarakannya seperti itu.

6

Ini kode Python saya untuk menghasilkan numtitik acak dari lingkaran jari-jari rad:

import matplotlib.pyplot as plt
import numpy as np
rad = 10
num = 1000

t = np.random.uniform(0.0, 2.0*np.pi, num)
r = rad * np.sqrt(np.random.uniform(0.0, 1.0, num))
x = r * np.cos(t)
y = r * np.sin(t)

plt.plot(x, y, "ro", ms=1)
plt.axis([-15, 15, -15, 15])
plt.show()

1
Kenapa tidak adil r = np.sqrt(np.random.uniform(0.0, rad**2, num))?

4

Saya pikir dalam hal ini menggunakan koordinat kutub adalah cara untuk mempersulit masalah, akan jauh lebih mudah jika Anda memilih titik acak ke dalam persegi dengan sisi panjang 2R dan kemudian memilih titik (x,y)sedemikian rupa x^2+y^2<=R^2.


Maksud Anda x ^ 2 + y ^ 2 <= R ^ 2 Saya pikir.
sigfpe

1
Ini adalah sampel penolakan. Tidak apa-apa, tetapi artinya waktu perhitungan agak berbeda, yang bisa menjadi masalah.
Steve Bennett

Semua kotak 4 sisi.
xaxxon

Algoritma ini lebih efisien daripada apa pun yang melibatkan akar kuadrat atau perhitungan sin / cos. Ia menolak kurang dari 21,5% poin alun-alun.
Ivan Kovtun

3

Solusi di Jawa dan contoh distribusi (2000 poin)

public void getRandomPointInCircle() {
    double t = 2 * Math.PI * Math.random();
    double r = Math.sqrt(Math.random());
    double x = r * Math.cos(t);
    double y = r * Math.sin(t);
    System.out.println(x);
    System.out.println(y);
}

Distribusi 2000 poin

berdasarkan solusi previus https://stackoverflow.com/a/5838055/5224246 dari @sigfpe


2

Pertama kita menghasilkan cdf [x]

Probabilitas suatu titik kurang dari jarak x dari pusat lingkaran. Asumsikan lingkaran memiliki jari-jari R.

jelas jika x adalah nol maka cdf [0] = 0

jelas jika x adalah R maka cdf [R] = 1

jelas jika x = r maka cdf [r] = (Pi r ^ 2) / (Pi R ^ 2)

Ini karena setiap "area kecil" pada lingkaran memiliki probabilitas yang sama untuk dipilih, Jadi probabilitasnya proporsional dengan area yang dimaksud. Dan area yang diberi jarak x dari pusat lingkaran adalah Pi ^ 2

jadi cdf [x] = x ^ 2 / R ^ 2 karena Pi membatalkan satu sama lain

kami memiliki cdf [x] = x ^ 2 / R ^ 2 di mana x pergi dari 0 ke R

Jadi kita pecahkan untuk x

R^2 cdf[x] = x^2

x = R Sqrt[ cdf[x] ]

Kami sekarang dapat mengganti cdf dengan angka acak dari 0 hingga 1

x = R Sqrt[ RandomReal[{0,1}] ]

Akhirnya

r = R Sqrt[  RandomReal[{0,1}] ];
theta = 360 deg * RandomReal[{0,1}];
{r,theta}

kita mendapatkan koordinat kutub {0,601168 R, 311,915 deg}


1

Ada hubungan linier antara jari-jari dan jumlah titik "dekat" jari-jari itu, sehingga ia perlu menggunakan distribusi jari-jari yang juga membuat jumlah titik data di dekat jari-jari rsebanding dengan r.


1

Saya pernah menggunakan metode ini: Ini mungkin benar-benar tidak dioptimalkan (yaitu menggunakan array titik sehingga tidak dapat digunakan untuk lingkaran besar) tetapi memberikan distribusi acak yang cukup. Anda dapat melewati pembuatan matriks dan menggambar langsung jika Anda mau. Metode ini untuk mengacak semua titik dalam persegi panjang yang berada di dalam lingkaran.

bool[,] getMatrix(System.Drawing.Rectangle r) {
    bool[,] matrix = new bool[r.Width, r.Height];
    return matrix;
}

void fillMatrix(ref bool[,] matrix, Vector center) {
    double radius = center.X;
    Random r = new Random();
    for (int y = 0; y < matrix.GetLength(0); y++) {
        for (int x = 0; x < matrix.GetLength(1); x++)
        {
            double distance = (center - new Vector(x, y)).Length;
            if (distance < radius) {
                matrix[x, y] = r.NextDouble() > 0.5;
            }
        }
    }

}

private void drawMatrix(Vector centerPoint, double radius, bool[,] matrix) {
    var g = this.CreateGraphics();

    Bitmap pixel = new Bitmap(1,1);
    pixel.SetPixel(0, 0, Color.Black);

    for (int y = 0; y < matrix.GetLength(0); y++)
    {
        for (int x = 0; x < matrix.GetLength(1); x++)
        {
            if (matrix[x, y]) {
                g.DrawImage(pixel, new PointF((float)(centerPoint.X - radius + x), (float)(centerPoint.Y - radius + y)));
            }
        }
    }

    g.Dispose();
}

private void button1_Click(object sender, EventArgs e)
{
    System.Drawing.Rectangle r = new System.Drawing.Rectangle(100,100,200,200);
    double radius = r.Width / 2;
    Vector center = new Vector(r.Left + radius, r.Top + radius);
    Vector normalizedCenter = new Vector(radius, radius);
    bool[,] matrix = getMatrix(r);
    fillMatrix(ref matrix, normalizedCenter);
    drawMatrix(center, radius, matrix);
}

masukkan deskripsi gambar di sini


3
Distribusi tidak "cukup acak". Entah itu acak atau tidak untuk definisi acak yang diberikan. Jawaban Anda miring: Anda tidak mengomentari kode Anda atau menjelaskan bagaimana Anda sampai di sana. Jawaban miring sulit untuk diikuti dan sulit dipercaya.
Richard

1

Elemen area dalam lingkaran adalah dA = rdr * dphi. Faktor ekstra itu menghancurkan ide Anda untuk secara acak memilih ar dan phi. Sementara phi didistribusikan rata, r tidak, tetapi datar dalam 1 / r (yaitu Anda lebih mungkin mengenai batas daripada "mata banteng").

Jadi untuk menghasilkan poin yang terdistribusi secara merata pada lingkaran, pilih phi dari distribusi datar dan r dari distribusi 1 / r.

Atau gunakan metode Monte Carlo yang diusulkan oleh Mehrdad.

EDIT

Untuk memilih flat r acak dalam 1 / r Anda bisa memilih x acak dari interval [1 / R, infinity] dan menghitung r = 1 / x. r kemudian didistribusikan flat dalam 1 / r.

Untuk menghitung phi acak, pilih x acak dari interval [0, 1] dan hitung phi = 2 * pi * x.


Bagaimana tepatnya saya memilih r dari "distribusi 1 / r" ?
aioobe

0

Saya tidak tahu apakah pertanyaan ini masih terbuka untuk solusi baru dengan semua jawaban yang sudah diberikan, tetapi saya sendiri juga menghadapi pertanyaan yang sama. Saya mencoba "beralasan" dengan diri saya sendiri untuk sebuah solusi, dan saya menemukannya. Mungkin hal yang sama dengan beberapa yang telah disarankan di sini, tetapi bagaimanapun ini adalah:

agar dua elemen permukaan lingkaran menjadi sama, dengan asumsi sama dengan dr, kita harus memiliki dtheta1 / dtheta2 = r2 / r1. Menulis ekspresi probabilitas untuk elemen tersebut sebagai P (r, theta) = P {r1 <r <r1 + dr, theta1 <theta <theta + dtheta1} = f (r, theta) * dr * dtheta1, dan mengatur keduanya probabilitas (untuk r1 dan r2) sama, kita sampai pada (dengan asumsi r dan theta independen) f (r1) / r1 = f (r2) / r2 = konstan, yang menghasilkan f (r) = c * r. Dan sisanya, menentukan konstanta c mengikuti dari kondisi pada f (r) menjadi PDF.


Pendekatan yang menarik untuk memulai dengan dtheta1 / dtheta2 = r2 / r1. Bisakah Anda menguraikan bagaimana Anda sampai pada persamaan itu?
aioobe

Seperti yang disebutkan orang lain (klakson, misalnya), elemen diferensial dari permukaan lingkaran diberikan sebagai r dr dtheta, jadi jika kita mengasumsikan r1 = r2, maka kita akan memiliki dr1 * dtheta1 = dr2 * dtheta2 dan sisanya mengikuti .
arsaKasra

0

Solusi programmer:

  • Buat peta bit (matriks nilai boolean). Itu bisa sebesar yang Anda inginkan.
  • Gambarlah sebuah lingkaran di peta bit itu.
  • Buat tabel pencarian titik-titik lingkaran.
  • Pilih indeks acak dalam tabel pencarian ini.
const int RADIUS = 64;
const int MATRIX_SIZE = RADIUS * 2;

bool matrix[MATRIX_SIZE][MATRIX_SIZE] = {0};

struct Point { int x; int y; };

Point lookupTable[MATRIX_SIZE * MATRIX_SIZE];

void init()
{
  int numberOfOnBits = 0;

  for (int x = 0 ; x < MATRIX_SIZE ; ++x)
  {
    for (int y = 0 ; y < MATRIX_SIZE ; ++y)
    {
      if (x * x + y * y < RADIUS * RADIUS) 
      {
        matrix[x][y] = true;

        loopUpTable[numberOfOnBits].x = x;
        loopUpTable[numberOfOnBits].y = y;

        ++numberOfOnBits;

      } // if
    } // for
  } // for
} // ()

Point choose()
{
  int randomIndex = randomInt(numberOfBits);

  return loopUpTable[randomIndex];
} // ()

Bitmap hanya diperlukan untuk penjelasan logika. Ini adalah kode tanpa bitmap:

const int RADIUS = 64;
const int MATRIX_SIZE = RADIUS * 2;

struct Point { int x; int y; };

Point lookupTable[MATRIX_SIZE * MATRIX_SIZE];

void init()
{
  int numberOfOnBits = 0;

  for (int x = 0 ; x < MATRIX_SIZE ; ++x)
  {
    for (int y = 0 ; y < MATRIX_SIZE ; ++y)
    {
      if (x * x + y * y < RADIUS * RADIUS) 
      {
        loopUpTable[numberOfOnBits].x = x;
        loopUpTable[numberOfOnBits].y = y;

        ++numberOfOnBits;
      } // if
    } // for
  } // for
} // ()

Point choose()
{
  int randomIndex = randomInt(numberOfBits);

  return loopUpTable[randomIndex];
} // ()

0

Saya masih tidak yakin tentang tepat '(2 / R2) × r' tetapi yang jelas adalah jumlah poin yang diperlukan untuk didistribusikan di unit 'dr' yang diberikan yaitu peningkatan r akan sebanding dengan r2 dan bukan r.

periksa dengan cara ini ... jumlah titik pada beberapa sudut theta dan antara r (0,1r hingga 0,2r) yaitu fraksi dari r dan jumlah titik antara r (0,6r hingga 0,7r) akan sama jika Anda menggunakan pembangkitan standar, karena perbedaannya hanya 0,1r antara dua interval. tetapi karena area yang dicakup antara titik (0,6r hingga 0,7r) akan jauh lebih besar daripada area yang dicakup antara 0,1r hingga 0,2r, jumlah poin yang sama akan jarang ditempatkan di area yang lebih besar, ini saya anggap Anda sudah tahu, Jadi fungsinya untuk menghasilkan titik acak tidak boleh linear tetapi kuadrat, (karena jumlah titik yang diperlukan untuk didistribusikan di unit 'dr' yaitu peningkatan r akan sebanding dengan r2 dan bukan r), jadi dalam hal ini akan kebalikan dari kuadrat, karena delta yang kita miliki (0.


Anda yang pertama merujuk teorema Pythagoras di sini. Saya akan senang jika Anda dapat memperluas ini dengan satu atau dua angka, mendukung penjelasan Anda. Saya mengalami kesulitan mengikuti seperti sekarang :-(
aioobe

@aioobe Saya telah mencoba untuk mengulangi jawabannya, saya dapat menambahkan diagram jika Anda membutuhkan :)
cheesefest

Saya mengerti mengapa saya tidak bisa menyebarkannya secara linear. Yang tidak saya mengerti di sini adalah hubungan dengan Pythagoras atau dengan dosa / cos. Mungkin diagram dapat membantu saya di sini.
aioobe

Pythagoras adalah kesalahan saya, tolong lupakan saja, tetapi harap Anda mengerti sifat fungsi kuadrat, tepat (2 / R2) × r perlu bukti dan saya tidak dapat menemukan bukti untuk ini
cheesefest

0

Masalah yang menyenangkan.
Dasar pemikiran dari probabilitas suatu titik yang dipilih menurun ketika jarak dari asalnya meningkat dijelaskan beberapa kali di atas. Kami memperhitungkannya dengan mengambil akar U [0,1]. Inilah solusi umum untuk r positif di Python 3.

import numpy
import math
import matplotlib.pyplot as plt

def sq_point_in_circle(r):
    """
    Generate a random point in an r radius circle 
    centered around the start of the axis
    """

    t = 2*math.pi*numpy.random.uniform()
    R = (numpy.random.uniform(0,1) ** 0.5) * r

    return(R*math.cos(t), R*math.sin(t))

R = 200 # Radius
N = 1000 # Samples

points = numpy.array([sq_point_in_circle(R) for i in range(N)])
plt.scatter(points[:, 0], points[:,1])

masukkan deskripsi gambar di sini


0

Anda juga bisa menggunakan intuisi Anda.

Luas lingkaran adalah pi*r^2

Untuk r=1

Ini memberi kita area pi . Mari kita asumsikan bahwa kita memiliki semacam fungsi fyang secara seragam akan mendistrubusikan N=10titik-titik di dalam lingkaran. Rasio di sini adalah10 / pi

Sekarang kita menggandakan luas dan jumlah poin

Untuk r=2 danN=20

Ini memberi area 4pidan rasionya sekarang 20/4piatau10/2pi . Rasio akan semakin kecil dan semakin kecil pula radiusnya, karena pertumbuhannya kuadratik dan Nskala linear.

Untuk memperbaikinya kita bisa katakan

x = r^2
sqrt(x) = r

Jika Anda akan menghasilkan vektor dalam koordinat polar seperti ini

length = random_0_1();
angle = random_0_2pi();

Lebih banyak poin akan mendarat di sekitar pusat.

length = sqrt(random_0_1());
angle = random_0_2pi();

length tidak terdistribusi secara merata lagi, tetapi vektor sekarang akan terdistribusi secara seragam.


-1

1) Pilih X acak antara -1 dan 1.

var X:Number = Math.random() * 2 - 1;

2) Menggunakan rumus lingkaran, hitung nilai maksimum dan minimum Y mengingat X dan jari-jari 1:

var YMin:Number = -Math.sqrt(1 - X * X);
var YMax:Number = Math.sqrt(1 - X * X);

3) Pilih Y acak antara yang ekstrem:

var Y:Number = Math.random() * (YMax - YMin) + YMin;

4) Masukkan nilai lokasi dan radius Anda dalam nilai akhir:

var finalX:Number = X * radius + pos.x;
var finalY:Number = Y * radois + pos.y;

2
Tidak seragam - probabilitas untuk [-1, 0] jauh lebih tinggi daripada untuk [0, 0], mengingat bahwa p ([- 1, Y]) = p ([0, Y]), dan hanya ada satu pilihan untuk [-1, Y] dan banyak pilihan untuk [0, Y].
Amadan

Solusi ini mendukung poin ke sisi kiri dan kanan lingkaran. Poin dengan x mendekati nol tidak terwakili. Bukan distribusi yang seragam sama sekali.
Dawood ibn Kareem
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.