Golf Acak Hari Ini # 4: Paradox Bertrand

19

Tentang Seri

Pertama, Anda dapat memperlakukan ini seperti tantangan golf kode lainnya, dan menjawabnya tanpa khawatir tentang seri sama sekali. Namun, ada papan peringkat di semua tantangan. Anda dapat menemukan papan peringkat bersama dengan beberapa informasi lebih lanjut tentang seri di posting pertama .

Meskipun saya memiliki banyak ide untuk seri ini, tantangan di masa depan belum ditetapkan. Jika Anda memiliki saran, beri tahu saya di pos kotak pasir yang relevan .

Lubang 4: Paradox Bertrand

The Bertrand Paradoks merupakan masalah yang menarik, yang menunjukkan metode bagaimana yang berbeda untuk memilih akord acak dalam lingkaran dapat menghasilkan distribusi yang berbeda dari akord, titik-titik tengah mereka dan panjang mereka.

Dalam tantangan ini, Anda diharapkan membuat akor acak dari lingkaran unit, menggunakan metode "benar", yaitu yang menghasilkan distribusi akor yang tidak berubah pada penskalaan dan terjemahan. Dalam artikel Wikipedia yang ditautkan, "Metode 2" adalah metode semacam itu.

Berikut aturan yang tepat:

  • Anda harus mengambil satu bilangan bulat positifN yang menentukan berapa banyak akor yang harus dikembalikan. Outputnya harus berupa daftar Nakor, masing-masing ditentukan sebagai dua titik pada lingkaran satuan, yang diberikan oleh sudut kutubnya dalam radian.
  • Kode Anda harus dapat mengembalikan setidaknya 20 nilai yang berbeda untuk masing-masing dari dua sudut . Jika RNG Anda yang tersedia memiliki rentang yang lebih kecil, Anda harus terlebih dahulu membangun RNG dengan rentang yang cukup besar di atas built-in satu atau Anda harus menerapkan RNG Anda sendiri yang sesuai . Halaman ini mungkin bermanfaat untuk itu.
  • Distribusi akord harus dapat dibedakan dari yang dihasilkan oleh "Metode 2" dalam artikel Wikipedia yang ditautkan. Jika Anda menerapkan algoritma yang berbeda untuk memilih akor, harap sertakan bukti kebenaran. Algoritma apa pun yang Anda pilih untuk diterapkan, secara teoretis harus dapat menghasilkan akor yang valid dalam lingkaran unit (kecuali batasan PRNG yang mendasari atau tipe data presisi terbatas).
  • Implementasi Anda harus menggunakan dan mengembalikan angka floating-point (lebar minimal 32 bit) atau angka fixed-point (setidaknya lebar 24 bit) dan semua operasi aritmatika harus akurat dalam paling banyak 16 ulp .

Anda dapat menulis program atau fungsi lengkap dan mengambil input melalui STDIN (atau alternatif terdekat), argumen baris perintah atau argumen fungsi dan menghasilkan output melalui STDOUT (atau alternatif terdekat), mengembalikan nilai fungsi atau parameter fungsi (keluar).

Keluaran mungkin dalam format daftar atau string yang mudah, selama nomor individual jelas dapat dibedakan dan jumlah totalnya selalu genap.

Ini adalah kode golf, jadi pengiriman terpendek (dalam byte) menang. Dan tentu saja, pengiriman terpendek per pengguna juga akan masuk ke papan peringkat keseluruhan seri.

Visualisasi

Anda dapat menggunakan potongan berikut untuk membuat garis yang dihasilkan dan memeriksa distribusinya. Cukup rekatkan daftar pasang sudut ke area teks. Cuplikan harus dapat menangani hampir semua format daftar, asalkan angkanya adalah angka desimal sederhana (tidak ada notasi ilmiah). Saya sarankan Anda menggunakan setidaknya 1000 baris untuk mendapatkan ide distribusi yang bagus. Saya juga memberikan beberapa contoh data untuk berbagai metode yang disajikan dalam artikel di bawah ini.

Contoh data yang dihasilkan dengan metode 1.

Contoh data yang dihasilkan dengan metode 2.

Contoh data yang dihasilkan dengan metode 3.

Papan peringkat

Pos pertama dari seri ini menghasilkan papan peringkat.

Untuk memastikan jawaban Anda muncul, mulailah setiap jawaban dengan tajuk utama, menggunakan templat Penurunan harga berikut:

# Language Name, N bytes

di mana Nukuran kiriman Anda. Jika Anda meningkatkan skor Anda, Anda dapat menyimpan skor lama di headline, dengan mencoretnya. Contohnya:

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

(Bahasa saat ini tidak ditampilkan, tetapi cuplikan memang membutuhkan dan menguraikannya, dan saya dapat menambahkan leaderboard berdasarkan bahasa di masa mendatang.)

Martin Ender
sumber

Jawaban:

12

Kode mesin IA-32, 54 byte

