Số ngẫu nhiên có tổng cố định


32

Nhiệm vụ của bạn là viết một chương trình hoặc một chức năngkết quả đầu ra n số ngẫu nhiên từ khoảng [0,1] với số tiền cố định s.

Đầu vào

n, n≥1, số lượng số ngẫu nhiên để tạo

s, s>=0, s<=n, tổng số được tạo

Đầu ra

Một tập hợp ngẫu nhiên ncác số dấu phẩy động với tất cả các phần tử từ khoảng [0,1] và tổng của tất cả các phần tử bằng s, xuất ra theo bất kỳ cách rõ ràng thuận tiện nào. Tất cả các dữ liệu hợp lệ nphải có khả năng như nhau trong giới hạn của số dấu phẩy động.

Điều này tương đương với việc lấy mẫu thống nhất từ ​​giao điểm của các điểm bên trong nkhối đơn vị hai chiều và n-1siêu phẳng hai chiều đi qua (s/n, s/n, …, s/n)và vuông góc với vectơ (1, 1, …, 1)(xem khu vực màu đỏ trong Hình 1 để biết ba ví dụ).

Ví dụ với n = 3 và tổng 0,75, 1,75 và 2,75

Hình 1: Mặt phẳng của các đầu ra hợp lệ với n = 3 và tổng 0,75, 1,75 và 2,75

Ví dụ

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]

Quy tắc

  • Chương trình của bạn sẽ kết thúc dưới một giây trên máy của bạn ít nhất là với n≤10bất kỳ s hợp lệ nào.
  • Nếu bạn rất muốn, chương trình của bạn có thể là độc quyền ở đầu trên, tức là s<nvà các số đầu ra từ khoảng thời gian nửa mở [0,1) (phá vỡ ví dụ thứ hai)
  • Nếu ngôn ngữ của bạn không hỗ trợ số dấu phẩy động, bạn có thể giả mạo đầu ra với ít nhất mười chữ số thập phân sau dấu thập phân.
  • Các lỗ hổng tiêu chuẩn không được phép và các phương thức đầu vào / đầu ra tiêu chuẩn được cho phép.
  • Đây là , vì vậy bài dự thi ngắn nhất, tính bằng byte, sẽ thắng.


Khi bạn nói This is equal to uniformly sampling from the intersection- tôi có thể thấy một chương trình chọn ngẫu nhiên từ chỉ các góc của giao lộ đó. Điều đó có hợp lệ không?
JayCe

2
@KevinCruijssen Không, điều đó chỉ đúng với s==0 or s==3. Đối với tất cả các giá trị khác của s, mặt phẳng có diện tích khác không và bạn phải chọn ngẫu nhiên một điểm trên mặt phẳng đó.
dùng202729

3
Yêu cầu khoảng thời gian được đóng hoặc đóng một nửa (trái ngược với mở) là một yêu cầu về mặt lý thuyết không thể quan sát được. Nhiều bộ tạo số ngẫu nhiên cho đầu ra trong (0,1). Làm thế nào để kiểm tra khoảng thời gian đầu ra là [0,1) chứ không phải (0,1)? Giá trị 0 "không bao giờ" xảy ra bằng mọi giá
Luis Mendo

2
Có ổn không nếu mã của chúng tôi sử dụng lấy mẫu từ chối, và do đó mất rất nhiều thời gian cho các trường hợp thử nghiệm như thế s=2.99999999999, n=3nào? Chúng ta có thể tạo ra các số thực ngẫu nhiên trong bội số của, nói 1e-9không?
xnor

Câu trả lời:


1

Ngôn ngữ Wolfram (Mathicala) , 92 90 byte

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

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

Mã không chơi gôn:

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

Đây là một giải pháp hoạt động trong 55 byte nhưng hiện tại (Mathicala phiên bản 12) bị hạn chế n=1,2,3do RandomPointtừ chối rút điểm từ các siêu máy bay có chiều cao hơn (trong phiên bản 11.3 của TIO, nó cũng không thành công n=1). Nó có thể hoạt động cho cao hơn ntrong tương lai:

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

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

