Golf ngẫu nhiên trong ngày # 4: Nghịch lý Bertrand


19

Giới thiệu về bộ

Trước hết, bạn có thể coi điều này giống như bất kỳ thử thách chơi gôn mã nào khác, và trả lời nó mà không phải lo lắng về loạt bài này. Tuy nhiên, có một bảng xếp hạng trên tất cả các thách thức. Bạn có thể tìm thấy bảng xếp hạng cùng với một số thông tin khác về loạt bài trong bài đầu tiên .

Mặc dù tôi có một loạt các ý tưởng được xếp hàng cho loạt bài này, những thách thức trong tương lai vẫn chưa được đặt ra. Nếu bạn có bất kỳ đề xuất nào, xin vui lòng cho tôi biết trên bài đăng hộp cát có liên quan .

Lỗ 4: Nghịch lý Bertrand

Các Bertrand nghịch lý là một vấn đề thú vị, trong đó cho thấy phương pháp cách khác nhau để chọn hợp âm ngẫu nhiên trong một vòng tròn có thể mang lại các bản phân phối khác nhau của hợp âm, trung điểm của họ và độ dài của họ.

Trong thử thách này, bạn phải tạo ra các hợp âm ngẫu nhiên của vòng tròn đơn vị, sử dụng phương thức "đúng", tức là tạo ra một phân phối các hợp âm bất biến theo tỷ lệ và dịch mã. Trong bài viết Wikipedia được liên kết, "Phương pháp 2" là một phương pháp như vậy.

Dưới đây là các quy tắc chính xác:

  • Bạn nên lấy một số nguyên dươngN xác định số lượng hợp âm sẽ được trả về. Đầu ra phải là một danh sách các Nhợp âm, mỗi hợp âm được chỉ định là hai điểm trên vòng tròn đơn vị, được cho bởi góc cực của chúng theo radian.
  • Mã của bạn sẽ có thể trả về ít nhất 2 20 giá trị khác nhau cho mỗi hai góc . Nếu RNG khả dụng của bạn có phạm vi nhỏ hơn, trước tiên bạn phải xây dựng RNG với phạm vi đủ lớn trên đầu trang có sẵn hoặc bạn phải triển khai RNG phù hợp của riêng mình . Trang này có thể hữu ích cho việc đó.
  • Việc phân phối các hợp âm phải không thể phân biệt được với hợp âm được tạo ra bởi "Phương pháp 2" trong bài viết Wikipedia được liên kết. Nếu bạn thực hiện một thuật toán khác để chọn hợp âm, vui lòng bao gồm bằng chứng về tính chính xác. Bất kỳ thuật toán nào bạn chọn để thực hiện, về mặt lý thuyết , nó phải có khả năng tạo ra bất kỳ hợp âm hợp lệ nào trong vòng tròn đơn vị (hạn chế các giới hạn của PRNG cơ bản hoặc các loại dữ liệu có độ chính xác giới hạn).
  • Việc triển khai của bạn nên sử dụng và trả về các số có dấu phẩy động (rộng tối thiểu 32 bit) hoặc số điểm cố định (rộng tối thiểu 24 bit) và tất cả các phép toán số học phải chính xác trong tối đa 16 ulp .

Bạn có thể viết một chương trình đầy đủ hoặc một hàm và nhận đầu vào thông qua STDIN (hoặc thay thế gần nhất), đối số dòng lệnh hoặc đối số hàm và tạo đầu ra thông qua STDOUT (hoặc thay thế gần nhất), tham số trả về hàm hoặc tham số hàm (out).

Đầu ra có thể ở bất kỳ định dạng chuỗi hoặc danh sách thuận tiện, miễn là các số riêng biệt có thể phân biệt rõ ràng và tổng số của chúng luôn luôn là số chẵn.

Đây là mã golf, vì vậy bài nộp ngắn nhất (tính bằng byte) sẽ thắng. Và tất nhiên, lần gửi ngắn nhất cho mỗi người dùng cũng sẽ tham gia vào bảng xếp hạng tổng thể của loạt bài.

Hình dung

Bạn có thể sử dụng đoạn mã sau để kết xuất các dòng được tạo và kiểm tra phân phối của chúng. Đơn giản chỉ cần dán một danh sách các cặp góc vào vùng văn bản. Đoạn mã có thể xử lý hầu hết mọi định dạng danh sách, miễn là các số đó là số thập phân đơn giản (không có ký hiệu khoa học). Tôi khuyên bạn nên sử dụng ít nhất 1000 dòng để có ý tưởng tốt về phân phối. Tôi cũng đã cung cấp một số dữ liệu mẫu cho các phương pháp khác nhau được trình bày trong bài viết dưới đây.

