固定合計の乱数


32

タスクは、間隔[0,1]から乱数を固定合計で出力するプログラムまたは関数を作成することです。 ns

入力

n, n≥1、生成する乱数の数

s, s>=0, s<=n、生成される数値の合計

出力

区間[0,1]のすべての要素とに等しいすべての要素の合計を持つランダムなnタプルの浮動小数点数は、便利な明確な方法で出力されます。すべての有効なタプルは、浮動小数点数の制限内に等しくある必要があります。sn

これは、- n次元の単位立方体とベクトルをn-1通り抜ける(s/n, s/n, …, s/n)- 次元の超平面内の点の交点から均一にサンプリングすることに等しい(1, 1, …, 1)(3つの例については、図1の赤い領域を参照)。

Examples with n=3 and sums 0.75, 1.75 and 2.75

図1:n = 3、合計0.75、1.75および2.75の有効な出力のプレーン

n=1, s=0.8 → [0.8]
n=3, s=3.0 → [1.0, 1.0, 1.0]
n=2, s=0.0 → [0.0, 0.0]
n=4, s=2.0 → [0.2509075946818119, 0.14887693388076845, 0.9449661625992032, 0.6552493088382167]
n=10, s=9.999999999999 → [0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999,0.9999999999999]

ルール

  • プログラムは、少なくともn≤10有効なs でマシン上で1秒未満で終了するはずです。
  • 必要に応じて、プログラムを上限で排他的にすることができます。つまりs<n、半開区間[0,1)からの出力番号(2番目の例を破ります)
  • ご使用の言語が浮動小数点数をサポートしていない場合、小数点の後に少なくとも10桁の10進数で出力を偽造できます。
  • 標準の抜け穴は許可されず、標準の入出力メソッドが許可されます。
  • これはであるため、バイト単位で測定された最短のエントリが優先されます。


あなたが言うときThis is equal to uniformly sampling from the intersection-私はその交差点のコーナーからランダムに選択するプログラムを見ることができます。それは有効でしょうか?
JayCe

2
@KevinCruijssenいいえ、それはにのみ当てはまりますs==0 or s==3。の他のすべての値のs場合、平面の面積は非ゼロであり、その平面上の点を一様ランダムに選択する必要があります。
user202729

3
間隔をクローズまたはハーフクローズ(オープンではなく)にすることは、理論的には観察できない要件です。多くの乱数ジェネレーターは、(0,1)の出力を提供します。出力間隔が[0,1)であり、(0,1)ではないことをテストするにはどうすればよいですか?とにかく「0」という値は発生しません
ルイスメンドー

2
コードでリジェクションサンプリングを使用しているため、次のようなテストケースの場合は非常に時間がかかりs=2.99999999999, n=3ますか?たとえば、倍数のランダムな実数を生成でき1e-9ますか?
xnor

回答:


1

Wolfram言語(Mathematica)92 90バイト