Mã không chơi gôn:

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

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


  • -20 byte, nhờ Kevin Cruijssen

@LuisMendo Nên được sửa ngay bây giờ
TFeld

họ dường như vẫn không đồng đều
l4m2

@ l4m2 Tôi đã chạy g(4, 2.0)1.000 lần để nhận được 4.000 điểm và kết quả trông như thế này có vẻ khá đồng đều.
Kỹ sư Toast



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 và 196 → 237) để sửa tốc độ của các trường hợp kiểm tra gần 1, cũng như sửa thuật toán nói chung.

Dùng thử trực tuyến

Giải trình:

Sử dụng cách tiếp cận trong câu trả lời StackoverFlow này , bên trong một vòng lặp miễn là một trong các mục vẫn lớn hơn 1.
Ngoài ra, nếu 2*s>n, ssẽ được thay đổi thành n-svà một cờ được đặt để cho biết chúng ta nên sử dụng 1-diffthay vì difftrong mảng kết quả (cảm ơn vì tiền boa @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

Hết giờ chotest(10, 9.99);
l4m2

@ l4m2 Vâng, nhận thấy điều tương tự 10, 9.0ngay sau khi tôi chỉnh sửa để sửa n=10, s=9.999999999999trường hợp kiểm tra .. Không chắc có lỗi nào trong Java hay không trong khi vẫn giữ tính ngẫu nhiên thống nhất của nó .. Sẽ phải suy nghĩ một lúc. Bây giờ tôi sẽ chỉnh sửa nó để nói ra.
Kevin Cruijssen

Nếu n-s<1bạn có thể gọi f(n,n-s)và lật mỗi số trên 1/2(tức là thay thế xbằng 1-x) như l4m2 đã làm. Điều này có thể giải quyết vấn đề cho các số sgần n.
soktinpk

@soktinpk Cảm ơn vì tiền boa. Nó thực sự s+s>nthay vì n-s<1, nhưng khi tôi nhìn vào các câu trả lời JavaScript khác, nó thực sự có ý nghĩa. Bây giờ mọi thứ đã được sửa, bao gồm một lỗi khác vẫn còn. Byte đã tăng lên khá nhiều, nhưng mọi công trình bây giờ. Sẽ làm việc đếm ngược byte từ đây. :)
Kevin Cruijssen

Tôi không biết về một bằng chứng chung nhưng tôi tin rằng thuật toán này hoạt động vì một hypercube N chiều có thể được cắt thành các hyperpyramids N-chiều.
Neil


3

C ++ 11, 284 267 byte

-17 byte nhờ Zacharý
Sử dụng thư viện ngẫu nhiên C ++, đầu ra trên đầu ra tiêu chuẩn

#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ọi, bạn chỉ cần làm điều đó:

g<2>(0.0);

Trong đó tham số mẫu (ở đây, 2) là N và tham số thực tế (ở đây, 0,0) là S


Tôi nghĩ bạn có thể xóa khoảng cách giữa <z>u
Zacharý

Tôi đã nhận nó xuống hơn nữa : 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]<<' ';}. Một dòng mới không phải là dấu phân cách giữa các mục
Zacharý

1
Đề nghị loại bỏ dhoàn toàn bằng cách thay đổi d=s/Nthành s/=NĐề xuất làm lại vòng lặp thứ hai for(z c;i<N;a[++i%N]-=c)a[i]+=c=u(e);thay vì for(;i<N;){z c=u(e);a[i]+=c;a[++i]-=c;}(lưu ý thêm %Nvào để làm cho chương trình tính toán số đầu tiên một cách chính xác)
Max Yekhlakov

2

Sạch sẽ , 221 201 byte

Sạch sẽ, mã golf, hoặc số ngẫu nhiên. Chọn hai.

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

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

Chức năng một phần theo nghĩa đen :: (Int Real -> [Real]). Sẽ chỉ tạo ra kết quả mới mỗi giây một lần.
Chính xác đến ít nhất 10 chữ số thập phân.


