Loading

算法笔记--简单数学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)\)很大时是无法接受的

解法二:

Snipaste_2020-03-25_10-12-21

所以:

\[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)

方法三:通过定义式变形

Snipaste_2020-03-25_11-39-30

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

posted @ 2020-03-25 12:04  ZHGQCN  阅读(274)  评论(0)    收藏  举报