If[2#2>#,1-#0[#,#-#2],While[Max[v=Differences@Sort@Join[{0,#2},RandomReal[#2,#-1]]]>1];v]&

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

ゴルフされていないコード:

R[n_, s_] := Module[{v},
  If[s <= n/2,             (* rejection sampling for s <= n/2:                        *)
    While[
      v = Differences[Sort[
            Join[{0},RandomReal[s,n-1],{s}]]];         (* trial randoms that sum to s *)
      Max[v] > 1           (* loop until good solution found                          *)
    ];
    v,                     (* return the good solution                                *)
    1 - R[n, n - s]]]      (* for s > n/2, invert the cube and rejection-sample       *)

55バイトで動作するソリューションを次に示しますが、現在(Mathematicaバージョン12)は高次元の超平面からポイントを描画するn=1,2,3ことをRandomPoint拒否しているため制限されています(TIOバージョン11.3では失敗しますn=1)。nしかし、将来的にはよりうまくいくかもしれません:

RandomPoint[1&~Array~#~Hyperplane~#2,1,{0,1}&~Array~#]&

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

ゴルフされていないコード:

R[n_, s_] :=
  RandomPoint[                           (* draw a random point from *)
    Hyperplane[ConstantArray[1, n], s],  (* the hyperplane where the sum of coordinates is s *)
    1,                                   (* draw only one point *)
    ConstantArray[{0, 1}, n]]            (* restrict each coordinate to [0,1] *)


6

Python 2144の 128 119バイト

from random import*
def f(n,s):
 r=min(s,1);x=uniform(max(0,r-(r-s/n)*2),r);return n<2and[s]or sample([x]+f(n-1,s-x),n)

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


  • -20バイト、Kevin Cruijssenのおかげ

@LuisMendoは今修正されるべきです
TFeld

彼らはまだ均一ではないようです
l4m2

@ l4m2 g(4, 2.0)1,000回実行して4,000ポイントを取得しましたが、結果はこのように見え、かなり均一に見えます。
エンジニアトースト



4

ジャワ8、194の 188 196 237 236バイト

n->s->{double r[]=new double[n+1],d[]=new double[n],t;int f=0,i=n,x=2*s>n?1:0;for(r[n]=s=x>0?n-s:s;f<1;){for(f=1;i-->1;)r[i]=Math.random()*s;for(java.util.Arrays.sort(r);i<n;d[i++]=x>0?1-t:t)f=(t=Math.abs(r[i]-r[i+1]))>1?0:f;}return d;}

+49バイト(188→196および196→237)。1に近いテストケースの速度を修正し、一般的なアルゴリズムを修正します。

オンラインで試す

説明:

内アプローチを使用し、このstackoverflowの回答項目のいずれかが1より依然として大きい限り、ループの内部に、
あれば、また2*s>nsに変更されn-s、フラグは、我々が使用する必要が示すように設定されている1-diffのではなく、diffその結果、アレイに(ヒント@soktinpk@ l4m2に感謝します)。

n->s->{              // Method with integer & double parameters and Object return-type
  double r[]=new double[n+1]
                     //  Double-array of random values of size `n+1`
         d[]=new double[n],
                     //  Resulting double-array of size `n`
         t;          //  Temp double
  int f=0,           //  Integer-flag (every item below 1), starting at 0
      i=n,           //  Index-integer, starting at `n`
      x=             //  Integer-flag (average below 0.5), starting at:
        2*s>n?       //   If two times `s` is larger than `n`:
         1           //    Set this flag to 1
        :            //   Else:
         0;          //    Set this flag to 0
  for(r[n]=s=        //  Set both the last item of `r` and `s` to:
       x>0?          //   If the flag `x` is 1:
        n-s          //    Set both to `n-s`
       :             //   Else:
        s;           //    Set both to `s`
      f<1;){         //  Loop as long as flag `f` is still 0
    for(f=1;         //   Reset the flag `f` to 1
        i-->1;)      //   Inner loop `i` in range (n,1] (skipping the first item)
      r[i]=Math.random()*s;
                     //    Set the i'th item in `r` to a random value in the range [0,s)
    for(java.util.Arrays.sort(r);
                     //   Sort the array `r` from lowest to highest
        i<n;         //   Inner loop `i` in the range [1,n)
        ;d[i++]=     //     After every iteration: Set the i'th item in `d` to:
          x>0?       //      If the flag `x` is 1:
           1-t       //       Set it to `1-t`
          :          //      Else:
           t)        //       Set it to `t`
      f=(t=Math.abs( //    Set `t` to the absolute difference of:
            r[i]-r[i+1])) 
                     //     The i'th & (i+1)'th items in `r`
        >1?          //    And if `t` is larger than 1 (out of the [0,1] boundary)
         0           //     Set the flag `f` to 0
        :            //    Else:
         f;}         //     Leave the flag `f` unchanged
  return d;}         //  Return the array `d` as result

タイムアウトtest(10, 9.99);
l4m2

@ l4m2ええ、テストケース10, 9.0を修正するために編集した直後に同じことに気づきn=10, s=9.999999999999ました。Javaに一様にランダムさを保持したままJavaに修正があるかどうかはわかりません。しばらく考える必要があります。今のところ、タイムアウトするように編集します。
ケビンクルーッセン

l4m2のように、すべての番号をn-s<1呼び出しf(n,n-s)て裏返す1/2(つまり、に置き換える)ことができる場合。これにより、に近い数値の問題を解決できる場合があります。x1-xsn
soktinpk

@soktinpk先端をありがとう。実際s+s>nにはの代わりになりn-s<1ますが、他のJavaScriptの回答を見たとき、それは本当に理にかなっています。まだ存在していた別のバグを含め、すべてが修正されました。バイトはかなり増えましたが、現在はすべて動作します。ここからバイトカウントを下げて動作します。:)
ケビンクルーッセン

一般的な証明はわかりませんが、N次元のハイパーキューブをN個のN次元のハイパーピラミッドにカットできるため、このアルゴリズムが機能すると考えています。
ニール


3

C ++ 11、284 267バイト

Zacharýが-17バイトを
使用C ++ランダムライブラリを使用し、標準出力に出力

#include<iostream>
#include<random>
typedef float z;template<int N>void g(z s){z a[N],d=s/N;int i=N;for(;i;)a[--i]=d;std::uniform_real_distribution<z>u(.0,d<.5?d:1-d);std::default_random_engine e;for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}for(;i;)std::cout<<a[--i]<<' ';}

