算法中基本数学问题详细总结(C++、最小公约数、最大公倍数、分数四则运算、素数、大整数运算)

一、最小公约数和最大公倍数

  • 最大公约数:采用辗转相除法
  • 递归式:gcd(a, b) = gcd(b, a % b);
  • 递归边界:gcd(a, 0) = a;
int gcd(int a, int b){
    return !b ? a : gcd(b, a % b);
}
  • 最小公倍数:公式a x b / d,d就是a和b的最大公约数;
int lmd(int a, int b){
    return a / gcd(a, b) * b;
}

二、分数四则运算

  • 1.分数化简和表示
struct Fraction{
    int up, down;
};
  • 2.分数化简函数
Fraction reduction(Fraction result){
    if(result.down < 0){
        result.up = -result.up;
        result.down = -result.down;
    }
    if(result.up == 0){
        result.down = 1;
    }else{
        int d = gcd(abs(a.up), abs(a.down));
        result.up /= d;
        result.down /= d;
    }
    return result;
}
  • 加减乘除的运算:
Fraction add(Fraction a, Fraction b){
    Fraction c;
    c.up = a.up * b.down + a.down * b.up;
    c.down = a.down * b.down;
    return reduction(c);
}

Fraction minu(Fraction a, Fraction b){
    Fraction c;
    c.up = a.up * b.down - a.down * b.up;
    c.down = a.down * b.down;
    return reduction(c);
}

Fraction multi(Fraction a, Fraction b){
    Fraction c;
    c.up = a.up * b.up;
    c.down = a.down * b.down;
    return reduction(c);
}

Fraction divide(Fraction a, Fraction b){
    Fraction c;
    c.up = a.up * b.down;
    c.down = a.down * b.up;
    return reduction(c);
}
  • 分数的输出函数
void showResult(Fraction a){
    Fraction c = reduction(a);
    if(c.down == 1) printf("%d", c.up);
    else if(abs(c.up) > c.down){
        printf("%d %d/%d", c.up / c.down, abs(c.up) % c.down, c.down);
    }else{
        printf("%d/%d", c.up, c.down);
    }
}

三、素数

1. 素数的判断

  • 定义:素数是除了1和本身外不能被其他整数整除的数,1既不是素数也不是合数;
bool isPrime(int n){
    if(n <= 1) return false;
    int sqr = (int)sqrt(n);
    for(int i = 2; i <= sqr; i++){
        if(n % i == 0) return false;
    }
    return true;
}

2. 素数表的获取

  • 方法一,在10^5的范围内可以获取;
const int maxn = 101;//表长,即想要查找的范围
int Prime[maxn], Num = 0;//数组用于存储素数,Num代表范围内的素数数量
bool isTrue[maxn] = {0};//用于判断i是否为素数
void find_Prime(){
    for(int i = 2; i < maxn; i++){
        if(isPrime(i)){
            Prime[Num++] = i;
            isTrue[i] = true;
        }
    }
}
  • 方法二, 埃氏筛法
  • 原理: 算法从小到大枚举所有数,对于每个素数,筛去它的倍数,剩下的就都是素数了;
  • isTrue中true代表不是素数,和上面正好相反;
void find_Prime(){
    for(int i = 2; i < maxn; i++){
        if(isTrue[i] == false){
            Prime[Num++] = i;因为从2开始是算素数
            for(int j = i + i; j < maxn; j += i){
                isTrue[j] = true;
            }
        }
    }
}

四、质因子分解

  • 定义,将一个正整数分解成一个或多个质数的乘积模式
  • 结构体,对于一个int的整数来说,数组开到10已经足够了;
struct factor{
    int x, cnt;//x为质因子,cnt为数量
}fac[10];

