一、Fisher–Yates Shuffle
1、算法思想:
从原始数组中随机抽取一个新的数字到新数组。
2、算法描述:
- 初始化原始数组和新数组,原始数组长度为n(已知);
- 针对未处理的原始数组元素(假如还剩k个),随机产生一个[0, k)之间的数字p(假设数组从0开始);
- 从原始数组剩下的k个数中把第p个数取出,放入新数组;
- 重复步骤(2)和(3)直到数字全部取完;
- 取出的数字序列(新数组)便是一个打乱了的数列。
3、时间复杂度:
O(n^2)
4、C++代码实现:
vector<int> Fisher_Yates(vector<int> &nums)
{
srand((unsigned)time(NULL));
vector<int> res;
int p;
for(int i=0;i<nums.size();++i)
{
p=rand()%nums.size();//k为nums.size()
res.push_back(nums.size);
nums.erase(nums.begin()+p);
}
return res;
}
二、Knuth-Durstenfeld Shuffle
1、算法思想:
Knuth和Durstenfeld在Fisher等人的基础上对算法进行了改进,在原始数组上对数字进行交互,省去了额外O(n)的空间。该算法的基本思想和 Fisher 类似,每次从未处理的数据中随机取出一个数字,然后把该数字放在原来数组的尾部,即该数组尾部存放的是已经处理过的数字。
2、算法描述:
- 初始化一个大小为n的原始数组,假设数组拥有i+1个未经处理的元素,此时i+1=n;
- 在[0,i]范围内,生成随机数p;
- 交换原始数组第i个元素与第p个元素(下标从0开始);
- i=i-1(原数组尾部位置已经随机处理完成,向前处理数组其他位置);
- 重复(2)-(4)步,直至未经处理的元素全被处理。
3、时间复杂度:
O(n)
4、C++代码实现:
void Knuth_Durstenfeld(vector<int> &nums)
{
srand(unsigned(time(NULL)));
for(int i=nums.size()-1;i>=0;--i)
{
int p=rand()%(i+1);//前i+1个元素未被处理过
swap(nums[i],nums[p]);
}
}