デイ#4のランダムゴルフ:バートランドパラドックス


19

シリーズについて

まず、これを他のコードゴルフチャレンジと同様に扱い、シリーズについてまったく心配することなく答えることができます。ただし、すべての課題にリーダーボードがあります。最初の投稿では、リーダーボードとシリーズに関する詳細情報を見つけることができます。

このシリーズにはたくさんのアイデアが並んでいますが、将来の課題はまだはっきりしていません。何か提案があれば、関連するサンドボックスの投稿でお知らせください。

穴4:バートランドパラドックス

バートランドパラドックスが示す円内にランダムコードを選ぶための方法の異なる方法が和音、その中点とその長さの異なる分布をもたらすことができる興味深い問題です。

この課題では、「正しい」方法、つまりスケーリングと変換の下で不変なコードの分布を生成する方法を使用して、単位円のランダムなコードを生成することになっています。リンクされたウィキペディアの記事では、「方法2」はそのような方法です。

正確なルールは次のとおりです。

  • 返される和音のNを指定する1つの正の整数を取る必要があります。出力はN、ラジアン単位の極角で指定された単位円上の2点としてそれぞれ指定されたコードのリストである必要があります。
  • コードは、2つの角度のそれぞれに対して少なくとも2 20の異なる値を返すことができるはずです。使用可能なRNGの範囲が狭い場合、最初に組み込みのRNGの上に十分に大きい範囲のRNGを構築するか、独自の適切なRNGを実装する必要があります。このページはそのために役立ちます。
  • 和音の分布は、リンクされたウィキペディアの記事の「方法2」で作成されたものと見分けがつかない必要があります。別のアルゴリズムを実装してコードを選択する場合は、正確性の証明を含めてください。どのアルゴリズムを実装する場合でも、理論的には単位円で有効なコードを生成できる必要があります(基礎となるPRNGの制限または精度の制限されたデータ型を除く)。
  • 実装では、浮動小数点数(少なくとも32ビット幅)または固定小数点数(少なくとも24ビット幅)を使用して返す必要があり、すべての算術演算は16 ulp以内で正確でなければなりません。

完全なプログラムまたは関数を作成し、STDIN(または最も近い代替)、コマンドライン引数または関数引数を介して入力を取得し、STDOUT(または最も近い代替)、関数の戻り値または関数(出力)パラメーターを介して出力を生成できます。

出力は、個々の数字が明確に区別可能であり、それらの合計数が常に偶数である限り、任意の便利なリスト形式または文字列形式にすることができます。

これはコードゴルフであるため、最短の提出(バイト単位)が優先されます。そしてもちろん、ユーザーごとの最短投稿もシリーズの総合リーダーボードに入ります。

可視化

次のスニペットを使用して、生成された行をレンダリングし、それらの分布を検査できます。角度のペアのリストをテキスト領域に貼り付けるだけです。スニペットは、数値が単純な10進数(科学表記法ではない)である限り、ほぼすべてのリスト形式を処理できる必要があります。分布をよく理解するには、少なくとも1000行を使用することをお勧めします。また、以下の記事に記載されているさまざまな方法のサンプルデータも提供しました。

方法1で生成されたデータの例。

方法2で生成されたデータの例

方法3で生成されたデータの例。

リーダーボード

シリーズの最初の投稿はリーダーボードを生成します。

回答が表示されるようにするには、次のマークダウンテンプレートを使用して、すべての回答を見出しで開始してください。

# Language Name, N bytes

N提出物のサイズはどこですか。スコアを改善する場合、古いスコアを打つことで見出しに残すことができます。例えば:

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

(言語は現在表示されていませんが、スニペットはそれを必要とし、解析します。将来、言語ごとのリーダーボードを追加するかもしれません。)

回答:


12

IA-32マシンコード、54バイト

コードのHexdump:

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

ウィキペディアが説明した(わずかに変更された)アルゴリズムを使用します。擬似コード内:

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