Dữ liệu ví dụ được tạo bằng phương pháp 1.

Dữ liệu ví dụ được tạo bằng phương pháp 2.

Dữ liệu ví dụ được tạo bằng phương pháp 3.

Bảng xếp hạng

Bài đầu tiên của loạt bài tạo ra một bảng thành tích.

Để đảm bảo rằng câu trả lời của bạn hiển thị, vui lòng bắt đầu mọi câu trả lời bằng tiêu đề, sử dụng mẫu Markdown sau:

# Language Name, N bytes

nơi Nlà kích thước của trình của bạn. Nếu bạn cải thiện điểm số của mình, bạn có thể giữ điểm số cũ trong tiêu đề, bằng cách đánh bại chúng thông qua. Ví dụ:

# Ruby, <s>104</s> <s>101</s> 96 bytes

(Ngôn ngữ hiện không được hiển thị, nhưng đoạn mã yêu cầu và phân tích nó và tôi có thể thêm bảng xếp hạng ngôn ngữ trong tương lai.)

Câu trả lời:


12

Mã máy IA-32, 54 byte

Mã thập phân của mã:

68 00 00 00 4f 0f c7 f0 50 db 04 24 58 d8 34 24
f7 d9 78 f1 d9 c0 dc c8 d9 e8 de e1 d9 fa d9 c9
d9 f3 dc c0 d9 eb de ca d8 c1 dd 1a dd 5a 08 83
c2 10 e2 d1 58 c3

Nó sử dụng thuật toán (sửa đổi một chút) mà Wikipedia đã mô tả. Trong mã giả:

x = rand_uniform(-1, 1)
y = rand_uniform(-1, 1)
output2 = pi * y
output1 = output2 + 2 * acos(x)

Tôi sử dụng phạm vi -1...1bởi vì thật dễ dàng để tạo các số ngẫu nhiên trong phạm vi này: rdrandhướng dẫn tạo ra một số nguyên giữa -2^312^31-1, có thể dễ dàng chia cho 2 ^ 31.

Tôi nên sử dụng phạm vi 0...1cho số ngẫu nhiên khác (x), được đưa vào acos; tuy nhiên, phần âm đối xứng với phần dương - số âm tạo ra các hợp âm có nhịp lớn hơn pi radian, nhưng với mục đích minh họa cho nghịch lý Bertrand, điều đó không thành vấn đề.

Do tập lệnh 80386 (hoặc x87) không có lệnh chuyên dụng acos, tôi phải thể hiện phép tính chỉ bằng cách sử dụng atanlệnh:

acos(x) = atan(sqrt(1-x^2)/x)

Đây là mã nguồn tạo mã máy ở trên:

__declspec(naked) void __fastcall doit1(int n, std::pair<double, double>* output)
{
    _asm {
        push 0x4f000000;        // [esp] = float representation of 2^32

    myloop:
        rdrand eax;             // generate a random number, -2^31...2^31-1
        push eax;               // convert it
        fild dword ptr [esp];   // to floating-point
        pop eax;                // restore esp
        fdiv dword ptr [esp];   // convert to range -1...1
        neg ecx;
        js myloop;              // do the above 2 times

        // FPU stack contents:  // x           | y
        fld st(0);              // x           | x   | y
        fmul st(0), st;         // x^2         | x   | y
        fld1;                   // 1           | x^2 | x | y
        fsubrp st(1), st;       // 1-x^2       | x   | y
        fsqrt;                  // sqrt(1-x^2) | x   | y
        fxch;                   // x           | sqrt(1-x^2) | y
        fpatan;                 // acos(x)     | y
        fadd st, st(0);         // 2*acos(x)   | y
        fldpi;                  // pi          | 2*acos(x) | y
        fmulp st(2), st;        // 2*acos(x)   | pi*y
        fadd st, st(1);         // output1     | output2
        fstp qword ptr [edx];   // store the numbers
        fstp qword ptr [edx + 8];

        add edx, 16;            // advance the output pointer
        loop myloop;            // loop

        pop eax;                // restore stack pointer
        ret;                    // return
    }
}

Để tạo hai số ngẫu nhiên, mã sử dụng một vòng lặp lồng nhau. Để tổ chức vòng lặp, mã tận dụng ecxthanh ghi (bộ đếm vòng ngoài) là dương. Nó tạm thời phủ nhận ecx, và sau đó thực hiện lại để khôi phục giá trị ban đầu. Lệnh jslặp lại vòng lặp khi ecxâm (điều này xảy ra chính xác một lần).


