数学问题

一 %运算符

以 a % b 语句为例,我们先不加说明的指出该运算的特点。其运算在行为上好像是按如下步骤进行的,首先计算出a的绝对值被b的绝对值除所得的余数,再使该余数的符号与a保持一致。即若a为正数,则该表达式结果必为非负数(可能为0);若a为负数,则表达式结果必为非正数(可能为0)。而表达式结果与

b 的符号没有直接关系,即 a % -b与a % b的结果相同。

常用方法如下:

(a * b)%c ==(a%c * b%c)%c;

(a + b)%c ==(a%c++ b%c)%c;

((a - b)%c +c)%c

二 数位拆解

数位拆解即把一个给定的数字(如 3241)各个数位上的数字拆开,对10整取或取余等,分离各个位数即可。

/***输入a,b,对他们数位分解后,做特殊乘法,如123*45=1*4+1*5+2*4+2*5+3*4+3*5*******/  
/***1,数位分解后,保存到整型数组,再相乘处理;2,直接输入到字符数组,再相乘******/  
#include <iostream>  
using namespace std; //int范围:-2147483648---2147483647,因此int最大长度为10,故大精度整数中可设置一个int单位保存十个位数  
/* 
int main(){ 
    int a, b; 
    while(cin >> a >> b){ 
        int ans1[11], ans2[11]; 
        memset(ans1, 0, sizeof(ans1)); //对数组初始化,整型数组中的值不全为0 
        memset(ans2, 0, sizeof(ans2)); 
        int size1 = 0, size2 = 0;; 
        do{ 
            ans1[size1++] = a%10; 
            a /= 10; 
        }while(a); 
        do{ 
            ans2[size2++] = b%10; 
            b /= 10; 
        }while(b); 
        int sum = 0; 
        int i, j; 
        for(i = 0; ans1[i]; i++) 
            for(j = 0; ans2[j]; j++) 
                sum += ans1[i]*ans2[j]; 
        cout << sum <<endl; 
    } 
    return 0; 
} 
*/  
int main(){  
    char a[11], b[11];  
    while(cin >> a >> b){  
        int i, j, sum = 0;  
        for(i = 0; i < strlen(a); i++)  
            for(j = 0; j < strlen(b); j++)  
                sum += (a[i]-'0')*(b[j]-'0');  
        cout << sum << endl;  
    }  
    return 0;  
}</span>  

3,进制转换

若题目要求整数比较大,则可用long long定义。
从 m 进制转换到 n 进制:1.从 m 进制转换到十进制;2.再从十进制转换到 n 进制。
对某进制数最开始取余得到的数为个位数,若将依次取余的数分别保存到整型数组的下标0,下标1......处,因此输出时需要逆序输出才能保证先输出的为最高位。如十进制数12356,保存在数组中,下标0-下标4:6 5 3 2 1,逆序输出为12356。
具体实现见代码:a+b.cpp和decimaltrans.cpp。

/***a+b.cpp:</span>输入m进制,和十进制long long a, b,输出m进制a+b。输入0时结束输入*******/  
/***先保存十进制c=a+b,对c进行m进制分解每位数,保存到数组中,对数组反向输出*************/  
#include <iostream>  
using namespace std;  
int main(){  
    long long a, b, c; //long long占64B  
    int m;  
    char ans[110];  
    while(cin >> m >> a >> b && m != 0){  
        c = a + b;  
        int i = 0, j;  
        do{  
            int tmp = c % m;  
            ans[i++] = tmp > 10 ? 'a'+tmp-10:'0'+tmp;  
            c /= m;  
        }while(c);  
        for(j = i-1; j >= 0; j--)  
            cout << ans[j]; //输出过多,可能是由于写成了死循环,如此处写成了j++  
        //cout << 1 << '1' << endl;  
        cout << endl;  
    }  
    return 0;  
}  

 

/***decimaltrans.cpp:输入进制a, a进制数n, 转换进制b,输出对应的b进制数,输出以大写字母******/  
/***先将n转换为十进制,这需要n加权累加;之后并可用数组保存各位数并逆序输出********/  
#include <iostream>  
using namespace std;  
int main(){  
    int a, b;  
    char str[110];  
    char ans[110];  
    while(cin >> a >> str >> b && a != 0){//判断时别以str[i]<=‘9’,而是以str[i] <= ‘9’判断  
        int i, j, q = 1;  
        long sum = 0;  
        for(i = strlen(str)-1; i >= 0; i--){  
            int tmp;                 
            if(str[i] >= '0' && str[i] <= '9') tmp = str[i] - '0';  
            else if(str[i] <= 'z' && str[i] >= 'a') tmp = str[i] - 'a' + 10;  
            else tmp = str[i] - 'A' + 10;  
            sum += q*tmp;  
            q *= a;  
        }  
        //cout << sum;  
        i = 0;  
        do{  
            int temp = sum % b;  
            ans[i++] = temp > 10? 'A'+temp-10 : '0'+temp;  
            sum /= b;  
        }while(sum);  
        for(j = i - 1; j >= 0; j--){  
            cout << ans[j]; //j别输错  
        }  
        cout << endl;  
    }  
    return 0;  
}  

 