呼び出すには、それを行う必要があります:

g<2>(0.0);

ここで、テンプレートパラメータ(ここでは2)はNであり、実際のパラメータ(ここでは0.0)はSです


私はあなたが間にスペースを削除することができると思う<z>u
ザカリー

私はさらにそれを取り下げました:typedef float z;template<int N>void g(z s){z a[N],d=s/N;int i=N;for(;i;)a[--i]=d;std::uniform_real_distribution<z>u(.0,d<.5?d:1-d);std::default_random_engine e;for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}for(;i;)std::cout<<a[--i]<<' ';}。改行はアイテム間の区切り文字である必要はありません
ザカリー

1
(プログラムが最初の数を正しく計算するために追加されることに注意してください)の代わりに2番目のループのリワークを提案するようにd変更するd=s/Nことにより、完全に取り除くことをおs/=Nfor(z c;i<N;a[++i%N]-=c)a[i]+=c=u(e);for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}%N
勧めします

2

クリーン221 201バイト

クリーン、コードゴルフ、または乱数。2つ選択します。

import StdEnv,Math.Random,System._Unsafe,System.Time
g l n s#k=toReal n
|s/k>0.5=[s/k-e\\e<-g l n(k-s)]
#r=take n l
#r=[e*s/sum r\\e<-r]
|all((>)1.0)r=r=g(tl l)n s

g(genRandReal(toInt(accUnsafe time)))

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

部分関数リテラル:: (Int Real -> [Real])。1秒に1回だけ新しい結果が生成されます。
小数点以下10桁まで正確。


2

R、99バイト(gtoolsパッケージ付き)

f=function(n,s){if(s>n/2)return(1-f(n,n-s))
while(any(T>=1)){T=gtools::rdirichlet(1,rep(1,n))*s}
T}

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

セットから均一にサンプリングしたい A={w1wn0<w<1;w=s}。私はすべてを分割しますw によって s 代わりにサンプル A={w1wn0<w<1s;w=1}

もし s=1、これは簡単です。これは、 Drchlet111 分布(シンプレックス全体で均一)。一般的な場合s1、棄却サンプリングを使用します。すべてのエントリが <1/s、次に乗算する s

ミラーリングするトリック s>n/2l4m2が最初考え出されたと思います)は不可欠です。それを見る前に、最後のテストケースで棄却サンプラーの反復回数が爆発したため、適切に選択された切り捨てられたベータ分布から効率的にサンプリングしようとして多くの時間を費やしました、最終的には必要ありません。


2

C、132 127 125 118 110 107バイト

@ceilingcatのおかげで-2バイト