Tôi thích câu trả lời này cho việc sử dụng lắp ráp IA32! Chỉ cần nói rằng: đây không hoàn toàn là mã máy 386 vì 80386 rõ ràng không có hướng dẫn thứ tự cũng như không cần bộ xử lý FP
user5572685

Vâng, IA32 là một cái tên tốt hơn. Không đủ cụ thể nhưng có lẽ đúng hơn 80386.
anatolyg

7

Pyth, 25 23 22 byte

Một cổng của câu trả lời C ++ 11 của RCrmn. Đây là lần đầu tiên tôi sử dụng Pyth và tôi đã có rất nhiều niềm vui!

VQJ,*y.n0O0.tOZ4,sJ-FJ

Phiên bản 23 byte:

VQJ*y.n0O0K.tOZ4+JK-JKd

Cắt một byte bằng cách thay đổi chương trình để sử dụng nếp gấp + tổng và đặt J thành một tuple, loại bỏ K.

Nguyên:

VQJ**2.n0O0K.tO0 4+JK-JKd

Cắt bỏ 2 byte nhờ @orlp.

Giải trình:

VQ                         loop as many times as the input number
  J,                       set J to the following tuple expression
    *y.n0O0                2 * .n0 (pi) * O0 (a random number between 0 and 1)
            .tOZ4          .tOZ 4 (acos of OZ (a random number))
                 ,sJ-FJ    print the sum of J and folding J using subtraction in parenthesis, separated by a comma, followed by another newline

1
Lời khuyên thứ: *2_cũng giống như y_. Biến Zban đầu là 0, vì vậy bạn có thể loại bỏ khoảng trắng .tO0 4bằng cách viết .tOZ4.
orlp

1
Tôi đang đếm 25 byte ...
Maltysen

Ngoài ra, bạn có thể định dạng đầu ra tốt hơn với,+JK-JK
Maltysen

@Maltysen Cả cố định. Cảm ơn!
kirbyfan64sos

@orlp Mình không có manh mối yvà quên mất Z. Đã sửa; cảm ơn!
kirbyfan64sos

6

Julia, 48 byte

n->(x=2π*rand(n);y=acos(rand(n));hcat(x+y,x-y))

Điều này sử dụng thuật toán phương pháp 2, giống như hầu hết các câu trả lời cho đến nay. Nó tạo ra một hàm lambda có một đầu vào số nguyên và trả về một mảng nx 2. Để gọi nó, đặt tên cho nó, vd f=n->....

Ungolfed + giải thích:

function f(n::Int64)
    # The rand() function returns uniform random numbers using
    # the Mersenne-Twister algorithm

    # Get n random chord angles
    x = 2π*rand(n)

    # Get n random rotations
    y = acos(rand(n))

    # Bind into a 2D array
    hcat(x+y, x-y)
end

Tôi thực sự thích cách hiển thị trực quan, vì vậy tôi sẽ bao gồm một. Đó là kết quả của f(1000).

Vòng tròn


5

Bình thường, 22 byte

Một cổng của câu trả lời C ++. Tôi đã có một giải pháp 23 byte khác (Bây giờ là 22!), Nhưng nó gần như là một bản sao của câu trả lời thứ ba của @ kirbyfan64sos với sự tối ưu hóa, vì vậy tôi phải suy nghĩ bên ngoài hộp một chút và sáng tạo (ab) sử dụng toán tử gập.

m,-Fdsdm,y*.nZOZ.tOZ4Q

Lưu ý rằng điều này không hoạt động ngay bây giờ vì một lỗi trong toán tử gấp sau khi giới thiệu reduce2. Tôi đang đưa ra một yêu cầu kéo.

m             Map    
 ,            Tuple of
  -Fd         Fold subtraction on input
  sd          Fold addition on input
 m      Q     Map over range input
  ,           Tuple           
   y          Double
    *         Product
     .nZ      Pi
     OZ       [0, 1) RNG
  .t  4       Acos
    OZ        [0, 1) RNG

Để làm mới, đây là giải pháp khác của tôi hoạt động theo cùng một cách: VQKy*.nZOZJ.tOZ4,+KJ-KJ


Bạn đã viết sai tên người dùng của tôi ... :(
kirbyfan64sos

@ kirbyfan64sos derp. Xin lỗi;)
Maltysen

4

IDL, 65 byte

Rõ ràng đây là thuật toán tương tự như @rcrmn, mặc dù tôi đã rút ra nó một cách độc lập trước khi đọc câu trả lời của họ.

