常见数学问题

最大公约数:辗转相除法, 0和a的最大公因子为a;

int gcd(int a, int b){
    if(b==0) return a;
    return gcd(b, a%b);
}
int gcd(int a, int b){
    return !b ? a : gcd(b, a%b);
}

a和b的最小公倍数: 设a,b的最大公因子为d, 则最小公倍数为ab/d, 但是ab可能会导致溢出,应该先算除法再算乘法,即最小公倍数为a/d*b

1 int gcd(int a, int b){
2     return !b ? a : gcd(b, a%b);
3 }
4 
5 int lcm(int a, int b){
6     return a/gcd(a, b)*b;
7 }

分数运算:

用结构体保存分子,分母;为了避免乘法溢出, 分子分母用long long 保存, 满足以下条件

1.让分母非负数

2.如果分母为0, 规定分子为1

3.分子,分母互质

分数的化简:

1.如果分母为负, 分母取绝对值, 分子取相反值

2.求出分子分母绝对值的gcd,然后约分

分数除法:用除法前,先判断除数的分母是否为0, 如果是就不要调用除法函数啦

分数的输出:一般有这样的要求:

1.如果分子大于分母 以带分数的形式输出

2.如果分子除以分母为整数,就不输出分母;

3.真分数输出最简形式

 1 struct Fraction{long long up, down;};
 2 
 3 Fraction reduction(Fraction result){
 4     if(result.down<0){
 5         result.down = -result.down;
 6         result.up = -result.up;
 7     }else if(result.up==0) result.down=1;
 8     else{
 9         int d=gcd(abs(result.up), abs(result.down));
10         result.up /= d;
11         result.down /= d;
12     }
13     return result;
14 }
15 
16 Fraction add(Fraction a, Fraction b){
17     Fraction result;
18     result.up = a.up*b.down + b.up*a.down;
19     result.down = a.down*b.down;
20     return reduction(result);
21 }
22 
23 Fraction minu(Fraction a, Fraction b){
24     Fraction result;
25     result.up = a.up*b.down - b.up*a.down;
26     result.down = a.down*b.down;
27     return reduction(result);
28 }
29 
30 Fraction multi(Fraction a, Fraction b){
31     Fraction result;
32     result.up = a.up*b.up;
33     result.down = a.down*b.down;
34     return reduction(result);
35 }
36 
37 Fraction divid(Fraction a, Fraction b){
38     Fraction result;
39     result.up = a.up*b.down;
40     result.down = a.down*b.up;
41     return reduction(result);
42 }
43 
44 void output(Fraction a){
45     a = reduction(a);
46     if(a.down==1) printf("%d
", a);
47     else if(abs(a.up)>abs(a.down)) printf("%d %d/%d
", a.up/a.down, a.up%a.down, a.down);
48     else printf("%d/%d
", a.up, a.down);
49 }

素数判断:循环次数最好不要用i*i<=n 来进行, 因为当i比较大的时候,可能溢出;  复杂度为O(N^1.5)) ; 在10^5范围内这个复杂度是可以接受的

1 bool isPrime(int n){
2     if(n<2) return false;
3     int cnt = (int) sqrt(1.0*n);
4     for(int i=2; i<=cnt; i++)
5         if(n%i==0) return false;
6     return true;
7 }

高效的求解素数的方法:筛选法 复杂度为nloglogn

能求出1~maxn之间的素数个数, 素数,以及是否是素数; 

 1 const int maxn = 101;
 2 int prime[maxn],  vis[maxn]={0};
 3 int getPrime(){
 4     int pNum=0;
 5     for(int i=2; i<maxn; i++){
 6         if(!vis[i]){
 7             prime[pNum++]=i; 9             
        for(int j=i+i; j<maxn; j += i) vis[j] = 1; 11 } 12 } 13 return pNum; 14 }

还有一种比较简单的素数表的建立方法

1 const maxn=50000;
2 vector<int> prime(maxn, 1);
3 for(int i=2; i<maxn; i++){
4      for(int j=2; j*i<maxn; j++)
5          prime[i*j]=0;  
6 }

2*3*5*7*11*13*17*19*23*19的结果已经超出int的范围,所以要求解某个数字的素数因子的时候,只需要一个大小为10的数组来保存就行了;

例题:https://www.cnblogs.com/mr-stn/p/9225971.html

求n!中有多少个素数因子;

1 int cal(int n, int p){
2      if(n<p) return 0;
3      return n/p + cal(n/p, p);
4 }

这个方法可以方便的求解n!的结尾有多少个0,n!末尾0的个数就是因子10的个数,等于因子5的个数(因为n!中2出现的频率远高于5),所以n!末尾0的个数等于cal(n, 5)

排列组合的计算:

1 long long res[67][67]={0};
2 long long C(long long n, long long m){
3     if(m==0 || n==m) return q;
4     if(res[n][m]) return res[n][m];
5     return res[n][m] = c(n-1, m) + c(n-1, m-1);  
6 }
 1 const int n=60;
 2 long long res[n][n]={0};
 3 void calC(){
 4     for(int i=0; i<=n; i++){
 5         res[i][0]=res[i][i]=1;
 6     }
 7     for(int i=2; i<=n; i++){
 8          for(int j=0; j<=i/2; j++){
 9               res[i][j] = res[i-1][j]+res[i-1][j-1];
10               res[i][i-j]=res[i][j];
11           }
12    }
13 }

更为简洁但是计算范围小一点的方法:在n=67, m=31的时候溢出;上面一种方法在n=67, m=33开始溢出

1 long long calC(int n, int m){
2     long long ans=1;
3     for(long long i=1; i<=m; i++)
4         ans = ans*(n-m+i)/i;
5     return ans;
6 }

一般处理阶乘问题都是进行取模

有疑惑或者更好的解决方法的朋友,可以联系我,大家一起探讨。qq:1546431565
原文地址:https://www.cnblogs.com/mr-stn/p/9225649.html