4,最大公约数

 

若a,b全为0,则他们的最大公约数不存在;若其中一个为0,则为非零的那一个数;若都不为0,则令a = b, b = a % b直到其中一个为0。

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

5,最大公倍数:(a*b)/gcd(a,b)

a、b 两数的最小公倍数为两数的乘积除以它们的最大公约数 注意(a*b)的溢出,溢出写成a/gcd(a,b)*b

六 素数筛法

确定素数(又叫质数)。素数即只能被自身和 1 整除的大于 1 的正整数,以2开始。

/***给定一个数 n,要求判断其是否为素数(0,1,负数都是非素数)*******/  
/***输入一个数后,对其进行judge函数判断,即只能被1和自身整除,则为素数*****/  
#include <iostream>  
using namespace std;  
bool judge(int a){  
    if(a <= 1) return false;  
    int i;  
    for(i = 2; i < (int)sqrt(double(a)) + 1; i++){  
        if(a % i == 0) return false;  
    }  
    return true;  
}  
int main(){  
    int n;  
    while(cin >> n){  
        if(judge(n))  
            cout << "YES" << endl;  
        else  
            cout << "NO" << endl;  
    }  
    return 0;  
} 

若一个数不是素数,则必存在一个小于它的素数为其的因数。则在我们获得一个素数时,即将它的所有倍数均标记成非素数。按照如下步骤完成工作:从 2 开始遍历 2 到 1000000 的所有整数,若当前整数没有因为它是某个小于其的素数的倍数而被标记成非素数,则判定其为素数,并标记它所有的倍数为非素数。这种算法被我们称为素数筛法。

 

/***输入一个整数 n(2<=n<=10000),输出所有小于n的个位为 1 的素数,如果没有则输出-1***/  
/***利用素数筛选算法和一个一维标记数组,标记出小于n的所有素数,并把全部素数放到一个数组里面,最后判断输出*******/  
#include <iostream>  
using namespace std;  
bool isPrime[10001];  
int Prime[10001];  
int cnt;  
void initPrime(int n){  
    int i, j;  
    for(i = 2; i <= n; i++) isPrime[i] = false;  
    cnt = 0;  
    for(i = 2; i <= n; i++){  
        if(!isPrime[i]){//直接从 i * i 开始标记。其原因是显然的,i * k (k < i)必已经在求得 k 的某个素因数(必小于 i)时被标记过了  
            for(j = i*i; j <= n; j += i)//注意此处别写成j++  
                isPrime[j] = true;  
            Prime[cnt++] = i;  
        }  
    }  
    return;  
}  
int main(){  
    int n;  
    while(cin >> n){  
        int i;  
        initPrime(n);  
        bool tag = false;  
        for(i = 0; i < cnt; i++){  
            if(Prime[i] % 10 == 1){  
                if(!tag) {cout << Prime[i]; tag = true;}  
                else  
                    cout << " " << Prime[i];  
            }  
        }  
        if(cnt == 0) cout << -1;  
        cout << endl;  
    }  
    return 0;  
}

七 分解素因数

 

对一个数 x 分解素因数即确定素数p1、p 2、... pn ,使其满足关系式:x = p1*p1*...*p2*...*pn*pn,此外,需要确定p1,p2...的幂指数。比如12=2*2*3,11=11只有1个质因数。

 

<span style="font-size:18px;">/***输入一个正整数 N(1<N<10^9),输出 N 的质因数的个数******/  
/***先用上节的素数筛选,然后一直从最小的素数开始,一直除,至不能整除就换下一个较小的素数来整除******/  
/***若遍历到最后,n 仍旧没被除成 1,则表明 n 存在一个大于 100000 的素因子,其幂指数必然为1******/  
#include <iostream>  
using namespace std;  
#define max 40000 //设置为100000时调试出错,故设置为40000  
bool isPrime[max+1];//如果最后一个素数大于40000,若其幂大于1,则超过了10^9,故幂必定为1;  
short int Prime[max+1];   //如果其不为素数,则其必定为大于40000的多个素数的乘积,超过了10^9,故必定为素数。  
short int PrimeSize;  
void initPrime(){  
    int i, j;  
    PrimeSize = 0;  
    for(i = 2; i <= max; i++)  
        isPrime[i] = false;  
    for(i = 2; i <= max; i++){  
        if(!isPrime[i]){  
            for(j = i*i; j <= max; j+=i)  
                isPrime[j] = true;  
            Prime[PrimeSize++] = i;  
        }  
    }  
    return;  
}  
int main(){  
    int n;  
    initPrime();  
    while(cin >> n && n > 1){  
        int cnt = 0, i, j;  
        for(i = 0; i < PrimeSize; i++){  
            if(n % Prime[i] == 0){  
                while(n % Prime[i] == 0){  
                    n /= Prime[i];  
                    ++cnt;  
                }  
            }  
        }  
        if(n != 1) ++cnt;  
        cout << cnt << endl;  
    }  
    return 0;  
}/**也可使用两个数组ans[]和cnt[]分别保存n的素因数和对应幂,用Primecnt来保存递增的素因数个数***/</span>  

