Chọn ngẫu nhiên các điểm trên đĩa


14

Tôi đã đọc về các vòng tròn ở đâu đó, và bây giờ mới biết về đĩa ( thực ra đó là một khái niệm khá phổ biến ) và nghĩ về codegolf.

Nhiệm vụ của bạn là chọn ngẫu nhiên một điểm / một vài điểm trên đĩa có bán kính 1.

Quy tắc:

  • Tất cả các điểm phải có xác suất bằng nhau để được tạo
  • Tọa độ điểm nổi phải được sử dụng; yêu cầu tối thiểu là hai số thập phân (ví dụ: điểm (0.12, -0.45)hoặc (0.00, -1.00)hợp lệ)
  • Bạn nhận được -20 byte nếu chương trình của bạn thực sự hiển thị vòng tròn giới hạn và (các) điểm được tạo trong đó. Các tọa độ vẫn phải hợp lệ nhưng không được hiển thị và hình ảnh được tạo phải có kích thước tối thiểu 201 x 201 pixel
  • Bạn nhận được -5 byte nếu chương trình của bạn lấy số điểm được tạo làm đầu vào trên stdin
  • Nếu bạn quyết định không vẽ đường tròn giới hạn và (các) điểm, chương trình của bạn phải xuất (các) điểm được tạo trên định dạng (x, y)hoặc (x,y)trên thiết bị xuất chuẩn
  • Nếu bạn quyết định lấy số lượng điểm được tạo làm đầu vào, nhưng không vẽ nó - chương trình của bạn phải xuất tất cả các điểm ngẫu nhiên trên định dạng đã nêu ở trên có hoặc không có một khoảng trắng ở giữa

Gửi ngắn nhất trong byte chiến thắng!


1
@sweerpotato Có, vui lòng xác định rằng tất cả các điểm trong và trên vòng tròn là hợp lệ. Tôi đã không nhận ra bạn có nghĩa là cả hai. Ngoài ra, câu hỏi này có vẻ như sẽ phù hợp với thử thách chơi gôn mã hơn là một cuộc thi phổ biến, nhưng đó chỉ là ý kiến ​​của tôi.
cole

5
" Làm XYZ theo cách sáng tạo " là Câu hỏi Bad Popcon cổ điển ™. Những gì một người coi là sáng tạo là những gì người khác coi là cách rõ ràng.
Peter Taylor

Vì tò mò, tại sao yêu cầu đầu ra pixel 201x201 cho các lô?
JohnE

@JohnE Tôi đã đề xuất 201x201 pixel vì nó phù hợp với độ chính xác 2 chữ số thập phân bắt buộc
trichoplax

Chúng ta có thể xuất tọa độ dưới dạng số phức không? Ví dụ : 0.3503082505747327+0.13499221288682994j.
orlp

Câu trả lời:


5

Pyth, 26 - 5 = 21 byte

VQp(sJ*@OZ2^.n1*yOZ.l_1)eJ

Lấy số lượng tọa độ để tạo trên stdin và xuất chúng trên thiết bị xuất chuẩn như sau:

(-0.5260190768964058, -0.43631187015380823)(-0.12127959509302746, -0.08556306418467638)(-0.26813756369750996, -0.4564539715526493)

Sử dụng một chiến lược tương tự như @ MartinBüttner, tạo tọa độ cực và bán kính, ngoại trừ nó sử dụng phép lũy thừa phức tạp.


Bạn có thể loại bỏ p, bạn có thể? Nó chỉ thay đổi đầu ra thành các dòng riêng biệt.
PurkkaKoodari

@ Pietu1998 Điều đó không được phép, xem các bình luận về câu hỏi chính.
orlp

Ồ, đúng rồi.
PurkkaKoodari

16

CJam, 28 27 byte

PP+mr_mc\ms]1.mrmqf*"(,)".\

Giải pháp này không dựa trên từ chối. Tôi đang tạo các điểm theo tọa độ cực, nhưng với phân bố bán kính không đồng đều để đạt được mật độ điểm đồng nhất.

Kiểm tra nó ở đây.

Giải trình

PP+     e# Push 2π.
mr_     e# Get a random float between 0 and 2π, make a copy.
mc\     e# Take the cosine of one copy and swap with the other.
ms]     e# Take the sine of the other copy and wrap them in an array.
        e# This gives us a uniform point on the unit circle.
1.mr    e# Get a random float between 0 and 1.
mq      e# Take the square root. This is the random radius.
f*      e# Multiply x and y by this radius.
"(,)".\ e# Put the resulting numbers in the required format.

