Angka acak dengan jumlah tetap

32

Tugas Anda adalah menulis program atau fungsi yang menghasilkan n angka acak dari interval [0,1] dengan jumlah tetap s.

Memasukkan

n, n≥1, jumlah angka acak untuk dihasilkan

s, s>=0, s<=n, jumlah angka yang akan dihasilkan

Keluaran

nDouble- acak angka floating point dengan semua elemen dari interval [0,1] dan jumlah semua elemen sama dengan s, output dengan cara yang jelas dan nyaman. Semua n-tuple yang valid harus memiliki kemungkinan yang sama dalam batasan angka floating point.

Ini sama dengan pengambilan sampel secara seragam dari persimpangan titik-titik di dalam nkubus unit -dimensi dan n-1hyperplane -dimensi yang melewati (s/n, s/n, …, s/n)dan tegak lurus terhadap vektor (1, 1, …, 1)(lihat area merah pada Gambar 1 untuk tiga contoh).

Contoh dengan n = 3 dan jumlah 0,75, 1,75 dan 2,75

Gambar 1: Bidang output yang valid dengan n = 3 dan jumlah 0,75, 1,75 dan 2,75

Contohnya

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]

Aturan

  • Program Anda harus selesai di bawah satu detik pada mesin Anda setidaknya dengan n≤10dan s yang valid.
  • Jika diinginkan, program Anda dapat eksklusif di ujung atas, yaitu s<ndan nomor output dari interval setengah terbuka [0,1) (melanggar contoh kedua)
  • Jika bahasa Anda tidak mendukung angka floating point, Anda dapat memalsukan output dengan setidaknya sepuluh digit desimal setelah titik desimal.
  • Celah standar tidak diizinkan dan metode input / output standar diizinkan.
  • Ini adalah , sehingga entri terpendek, diukur dalam byte, menang.
Angs
sumber
Terkait
Martin Ender
Ketika Anda mengatakan This is equal to uniformly sampling from the intersection- saya dapat melihat program memilih secara acak hanya dari sudut persimpangan itu. Apakah itu valid?
JayCe
2
@KevinCruijssen Tidak, itu hanya berlaku untuk s==0 or s==3. Untuk semua nilai lainnya s, bidang memiliki bidang nol dan Anda harus memilih titik pada bidang tersebut secara acak.
user202729
3
Membutuhkan interval untuk ditutup atau setengah tertutup (sebagai lawan membuka) adalah persyaratan yang secara teoritis tidak dapat diobservasi. Banyak generator bilangan acak memberikan output dalam (0,1). Bagaimana cara menguji bahwa interval output adalah [0,1) dan tidak (0,1)? Nilai 0 "tidak pernah" tetap terjadi
Luis Mendo
2
Apakah boleh jika kode kami menggunakan sampel penolakan, dan karenanya sangat lama untuk kasus uji seperti s=2.99999999999, n=3? Bisakah kita menghasilkan real acak dalam kelipatan, katakanlah 1e-9,?
xnor

Jawaban:

1

Bahasa Wolfram (Mathematica) , 92 90 byte

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

Cobalah online!

Hapus kode golf:

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

Berikut adalah solusi yang bekerja dalam 55 byte tetapi untuk saat ini (Mathematica versi 12) dibatasi n=1,2,3karena RandomPointmenolak untuk menarik poin dari hyperplanes dimensi yang lebih tinggi (dalam versi TIO 11.3 juga gagal untuk n=1). Ini mungkin bekerja untuk yang lebih tinggi ndi masa depan:

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

Cobalah online!

Hapus kode golf:

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] *)
Roma
sumber
7

JavaScript (Node.js) , 122 115 byte

N=>W=S=>2*S>N?W(N-S).map(t=>1-t):(t=(Q=s=>n?[r=s-s*Math.random()**(1/--n),...r>1?[++Q]:Q(s-r)]:[])(S,n=N),Q?t:W(S))

Cobalah online!

l4m2
sumber
6

Python 2 , 144 128 119 byte

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)

Cobalah online!


  • -20 byte, terima kasih kepada Kevin Cruijssen