2

R , 99 byte (vớigtools gói)

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}

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

Một~= ={w1,Giáo dục,wn:tôi,0<wtôi<1;Σwtôi= =S}wtôiSMột= ={w1,Giáo dục,wn:tôi,0<wtôi<1S;Σwtôi= =1}

S= =1Dtôirtôichtôiet(1,1,Giáo dục,1) S1<1/SS

S>n/2


2

C, 132 127 125 118 110 107 byte

-2 byte nhờ @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);}

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


Thật không may, câu trả lời này không đáp ứng các đặc điểm kỹ thuật thách thức. Các số ngẫu nhiên đầu ra không bị hạn chế [0,1]và phân phối chung của chúng không đồng nhất.
Nitrodon

@Nitrodon này, bạn có thể vui lòng cung cấp đầu vào mà đầu ra không bị giới hạn ở [0,1] không? Tôi đã thử một vài ví dụ khác nhau và tất cả đều có vẻ đúng, trừ khi tôi hiểu sai mục tiêu.
OverclockedSanic

Với trạng thái RNG trên TIO và giữ cho bạn n=4, các giá trị s=3.23s=0.89cung cấp đầu ra ngoài phạm vi. Hơn nữa, việc phân phối X-s/nnên phụ thuộc vào s, nhưng không.
Nitrodon

@Nitrodon Rất tiếc, xấu của tôi. Tôi đã chuyển đổi một số phần từ câu trả lời C ++ ở trên và quên thêm một điều. Nó nên được chữa ngay bây giờ? Cũng đánh gôn một vài byte trong quá trình.
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

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

Đôi khi, các câu trả lời hơi bị sai do, tôi cho rằng, lỗi dấu phẩy động. Nếu đó là một vấn đề tôi có thể sửa nó với chi phí 1 byte. Tôi cũng không chắc nó đồng nhất như thế nào (khá chắc chắn là nó ổn nhưng tôi không giỏi lắm với loại điều này), vì vậy tôi sẽ mô tả thuật toán của mình.

Ý tưởng cơ bản là tạo ra một số xsau đó trừ đi svà lặp lại cho đến khi chúng ta có ncác phần tử sau đó xáo trộn chúng. Tôi tạo ra xvới giới hạn trên là 1 hoặc s(tùy theo giá trị nào nhỏ hơn) và giới hạn dưới là s-n+10 (tùy theo giá trị nào lớn hơn). Giới hạn dưới đó là có để lần lặp tiếp theo svẫn sẽ nhỏ hơn hoặc bằng n(đạo hàm: s-x<=n-1-> s<=n-1+x-> s-(n-1)<=x-> s-n+1<=x).

EDIT: Cảm ơn @ michi7x7 vì đã chỉ ra một lỗ hổng trong tính đồng nhất của tôi. Tôi nghĩ rằng tôi đã sửa nó bằng cách xáo trộn nhưng hãy cho tôi biết nếu có vấn đề khác

EDIT2: Số byte được cải thiện cộng với hạn chế loại cố định


3
Các mẫu đồng phục chuỗi sẽ không bao giờ dẫn đến phân phối đồng đều (tọa độ cuối cùng hầu như luôn lớn hơn 0,99 trong ví dụ của bạn)
michi7x7

@ michi7x7 Tôi thấy quan điểm của bạn. Điều gì xảy ra nếu tôi xáo trộn thứ tự của danh sách sau khi tạo nó? Tôi nên tham gia nhiều lớp thống kê hơn
user1472751

Các con số trông không đồng đều. Ở đây , 8 kết quả là> 0,99, 1 là 0,96 và cuối cùng là 0,8. Đây là những gì nó trông giống như.
Stewie Griffin

@ user1472751 có một số câu trả lời hay ở đây: stackoverflow.com/q/8064629/6774250
michi7x7

1
Tính đồng nhất vẫn có một số vấn đề, xem tại đây - có quá nhiều số 0 được tạo ra (biểu đồ của các giá trị được sắp xếp từ 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

Ung dung:

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

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

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.