Tại sao nó hoạt động? Xem xét một biên độ hẹp của bán kính rvà chiều rộng (nhỏ)dr . Diện tích xấp xỉ 2π*r*dr(nếu annulus hẹp, chu vi bên trong và bên ngoài gần như giống hệt nhau và có thể bỏ qua độ cong, sao cho diện tích có thể được coi là hình chữ nhật có chiều dài cạnh chu vi và chiều rộng của annulus). Vì vậy, diện tích tăng tuyến tính với bán kính. Điều này có nghĩa là chúng tôi cũng muốn phân phối tuyến tính của bán kính ngẫu nhiên, để đạt được mật độ không đổi (tại bán kính gấp đôi, có diện tích gấp đôi diện tích, vì vậy chúng tôi muốn gấp đôi số điểm ở đó).

Làm thế nào để chúng ta tạo ra một phân phối ngẫu nhiên tuyến tính từ 0 đến 1? Trước tiên hãy xem xét trường hợp riêng biệt. Giả sử, chúng tôi có phân phối mong muốn gồm 4 giá trị, như {0.1, 0.4, 0.2, 0.3}(nghĩa là chúng tôi muốn 1phổ biến gấp 4 lần và phổ biến 0gấp đôi 2; chúng tôi muốn 3phổ biến gấp ba lần 0):

nhập mô tả hình ảnh ở đây

Làm thế nào có thể chọn một trong bốn giá trị với phân phối mong muốn? Chúng ta có thể xếp chúng lên nhau, chọn một giá trị ngẫu nhiên đồng đều từ 0 đến 1 trên trục y và chọn phân đoạn tại điểm đó:

nhập mô tả hình ảnh ở đây

Có một cách khác để hình dung sự lựa chọn này mặc dù. Thay vào đó, chúng ta có thể thay thế từng giá trị của phân phối bằng sự tích lũy của các giá trị cho đến thời điểm đó:

nhập mô tả hình ảnh ở đây

Và bây giờ chúng ta coi dòng trên cùng của biểu đồ này là một hàm f(x) = yvà đảo ngược nó để có được một hàm , mà chúng ta có thể áp dụng cho một giá trị ngẫu nhiên thống nhất trong :g(y) = f-1(y) = xy ∈ [0,1]

nhập mô tả hình ảnh ở đây

Thật tuyệt, vậy làm thế nào để có thể sử dụng điều này để tạo ra phân phối tuyến tính của bán kính? Đây là bản phân phối mà chúng tôi muốn:

nhập mô tả hình ảnh ở đây

Bước đầu tiên là tích lũy các giá trị của phân phối. Nhưng phân phối là liên tục, vì vậy thay vì tổng hợp tất cả các giá trị trước đó, chúng tôi lấy một tích phân từ 0đến r. Chúng ta có thể dễ dàng giải quyết điều đó một cách phân tích : . Tuy nhiên, chúng tôi muốn điều này được chuẩn hóa, tức là nhân nó với một hằng số sao cho điều này mang lại giá trị tối đa , vì vậy điều chúng tôi thực sự muốn là :0r r dr = 1/2 r21rr2

nhập mô tả hình ảnh ở đây

Và cuối cùng, chúng ta đảo ngược điều này để có được một hàm mà chúng ta có thể áp dụng cho một giá trị thống nhất [0,1], trong đó chúng ta có thể thực hiện lại một cách phân tích: đó chỉ là r = √y, ygiá trị ngẫu nhiên ở đâu:

nhập mô tả hình ảnh ở đây

Đây là một kỹ thuật khá hữu ích, thường có thể được sử dụng để tạo các phân phối đơn giản chính xác (nó hoạt động cho bất kỳ phân phối nào, nhưng đối với các phân phối phức tạp, hai bước cuối cùng có thể phải được giải quyết bằng số). Tuy nhiên, tôi sẽ không sử dụng nó trong trường hợp cụ thể này trong mã sản xuất, bởi vì căn bậc hai, sin và cos rất đắt: sử dụng thuật toán dựa trên từ chối trung bình nhanh hơn nhiều, vì nó chỉ cần cộng và nhân.


1
Giải thích rất hay!
sweerpotato

2
Hình ảnh Mmm: D
Beta Decay

12

Toán học, 68 44 - 20 = 24 byte

Rất cám ơn David Carraher đã cho tôi biết RandomPoint, đã lưu 24 byte (!). Mathematica không có một built-in cho tất cả mọi thứ.

Graphics@{Circle[],Point@RandomPoint@Disk[]}