i;f(s,n,o,d)float*o,s,d;{for(i=n;i;o[--i]=d=s/n);for(;i<n;o[++i%n]-=s)o[i]+=s=(d<.5?d:1-d)*rand()/(1<<31);}

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


残念ながら、この答えはチャレンジ仕様を満たしていません。出力乱数はに制限されず[0,1]、それらの結合分布は均一ではありません。
ニトロドン

@Nitrodonちょっと、出力が[0,1]に制限されていない入力を提供してください。いくつかの異なる例を試しましたが、目的を誤解しない限り、それらはすべて正しいように見えました。
オーバークロック

TIOのRNG状態で、を維持しn=4、値s=3.23s=0.89範囲外の出力を提供します。要するに、の分布はにX-s/n依存する必要がありますがs、依存しません。
ニトロドン

@Nitrodonおっと、私の悪い。上記のC ++の回答の一部を変換していて、何かを追加するのを忘れていました。今すぐ修正する必要がありますか?その過程で数バイトもゴルフをしました。
OverclockedSanic

1

ハスケル 122の 217 208バイト

import System.Random
r p=randomR p
(n#s)g|n<1=[]|(x,q)<-r(max 0$s-n+1,min 1 s)g=x:((n-1)#(s-x)$q)
g![]=[]
g!a|(i,q)<-r(0,length a-1)g=a!!i:q![x|(j,x)<-zip[0..]a,i/=j]
n%s=uncurry(!).(n#s<$>).split<$>newStdGen

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

時々、浮動小数点エラーのために、答えが少しずれていることがあります。それが問題であれば、1バイトのコストで修正できます。また、これがどの程度均一であるのかもわかりません(かなり良いとは思いますが、この種のことはそれほど得意ではありません)。そのため、アルゴリズムについて説明します。

基本的な考え方は、数値を生成しxてから要素sを減算し、n要素ができるまで繰り返すことです。x1またはs(どちらか小さい方)の上限と、s-n+1または0(大きい方)の下限で生成します。その下限が存在するので、次の反復でsまだn(派生:s-x<=n-1-> s<=n-1+x-> s-(n-1)<=x-> s-n+1<=x)以下になります。

編集:私の均一性の欠陥を指摘してくれた@ michi7x7に感謝します。シャッフルで修正したと思いますが、別の問題がある場合はお知らせください

EDIT2:改善されたバイト数と固定型制限


3
均一なサンプルの連鎖は、(最後の座標あなたの例では、ほとんどの場合、150よりも大きい)均一な分布につながることはありません
michi7x7

@ michi7x7あなたのポイントがわかります。リストの生成後にリストの順序を入れ替えたらどうなりますか?私はより多くの統計情報クラスを取ったはずです
user1472751

数字はあまり均一に見えません。ここでは、8個の結果が> 0.99、1個が0.96、最後が0.8です。これはどのように見えるかです。
スチューウィーグリフィン

@ user1472751ここにいくつかの良い答えがあります:stackoverflow.com/q/8064629/6774250
michi7x7

1
均一性にはまだ問題があります。ここを参照してください -生成されるゼロが多すぎます(1000%500からソートされた値のプロット)
Angs

1

Haskell、188バイト

import System.Random
import Data.List
n!s|s>n/2=map(1-)<$>n!(n-s)|1>0=(zipWith(-)=<<tail).sort.map(*s).(++[0,1::Double])<$>mapM(\a->randomIO)[2..n]>>= \a->if all(<=1)a then pure a else n!s

ゴルフをしていない:

n!s
 |s>n/2       = map (1-) <$> n!(n-s)       --If total more than half the # of numbers, mirror calculation 
 |otherwise   = (zipWith(-)=<<tail)        --Calculate interval lengths between consecutive numbers
              . sort                       --Sort
              . map(*s)                    --Scale
              . (++[0,1::Double])          --Add endpoints
              <$> mapM(\a->randomIO)[2..n] --Calculate n-1 random numbers
              >>= \a->if all(<=1)a then pure a else n!s   --Retry if a number was too large

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.