[洗牌算法] - 从长度为m的int数组中随机取出n个元素,每次取的元素都是之前未取过的

题目:从长度为m的int数组中随机取出n个元素,每次取的元素都是之前未取过的

Fisher-Yates洗牌算法是由 Ronald A.Fisher和Frank Yates于1938年发明的,后来被Knuth在书中介绍,很多人直接称Knuth洗牌算法, Knuth大家应该比较熟悉,《The Art of Computer Programming》作者,算法理论的创始人。我们现在所使用的各种算法复杂度分析的符号,就是他发明的。

等概率: 洗牌算法有些人也称等概率洗牌算法,其实发牌的过程和我们抽签一样的,大学概率论讲过抽签是等概率的,同样洗牌算法选中每个元素是等概率的。

用洗牌算法思路从1、2、3、4、5这5个数中,随机取一个数
1
4被抽中的概率是1/5

5被抽中的概率是1/4 * 4/5 = 1/5

2被抽中的概率是1/3 * 3/4 * 4/5 = 1/5

1被抽中的概率是1/2 * 1/3 * 3/4 * 4/5= 1/5

3被抽中的概率是1 * 1/2 * 1/3 * 3/4 * 4/5 = 1/5

时间复杂度为O(n^2), 空间复杂度为O(n)

代码如下:

//O(N^2)time
//O(N)space
void test(int n, int m) {
    List<Integer> list = new ArrayList<>();
    for (int i = 1; i <= n; i++) {
        list.add(i);
    }
    for (int i = 0; i < m; i++) {
        int t = (int) (list.size() * Math.random());
        System.out.println(list.remove(t));
    }
}

Knuth洗牌算法

在上面的介绍的发牌过程中, Knuth 和 Durstenfeld 在Fisher 等人的基础上对算法进行了改进,在原始数组上对数字进行交互,省去了额外O(n)的空间。该算法的基本思想和 Fisher 类似,每次从未处理的数据中随机取出一个数字,然后把该数字放在数组的尾部,即数组尾部存放的是已经处理过的数字。

时间复杂度为O(n), 空间复杂度为O(n)

//O(N)time
//O(N)space
void knuth(int n, int m) {
    int[] arr = new int[n];
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }
    for (int i = 0; i < m; i++) {
        int index = (int) ((n - i) * Math.random());
        System.out.println(arr[i + index]);
        swap(arr, i, i + index);
    }
}

void swap(int[] arr, int i, int index) {
    if (i == index) return;
    int temp = arr[i];
    arr[i] = arr[index];
    arr[index] = temp;
}

参考链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值