read,n
print,[2,2]#randomu(x,n)*!pi+[-1,1]#acos(randomu(x,n))
end

Hàm Randomu của IDL sử dụng Mersenne Twister, có thời gian là 2 19937 -1.

EDIT: Tôi đã chạy 1000 hợp âm thông qua trình hiển thị ở trên, đây là ảnh chụp màn hình kết quả:

IDL bertrand


4

C ++ 11, 214 byte

#include<random>
#include<iostream>
#include<cmath>
int main(){using namespace std;int n;cin>>n;random_device r;uniform_real_distribution<> d;for(;n;--n){float x=2*M_PI*d(r),y=acos(d(r));cout<<x+y<<' '<<x-y<<';';}}

Vì vậy, đây là một triển khai thẳng của thuật toán đúng từ trang wikipedia. Vấn đề chính ở đây trong việc chơi golf là những cái tên quá dài mà các lớp tạo ngẫu nhiên có. Nhưng, trái ngược với ol 'rand tốt, ít nhất là nó đồng đều.

Giải trình:

#include<random>
#include<iostream>
#include<cmath>
int main()
{
    using namespace std;
    int n;
    cin>>n; // Input number
    random_device r; // Get a random number generator
    uniform_real_distribution<> d;   // Get a uniform distribution of 
                                     // floats between 0 and 1
    for(;n;--n)
    {
        float x = 2*M_PI*d(r),       // x: Chosen radius angle
              y = acos(d(r));        // y: Take the distance from the center and 
                                     // apply it an inverse cosine, to get the rotation

        cout<<x+y<<' '<<x-y<<';';    // Print the two numbers: they are the rotation
                                     // of the radius +/- the rotation extracted from
                                     // the distance to the center
    }
}

1
Yếu tố đó có M_PI_2vẻ đáng ngờ. Tôi nghĩ rằng nó nên là 1 thay thế.
anatolyg

Vâng, hoàn toàn đúng, sẽ sửa nó ngay! Cảm ơn rất nhiều!
rorlork

4

APL, 46 byte

f←{U←⍵ 2⍴∊{(○2×?0)(¯1○?0)}¨⍳⍵⋄⍉2⍵⍴∊(+/U)(-/U)}

Chương trình APL đầu tiên của tôi từ trước đến giờ! Chắc chắn nó có thể được cải thiện rất nhiều (vì sự hiểu biết chung của tôi về APL còn thiếu), vì vậy mọi đề xuất sẽ rất tuyệt vời. Điều này tạo ra một chức năngf lấy một số nguyên làm đầu vào, tính toán các cặp điểm hợp âm bằng phương thức 2 và in mỗi cặp được phân tách bằng một dòng mới.

Bạn có thể thử trực tuyến !

Giải trình:

f←{ ⍝ Create the function f which takes an argument ⍵

    ⍝ Define U to be an ⍵ x 2 array of pairs, where the first
    ⍝ item is 2 times a random uniform float (?0) times pi (○)
    ⍝ and the second is the arccosine (¯1○) of another random
    ⍝ uniform float.

    U ← ⍵ 2 ⍴ ∊{(○2×?0)(¯1○?0)}¨⍳⍵

    ⍝ Create a 2 x ⍵ array filled with U[;1]+U[;2] (+/U) and
    ⍝ U[;1]-U[;2] (-/U). Transpose it into an ⍵ x 2 array of
    ⍝ chord point pairs and return it.

    ⍉ 2 ⍵ ⍴ ∊(+/U)(-/U)
}

Lưu ý: Giải pháp 19 byte trước đây của tôi không hợp lệ vì nó trả về (x, y) thay vì (x + y, xy). Nỗi buồn đầy dẫy.


3

Java, 114 byte

n->{for(;n-->0;){double a=2*Math.PI*Math.random(),b=Math.acos(Math.random());System.out.println(a+b+" "+(a-b));}};

Thực hiện cơ bản trong java. Sử dụng như một biểu thức lambda.

Ví dụ sử dụng


Bạn không thể giảm kích thước bằng cách lưu trữ Mathở đâu đó? Hay cái gì đó? (Tôi không phải là lập trình viên Java)
Ismael Miguel

@IsmaelMiguel Điều đó sẽ tốn thêm 2 ký tự.
TheNumberOne

Xin lỗi: / Thật hấp dẫn khi cố gắng giảm số lần Mathhiển thị. Meta nói gì về việc sử dụng một mã để tạo mã khác để giải quyết vấn đề?
Ismael Miguel

