ตัวเลขสุ่มที่มีผลรวมคงที่


32

งานของคุณคือการเขียนโปรแกรมหรือฟังก์ชั่นที่ส่งออก nตัวเลขสุ่มจากช่วงเวลา [0,1] ด้วยผลรวมคงที่sด้วยผลรวมคงที่

อินพุต

n, n≥1จำนวนของตัวเลขสุ่มที่จะสร้าง

s, s>=0, s<=nผลรวมของตัวเลขที่จะสร้าง

เอาท์พุต

แบบสุ่ม ntupleของตัวเลขจำนวนจุดลอยตัวที่มีองค์ประกอบทั้งหมดจากช่วงเวลา [0,1]และผลรวมขององค์ประกอบทั้งหมดเท่ากับs, ส่งออกในทางที่ไม่คลุมเครือสะดวกใด ๆ ถูกต้องทั้งหมดn-tuples ที่มีโอกาสเท่ากันภายในข้อ จำกัด ของตัวเลขทศนิยม

นี่เท่ากับการสุ่มตัวอย่างอย่างสม่ำเสมอจากจุดตัดของจุดภายในnลูกบาศก์หน่วยมิติและn-1มิติระนาบมิติที่ผ่าน(s/n, s/n, …, s/n)และตั้งฉากกับเวกเตอร์(1, 1, …, 1) (ดูพื้นที่สีแดงในรูปที่ 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 ที่ถูกต้องใด ๆ
  • หากคุณต้องการโปรแกรมของคุณจะเป็นแบบเอกสิทธิ์เฉพาะบุคคลที่ด้านบนเช่น s<nและหมายเลขผลลัพธ์จากช่วงครึ่งเวลาที่เปิด [0,1) (แบ่งตัวอย่างที่สอง)
  • หากภาษาของคุณไม่รองรับหมายเลขทศนิยมคุณสามารถปลอมผลลัพธ์ด้วยทศนิยมอย่างน้อยสิบหลักหลังจุดทศนิยม
  • ช่องโหว่มาตรฐานไม่ได้รับอนุญาตและอนุญาตให้ใช้วิธีการอินพุต / เอาต์พุตมาตรฐาน
  • นี่คือดังนั้นรายการที่สั้นที่สุดวัดเป็นไบต์ชนะ


เมื่อคุณพูด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 "ไม่เคยเกิดขึ้นแล้ว"
Luis Mendo

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 ไบต์แต่สำหรับตอนนี้ (มาติการุ่น 12) จะมีการ จำกัดn=1,2,3เพราะRandomPointปฏิเสธที่จะดึงคะแนนจาก hyperplanes สูงมิติ (ในรุ่น 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 2 , 144 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 คะแนนและผลลัพธ์ดูเหมือนว่าจะเหมือนกัน
Engineer Toast



4

Java 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>n, sจะถูกเปลี่ยนเป็น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 ในขณะที่ยังคงถือแบบสุ่มสม่ำเสมอ .. จะต้องคิดในขณะที่ สำหรับตอนนี้ฉันจะแก้ไขเพื่อระบุว่าหมดเวลาแล้ว
Kevin Cruijssen

หากn-s<1คุณสามารถโทรออกf(n,n-s)และพลิกทุกหมายเลขเหนือ1/2(เช่นแทนที่xด้วย1-x) เช่นเดียวกับ l4m2 นี้อาจช่วยแก้ปัญหาสำหรับตัวเลขที่อยู่ใกล้กับs n
soktinpk

@soktinpk ขอบคุณสำหรับเคล็ดลับ มันเป็นเรื่องจริงs+s>nแทนที่จะเป็นn-s<1แต่เมื่อฉันดูจาวาสคริปต์อื่น ๆ ก็ตอบได้ว่ามันสมเหตุสมผลแล้ว ตอนนี้ทุกอย่างได้รับการแก้ไขรวมถึงข้อผิดพลาดอื่นที่ยังคงมีอยู่ ไบต์เพิ่มขึ้นเล็กน้อย แต่ตอนนี้ทุกงาน จะทำงานนับไบต์จากที่นี่ :)
Kevin Cruijssen

ฉันไม่รู้หลักฐานทั่วไป แต่ฉันเชื่อว่าอัลกอริทึมนี้ใช้งานได้เนื่องจากไฮเพอร์คิวบิก N-Dim สามารถถูกตัดเป็นไฮเปอร์แพนรามิด N N-Dim
Neil


3

C ++ 11, 284 267 ไบต์

-17 ไบต์ขอบคุณZacharý
ใช้ไลบรารี่แบบสุ่ม 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
Zacharý

ฉันลงไปอีก: 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]<<' ';}. ขึ้นบรรทัดใหม่ไม่จำเป็นต้องเป็นตัวคั่นระหว่างรายการ
Zacharý

