합계가 고정 된 난수


32

당신의 임무는 고정 합계와 간격 [0,1]에서 난수 를 출력 하는 프로그램이나 함수 를 작성하는 것 입니다. ns

입력

n, n≥1, 생성 할 난수

s, s>=0, s<=n, 생성 할 숫자의 합

산출

간격 [0,1] 의 모든 요소와 모든 요소의 합이 동일한 n부동 소수점 수 의 임의 튜플은 편리한 모호하지 않은 방식으로 출력됩니다. 유효한 모든 튜플은 부동 소수점 숫자의 제한 내에서 똑같이 가능해야합니다.sn

이것은 n차원 단위 큐브 내부의 점 과 벡터에 직교 하는 n-1차원 초평면 의 교차점에서 균일하게 샘플링하는 것과 같습니다 (세 가지 예는 그림 1의 빨간색 영역 참조).(s/n, s/n, …, s/n)(1, 1, …, 1)

n = 3이고 합이 0.75, 1.75 및 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유효한 시스템으로 컴퓨터에서 1 초 안에 완료해야합니다 .
  • 원하는 경우 프로그램을 상단에서 독점적으로 사용할 수 있습니다. s<n , 반 개방 간격 [0,1)의 출력 번호 (두 번째 예를 깨짐)
  • 언어가 부동 소수점 숫자를 지원하지 않으면 소수점 다음에 10 자리 이상의 소수점 이하 자릿수로 출력을 위조 할 수 있습니다.
  • 표준 허점은 허용되지 않으며 표준 입 / 출력 방법이 허용됩니다.
  • 이것은 이므로 바이트 단위로 측정 된 가장 짧은 항목이 이깁니다.


당신이 말할 때 This is equal to uniformly sampling from the intersection-나는 그 교차로의 모서리에서 무작위로 선택하는 프로그램을 볼 수 있습니다. 유효합니까?
JayCe

2
@KevinCruijssen 아니요, 이는 사실입니다 s==0 or s==3. 의 다른 모든 값의 s경우 평면의 면적이 0이 아니므로 해당 평면의 점을 임의로 무작위로 선택해야합니다.
user202729

3
폐쇄 또는 반 폐쇄 간격을 요구하는 것은 이론적으로 관찰 할 수없는 요건입니다. 많은 난수 생성기는 (0,1)로 출력을 제공합니다. 출력 간격이 (0,1)이 아닌 [0,1)인지 테스트하는 방법은 무엇입니까? 0 "never"값은 어쨌든 발생합니다
Luis Mendo

2
코드에서 거부 샘플링을 사용하면 괜찮 s=2.99999999999, n=3습니까? 예를 들어 1e-9?의 배수로 랜덤 실수를 생성 할 수 있습니까?
xnor

답변:


1

Wolfram Language (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

파이썬 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)

온라인으로 사용해보십시오!


  • Kevin Cruijssen 덕분에 -20 바이트

@LuisMendo 지금
고쳐 져야합니다

그들은 여전히 ​​균일하지 않은 것 같습니다
l4m2

@ 내가 달릴 l4m2 g(4, 2.0)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에 가까운 테스트 사례의 속도를 수정하고 일반적인 알고리즘을 수정합니다.

온라인으로 사용해보십시오

설명:

의 접근 방식을 사용하여 이 유래 응답 항목 중 하나가 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. 자바에서 균일하게 임의성을 유지하면서 픽스가 있는지 확실하지 않다. 잠시 동안 생각해야 할 것이다. 지금은 시간 초과를 나타내도록 편집하겠습니다.
케빈 크루이 센

경우 n-s<1당신이 호출 할 수있는 f(n,n-s)모든 수를 뒤집어 1/2(즉, 교체 x1-xl4m2 같은)했다. 이것은에 s가까운 숫자의 문제를 해결할 수 있습니다 n.
soktinpk

팁을 주셔서 감사합니다. 실제로 s+s>n대신 대신 n-s<1다른 JavaScript 답변을 볼 때 실제로 의미가 있습니다. 여전히 존재하는 다른 버그를 포함하여 모든 것이 수정되었습니다. 바이트는 꽤 많이 올라갔지 만 이제는 모든 것이 작동합니다. 여기에서 바이트 카운트를 줄입니다. :)
Kevin Cruijssen '

나는 일반적인 증거를 모르지만 N 차원 하이퍼 큐브를 N N 차원 하이퍼 피라미드로자를 수 있기 때문에이 알고리즘이 작동한다고 생각합니다.
Neil


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
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/=N두 번째 루프 재 작업 제안 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프로그램 계산을하기 위해를 첫 번째 숫자를 올바르게)
최대 Yekhlakov

2

청소 , 221 바이트

깨끗하고 코드 골프 또는 임의의 숫자. 두 개를 선택하십시오.

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}

온라인으로 사용해보십시오!

에이~={1,,:나는,0<나는<1;나는=에스}나는에스에이={1,,:나는,0<나는<1에스;나는=1} .

에스=1나는아르 자형나는기음h이자형(1,1,,1) 에스1<1/에스에스 .

때 미러링하는 트릭에스>/2(나는 l4m2가 처음 으로 알아 낸 것 같아요 ) 필수적입니다. 이를 확인하기 전에 거부 샘플러의 반복 횟수가 마지막 테스트 사례에서 폭발적으로 증가했기 때문에 잘 선택된 잘린 베타 배포에서 효율적으로 샘플링하는 데 많은 시간을 보냈지 만 결국에는 필요하지 않습니다.


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

@Nitrodon 안녕하세요, 출력이 [0,1]로 제한되지 않은 입력을 제공해 주시겠습니까? 나는 두 가지 다른 예를 시도했지만 목표를 오해하지 않는 한 모두 정확 해 보였습니다.
OverclockedSanic

상기 RNG를 온 상태 TIO하고 유지하면서 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하여 요소 가있을 때까지 반복 하는 것입니다. I는 생성 x상부 어느 하나와의 결합 또는 s(작은 쪽) 및이의 하한 s-n+1(큰 쪽) 또는 0. 그 하한은 다음 반복 s에서 n(derivation : s-x<=n-1-> s<=n-1+x-> s-(n-1)<=x-> s-n+1<=x) 보다 작거나 같습니다 .

편집 : 내 균일성에 결함을 지적 한 @ michi7x7에게 감사드립니다. 셔플 링으로 수정했지만 다른 문제가 있으면 알려주세요.

EDIT2 : 향상된 바이트 수와 고정 유형 제한


3
균일 한 샘플을 체인으로 연결하는 것은 (마지막 좌표는 귀하의 예제에서 거의 항상 0.99보다 큰) 균일 한 분배로 이어질하지 않습니다
michi7x7

@ michi7x7 당신의 요점을 참조하십시오. 목록을 생성 한 후 목록의 순서를 섞으면 어떻게됩니까? 더 많은 통계 수업을 수강해야
했음

숫자가 매우 균일하게 보이지 않습니다. 여기서 8 개의 결과는> 0.99이고 1은 0.96이며 마지막은 0.8입니다. 이것이 어떻게 생겼는지입니다.
Stewie Griffin

@ user1472751 여기에 몇 가지 좋은 답변이 있습니다. stackoverflow.com/q/8064629/6774250
michi7x7

1
균일성에 여전히 문제가 있습니다. 여기를 참조 하십시오. 너무 많은 0이 생성되었습니다 (1000 % 500에서 정렬 된 값의 플롯)
Angs

1

하스켈 , 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.