Điều này vẽ đồ thị điểm và vòng tròn giới hạn để đủ điều kiện nhận thưởng:

nhập mô tả hình ảnh ở đây

Kết quả là một hình ảnh vector, do đó, thông số kích thước của 201x201 pixel không thực sự có ý nghĩa, nhưng theo mặc định, nó hiển thị lớn hơn thế.


Thế còn Graphics[{Circle[], Point@RandomPoint@Disk[]}]?
DavidC

Cứ tự nhiên. Ngoài ra, để tiết kiệm 1 byte ...Graphics@{Circle[], Point@RandomPoint@Disk[]}
DavidC

@DavidCarraher Cảm ơn rất nhiều! :)
Martin Ender

Tôi không biết cú pháp Mathicala nhưng chắc chắn bạn có thể lưu một byte khác bằng cách xóa khoảng trắng sau dấu ,?
lông mịn

@fluffy Tôi đã làm trong phiên bản đã đăng
Martin Ender

9

CJam, 31 26 byte

{];'({2dmr(_}2*@mhi}g',\')

Điều này hoạt động bằng cách liên tục tạo các điểm ngẫu nhiên trong một hình vuông có độ dài cạnh 2 và giữ điểm đầu tiên nằm trong đĩa đơn vị.

Cảm ơn @ MartinBüttner vì đã chơi golf 3 byte!

Hãy thử trực tuyến trong trình thông dịch CJam .

Làm thế nào nó hoạt động

{                  }g       Do:
 ];'(                         Clear the stack and push a left parenthesis.
     {      }2*               Do twice:
      2dmr                      Randomly select a Double between 0 and 2.
          (_                    Subtract 1 and push a copy.
               @              Rotate the copy of the first on top of the stack.
                mh            Compute the Euclidean norm of the vector consisting
                              of the two topmost Doubles on the stack.
                  i           Cast to integer.
                            If the result is non-zero, repeat the loop.
                     ',\    Insert a comma between the Doubles.
                        ')  Push a right parenthesis.

8

tôi , 53 51 byte

Không có gì đặc biệt, nhưng tôi cho rằng chúng ta nên có ít nhất một giải pháp đồ họa:

,(80+160*t@&{.5>%(x*x)+y*y}.+t:0N 2#-.5+?9999;cga;3)

âm mưu

Hãy thử nó trong trình duyệt của bạn .

Chỉnh sửa: Tôi có thể tắt hai byte bằng cách áp dụng phương pháp của @ MartinBüttner để sửa đổi phân phối tọa độ cực. Tôi nghĩ nó cũng trực tiếp hơn một chút:

,(80*1+(%?c){x*(cos y;sin y)}'6.282*?c:9999;cga;3)

3
Nếu bạn cũng vẽ vòng tròn giới hạn, bạn đủ điều kiện cho -20.
orlp

1
iKe có một mô hình vẽ dựa trên raster, làm cho yêu cầu đó khá không công bằng. Tôi nghĩ rằng nó cũng sẽ tốn hơn 20 ký tự để thể hiện xấp xỉ một vòng tròn giới hạn.
JohnE

7

Perl, 59 byte

while(($x=1-rand 2)**2+($y=1-rand 2)**2>1){};print"($x,$y)"

Đây chỉ là một giải pháp đơn giản, tạo các điểm trong một hình vuông và từ chối những điểm quá xa. Thủ thuật đánh gôn đơn lẻ của tôi là bao gồm các bài tập bên trong điều kiện.

Chỉnh sửa: Trong quá trình chơi gôn, tôi đã tìm thấy một cách thú vị để in các điểm ngẫu nhiên trên một vòng tròn .

use Math::Trig;$_=rand 2*pi;print"(",sin,",",cos,")"

7

Octave, 24 53 - 20 = 33 byte

polar([0:2e-3:1,rand]*2*pi,[ones(1,501),rand^.5],'.')

Tạo 501 giá trị theta cách đều nhau cộng với một số ngẫu nhiên và chia tỷ lệ tất cả thành [0..2π]. Sau đó tạo 501 1 'cho bán kính của vòng tròn, cộng với bán kính ngẫu nhiên cho điểm và lấy căn bậc hai để đảm bảo phân phối đồng đều trên đĩa. Sau đó vẽ tất cả các điểm dưới dạng tọa độ cực.

nhập mô tả hình ảnh ở đây


Dưới đây là một minh họa nhanh về phân phối (không có vòng tròn đơn vị):

polar(2*pi*rand(99),rand(99).^.5,'.')

9801 điểm


5

Octave / Matlab, 74 64 byte

Phương pháp từ chối , 64 byte:

u=1;v=1;while u^2+v^2>1
u=rand;v=rand;end
sprintf('(%f,%f)',u,v)

Phương thức trực tiếp , 74 byte (cảm ơn Martin Büttner đã giúp tôi sửa hai lỗi):

t=rand*2*pi;r=1-abs(1-sum(rand(2,1)));sprintf('(%f,%f)',r*cos(t),r*sin(t))

5

R, 99 95 81-20 = 79 75 61 byte

symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))

Sử dụng cấu trúc số phức để xây dựng x / y từ tọa độ cực. Lấy đầu vào là một chút tốn kém và có lẽ có một cách tốt hơn để làm điều này. Các ylim xlim là để đảm bảo toàn bộ vòng tròn được vẽ vàasp đảm bảo điểm được trình bày dưới biểu tượng vòng tròn.

Cảm ơn @jbaums và @flodel vì sự tiết kiệm

Hãy thử nó ở đây


runif(9,0,1)có thể được đơn giản hóa thànhrunif(9)
jbaums

@jbaums, cảm ơn ... một trong những điều mà tôi dường như luôn quên :)
MickyT

