均匀生成排列,最多重复 k 次?

Uniform generation of permutation with repetition at most k times?

提问人:Kornel 提问时间:6/2/2019 最后编辑:Kornel 更新时间:6/2/2019 访问量:647

问:

我们有一组数字。我们希望生成这些数字创建的 m 长度的排列,并在大多数时候重复每个数字。{1, 2, 3, ...,n}k

如果我们假设 ,那么我们可以得到: ,但不像第二个例子中那样恰好是输出的三倍,这比 k 多。n=5, k=2, m=3{3,3,1}{3, 3, 3}3

有没有办法快速均匀地生成这种排列?

我尝试了两种不同的解决方案。

第一:

1)产生随机排列与重复,有不同的排列。n^m

2)检查这是否是正确的排列(如果它包含的不是超过相同数字的倍数k

3)如果是,则返回,否则转到1)

Python 代码段:

import numba
import numpy as np


@numba.jit(nopython=True)
def gen_sequence1(n, k, m):
    result = np.random.randint(0, n, (1, m))[0]
    while not is_correct(result, k):
        result = np.random.randint(0, n, (1, m))[0]
    return result


@numba.jit(nopython=True)
def most_frequent(iter):
    return np.bincount(iter).max()


@numba.jit(nopython=True)
def is_correct(pruf, k):
    return most_frequent(pruf) <= k

第二种方法:

生成随机整数,仅当它没有出现在时间之前时才将其添加到序列中。下面介绍了这些单词的优化版本(用 Python 编写)。 Python 代码段:k

def gen_seq(n, d, m):
    choices = list(range(n))
    degrees = [0] * n
    result = []
    k = n - 1
    for i in range(m):
        rand = np.random.randint(0, k)
        result.append(choices[rand])
        degrees[choices[rand]] += 1
        if degrees[choices[rand]] == d:
            choices[rand], choices[k] = choices[k], choices[rand]
            k -= 1
    return result

问题是第一种方法非常慢,因为它需要时间来生成序列,这很明显。n=30, m=28, d=110^9

第二个是没有产生均匀的排列(有些比其他的概率更大)。

你有什么想法如何快速、均匀地生成这样的序列吗?

算法 随机、与语言无关 排列 均匀

评论

0赞 Kornel 6/2/2019
不必在 Python 中。我正在寻找更多的想法,而不是程序。

答:

0赞 rossum 6/2/2019 #1

这假设您有足够的内存来保存数字 [1..n] k 次。

  1. 设置阵列 [1..n]。

  2. 复制数组 k 次:[1..n, 1..n, 1..n, ...1..n] 添加到一个大数组中。

  3. 在大型重复数组上运行 Fisher-Yates 洗牌的前 m 步,以获得所需的排列。没有必要洗牌整个数组,因为你只需要 m 个数字。

评论

0赞 Kornel 6/2/2019
不返回均匀随机排列。有了列表 [1, 2, 1, 2] 和 m=2,我得到的概率是:(1, 1) -> 1/6, (2, 2) -> 1/6, (1, 2) -> 2/6, (2, 1) -> 2/6
0赞 rossum 6/2/2019
尝试检查您的 F-Y 随机播放代码是否正常工作。您需要移动大数组中的元素,以便它们只能被选取一次。它是否正确随机播放 [1, 2, 3, 4, 5, 6]?还要查看 RNG 的输出;它是否显示出一种模式?例如,一些简单的 RNG 交替使用奇数和偶数。
1赞 rici 6/3/2019
@rossum:(评论已修改)不,OP是对的。您的算法不统一。假设你计算了所有 2n![1..2N] 的排列,并查看每个排列中的前两个数字。您更愿意打赌两个数字具有相同的奇偶校验还是它们具有不同的奇偶校验?答案: (b). 有 2n(2n-1) 对;其中 n(n-1) 都是偶数,n(n-1) 都是奇数,而 n² 是偶数/奇数,n² 是奇数/偶数。因此,不同的奇偶校验比相同的奇偶校验更有可能 n/(n-1)。如果 n 为 2,这一点尤其明显,如上面 OP 的示例所示。
0赞 rici 6/3/2019
当然,这取决于“制服”的确切定义。
0赞 Kornel 6/11/2019
通过均匀,我的意思是任何一对可能的配置都具有相同的概率。
0赞 Gokuruto 6/2/2019 #2

如果我没记错的话,np.choice 有一个选项来给出概率,那么你可以做这样的事情:

  1. 设置阵列 [1..n]。

  2. 复制数组 k 次:[1..n, 1..n, 1..n, ...1..n] 添加到一个大数组中。 就像@rossum提议的那样。

  3. 生成这个大数组均匀的概率 (1/(k*n))。

重复 m 次:

  1. 获取一个数字到结果数组
  2. 设置 Probabilities,对于绘制的项目概率为 0,将 rest 设置为 相同的值在它们之间均匀分布 1/(k*n),我们刚刚设置为 0

例:

设 S=[1,1,1,2,2,2,3,3,3,4,4,4] 是一个大数组,其中每个项目都有 k,k=3 和 m = 4。

  1. 生成 P = [1/12]*len(S)

  2. 结果 = 随机(S,P) 假设结果 = [1]

  3. 概率是这样的 P = [0,1/12+1/36,1/12+1/36,1/12+1/36,其余保持不变]

重复步骤 2 和 3 m 次

如果没有更多与绘制的值相同的值,请将其设置为 0 并进行静止概率以保持此比率和总和为 1 。我认为最困难的部分是操纵概率。

评论

0赞 Kornel 6/2/2019
你是怎么想出这样的概率的?我需要均匀分布,看起来不会是均匀的。
0赞 Gokuruto 6/2/2019
对于 S 上的每隔 1,您需要添加像 1/3*1/12 这样的概率,以使其总和为 1 并补偿概率,其他解决方案可以是每个值的数组,如果计数器 ==k,则从数组中删除该值并绘制下一个数字