【问题标题】:Unbiased random Array shuffle无偏随机数组洗牌
【发布时间】:2021-08-06 22:15:41
【问题描述】:

当我想从 [1...n] 中对数组“perm”中的数字数组进行洗牌时,我用 Java 编写:

int[] perm = new int[n];
for (int i = 0; i < n; i++) {
     perm[i] = i;
}

for (int i = 0; i < n; i++) {
    int new_place = (int) (Math.random()*n); // Exchange element with any random element.
    int temp = perm[new_place];
    perm[new_place] = perm[i];
    perm[i] = temp;
}

但是本书将随机化代码写成(唯一的变化是在 for 循环中):

for (int i = 0; i < n; i++) {
    int new_place = i + (int) (Math.random() * (n-i)); // Exchange element with a random element to its right.
    int temp = perm[new_place];
    perm[new_place] = perm[i];
    perm[i] = temp;
}

他们说,通过他们编写的代码,他们通过从尚未选择的卡片中统一选择来确保洗牌是随机的。 (与我的方法相反,我从所有卡片中选择)。 (我相信这是他们写的Fisher-Yates算法的修改版)。

我只想知道我的代码(上面的代码)是否也对随机数生成没有偏见?如果是,为什么?如果不是,为什么?

(前 2 个 cmets 是针对我问我所做的是否对随机数生成正确的问题,但我想问它是否无偏。但我当时不知道这个术语,对不起。对于这个当前的问题,第4条评论有一个很好的理解链接)。

【问题讨论】:

  • 只要您的随机数生成生成从 0 到 n 的值,最终数组的所有排列都是同样可能的。你的代码是正确的。
  • 小心将Math.random() 转换为int,因为如果没有括号,它将返回0。您可以声明一个随机对象Random rand = new Random() 并说int r = rand.nextInt(n)
  • @NeonFire 对不起,我忘了添加随机数组应该是无偏的。你知道我应该编辑这个问题还是创建一个新问题?谢谢。
  • 不,你的算法(一个天真的洗牌)不是没有偏见的。这是一个很好的explanation 为什么不呢。
  • NeonFIre 和 Gene 都不正确,RaffleBuffle 的链接对此进行了解释,saka1029 的答案在示例输出中进行了演示。正如 Jeff 解释的那样,当您将元素与 any 元素交换时,当前元素“左侧”的元素之前已经至少交换过一次,从而导致比实际可能的排列更多的组合在 Coding Horror 博客文章中。

标签: java arrays shuffle fisher-yates-shuffle


【解决方案1】:

您的代码似乎对生成的数组的分布存在偏差。 n = 3的6000条统计结果如下。

static int[] shuffle(int n) {
    int[] perm = IntStream.range(0, n).toArray();
    for (int i = 0; i < n; i++) {
        int new_place = (int) (Math.random()*n);
        int temp = perm[new_place];
        perm[new_place] = perm[i];
        perm[i] = temp;
    }
    return perm;
}

public static void main(String[] args) {
    Map<int[], Integer> counts = new TreeMap<>(Arrays::compare);
    int n = 3;
    for (int i = 0; i < 6000; ++i)
        counts.compute(shuffle(n), (k, v) -> v == null ? 1 : v + 1);
    counts.entrySet()
        .forEach(e -> System.out.println(
            Arrays.toString(e.getKey()) + "=" + e.getValue()));
}

输出:

[0, 1, 2]=865
[0, 2, 1]=1113
[1, 0, 2]=1161
[1, 2, 0]=1093
[2, 0, 1]=873
[2, 1, 0]=895

[0, 1, 2][2, 1, 0] 似乎很少见。

用本书的代码执行时,变成如下。

static int[] shuffle(int n) {
    int[] perm = IntStream.range(0, n).toArray();
    for (int i = 0; i < n; i++) {
        int new_place = i + (int) (Math.random() * (n-i));
        int temp = perm[new_place];
        perm[new_place] = perm[i];
        perm[i] = temp;
    }
    return perm;
}

输出:

[0, 1, 2]=987
[0, 2, 1]=978
[1, 0, 2]=1014
[1, 2, 0]=994
[2, 0, 1]=1041
[2, 1, 0]=986

我无法解释为什么会这样,但你应该尝试一下。

【讨论】:

    猜你喜欢
    • 2010-12-03
    • 2016-04-26
    • 1970-01-01
    • 1970-01-01
    • 2011-05-05
    • 2015-01-03
    • 1970-01-01
    • 2015-10-30
    相关资源
    最近更新 更多