Có thể cạo 14:symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))
flodel

@flodel rất đẹp cảm ơn bạn.
MickyT

Một tiết kiệm tuổi teen: ylilàm việc thay thế ylim.
jbaums

4

Xử lý / Java 141 byte-20 = 121

yêu cầu cho 201 * 201 là kích thước tối thiểu yêu cầu tôi đưa vào setupphương thức kể từ khi Chế độ xử lý mặc định là 200x200 :(

void setup(){noFill();size(201,201);}void draw(){float f=10,a=PI*2*random(),r=random();point(f+f*sin(a)*r,f+f*cos(a)*r);ellipse(f,f,f*2,f*2)}

Tôi không biết xử lý / java đã được cho phép, gọn gàng!
J Atkin

4

QBasic, 138 byte - 20 - 5 = 113

INPUT n
r=200
SCREEN 12
RANDOMIZE TIMER
CIRCLE(r,r),r
PAINT(r,r)
FOR i=1TO n
DO
x=RND*r*2
y=RND*r*2
LOOP UNTIL POINT(x,y)
PSET(x,y),1
NEXT

Đưa người dùng nhập liệu và vẽ đĩa và điểm. Đã thử nghiệm trên QB64 .

Đây là một chiến lược "ném vào phi tiêu và giữ những gì dính" khá cơ bản. Điều hấp dẫn là "những gì gậy" không được xác định bằng toán học mà là về mặt đồ họa: một đĩa trắng được vẽ trên nền đen và sau đó các điểm được tạo ngẫu nhiên bị từ chối cho đến khi chúng không có màu đen. Các điểm được vẽ bằng màu xanh lam (mặc dù rất khó để biết khi nào chúng là các pixel đơn lẻ - nhấp vào hình ảnh để phóng to).


3

awk - 95 - 5 = 90

{
    for(;$1--;printf"("(rand()<.5?x:-x)","(rand()<.5?y:-y)")")
        while(1<(x=rand())^2+(y=rand())^2);
}

Vì tôi không chắc lắm về phần rand () <. 5, tôi đã thực hiện một số thử nghiệm phân phối với phần này, sử dụng tập lệnh này:

BEGIN{ srand() }
{ 
    split("0 0 0 0", s)
    split("0 0 0 0", a)

    for(i=$1; i--; )
    {
        while( 1 < r2 = ( x=rand() )^2 + ( y=rand() )^2 );

        x = rand()<.5 ? x : -x
        y = rand()<.5 ? y : -y

        ++s[ x>=0 ? y>=0 ? 1 : 4 : y>=0 ? 2 : 3 ]

        ++a[ r2>.75 ? 1 : r2>.5 ? 2 : r2>.25 ? 3 : 4]
    }

    print "sector distribution:"
        for(i in s) print "sector " i ": " s[i]/$1

    print "quarter area distribution:"
        for(i in a) print "ring " i ":   " a[i]/$1
}

mà với đầu vào 1e7 cho tôi kết quả này, sau khi tôi nhấm nháp một hoặc hai lần tại cà phê của mình:

1e7
sector distribution:
sector 1: 0.250167
sector 2: 0.249921
sector 3: 0.249964
sector 4: 0.249948
quarter area distribution:
ring 1:   0.24996
ring 2:   0.25002
ring 3:   0.250071
ring 4:   0.249949

mà tôi nghĩ là khá ổn

Một lời giải thích nhỏ:
Sau khi viết nguệch ngoạc một lúc, nếu bạn muốn chia đĩa thành bốn vòng có diện tích bằng nhau, bán kính nơi bạn sẽ phải cắt là sqrt (1/4), sqrt (1/2 ) và sqrt (3/4). Vì bán kính thực tế của điểm tôi kiểm tra sẽ là sqrt (x ^ 2 + y ^ 2), tôi có thể bỏ qua tất cả các root vuông. "Sự trùng hợp" 1/4, 2/4, 3/4 có thể liên quan đến những gì M. Buettner đã chỉ ra trước đó.


3

HPPPL , 146 (171-20-5) byte

EXPORT r(n)BEGIN LOCAL R,A,i,Q;RECT();Q:=118.;ARC_P(Q,Q,Q);FOR i FROM 1 TO n DO R:=√RANDOM(1.);A:=RANDOM(2*π);PIXON_P(G0,IP(Q+Q*R*COS(A)),IP(Q+Q*R*SIN(A)));END;FREEZE;END;

Ví dụ cho 10000 điểm (bao gồm thời gian tính bằng giây cho thiết bị thực):

Chọn ngẫu nhiên các điểm trên đĩa, thời gian

Hàm được gọi bởi r(n) . Phần còn lại trong hình trên chỉ dành cho mục đích thời gian.

Kết quả (đường kính đĩa là 236 pixel):

nhập mô tả hình ảnh ở đây

Phiên bản trên không lưu trữ tọa độ điểm, vì vậy tôi đã viết một phiên bản có hai tham số r(n,p). nlà số điểm và p=0trả về các điểm cho thiết bị đầu cuối, p=1vẽ các điểm và đĩa), trong trường hợp lưu trữ tọa độ là bắt buộc. Phiên bản này dài 283 (308-20-5) byte:

