Knuth-Shuffle:一个公平的洗牌算法

问题介绍

洗牌,简单来说就是随机交换牌的位置,但是如何才是公平的呢,洗牌的结果是所有元素的一个排列。一副牌如果有 n 个元素,最终排列的可能性一共有 n! 个。公平的洗牌算法,应该能等概率地给出这 n! 个结果中的任意一个。这样的暴力解法时间复杂度为O(n!),不可取。公平是说,对于每一个排列,每一个元素都能等概率的出现在每一个位置,也可以反过来,每一个位置都能等概率的放置每一个元素。基于此,我们可以设计出下面的算法。

算法实现

import java.util.Arrays;

public class KnuthShuffle {

  /**
   * 洗牌
   */
  public void shuffle(int[] nums) {
    for (int i = nums.length - 1; i >= 0; i--) {
      swap(nums, i, (int) (Math.random() * (i + 1)));
    }
  }

  private void swap(int[] nums, int left, int right) {
    int temp = nums[left];
    nums[left] = nums[right];
    nums[right] = temp;
  }

  public static void main(String[] args) {
    int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    new KnuthShuffle().shuffle(nums);
    System.out.println(Arrays.toString(nums));
  }

}

上面的算法就是大名鼎鼎的Knuth 洗牌算法。

第一次循环从1,2,3,4,5之间随机选出一个数,假设选出为2,和最后的5交换,任意一个元素出现在最后一个位置的概率都是1/5,

第二次循环从1,5,3,4之间随机选出一个数,假设为3,和最后的4交换,3没有满足第一次循环的概率是4/5,满足这一次循环的概率是1/4,最终为4/5*1/4=1/5。
以此类推,每一个位置放置每一个元素的概率都是1/5。
java中Collections.shuffle()就是使用的这种算法。

参考

【算法杂谈4】神一样的随机算法

posted @ 2021-03-06 10:13  strongmore  阅读(635)  评论(0编辑  收藏  举报