Hexdump kode:

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

Ini menggunakan (sedikit dimodifikasi) algoritma yang dijelaskan Wikipedia. Dalam pseudo-code:

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

Saya menggunakan rentang -1...1karena mudah untuk membuat angka acak dalam rentang ini: rdrandinstruksi menghasilkan bilangan bulat antara -2^31dan 2^31-1, yang dapat dengan mudah dibagi dengan 2 ^ 31.

Saya seharusnya menggunakan rentang 0...1untuk nomor acak lainnya (x), yang dimasukkan ke dalam acos; Namun, bagian negatif simetris dengan bagian positif - angka negatif menghasilkan akord yang rentangnya lebih besar dari pi radian, tetapi untuk tujuan menggambarkan paradoks Bertrand, itu tidak masalah.

Karena set instruksi 80386 (atau x87) tidak memiliki acosinstruksi khusus , saya harus menyatakan perhitungan hanya menggunakan ataninstruksi:

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

Berikut adalah kode sumber yang menghasilkan kode mesin di atas:

__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
    }
}

Untuk menghasilkan dua angka acak, kode menggunakan loop bersarang. Untuk mengatur loop, kode memanfaatkan ecxregister (penghitung loop luar) menjadi positif. Ini sementara meniadakan ecx, dan kemudian melakukannya lagi untuk mengembalikan nilai aslinya. The jsinstruksi mengulangi loop ketika ecxnegatif (ini terjadi tepat sekali).

anatolyg
sumber
Saya suka jawaban ini untuk menggunakan perakitan IA32! Hanya untuk mengatakan: ini bukan sepenuhnya 386 kode mesin karena 80.386 jelas tidak memiliki instruksi rdrand atau perlu coprocessor FP
user5572685
Ya, IA32 adalah nama yang lebih baik. Tidak cukup spesifik tetapi mungkin lebih benar daripada 80386.
anatolyg
7

Pyth, 25 23 22 byte

Port jawaban C ++ 11 rcrmn. Ini adalah penggunaan pertama saya di Pyth, dan saya punya banyak kesenangan!

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

Versi 23 byte:

VQJ*y.n0O0K.tOZ4+JK-JKd

Potong satu byte dengan mengubah program untuk menggunakan lipatan + jumlah dan mengatur J ke tupel, menghapus K.

Asli:

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

Potong 2 byte berkat @orlp.

Penjelasan:

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
kirbyfan64sos
sumber
1
Tips Pyth: *2_sama dengan y_. Variabel Zawalnya 0, sehingga Anda dapat menghapus spasi .tO0 4dengan menulis .tOZ4.
orlp
1
Saya menghitung 25 byte ...
Maltysen
Anda juga dapat memformat output lebih baik dengan,+JK-JK
Maltysen
@Maltysen Keduanya diperbaiki. Terima kasih!
kirbyfan64sos
@ Atau saya tidak tahu ydan lupa tentang Z. Tetap; Terima kasih!
kirbyfan64sos
6

Julia, 48 byte

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

Ini menggunakan algoritma metode 2, seperti sebagian besar jawaban sejauh ini. Ini menciptakan fungsi lambda yang mengambil input integer dan mengembalikan array nx 2. Untuk menyebutnya, berikan nama, mis f=n->....

Penjelasan + tidak dikumpulkan:

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

Saya sangat suka bagaimana visualisasi terlihat, jadi saya akan memasukkan satu. Ini hasil dari f(1000).

Lingkaran

Alex A.
sumber
5

Pyth, 22 byte

Port jawaban C ++. Saya punya solusi 23 byte lainnya (Sekarang 22!), Tapi itu hampir merupakan salinan jawaban pyth @ kirbyfan64sos dengan optimisasi, jadi saya harus berpikir sedikit di luar kotak dan secara kreatif (ab) menggunakan operator flip.

m,-Fdsdm,y*.nZOZ.tOZ4Q

Perhatikan bahwa ini tidak berfungsi sekarang karena bug di operator lipat setelah pengenalan reduce2. Saya mengajukan permintaan tarik.

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

Untuk refence ini adalah solusi saya yang lain yang bekerja dengan cara yang sama: VQKy*.nZOZJ.tOZ4,+KJ-KJ

Maltysen
sumber
Anda salah
mengeja
@ kirbyfan64sos derp. Maaf;)
Maltysen
4

IDL, 65 byte

Jelas ini adalah algoritma yang sama dengan @rcrmn, meskipun saya mendapatkannya sendiri sebelum membaca jawaban mereka.

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

Fungsi acak IDL menggunakan Mersenne Twister, yang memiliki periode 2 19937 -1.

EDIT: Saya menjalankan 1000 akor melalui visualizer di atas, berikut adalah screenshot dari hasilnya:

IDL bertrand

Sirpercival
sumber
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<<';';}}