EXPORT r(n,p)BEGIN LOCAL R,A,j,Q,x,y;Q:=118.0;CASE IF p==0 THEN print() END IF p==1 THEN RECT();ARC_P(Q,Q,Q) END END;FOR j FROM 1 TO n DO R:=√RANDOM(1.0);A:=RANDOM(2*π);x:=R*COS(A);y:=R*SIN(A);CASE IF p==0 THEN print("("+x+", "+y+")") END IF p==1 THEN PIXON_P(G0,IP(Q+Q*x),IP(Q+Q*y)) END END;END;FREEZE;END;

Phiên bản chưa được chỉnh sửa:

EXPORT r(n,p)
BEGIN
LOCAL R,A,j,Q,x,y;
  Q:=118.0;
  CASE
    IF p==0 THEN print() END
    IF p==1 THEN RECT();ARC_P(Q,Q,Q) END
  END;
  FOR j FROM 1 TO n DO
    R:=√RANDOM(1.0);
    A:=RANDOM(2*π);
    x:=R*COS(A);
    y:=R*SIN(A);
    CASE
      IF p==0 THEN print("("+x+", "+y+")") END
      IF p==1 THEN PIXON_P(G0,IP(Q+Q*x),IP(Q+Q*y)) END
    END;
  END;
  FREEZE;
END;

Đầu ra thiết bị đầu cuối cho r(10,0):

Chọn ngẫu nhiên các điểm trên đầu ra của đĩa

r(10,1) hiển thị đĩa với các điểm, như hiển thị ở trên.


2

JavaScript, 75 byte

Dựa trên từ chối:

do x=(r=()=>4*Math.random()-2)(),y=r()
while(x*x+y*y>1)
alert(`(${[x,y]})`)

Phương thức trực tiếp (80 byte):

alert(`(${[(z=(m=Math).sqrt((r=m.random)()))*m.sin(p=m.PI*2*r()),z*m.cos(p)]})`)

2

Python, 135 130 byte

from random import*
def r():return uniform(-1,1)
p=[]
while not p:
    x,y=r(),r()
    if x**2+y**2<=1:p=x,y
print'(%.2f, %2f)'%p

Đã xóa **0.5lời cảm ơn với đề xuất của @ jimmy23013 (vì đây là một vòng tròn đơn vị, tôi hiện đang kiểm tra xem khoảng cách bình phương giữa (x, y) và (0, 0) có bằng 1 2 không Đây là điều tương tự).

Điều này cũng giải phóng tôi để loại bỏ dấu ngoặc đơn.


Tôi nghĩ bạn không cần **0.5.
jimmy23013

@ jimmy23013 Cảm ơn bạn! loại bỏ.
JF
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.