算法笔记--简单数学2
这几个数学例子并不是很简单,作者太强了😂
扩展欧几里得算法
扩展欧几里得算法
给定两个非零整数a和b,求一组整数解(x, y),使得
ax+by=gcd(a,b)成立,其中gcd(a,b)表示a和b的最大公约数
💔💔💔💔有点难, 后面在学。
组合数
关于 n! 的一个问题
求 n! 中有多少个质因子p
如: 6! = 1×2×3×4×5×6
于是 6! 中有4个质因子2,因为2、4、6中各有1个2,2个2,1个2
而 6! 中有2个质因子3,因为3、6中均各有1个3
解法一:计算从1~n中每个数各有多少个质因子p,然后将结果累加,时间复杂度为O(nlogn)
int cal(int n, int p){
int ans = 0;
for(int i = 2; i <= n; i++){ // 遍历2~n
int temp = i;
while(temp % p == 0){ // 只要temp还是p的倍数
ans++; // p的个数加1
temp /= p; // temp除以p
}
}
return ans;
}
这种做法当n\((n是10^18)\)很大时是无法接受的
⭕解法二:

所以:
\[n!中有\left ( \frac{n}{p} + \frac{n}{p^{2}} + \frac{n}{p^{3}} + ...... \right )个质因子p
\]
其中,除法均为向下取整,时间复杂度为O(logn)
int cal(int n, int p){
int ans = 0;
while(n){
ans += n / p; // 累加 n/p^k
n /= p; // 相当于分母多乘一个p
}
return ans;
}
通过这个算法,可以很快计算出n!的末尾有多少个零:由于末尾0的个数等于n!中因子10的个数,而这又等于n!中质因子5的个数,因此,只要代入cal(n, 5)就可以得到结果
理解可以参考:为什么是5?
解法三
n!中质因子p的个数,实际上等于1~n中p的倍数的个数\(\frac{n}{p}\)加上\(\frac{n}{p}!\)中质因子p的个数
int cal(int n, int p){
if(n < p) return 0; // n<p时 1~n中不可能有质因子p
return n / p + cal(n / p, p); // 返回n/p加上(n/p)!中质因子p的个数
}
组合数的计算
计算\({C_{n}}^{m}\)
方法一:通过定义计算
\[{C_{n}}^{m}=\frac{n!}{m!(n-m)!}
\]
这个阶乘相当庞大,能接受数据范围很小,即使用long long也只能承受n≤20
long long C(long long n, long long m){
long long ans = 1;
for(long long i = 1; i <= n; i++){
ans *= i;
}
for(long long i = 1; i <= m; i++){
ans /= i;
}
for(long long i = 1; i <= n - m; i++){
ans /= i;
}
return ans;
}
方法二:通过递推公式计算
\[{C_{n}}^{m}={C_{n-1}}^{m} + {C_{n-1}}^{m-1}
\]
即:从n个不同的数中选m个,变成两种方案之和:
- 不选最后一个数,从前n-1个数中选m个数
- 选最后一个数,从n-1个数中选m-1个数
long long C(int n, int m){
if(m == 0 || n == 0) return 1; // C(n, 0) = C(n, n) = 1
return C(n - 1, m) + C(n - 1, m - 1);
}
这种算法没有递推,但是会产生重复计算,所以要记录下已经计算过的C(n,m),在下次碰到时就可以直接作为结果返回
long long res[67][67] = {0};
long long C(long long n, long long m){
if(m == 0 || n == 0) return 1;
if(res[n][m] != 0) return res[n][m];
return res[n][m] = C(n - 1, m) + C(n - 1, m - 1);
}
这种算法时间复杂度为O(n^2)
方法三:通过定义式变形

long long C(int n, int m){
long long ans = 1;
for(long long i = 1; i <= m; i++){
ans = ans * (n - m + i) / i; // 注意先乘再除
}
return ans;
}
时间复杂度为O(m)
计算\({C_{n}}^{m}\)%p
通过递推公式计算:基于上面的方法二,是最容易、最实用的一种方法,只需要在原先代码中适当的地方对p取模即可
int res[1010][1010] = {0};
int C(int n, int m, int p){
if(m == 0 || m == n) return 1;
if(res[n][m] != 0) return res[n][m];
return res[n][m] = (C(n-1, m) + C(n-1, m-1)) % p;
}
Write by Gqq

浙公网安备 33010602011771号