Jadi ini adalah implementasi langsung dari algoritma yang tepat dari halaman wikipedia. Masalah utama di sini dalam bermain golf adalah nama yang sangat panjang yang dimiliki kelas generator acak. Tapi, berbeda dengan rand bagus, setidaknya seragam dengan benar.

Penjelasan:

#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
    }
}
rorlork
sumber
1
Faktor itu M_PI_2terlihat mencurigakan. Saya pikir itu harus 1 sebagai gantinya.
anatolyg
Ya, sepenuhnya benar, akan memperbaikinya sekarang! Terima kasih banyak!
rorlork
4

APL, 46 byte

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

Program APL pertama saya! Tentunya ini bisa sangat ditingkatkan (karena pemahaman saya tentang APL secara keseluruhan masih kurang), jadi setiap saran akan sangat fantastis. Ini menciptakan fungsi fyang mengambil integer sebagai input, menghitung pasangan titik akor menggunakan metode 2, dan mencetak setiap pasangan yang dipisahkan oleh baris baru.

Anda dapat mencobanya secara online !

Penjelasan:

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

Catatan: Solusi 19-byte saya sebelumnya tidak valid karena kembali (x, y) daripada (x + y, xy). Kesedihan berlimpah.

Alex A.
sumber
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));}};

Implementasi dasar di java. Gunakan sebagai ekspresi lambda.

Contoh penggunaan

TheNumberOne
sumber
Tidak bisakah Anda mengurangi ukuran dengan menyimpan di Mathsuatu tempat? Atau sesuatu? (Saya bukan programmer Java)
Ismael Miguel
@IsmaelMiguel Itu akan membutuhkan 2 karakter tambahan.
TheNumberOne
Maaf: / Sangat menggoda untuk mencoba mengurangi jumlah yang Mathditampilkan. Apa yang dikatakan oleh meta tentang menggunakan kode untuk menghasilkan kode lain untuk menyelesaikan masalah?
Ismael Miguel
2
@IsmaelMiguel Itu pertandingan yang adil, meskipun saya akan terkejut jika Anda benar-benar lebih baik dalam metagolf daripada bermain golf.
Martin Ender
3

Ruby, 72 byte

Golf pertamaku di sini! Saya menggunakan kode yang sama dengan semua orang, saya harap tidak apa-apa

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

Jawa, 115 123

Ini pada dasarnya sama dengan kebanyakan orang lain, tetapi saya membutuhkan skor Java untuk lubang ini, jadi begini:

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 chord sampel dapat ditemukan di pastebin , berikut adalah lima pertama dari satu kali run:

8.147304676211474 3.772704020731153
8.201346559916786 3.4066194978900106
4.655131524088468 2.887965593766409
4.710707820868578 3.8493686706403984
3.3839198612642423 1.1604092552846672
Geobit
sumber
1

CJam, 24 22 byte

Mirip dengan algoritma lain, ini adalah versi dalam CJam.

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

Input 1000 menghasilkan distribusi seperti:

masukkan deskripsi gambar di sini

Bagaimana itu bekerja

Algoritma itu sederhana 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

Pembaruan : 2 byte disimpan berkat Martin!

Coba di sini

Pengoptimal
sumber
1

Python 3, 144 117 byte

(terima kasih kepada Blckknght untuk lambdapenunjuknya)

Menggunakan metode yang sama dengan yang lain:

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

Dari dokumentasi Python:

Python menggunakan Mersenne Twister sebagai generator inti. Ini menghasilkan pelampung presisi 53-bit dan memiliki periode 2 19937 -1.

Keluaran

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

Dan seterusnya.

Visualisasi

visualisasi

Zach Gates
sumber
Anda dapat menyimpan sekitar 20 byte jika Anda menggunakan lambda untuk fungsi dan mengembalikan pemahaman daftar (dengan ekspresi generator bagian dalam):f=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]
Blckknght
Ah, awalnya saya punya lambda. Saya kira saya tidak berpikir tentang menggandakan pemahaman daftar. Terima kasih! @ Blckknght
Zach Gates
Dapat disingkat menjadi 109 byte dengan mengutak-atik impor: tio.run/#python2
Triggernometry
1

Perl, 60

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

R, 60 56 53 49 byte

Tambahan 4 byte berkat @JayCe dan mengubahnya menjadi fungsi.

Menggunakan rumus dasar yang sama dengan yang lain. R menggunakan metode Mersenne-Twister secara default, tetapi yang lain dapat diatur. Mengeluarkan daftar yang dipisahkan ruang.

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

Cobalah online!

MickyT
sumber
Halo Micky, Anda dapat menyimpan 4 byte dengan membuatnya berfungsi dan tidak mendefinisikan x.
JayCe
@ JayCe, itu jauh lebih baik, terima kasih
MickyT
0

SmileBASIC, 62 byte

INPUT N
FOR I=1TO N
X=PI()*2*RNDF()Y=ACOS(RNDF())?X+Y,X-Y
NEXT
12Me21
sumber