TFeld
sumber
@LuisMendo Harus diperbaiki sekarang
TFeld
mereka masih tampak tidak seragam
l4m2
@ l4m2 Saya berlari g(4, 2.0)1.000 kali untuk mendapatkan 4.000 poin dan hasilnya terlihat seperti ini yang tampak seragam.
Engineer Toast
122 byte?
Giuseppe
2
Masih salah
l4m2
4

Java 8, 194 188 196 237 236 byte

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 byte (188 → 196 dan 196 → 237) untuk memperbaiki kecepatan kasus uji mendekati 1, serta memperbaiki algoritme secara umum.

Cobalah online

Penjelasan:

Menggunakan pendekatan dalam jawaban StackoverFlow ini , di dalam lingkaran selama salah satu item masih lebih besar dari 1.
Selain itu, jika 2*s>n, sakan diubah menjadi n-s, dan sebuah bendera ditetapkan untuk menunjukkan bahwa kita harus menggunakan 1-diffalih-alih diffdalam array hasil (terima kasih atas tip @soktinpk dan @ 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
Kevin Cruijssen
sumber
Waktu untuktest(10, 9.99);
l4m2
@ l4m2 Ya, perhatikan hal yang sama dengan 10, 9.0setelah saya diedit untuk memperbaiki n=10, s=9.999999999999test case .. Tidak yakin apakah ada perbaikan di Java sementara masih menggunakan keacakan yang seragam .. Harus memikirkannya sebentar. Untuk sekarang saya akan mengeditnya untuk menyatakan waktu habis.
Kevin Cruijssen
Jika n-s<1Anda dapat menelepon f(n,n-s)dan membalikkan setiap nomor 1/2(mis. Ganti xdengan 1-x) seperti yang dilakukan l4m2. Ini mungkin memecahkan masalah untuk nomor syang dekat n.
soktinpk
@ Soktinpk Terima kasih atas tipnya. Sebenarnya s+s>nbukan n-s<1, tetapi ketika saya melihat jawaban JavaScript yang lain itu memang masuk akal. Semuanya sudah diperbaiki sekarang, termasuk bug lain yang masih ada. Bytes naik sedikit, tetapi setiap berfungsi sekarang. Akan bekerja menghitung mundur byte dari sini. :)
Kevin Cruijssen
Saya tidak tahu bukti umum tetapi saya percaya algoritma ini berfungsi karena hypercube N-dimensional dapat dipotong menjadi hyperpyramids N-dimensional.
Neil
3

JavaScript (Node.js) , 153 byte

(n,s)=>s+s>n?g(n,n-s).map(r=>1-r):g(n,s)
g=(n,s)=>{do(a=[...Array(n-1)].map(_=>Math.random()*(s<1?s:1))).map(r=>t-=r,t=s);while(t<0|t>=1);return[...a,t]}

Cobalah online!

Neil
sumber
3

C ++ 11, 284 267 byte

-17 byte berkat Zacharý
Menggunakan perpustakaan acak C ++, keluaran pada keluaran standar

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

Untuk menelepon, Anda hanya perlu melakukan itu:

g<2>(0.0);

Di mana parameter templat (di sini, 2) adalah N, dan parameter aktual (di sini, 0,0) adalah S

HatsuPointerKun
sumber
Saya pikir Anda dapat menghapus ruang antara <z>danu
Zacharý
Aku mendapatkannya turun lebih lanjut: 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]<<' ';}.
Baris
1
Sarankan singkirkan dsepenuhnya dengan mengubah d=s/Nke s/=NSarankan pengerjaan ulang lingkaran kedua for(z c;i<N;a[++i%N]-=c)a[i]+=c=u(e);alih-alih for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}(perhatikan yang ditambahkan %Nuntuk membuat program menghitung angka pertama dengan benar)
Max Yekhlakov
2

Bersih , 221 201 byte

Membersihkan, kode-golf, atau angka acak. Ambil dua.

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

Cobalah online!