1
แนะนำกำจัดให้dหมดโดยเปลี่ยนd=s/Nเป็นs/=NSuggest reworking ลูปที่สองfor(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เพื่อให้โปรแกรมคำนวณหมายเลขแรกได้อย่างถูกต้อง)
Max Yekhlakov

2

ทำความสะอาด , 221 201 ไบต์

หมายเลขสะอาดรหัสกอล์ฟหรือสุ่ม เลือกสอง

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])ตัวอักษรฟังก์ชั่นบางส่วน จะสร้างผลลัพธ์ใหม่เพียงครั้งเดียวต่อวินาที
แม่นยำอย่างน้อย 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~={W1,...,Wn:ผม,0<Wผม<1;ΣWผม=s}. ฉันจะหารทั้งหมดWผม โดย s และแทนตัวอย่างจาก A={W1,...,Wn:ผม,0<Wผม<1s;ΣWผม=1}.

ถ้า s=1นี่เป็นเรื่องง่าย: มันสอดคล้องกับการสุ่มตัวอย่างจาก DผมRผมชั่วโมงล.อีเสื้อ(1,1,...,1) การแจกแจง (ซึ่งเป็นชุดเครื่องแบบของ simplex) สำหรับกรณีทั่วไปs1เราใช้การสุ่มตัวอย่างการปฏิเสธ: ตัวอย่างจากการแจกแจงดีริชเลต์จนกระทั่งรายการทั้งหมดเป็น <1/sแล้วคูณด้วย s.

เคล็ดลับที่จะสะท้อนเมื่อ s>n/2(ซึ่งฉันคิดว่าl4m2 เป็นคนแรกที่คิดออก) เป็นสิ่งจำเป็น ก่อนที่ฉันจะเห็นจำนวนของการวนซ้ำในตัวอย่างการปฏิเสธถูกระเบิดสำหรับกรณีทดสอบครั้งสุดท้ายดังนั้นฉันจึงใช้เวลานานมากในการพยายามสุ่มตัวอย่างอย่างมีประสิทธิภาพจากการแจกแจงแบบเบต้าที่ถูกเลือกอย่างดี แต่มันไม่จำเป็นในตอนท้าย


2

C, 132 127 125 118 110 107 ไบต์

-2 ไบต์ขอบคุณ@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);}

ลองออนไลน์!


น่าเสียดายที่คำตอบนี้ไม่เป็นไปตามข้อกำหนดการท้าทาย ตัวเลขสุ่มเอาท์พุทไม่ได้ถูก จำกัด[0,1]และการกระจายข้อต่อไม่เหมือนกัน
Nitrodon

@Nitrodon เดี๋ยวก่อนคุณช่วยกรุณาป้อนข้อมูลที่เอาท์พุทไม่ จำกัด เพียง [0,1]? ฉันลองสองสามตัวอย่างที่แตกต่างกันและพวกเขาทั้งหมดดูเหมือนจะถูกต้องเว้นแต่ว่าฉันเข้าใจผิดวัตถุประสงค์
OverclockedSanic

ด้วยสถานะ RNG บน TIO และการเก็บรักษาของn=4คุณค่าs=3.23และs=0.89ส่งออกนอกช่วง ยิ่งกว่านั้นการกระจายของX-s/nควรขึ้นอยู่กับsแต่มันก็ไม่ได้
Nitrodon

@Nitrodon โอ๊ะฉันไม่ดี ฉันกำลังแปลงบางส่วนจากคำตอบ C ++ ด้านบนและลืมเพิ่มบางสิ่ง มันควรได้รับการแก้ไขแล้วหรือยัง? ยัง golfed ไม่กี่ไบต์ในกระบวนการ
OverclockedSanic

1

Haskell , 122 217 208 bytes

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องค์ประกอบแล้วสับเปลี่ยนพวกเขา ฉันสร้างxโดยมีขอบเขตบนของ 1 หรือ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
ตัวอย่างเครื่องแบบผูกมัดจะไม่นำไปสู่การแจกแจงแบบสม่ำเสมอ (พิกัดสุดท้ายมักจะใหญ่กว่า 0.99 ในตัวอย่างของคุณ)
michi7x7

@ michi7x7 ฉันเห็นจุดของคุณ จะเกิดอะไรขึ้นถ้าฉันสับลำดับของรายการหลังจากสร้างมัน? ฉันควรจะได้นำสถิติอื่น ๆ เรียน
user1472751

ตัวเลขดูไม่เหมือนกันมาก ที่นี่ผลลัพธ์ 8 รายการคือ> 0.99, 1 คือ 0.96 และสุดท้ายคือ 0.8 นี่คือสิ่งที่ดูเหมือน
Stewie Griffin

@ user1472751 มีคำตอบที่ดีมากมายที่นี่: stackoverflow.com/q/8064629/6774250
michi7x7

1
ความสม่ำเสมอยังคงมีปัญหาดูที่นี่ - มีจำนวนศูนย์ที่สร้างมากเกินไป (พล็อตของค่าที่เรียงจาก 1,000% 500)
อัง

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

Ungolfed:

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.