n!的素因子算法:

1.计算器数组清零,存放 n!分解质因数后各个素因子 p(p为Prime[]从2开始依次对应的素数,即对应的幂指数可为0,下标和对应素数与Prime[]对应) 对应的幂指数;
2.计算 n/p,表示有 n/p 个整数可以向 n!提供一个 p 因子,则计数器累加 n/p,即1*..*p*..*2p*..*floor(n/p)*p..*n;故cnt[ap]=n/p;
3.计算 n/(p*p),有 n/(p*p)个整数可以向 n!提供两个 p 因子,即1*..*(p*p)*..*(2p*p)*..*(floor(n/(p*p))*p*p)..*n;由于k*p*p的集合必定属于k*p的集合,因此在cnt[ap]=n/p的基础上加上n/(p*p)即可;
4.计算 n/(p*p*p),有 n/(p*p*p)个整数可以向 n!提供三个 p 因子,1*..*(p*p*p)*..*(2p*p*p)*..*(floor(n/(p*p*p))*p*p*p)..*n;由于k*p*p*p的集合必定属于k*p*p的集合,因此在cnt[ap]=n/p+n/(p*p)的基础上加上n/(p*p*p)即可;
5,依次类推cnt[ap] = n/p + n/(p*p) + n/(p*p*p)+...,退出条件为n/(p^h)=0。对Prime[]中的素数都做此处理。

<span style="font-size:18px;">/***给定 n,a 求最大的 k,使 n!可以被 a^k 整除但不能被 a^(k+1)整除(2<=n<=1000),(2<=a<=1000)**********/  
/***筛选素数后,先对n!进行素数分解,再对a进行素数分解,分别计算对应的幂数商值,选择最小的为输出答案**********/  
#include <iostream>  
using namespace std;  
int cnt1[1001], cnt2[1001];  
bool isPrime[1001];  
int Prime[1001];  
int PrimeSize;  
void judgePrime(){  
    int i, j;  
    for(i = 2; i <= 1000; i++) isPrime[i] = false;  
    PrimeSize = 0;  
    for(i = 2; i <= 1000; i++){  
        if(!isPrime[i]){  
            for(j = i*i; j <= 1000; j+=i)  
                isPrime[j] = true;  
            Prime[PrimeSize++] = i;  
        }  
    }  
}  
int main(){  
    int n, a;  
    judgePrime();  
    while(cin >> n >> a){  
        memset(cnt1, 0, sizeof(cnt1));  
        memset(cnt2, 0, sizeof(cnt2));  
        int i, j;  
        for(i = 0; i < PrimeSize; i++){  
            //if(n % Prime[i] == 0){ //注意此处不应该加此限定条件  
                int t = n;  
                while(t){  
                    cnt1[i] += t / Prime[i];  
                    t /= Prime[i];  
                }  
            //}  
        }  
        for(i = 0; i < PrimeSize; i++){  
            if(a % Prime[i] == 0){  
                while(a % Prime[i] == 0){  
                    cnt2[i]++;  
                    a /= Prime[i];  
                }  
            }  
        }  
        int ans = 999999999;  
        for(i = 0; i < PrimeSize; i++){  
            if(cnt2[i] == 0) continue; //此处应加判断  
            else if(cnt1[i] == 0 && cnt2[i] != 0) {ans = 0; break;}  
            else if(ans > cnt1[i]/cnt2[i]) ans = cnt1[i]/cnt2[i];  
        }  
        cout << ans << endl;  
    }  
    return 0;  
}</span>  

八 二分求幂

以 2的 31 次方为例,我们首先求得 31 的二进制数 11111,在二进制表达式中 31 即被表达成(11111) = 2^0 + 2^1 + 2^2 + 2^3 + 2^4,这就是我们所需的分解结果。即拆
2 的 31 次为 2 的 0 次、1 次、2 次、3 次、4 次的乘积,即得到前文中所讲的结果。即2^31=2^1*2^2*2^4*2^8*2^16。

<span style="font-size:18px;">/***求 A^B 的最后三位数表示的整数*******/  
/***(XXXabc*YYYdef)%1000 = ((XXX000+abc)*YYYdef)%1000 = ... = (abc*def)%1000,只需对每次的乘数和乘积去余即可******/  
/***注意到 A^B 的后三位数只与 A 的后三位数和 B 有关,再使用二分求幂效率更好************/  
#include <iostream>  
using namespace std;  
int ans[12];  
int main(){  
    int a, b;  
    while(cin >> a >> b){ //对0^a,a^0做判断,运行正常  
        if(a == 0 && b == 0) break;  
        int size = 0, i;  
        while(b) {ans[size++] = b % 2; b /= 2;}  
        int result = 1;  
        for(i = 0; i < size; i++){  
            a = a % 1000;  
            result = result % 1000;  
            if(ans[i]) result = (a * result);  
            a = a * a;  
        }  
        cout << result%1000 << endl;  
    }  
    return 0;  
}</span>  

 

9,高精度整数(看我博客的数论大数的一些运算,这里就不详写了)

 

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值