私は範囲を使用する-1...1ことは、この範囲の乱数を作るのは簡単だから:rdrand命令は、間の整数を生成-2^31し、2^31-1簡単に2 ^ 31で分割することができ、。

0...1入力される他の乱数(x)の範囲を使用する必要がありましたacos。ただし、負の部分は正の部分と対称です。負の数は、ラジアンより大きいスパンの和音を生成しますが、バートランドのパラドックスを説明するためには重要ではありません。

80386(またはx87)命令セットには専用のacos命令がないため、命令のみを使用して計算を表現する必要がありましたatan

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

上記のマシンコードを生成するソースコードは次のとおりです。

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

2つの乱数を生成するために、コードはネストされたループを使用します。ループを整理するために、コードはecx正のレジスタ(外部ループカウンター)を利用します。一時的にを無効ecxにしてから、再度実行して元の値を復元します。jsとき命令は、ループを繰り返しecx(これは正確に一度起こる)負です。


IA32アセンブリを使用するためのこの回答が好きです!ちょうど言うと:これは厳密には386マシンコードではありません。80386には明らかにrdrand命令もFPコプロセッサーも必要ないため
です-user5572685

ええ、IA32のほうがいい名前です。十分になく、80386よりもおそらくより正確な具体的ではない
anatolyg

7

Pyth、25 23 22バイト

rcrmnのC ++ 11回答の移植版。これがPythの最初の使用法であり、私はたくさんの楽しみを持っています!

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

23バイトバージョン:

VQJ*y.n0O0K.tOZ4+JK-JKd

folds + sumsを使用するようにプログラムを変更し、Kを削除してJをタプルに設定することにより、バイトをカットします。

元の:

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

@orlpのおかげで2バイトを切り捨てます。

説明:

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
Pythのヒント:はと*2_同じy_です。変数Zは最初は0であるため、.tO0 4を書き込むことでスペースを削除できます.tOZ4
orlp

1
私は25バイトをカウントしています
...-Maltysen

また、あなたがより良い出力をフォーマットすることができます,+JK-JK
Maltysen

@Maltysen両方修正されました。ありがとう!
kirbyfan64sos

@orlp私は何も手がかりがなくy、忘れていましたZ。一定; ありがとう!
kirbyfan64sos

6

ジュリア、48バイト

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

これは、これまでのほとんどの答えのように、方法2アルゴリズムを使用します。整数入力を受け取り、nx 2配列を返すラムダ関数を作成します。呼び出すには、名前を付けf=n->...ます。

Ungolfed +説明:

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

ビジュアライゼーションの外観がとても気に入っているので、これを含めます。の結果ですf(1000)

サークル


5

Pyth、22バイト

C ++回答の移植版。私は別の23バイトのソリューション(今22!)を持っていましたが、それはほとんど最適化された@ kirbyfan64sosのpyth answerのコピーでしたので、私はボックスの外側を少し考えて、折り畳み演算子を(ab)使用しなければなりませんでした。

m,-Fdsdm,y*.nZOZ.tOZ4Q

これは、の導入後のfold演算子のバグのため、現在は機能しないことに注意してくださいreduce2。プルリクエストを送信しています。

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

参照のために、これは同じように機能する他のソリューションでした: VQKy*.nZOZJ.tOZ4,+KJ-KJ


あなたは私のユーザー名のスペルを
間違えました

@ kirbyfan64sos derp。ごめんなさい;)
マルティセン

4

IDL、65バイト

明らかに、これは@rcrmnと同じアルゴリズムですが、答えを読む前に独自に導出しました。

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

IDLのrandomu関数は、2 19937 -1の周期を持つMersenne Twisterを使用します。

編集:上記のビジュアライザーで1000のコードを実行しました。結果のスクリーンショットは次のとおりです。

IDLベルトラン


4

C ++ 11、214バイト

#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<<';';}}