2
@IsmaelMiguel Đó là một trò chơi công bằng, mặc dù tôi sẽ ngạc nhiên nếu bạn thực sự giỏi môn metagolf hơn là chơi gôn.
Martin Ender

3

Ruby, 72 byte

Golf đầu tiên của tôi ở đây! Tôi đã sử dụng cùng một mã như mọi người, tôi hy vọng điều đó ổn

gets.chomp.to_i.times{puts"#{x=2*Math::PI*rand},#{x+2*Math.acos(rand)}"}

2

Java, 115 123

Điều này về cơ bản giống như hầu hết những người khác, nhưng tôi cần điểm Java cho lỗ hổng này, vì vậy đây là:

void i(int n){for(double x;n-->0;System.out.println(x+2*Math.acos(Math.random())+" "+x))x=2*Math.PI*Math.random();}

1000 hợp âm mẫu có thể được tìm thấy tại pastebin , đây là năm hợp âm đầu tiên trong một lần chạy:

8.147304676211474 3.772704020731153
8.201346559916786 3.4066194978900106
4.655131524088468 2.887965593766409
4.710707820868578 3.8493686706403984
3.3839198612642423 1.1604092552846672

1

Camam 24 22 byte

Tương tự như các thuật toán khác, đây là một phiên bản trong CJam.

{2P*mr_1dmrmC_++]p}ri*

Một đầu vào 1000 tạo ra một phân phối như:

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

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

Thuật toán đơn giản là x = 2 * Pi * rand(); print [x, x + 2 * acos(rand())]

{                 }ri*        e# Run this loop int(input) times
 2P*mr                        e# x := 2 * Pi * rand()
      _                       e# copy x
       1dmr                   e# y := rand()
           mC                 e# z := acos(y)
             _++              e# o := x + z + z
                ]             e# Wrap x and o in an array
                 p            e# Print the array to STDOUT on a new line

Cập nhật : 2 byte được lưu nhờ Martin!

Hãy thử nó ở đây


1

Trăn 3, 144 117 byte

(cảm ơn Blckknght vì lambda con trỏ)

Sử dụng phương pháp tương tự như những người khác:

import math as m;from random import random as r;f=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]

Từ tài liệu Python:

Python sử dụng Mersenne Twister làm trình tạo lõi. Nó tạo ra các phao chính xác 53 bit và có chu kỳ 2 19937 -1.

Đầu ra

>>> f(10)
[(4.8142617617843415, 0.3926824824852387), (3.713855302706769, 1.4014527571152318), (3.0705105305032188, 0.7693910749957577), (1.3583477245841715, 0.9120275474824304), (3.8977143863671646, 1.3309852045392736), (0.9047010644291349, 0.6884780437147916), (3.333698164797664, 1.116653229885653), (3.0027328050516493, 0.6695430795843016), (5.258167740541786, 1.1524381034989306), (4.86435124286598, 1.5676690324824722)]

Và như vậy.

Hình dung

hình dung


Bạn có thể lưu khoảng 20 byte nếu bạn sử dụng lambda cho hàm và trả về mức độ hiểu danh sách (với biểu thức trình tạo bên trong):f=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]
Blckknght

Ah, ban đầu tôi có một lambda. Tôi đoán tôi đã không nghĩ về việc tăng gấp đôi về khả năng hiểu danh sách. Cảm ơn! @Blckknght
Zach Gates

Có thể rút ngắn xuống 109 byte bằng cách thay đổi nhập khẩu: tio.run/#python2
Triggernometry

1

Perl, 60

#!perl -l
use Math::Trig;print$x=2*pi*rand,$",$x+2*acos rand for 1..<>

1

R, 60 56 53 49 byte

Thêm 4 byte nhờ @JayCe và thay đổi nó thành một hàm.

Sử dụng công thức cơ bản giống như những người khác. R sử dụng phương thức Mersenne-Twister theo mặc định, nhưng các phương thức khác có thể được đặt. Xuất ra một danh sách ngăn cách không gian.

function(n,y=acos(runif(n)))runif(n)*2*pi+c(y,-y)

Hãy thử trực tuyến!


Xin chào Micky, bạn có thể tiết kiệm 4 byte bằng cách biến nó thành hàm và không xác định x.
JayCe

@JayCe điều đó tốt hơn rất nhiều cảm ơn
MickyT

0

SmileBASIC, 62 byte

INPUT N
FOR I=1TO N
X=PI()*2*RNDF()Y=ACOS(RNDF())?X+Y,X-Y
NEXT
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.