洗牌算法

洗牌算法

 

 

 

点评:上面即为洗牌算法的思想,其本质是对数组元素进行随机重排。数组中每个元素经过洗牌算法后落在数组某个位置上的概率是相等的,洗牌算法在牌类游戏中非常有用。我们最终将算法的时间复杂度优化到了O(n),空间复杂度优化到了O(1)。

java代码实现:

复制代码
import java.util.Random;

public class Test4 {

    public static void main(String[] args) {
        int[] datas = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,12,  13, 14, 15, 16, 17, 18, 19, 20};
        int[] returnDatas = shuffle(datas);
        System.out.println("nums:"+ returnDatas.length);
        for(int i = 0, length = returnDatas.length; i < length; i++) {
            System.out.print(returnDatas[i]+", ");
        }
    }
    
    public static int[] shuffle(int[] nums) {
        Random rnd = new Random();
        for(int i = nums.length-1; i > 0; i-- ) {
            int j = rnd.nextInt(i+1);
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
        return nums;
    }

}
复制代码

输出信息:

nums:20
6, 9, 18, 10, 2, 16, 7, 19, 14, 1, 12, 5, 3, 4, 17, 20, 8, 15, 13, 11,

 
 

FisherYates洗牌算法

算法思想是每次从未选中的数字中随机挑选一个加入排列,时间复杂度为O(n),wiki上的伪代码如下:
To shuffle an array a of n elements (indices 0..n-1):
  for i from n − 1 downto 1 do
       j ← random integer with 0 ≤ ji
       exchange a[j] and a[i]

代码实现:

void shuffle_fisheryates(char *arr, const int len){
    for(int i = len-1; i > 0; i--){
        int a = rand() % (i+1);         // 生成0到i闭区间的任意一个数

        // 交换坐标a和坐标i处的元素
        int temp = arr[i];
        arr[i] = arr[a];
        arr[a] = temp;
    }
}

 

 

蓄水池采样

链接:https://www.nowcoder.com/questionTerminal/12796031452e4ced8a16255bb02c4168
来源:牛客网

如何等概率地从n个数中随机抽出m个数?


我们最先想到也是最容易想到的是等概率抽取法,每次都随机在(0,n-1)之间抽取一个数,并与之前的数相比较,若相同,则继续随机生成;若不相同,则继续随机抽取,直到生成一个与之前所有生成数不同的数,将上述这样的随机生成做m次。那么问题来了,如果m较大呢?带来的变化是每次调用rand()生成的数与之前的数相重合的概率会变大,换句话说,我要随机取出m个的值,则调用rand()函数的次数会增多,这样的方法开销肯定会增大。

 

蓄水池抽样:我们先选取前m个数放入池中,然后我们每次以m/k的概率选择第k (k>m) 个数a[k],然后再在蓄水池中随机选取一个元素 a[j],交换a[k]和a[j];

 

 

 

 

 

 

 
posted @ 2022-11-18 00:30  7aughing  阅读(52)  评论(0编辑  收藏  举报