原理

  • 对于一个正整数n来说,如果它存在[2, n]范围的质因子,要么这些质因子全部都小于等于sqrt(n);要么只存在一个大于sqrt(n)的质因子,而其余的质因子都小于等于sqrt(n);
  • 枚举1~sqrt(n)范围内的所有质因子p,判断其是否为n的因子;
  • 如果p是n的因子,那么给fac数组增加质因子p,并初始化个数为0,然后只要p还是n的因子,就让n不断除以p,每次操作令p的个数加1,直到p不再是n的因子;
  • 如果p不是n的因子直接跳过;
        int num = 0;
		int sqr = sqrt(n);
		for(int i = 0; i < sum && prime[i] <= sqr; i++){
			if(n % prime[i] == 0){
				ans[num].a = prime[i];
				ans[num].sum = 0;
				while(n % prime[i] == 0){
					ans[num].sum++;
					n /= prime[i];
				}
				num++;
			}
			if(n == 1) break;
		}
		if(n != 1){
			ans[num].a = n;
			ans[num++].sum = 1;
		}

五、大整数运算

1. 大整数的存储

  • 首先明白使用数组进行存储,然后低位在数组的低位进行存储,然后高位在数组高位进行存储,为后续计算提供便利
struct bign{
    int d[1000];
    int len;
    bign(){
        memset(d, 0, sizeof(d));
        len = 0;
    }
}
  • 一般的读入大整数是按照字符串先读入的,然后将字符串存至结构体
bign change(char str[]){
    bign a;
    a.len = strlen(str);
    for(int i = 0; i < a.len; i++){
        a.d[i] = str[a.len - 1 - i] - '0';
    }
    return a;
}

2. 高精度加法

  • 保证以长的作为界限进行计算
  • 最后有进位记得加上
bign add(bign a, bign b){
   bign c;
   int carry = 0;
   for(int i = 0; i < a.len || i < b.len){//以长的为界限,不用怕,短的面前缺失的部分默认为0,相当于直接加长的
        int temp = a.d[i] + b.d[i] + carry;
        c.d[len++] = temp % 10;
        carry = temp / 10;
   }
   if(carry != 0){
       c.d[len++] = carry;
   }
   return c;
}

3. 高精度减法

  • 注意点一个数,位数不够,向高位借位
  • 还有就是可能出现高位很多0的情况,最后使用while保留一位即可;
bign sub(bign a, bign b){
   bign c;
   for(int i = 0; i < a.len || i < b.len; i++){
       if(a.d[i] < b.d[i]){//不够向高位借位
           a.d[i + 1]--;
           a.d[i] += 10;
       }
       c.d[c.len++] = a.d[i] - b.d[i]; 
   }
   while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
       c.len--;
   }
   return c;
}

4. 高精度与低精度的乘法

  • 低精度就是可用使用int进行存储的数字
  • 取bign的某一位与int型整体相乘,再与进位进行相加,所得结果的个位数作为该为的结果,高位部分作为新的进位;
  • 最后因为进位可能不只一位,使用while进行处理
bign multi(bgin a, int b){
    bign c;
    int carry = 0;
    for(int i = 0; i < a.len; i++){
        int temp = a.d[i] * b + carry;
        c.d[c.len++] = temp % 10;
        carry = temp / 10;
    }
    while(carry != 0){
        c.d[c.len++] = carry % 10;
        carry /= 10;
    }
    return c;
}

5. 高精度与低精度的除法

  • 首先将c的长度赋值和a的长度一样
  • 传参比之前多了一个r余数,使用引用,初始一般为0
  • 同时i初始是从数组的高位开始,即i = a.len - 1
  • 在跟之前的余数相加后,如果不够除商0;
  • 同时也可能出现高位很多0的情况,使用while进行删除;
bign divide(bign a, int b, int& r){
    bign c;
    c.len = a.len;
    for(int i = a.len - 1; i >= 0; i--){
        r = r * 10 + a.d[i];
        if(r < b) c.d[i] = 0;
        else{
            c.d[i] = r / b;
            r = r % b;
        }
    }
    while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
        c.len--;
    }
    return c;
}
原文地址:https://www.cnblogs.com/tsruixi/p/13328058.html