采样随机非递减序列


20

输入:两个整数n和k,以便于代码输入的任何形式给出

输出由k个整数组成的随机非递减序列,每个整数的范围为1到n。应该从k个整数的所有非递减序列中均匀选择样本,该整数的范围在1到n之间。

输出可以采用您认为方便的任何合理格式。

您可以使用自己喜欢的库/语言提供的任何伪随机生成器。

我们可以假设整数n,k> 0。

假设n,k =2。非递减序列为

1,1
1,2
2,2

每个序列应具有输出概率1/3。

限制

对于k = 20和n = 100,您的代码应在不超过几秒钟的时间内运行。

什么不起作用

如果仅从1到n范围内随机采样每个整数,然后对列表进行排序,则不会获得均匀分布。


如果尚未输出n,k的非递减序列数,那么它本身可能会带来一个有趣的挑战……
ETHproductions

1
@ETHproductions不完全是,它只是一个二项式(与相关)
Sp3000

@ Sp3000啊,好的。然而,弄清楚如何有效地计算它对我来说是一个有趣的挑战。
ETHproductions 2016年

对于大多数具有32位或48位状态的花园品种PRNG,您无法满足每个序列具有相等输出概率的要求。根据Wolfram的说法,共有535个5千个20个元素的子序列,分别为1,...,100(无法检查其中有多少是不递减的)。2 ^ 64仅为18亿。
SinanÜnür16年

Answers:


1

实际上14 12个字节

该答案基于Emigna的05AB1E答案以及此Math.SE问题的答案。欢迎打高尔夫球!在线尝试!

;;ra+DR╚HS♀-

开球

      Implicit input n, then k.
;;    Duplicate k twice.
r     Push range [0...k] for later.
a     Invert the stack. Stack: n, k, k, [0...k]
+DR   Push the range [1..n+k-1].
╚     Shuffle the range. Stack: shuffled_range, k, [0...k]
H     Push the first k elements of shuffled_range. Call this increasing.
S     Sort increasing so the elements are actually increasing.
♀-    Subtract each element of [0...k] from each element of increasing.
      This gives us our non-decreasing sequence.
      Implicit return.

13

Python,89个字节

from random import*
lambda n,k:[x-i for i,x in enumerate(sorted(sample(range(1,n+k),k)))]

生成一个递增的序列而不是一个非递减的序列将很简单:这只是k1和之间n排序的数字的随机子集。

但是,我们可以通过将连续数字之间的每个间隔缩小1来将一个递增序列转换为一个非递减序列。因此,间隔1变为间隔0,即等于数字。为此,将i“ th最大值”减小i

r[0], r[1], ..., r[n-1]  =>  r[0]-0, r[1]-1, ..., r[n-1]-(n-1)

对于结果从1n,输入必须是1n+k-1。这给出之间的数字的非递减序列之间的双射1n,以增加之间的序列1n+k-1。在stars and bars参数中使用相同的双射来计数此类序列。

该代码使用python函数random.sample,该函数k无需从输入列表中进行替换即可获取样本。对其进行排序将增加顺序。


这令人印象深刻。您能补充一下方法的解释吗?

是的,现在很忙,稍后再解释。
xnor

我数了90个字节...(您也import*可以保存1个字节)
Rod

@Rod谢谢,我忘记了这一点。
xnor

7

05AB1E,13个字节

+<L.r¹£{¹L<-Ä

在线尝试!

说明

+<L            # range [1 ... n+k-1]
   .r          # scramble order
     ¹£        # take k numbers
       {       # sort
        ¹L<-   # subtract from their 0-based index
            Ä  # absolute value

7

Python,87个字节

from random import*
f=lambda n,k:k>random()*(n+k-1)and f(n,k-1)+[n]or k*[7]and f(n-1,k)

n包含最大可能值的概率等于k/(n+k-1)。要包括它,请将其放在列表的末尾,并减少所需的剩余数量k。要排除它,请减小上限n。然后,递归直到不需要更多值(k==0)。

Python random似乎没有为Bernoulli变量内置的函数:概率为1,否则为0。因此,这会检查由生成的介于0和1之间的随机值是否random低于k/(n+k-1)。Python 2将比率作为浮点除法,因此我们乘以分母:k>random()*(n+k-1)


numpy在这里有帮助吗?

@Lembik好主意,但看来您必须导入numpy.random,这太长了。
xnor

5

JavaScript(Firefox 30 +),74字节

(n,k,i=0,j=k)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)i-j+k--]

说明

xnor出色的Python答案很好地总结了此处使用的技术如何/为什么起作用。第一步是创建范围[1,2,...,n + k-1]

(n,k,i=0)=>[for(_ of Array(q=k+n-1))++i]

接下来,我们需要从该范围中提取k个随机项。为此,我们需要选择概率为s / q的每个项目,其中s是仍需要的项目数,q是该范围内剩余的项目数。由于我们使用的是数组推导,因此这很简单:

(n,k,i=0)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)k--&&i]

这给了我们一个均匀分布的递增数字序列。可以通过减去我们先前发现的项j的数量来解决此问题:

(n,k,i=0,j=0)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)k--&&i-j++]

最后,通过将k存储在j中,我们可以将其合并k--到表达式中并节省一些字节:

(n,k,i=0,j=k)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)i-j+k--]

2

TI-BASIC,54字节

Prompt N,K
K→dim(L1
While K
If rand<K/(N+K-1
Then
N→L1(K
K-1→K
Else
N-1→N
End
End
Disp L1

遵循xnor的逻辑,但要注意一点。从理论上讲,我们可以通过执行以下操作来剃掉一个字节:

K>rand(N+K-1

但是rand(保留用于创建随机数列表,因此我们将无法执行所需的隐式乘法来保存字节。

根据限制,这应该可以在84+上快速运行,但是我会检查以确保可以。


1

PHP,77 75 73字节

foreach(array_rand(range(2,$argv[1]+$k=$argv[2]),$k)as$v)echo$v+1-$i++,_;

像这样运行:

php -r 'foreach(array_rand(range(2,$argv[1]+$k=$argv[2]),$k)as$v)echo$v+1-$i++,_;' -- 10 5 2>/dev/null;echo
> 1_4_6_9_9_

说明

foreach(                    # Iterate over...
  array_rand(               #   a (sorted) random number of items from...
    range(                  #     an array with items...
      2,                    #       from 2
      $argv[1]+$k=$argv[2]  #       to n + k (set arg 2 to $k)
    ),
    $k                      #     Take k number of items (their keys)
  )
  as $v
)
  echo $v +1 - $i++,"_";    # Print the value subtracted by the index.
                            # Need to add 1, because keys are 0-indexed.

调整

  • 通过删除end()调用节省2个字节,并将其设置$argv[2]为,$k以缩短对参数的访问
  • 通过从foreach删除索引节省了2个字节,因为它只是一个递增数字。只需增加$i每次迭代

首先是JavaScript,现在是PHP。所有最好的科学编程语言:)谢谢。

@Lembik,不客气。请注意,它使用基本的PRNG。不要用于加密的东西。:)
aross 16-11-18
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.