固定和的随机数


32

您的任务是编写一个程序或函数,以固定的总和从间隔[0,1] 中输出 n随机数s

输入值

n, n≥1,要生成的随机数

s, s>=0, s<=n,要生成的数字总和

输出量

n浮点数的随机元组,所有元素的间隔都为[0,1],且所有元素的总和等于s,以任何方便的明确方式输出。n在浮点数的限制内,所有有效元组必须具有同等的可能性。

这等于从- n维单位立方和n-1穿过(s/n, s/n, …, s/n)并垂直于矢量的- 维超平面内的点的交点进行均匀采样(1, 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或任何有效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

Answers:


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



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>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是的,10, 9.0我编辑修复n=10, s=9.999999999999测试用例后就注意到了同样的情况。现在,我将对其进行编辑以声明其超时。
凯文·克鲁伊森 Kevin Cruijssen)

如果n-s<1您可以像l4m2一样拨打f(n,n-s)和翻转每个号码1/2(即替换x1-x)。这可能会解决s接近的数字的问题n
soktinpk

@soktinpk感谢您的提示。它实际上是,s+s>n而不是n-s<1,但是当我看着其他JavaScript答案时,它的确有意义。现已修复所有问题,包括仍然存在的另一个错误。字节增加了很多,但是现在每个都可以工作。从这里开始将字节数减少。:)
凯文克鲁伊森

我不知道一般的证明,但我相信该算法有效,因为可以将N维超立方体切成N个N维超金字塔。
尼尔


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ý18年

我进一步了解了: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
建议摆脱d通过改变完全d=s/Ns/=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}

在线尝试!

A~={w1,,wn:i,0<wi<1;wi=s}wisA={w1,,wn:i,0<wi<1s;wi=1}

s=1Dirichlet(1,1,,1) s1<1/ss

当时镜像的技巧s>n/2(我认为l4m2是第一个发现的)至关重要。在我看到之前,对于最后一个测试用例,拒绝采样器中的迭代次数激增了,因此我花了很多时间试图从精心选择的截断的Beta分布中高效采样,但是最后没有必要。


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

在TIO上处于RNG状态的情况下,并保持your n=4的值s=3.23s=0.89给出的输出超出范围。更重要的是,的分布X-s/n应取决于s,但不是。
Nitrodon

@Nitrodon糟糕,我不好。我正在转换上面C ++答案中的某些部分,却忘了添加东西。现在应该解决吗?在此过程中还打了几个字节。
OverclockedSanic

1

Haskell中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元素,然后将它们洗牌。我生成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
均匀度仍然存在一些问题,请参见此处 -生成的零太多(从1000%500开始的排序值图)
Angs,

1

188字节的Haskell

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

在线尝试!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.