zoukankan      html  css  js  c++  java
  • 算法中基本数学问题详细总结(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;
    }
    
  • 相关阅读:
    Reverse Bits
    Jump Game
    Valid Palindrome
    【计算几何初步-线段相交】【HDU1089】线段交点
    【数位DP】【HDU2089】不要62
    【二分图最大匹配】【HDU2063】过山车
    【分割平面,分割空间类题】【HDU1290 HDU2050】
    【特殊的图+DP】【11月校赛】大家一起玩游戏
    【考虑周全+数学变形】【11月赛】Is it a fantastic matrix?
    【进制问题】【HDU2056】A + B Again
  • 原文地址:https://www.cnblogs.com/tsruixi/p/13328058.html
Copyright © 2011-2022 走看看