したがって、これはウィキペディアのページからの正しいアルゴリズムのまっすぐな実装です。ここでのゴルフの主な問題は、ランダムジェネレータークラスが持つ非常に長い名前です。しかし、古き良きとは対照的に、それは少なくとも適切に均一です。

説明:

#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
その要因M_PI_2は疑わしいようです。代わりに1にすべきだと思います。
アナトリグ

はい、完全に正しい、今すぐ修正します!どうもありがとう!
-rorlork

4

APL、46バイト

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

初めてのAPLプログラム!(APLについての私の全体的な理解が不足しているので)確実に大幅に改善できるので、提案は素晴らしいでしょう。これfは、整数を入力として受け取り、方法2を使用してコードポイントのペアを計算し、改行で区切られた各ペアを出力する関数を作成します。

オンラインで試すことができます

説明:

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

注:以前の19バイトのソリューションは、(x + y、xy)ではなく(x、y)を返したため無効でした。悲しみはたくさんあります。


3

Java、114バイト

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

Javaでの基本的な実装。ラムダ式として使用します。

使用例


Mathどこかに保管してサイズを小さくすることはできませんか?か何か?(私はJavaプログラマーではありません)
イスマエルミゲル

@IsmaelMiguelこれは、余分な2文字がかかります。
TheNumberOne

申し訳ありません:/表示される回数を減らすことは魅力的Mathです。メタは、コードを使用して別のコードを生成して問題を解決することについて何と言っていますか?
イスマエルミゲル

2
@IsmaelMiguelそれは公正なゲームですが、実際にゴルフよりもメタゴルフが得意であれば驚くでしょう。
マーティンエンダー

3

ルビー、72バイト

ここで私の最初のゴルフ!私は皆と同じコードを使用しました、それでいいと思います

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

2

Java、115 123

これは基本的に他のほとんどと同じですが、このホールにはJavaスコアが必要なので、ここで説明します。

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のサンプルコードはで見つけることができるペーストビン、ここでは一度の実行から最初の5つは、以下のとおりです。

8.147304676211474 3.772704020731153
8.201346559916786 3.4066194978900106
4.655131524088468 2.887965593766409
4.710707820868578 3.8493686706403984
3.3839198612642423 1.1604092552846672

1

CJam、24 22バイト

他のアルゴリズムと同様に、ここにCJamのバージョンがあります。

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

1000を入力すると、次のような分布が生成されます。

ここに画像の説明を入力してください

使い方

アルゴリズムは単純です 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

更新:Martinのおかげで2バイト節約!

ここで試してみてください


1

Python 3、144 117バイト

lambdaポインターについてBlckknghtに感謝)

他と同じ方法を使用する:

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

Pythonドキュメントから:

Pythonは、Mersenne Twisterをコアジェネレーターとして使用します。53ビット精度の浮動小数点数を生成し、周期は2 19937です。 -1です。

出力

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

等々。

可視化

視覚化


関数にラムダを使用し、リスト内包表記(内部ジェネレーター式)を返す場合、約20バイトを節約できますf=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]
。– Blckknght

ああ、私は元々ラムダを持っていました。リストの内包表記を2倍にすることは考えなかったと思います。ありがとう!@Blckknght
ザック・ゲイツ

インポートをいじることで109バイトに短縮できます: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バイト

@JayCeと、それを関数に変更したおかげで、さらに4バイト増えました。

他と同じ基本式を使用します。RはデフォルトでMersenne-Twisterメソッドを使用しますが、他のメソッドも設定できます。スペース区切りのリストを出力します。

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

オンラインでお試しください!


こんにちは、ミッキー、関数にしてxを定義しないことで4バイト節約できます
JayCe

多くの方のおかげだ@JayCe
MickyT

0

SmileBASIC、62バイト

INPUT N
FOR I=1TO N
X=PI()*2*RNDF()Y=ACOS(RNDF())?X+Y,X-Y
NEXT
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.