【随机算法】洗牌

(1) 基于抽取 Fisher-Yates Shuffle

初始化 vec, new_vec，长度为 n

取 0 <= random_idx < k
从 vec 中将 k 取出，放入 new_vec



正确性证明

p = n − 1 n × n − 2 n − 1 × . . . × n − i + 1 n − i + 2 × 1 n − i + 1 = 1 n p = \frac{n - 1}{n} \times \frac{n-2}{n-1} \times ... \times \frac{n-i+1}{n-i+2} \times \frac{1}{n-i+1} = \frac{1}{n}

代码

class Solution {
public:
Solution(vector<int>& nums) {
vec = nums;
int seed = rand();
dre = std::default_random_engine(seed);
dr = std::uniform_real_distribution<double>(0.0, 1.0);
}

vector<int> reset() {
return vec;
}

vector<int> shuffle() {
vector<int> tmp(vec.begin(), vec.end());
vector<int> result;
int n = tmp.size();
for(int i = 0; i < n; ++i)
{
int k = tmp.size();
int random_idx = floor(dr(dre) * k);
result.push_back(tmp[random_idx]);
tmp.erase(tmp.begin() + random_idx);
}
return result;
}

private:
vector<int> vec;
std::default_random_engine dre;
std::uniform_real_distribution<double> dr;
};


(2) 基于交换 Knuth-Durstenfeld Shuffle

代码

class Solution {
public:
Solution(vector<int>& nums) {
vec = nums;
int seed = rand();
dre = std::default_random_engine(seed);
dr = std::uniform_real_distribution<double>(0.0, 1.0);
}

vector<int> reset() {
return vec;
}

vector<int> shuffle() {
vector<int> result(vec.begin(), vec.end());
int n = result.size();
for(int k = n; k >= 1; --k)
{
int random_idx = floor(dr(dre) * k);
swap(result[random_idx], result[k - 1]);
}
return result;
}

private:
vector<int> vec;
std::default_random_engine dre;
std::uniform_real_distribution<double> dr;
};


(3) 基于插入 Inside-Out Algorithm

Inside-Out Algorithm 算法的基本思思是从前向后扫描数据，把位置 i 的数据随机插入到前 i 个位置中:

vec[i] 放进新数组的 random_idx ，但该位置可能已经插入了前面的值。此时将原有的值交换到新数组的 i 位置(此位置当前肯定没有插入过元素)。

正确性证明

• 0 <= j <= i

p ( j ) = 1 i × i i + 1 × i + 1 i + 2 × . . . × n − 1 n = 1 n p(j) = \frac{1}{i} \times \frac{i}{i+1} \times \frac{i+1}{i+2} \times ... \times \frac{n-1}{n} = \frac{1}{n}

• i + 1 <= j < n

p ( j ) = 1 k × k k + 1 × k + 1 k + 2 × . . . × n − 1 n = 1 n p(j) = \frac{1}{k} \times \frac{k}{k+1} \times \frac{k+1}{k+2} \times ... \times \frac{n-1}{n} = \frac{1}{n}

代码

class Solution {
public:
Solution(vector<int>& nums) {
vec = nums;
int seed = rand();
dre = std::default_random_engine(seed);
dr = std::uniform_real_distribution<double>(0.0, 1.0);
}

vector<int> reset() {
return vec;
}

vector<int> shuffle() {
int n = vec.size();
vector<int> result(n);
for(int i = 0; i < n; ++i)
{
int random_idx = floor(dr(dre) * (i + 1));
swap(result[random_idx], result[i]);
result[random_idx] = vec[i];
}
return result;
}

private:
vector<int> vec;
std::default_random_engine dre;
std::uniform_real_distribution<double> dr;
};


$2 std::shuffle 的实现 算法 Ref: 《计算机程序设计艺术》 $3-4-2

void shuffle(RandomAccessIterator first, RandomAccessIterator last, RandomNumberGenerator& rand)
{
if(first == last) return;
for(auto i = first + 1; i != last; ++i)
iter_swap(i, first + rand((i - first) + 1));
}


代码测试

class Solution {
public:
Solution(vector<int>& nums) {
vec = nums;
int seed = rand();
dre = std::default_random_engine(seed);
dr = std::uniform_real_distribution<double>(0.0, 1.0);
}

vector<int> reset() {
return vec;
}

vector<int> shuffle() {
vector<int> result = vec;
shuffle(result.begin(), result.end(), dre);
return result;
}

private:
void shuffle(vector<int>::iterator first, vector<int>::iterator last, std::default_random_engine& rand)
{
if(first == last) return;
std::uniform_real_distribution<double> dr(0.0, 1.0);
for(auto i = first + 1; i != last; ++i)
iter_swap(i, first + floor(dr(rand) * (i - first + 1)));
}

vector<int> vec;
std::default_random_engine dre;
std::uniform_real_distribution<double> dr;
};

• 点赞
• 评论
• 分享
x

海报分享

扫一扫，分享海报

• 收藏
• 打赏

打赏

fennelDumplings

你的鼓励将是我创作的最大动力

C币 余额
2C币 4C币 6C币 10C币 20C币 50C币
• 举报
• 一键三连

点赞Mark关注该博主, 随时了解TA的最新博文
03-16 51
01-19 1598

01-28 365
11-13 3153
04-01 846
01-07 161
01-14 1万+
08-21 5511
09-29 2517
11-16 1283
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客