Fungsi parsial literal :: (Int Real -> [Real]). Hanya akan menghasilkan hasil baru sekali per detik.
Akurat hingga setidaknya 10 desimal.

Suram
sumber
2

R , 99 byte (dengan gtoolspaket)

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}

Cobalah online!

SEBUAH~={w1,...,wn:saya,0<wsaya<1;wsaya=s}wsayasSEBUAH={w1,...,wn:saya,0<wsaya<1s;wsaya=1} .

Jika s=1Dsayarsayachlet(1,1,...,1) s1<1/ss .

s>n/2

Robin Ryder
sumber
2

C, 132 127 125 118 110 107 byte

-2 byte terima kasih kepada @ceilingcat

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

Cobalah online!

OverclockedSanic
sumber
Sayangnya, jawaban ini tidak memenuhi spesifikasi tantangan. Angka acak output tidak terbatas [0,1], dan distribusi bersama mereka tidak seragam.
Nitrodon
@Nitrodon hei, bisakah Anda memberikan input yang hasilnya tidak terbatas pada [0,1]? Saya mencoba beberapa contoh berbeda dan semuanya tampak benar, kecuali saya salah memahami tujuannya.
OverclockedSanic
Dengan status RNG pada TIO, dan pertahankan n=4nilai-nilai Anda s=3.23dan s=0.89berikan output di luar kisaran. Lebih tepatnya, distribusi X-s/nharus bergantung pada s, tetapi tidak.
Nitrodon
@Nitrodon Ups, salahku. Saya mengubah beberapa bagian dari jawaban C ++ di atas dan lupa menambahkan sesuatu. Seharusnya sudah diperbaiki sekarang? Juga golf beberapa byte dalam proses.
OverclockedSanic
1

Haskell , 122 217 208 byte

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

Cobalah online!

Kadang-kadang jawabannya sedikit tidak baik, saya berasumsi, karena kesalahan floating point. Jika masalah, saya bisa memperbaikinya dengan biaya 1 byte. Saya juga tidak yakin seberapa seragam ini (cukup yakin itu baik-baik saja tapi saya tidak begitu pandai dalam hal semacam ini), jadi saya akan menjelaskan algoritma saya.

Ide dasarnya adalah untuk menghasilkan angka xkemudian mengurangi sdan mengulanginya sampai kita memiliki nelemen kemudian mengocoknya. Saya menghasilkan xdengan batas atas baik 1 atau s(mana yang lebih kecil) dan batas bawah s-n+1atau 0 (mana yang lebih besar). Batas bawah itu ada sehingga pada iterasi berikutnya smasih akan kurang dari atau sama dengan n(derivasi: s-x<=n-1-> s<=n-1+x-> s-(n-1)<=x-> s-n+1<=x).

EDIT: Terima kasih kepada @ michi7x7 karena telah menunjukkan kekurangan pada keseragaman saya. Saya pikir saya sudah memperbaikinya dengan mengocok tetapi beri tahu saya jika ada masalah lain

EDIT2: Peningkatan jumlah byte ditambah pembatasan tipe tetap

pengguna1472751
sumber
3
Pengambilan sampel yang seragam tidak akan menghasilkan distribusi yang seragam (koordinat terakhir hampir selalu lebih besar dari 0,99 pada contoh Anda)
michi7x7
@ michi7x7 Saya mengerti maksud Anda. Bagaimana jika saya mengocok urutan daftar setelah menghasilkannya? Saya seharusnya mengambil lebih banyak kelas statistik
user1472751
Angka-angka tidak terlihat sangat seragam. Di sini , 8 hasil adalah> 0,99, 1 adalah 0,96, dan yang terakhir adalah 0,8. Ini apa.
Stewie Griffin
@ user1472751 ada beberapa jawaban bagus di sini: stackoverflow.com/q/8064629/6774250
michi7x7
1
Keseragaman masih memiliki beberapa masalah, lihat di sini - ada terlalu banyak nol yang dihasilkan (sebidang nilai yang diurutkan dari 1000% 500)
Angs
1

Haskell , 188 byte

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

Tidak Disatukan:

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

Cobalah online!

Angs
sumber