【问题标题】:Uniform generation of permutation with repetition at most k times?最多重复 k 次的统一排列生成?
【发布时间】:2019-10-17 22:49:48
【问题描述】:

我们设置了{1, 2, 3, ...,n} 个数字。我们希望生成这些数字的长度为 m 的排列,每个数字最多重复 k 次。

如果我们假设n=5, k=2, m=3,那么我们可以收到:{3,3,1},但不是{3, 3, 3},因为第二个例子中的3恰好是输出的三倍,大于k。

有没有一种快速统一生成这种排列的方法?

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

第一:

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

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

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

Python sn-p:

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

第二种方法:

生成随机整数,仅当它在k 次之前没有出现时才将其添加到序列中。这些词的优化版本如下所示(用 Python 编写)。 Python sn-p:

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=1 非常慢,它需要10^9 次来生成序列,这很明显。

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

您有什么想法可以快速统一地生成这样的序列吗?

【问题讨论】:

  • 不必在 Python 中。我正在寻找更多的想法,而不是程序。

标签: algorithm random language-agnostic permutation uniform


【解决方案1】:

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

  1. 设置数组 [1..n]。

  2. 将数组复制 k 次:[1..n, 1..n, 1..n, ... 1..n] 成一个大数组。

  3. 在大型重复数组上运行 Fisher-Yates shuffle 的前 m 步以获得所需的排列。无需打乱整个数组,因为您只需要 m 个数字。

【讨论】:

  • 不返回统一的随机排列。有列表 [1, 2, 1, 2] 和 m=2 我收到概率: (1, 1) -> 1/6, (2, 2) -> 1/6, (1, 2) -> 2/ 6, (2, 1) -> 2/6
  • 尝试检查您的 F-Y 洗牌代码是否正常工作。您需要移动大数组中的元素,以便它们只能被拾取一次。它是否正确地洗牌 [1, 2, 3, 4, 5, 6]?也看看你的RNG的输出;它是否显示出一种模式?例如,一些简单的 RNG 会交替奇数和偶数。
  • @rossum:(评论已修改)不,OP 是对的。你的算法不统一。假设你计算所有 2n! [1..2n] 的排列并查看每个排列中的前两个数字。您愿意打赌这两个数字具有相同的奇偶性还是它们具有不同的奇偶性?答案:(b)。有 2n(2n-1) 对;其中 n(n-1) 都是偶数,n(n-1) 都是奇数,而 n² 是偶数/奇数,n² 是奇数/偶数。所以不同的奇偶校验比相同的奇偶校验更有可能是 n/(n-1)。如果 n 为 2,这一点尤其明显,如上面 OP 的示例。
  • 当然,这取决于“统一”的准确定义。
  • 统一是指任何一对可能的配置都有相同的概率。
【解决方案2】:

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

  1. 设置数组 [1..n]。

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

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

重复m次:

  1. 获取一个数字到结果数组
  2. 设置绘制项目概率为 0 的概率为 0,其余为 相同的值在它们之间均匀分布 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. result = random(S,P) 假设 result = [1]

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

重复步骤 2 和 3 m 次

如果没有更多与绘制相同的值,则将其设置为 0 并设置剩余概率以保持该比率和总和为 1 。我认为最难的部分是操纵概率。

【讨论】:

  • 你是怎么得出这样的概率的?我需要均匀分布,它看起来不像是均匀分布的。
  • S 上的每隔一个 1,您需要添加像 1/3*1/12 这样的概率,以使其总和为 1,并补偿概率,如果 counter =,则其他解决方案可以为每个值的计数器数组=k 然后从数组中删除该值并绘制下一个数字
猜你喜欢
  • 2014-03-15
  • 2017-08-02
  • 2012-10-29
  • 2011-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多