fre*_*low 11 c++ algorithm shuffle probability permutation
经典的Fisher Yates看起来像这样:
void shuffle1(std::vector<int>& vec)
{
int n = vec.size();
for (int i = n - 1; i > 0; --i)
{
std::swap(vec[i], vec[rand() % (i + 1)]);
}
}
Run Code Online (Sandbox Code Playgroud)
昨天,我错误地"向后"实现了迭代:
void shuffle2(std::vector<int>& vec)
{
int n = vec.size();
for (int i = 1; i < n; ++i)
{
std::swap(vec[i], vec[rand() % (i + 1)]);
}
}
Run Code Online (Sandbox Code Playgroud)
这个版本是否比第一个版本更糟(或更好)?它是否会扭曲由此产生的概率?
是的,假设是均匀分布rand()。我们将通过证明每个输入可以以相等的概率生成每个排列来证明这一点。
N=2很容易证明。我们将其绘制为一棵树,其中子级代表通过将逗号后的字符插入最左边的字符串可以获得的每个字符串。
0,1 //input where 0,1 represent indices
01 10 //output. Represents permutations of 01. It is clear that each one has equal probability
Run Code Online (Sandbox Code Playgroud)
对于 N,我们将拥有 N-1 的所有排列,并随机交换 N 的最后一个字符
(N-1 0th permutation),N ..... (N-1 Ith permutation),N ________________________
/ \ / \ \
0th permutation of N 1st permutation.... (I*N)th permutation ((I*N)+1)th permutation .... (I*N)+(I-1)th permutation
Run Code Online (Sandbox Code Playgroud)
这种糟糕的归纳应该会让你得到均匀的分布。
例子:
N=2:
0,1
01 10 // these are the permutations. Each one has equal probability
Run Code Online (Sandbox Code Playgroud)
N=3:
0,1|2 // the | is used to separate characters that we will insert later
01,2 10,2 // 01, 10 are permutations from N-1, 2 is the new value
210 021 012 201 120 102 // these are the permutations, still equal probability
Run Code Online (Sandbox Code Playgroud)
N=4:(弯曲以帮助阅读)
0,1|23
01,2|3 10,2|3
012,3 021,3 210,3 102,3 120,3 201,3
0123 0132 0321 3230 2013 2031 2310 3012
0213 0231 0312 3210 1203 1230 1302 3201
2103 2130 2301 3102 1023 1032 1320 3021
Run Code Online (Sandbox Code Playgroud)
ETC