错排的定义:https://baike.baidu.com/item/%E9%94%99%E6%8E%92%E5%85%AC%E5%BC%8F/10978508?fr=aladdin
// 来自百度
精简: f[n] = (n - 1) * (f[n - 1] + f[n - 2])
f[n] 表示从第一个放到第n个的最大错排方案数,则 假设咱们要放第n号物品,首先把第n号物品拿在手里,那么n号就有一个空位了,现在咱们把第n号物品随意放在一个位置,因为是错排,不能放在n上,所以有(n - 1)个位置可以放,放完之后肯定会把另一个号的物品挤出来,假设是x,那么此时n就有了一个空位,并且手里拿这一个x。 此时,咱们分两种情况来讨论,第一种是x放在第n号的空位处,此时只是将x和n位置上的物品交换了位置,其他没有改变,因此接下来的操作就是把(n - 2)个物品错排,即:f[n - 2]。 第二种情况:x不放在第n的空位,此时咱们把x当成n,那么就变成了x位置不动了(因为已经有物品了),n不能放在n上,剩下的(n - 2) 个物品也要错排,所以就是n - 1个物品错排,即f [ n - 1];
so : 证毕
简化公式:
D(n) = [n! / e + 0.5] 挺对的!!!
发生错排的概率:
D(n) / n! (错排方案 / n的全排列)
当n!很大的时候这个概率减少的很少,不足1e-6的精确度
代码:
#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const double DINF = 1e20;
const double eps = 1e-8, e = 2.718282828;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 4e2 + 50;
inline double Get_mul(int n)
{
double res = 1;
for (int i = 2; i <= n; i++) res *= i;
return res;
}
ll f[N];
signed main(int argc, char* argv[])
{
ll n; cin >> n;
for (int n = 1; n <= 20; n++)
printf("%.6lf\n", 1.0 - floor(Get_mul(n) / e + 0.5) / Get_mul(n));
f[1] = 0, f[2] = 1;
for (int i = 3; i <= 15; i++) f[i] = (i - 1) * (f[i - 1] + f[i - 2]);
return 0;
}