算法题当中的数学问题

进制转换

进制转换主要方法有两个,一个是十进制转为别的进制,只要不断求余数然后做除法,直到得到的数为0即可,注意得到的结果是反向的。其他进制转化为十进制则为从低位到高位不断加上对应的数,每次相加后都乘以进制数。代码如下:

//十进制转二进制
int n;
cin>>n;
vector<int> ans;
while(n!=0){
    ans.push_back(n%2);
    n/=2;
}
//ans中的二进制是反向的
//二进制转十进制
int n=0;
vector<int> binary;
//binary中按从低位到高位(反向)的顺序存储了二进制的值
for(int i=0;i<binary.size();i++){
    n+=binary[i];
    n*=2;
}

附一道练习题,10进制 VS 2进制
这道题从十进制转化为2进制,又转化为10进制,同时还包含了字符串的加除乘。

#include<iostream>
using namespace std;
string divide(string str,int x){
    int remainder=0;
    for(int i=0;i<str.length();i++){
        int current=str[i]-'0'+remainder*10;
        str[i]=current/x+'0';
        remainder=current%x;
    }
    int pos=0;
    while(str[pos]=='0'&&str.length()-pos>1)
        pos++;
    return str.substr(pos);
}
string multiple(string str,int x){
    int carry=0;
    for(int i=str.length()-1;i>=0;i--){
        int current=x*(str[i]-'0')+carry;
        str[i]=current%10+'0';
        carry=current/10;
    }
    if(carry!=0)
        str=to_string(carry)+str;
    return str;
}
string add(string str,int x){
    int carry=x;
    for(int i=str.length()-1;i>=0;i--){
        int current=str[i]-'0'+carry;
        str[i]=current%10+'0';
        carry=current/10;
    }
    if(carry!=0)
        str="1"+str;
    return str;
}
int main(){
    string n;
    while(cin>>n){
        if(n=="0"){
            cout<<"0"<<endl;
            continue;
        }
        string ans;
        while(n!="0"){
            ans.push_back((n[n.length()-1]-'0')%2+'0');
            n=divide(n,2);
            
        }
        string num="0";
        for(int i=0;i<ans.length();i++){
            num=multiple(num,2);
            num=add(num,ans[i]-'0');
        }
        cout<<num<<endl;
    }
    return 0;
}

随题附赠一个字符串的减法,不保证正确,而且不优雅

string myMinus(string str,int x){
	int carry=0;
	for(int i=str.size()-1;i>=0;i--){
		int current=str[i]-'0'-carry;
		if(current-x%10<0){
		    current+=10;
			carry=1;
		}else
			carry=0;
		str[i]=current+'0'-x%10;
		x/=10;
	}
	while(str[0]=='0'&&str.length()>1)
	    str.erase(0,1);
	return str;
}

最大公约数与最小公倍数

求最大公约数,原理是a,b的最大公约数与a mod b的最大公约数大小一致,因此可以一直取余直到某一数为0,注意,如果小数%大数,结果仍为小数,会出现死循环的情况,因此需要判断数的大小

#include<iostream>
using namespace std;
int main(){
    int a=0,b=0;
    while(cin>>a>>b){
        
        while(a!=0&&b!=0){
            if(a>b)
                a=a%b;
            else
                b=b%a;
        }
        cout<<(a==0?b:a)<<endl;
    }
}

一种不需要每轮判断大小的方式如下,需要每轮交换a与b的值(Q:如何不借助第三个变量交换两个变量的值

int main() {
    int a,b;
    while (scanf("%d%d", &a,&b) != EOF) {
        while ( b != 0) {
            int temp = a % b;
            a=b; b = temp;
        }
        printf("%d\n", a);
    }
    return 0;
}

更为常见的递归形式如下,也是交换两个变量的值

int gcd(int a, int b) {
    if (a==0||b == 0)return a + b;
    else return gcd(b, a%b);
}

求出了最大公约数后,很容易求出两数的最小公倍数,即为两数的乘积除以最大公约数

质数

  • 求单个数n是否为素数:
    只要判断[2,sqrt(n)]这个范围内的数是否有n的因子即可,值得注意的是,sqrt(n)返回值是double类型,由于精度的原因,可能出现漏掉sqrt(n)这个数的情况
  • 素数筛
    求出一个范围内的素数,因为每个合数必然存在一个质因子(每个数都可以表示成若干个质数的乘积),因此所求范围内所有质数的非1倍数即为所有合数,代码如下,注意循环开始与结束的条件
Bool num[MAXN]
void init(){
    for(int i=0;i<=MAXN;i++)
        num[i]=true;
    num[0]=false;
    num[1]=false;
    for(int i=2;i<=MAXN;i++){
        if(!num[i])
            continue;
        if(i*i>MAXN)
            continue;
        for(int j=i*i;j<=MAXN;j+=i)
            num[j]=false;
    }
}

素数筛的进一步优化:欧拉筛……

分解质因数

每个数都可以表示成若干个质数的乘积,分解方法即为用[2,sqrt(n)+1]的质数不断试除,因为一个数n最多只有一个大于sqrt(n)的因子,用上述范围的质因子与其不断相除,最终得到的非1的数即为唯一大于sqrt(n)的质因子。
附一个流程:
来自《王道考研机试》

代码如下:

#include<iostream>
#include<vector>
using namespace std;
#define MAXN 10001
bool num[MAXN];
vector<int> prime;
void init(){
    for(int i=0;i<=MAXN;i++)
        num[i]=true;
    num[0]=false;
    num[1]=false;
    
    for(int i=2;i<=MAXN;i++){
        if(!num[i])
            continue;
        prime.push_back(i);
        if(i*i>MAXN)
            continue;
        for(int j=i*i;j<=MAXN;j+=i)
            num[j]=false;
    }
}
int main(){
    init();
    int n=0;
    while(cin>>n){
        int count=0;
        for(int i=0;i<prime.size();i++){
            if(prime[i]>n)
                break;
            while(n%prime[i]==0){
                n/=prime[i];
                count++;
            }
        }

注意这里更通用的范围MAXN。

快速幂

快速幂是快速求a^b的做法,为此需要将b转化为二进制,从低位到高位依次遍历b的二进制的各位,每遍历一位就求a*=a,如果b的改位为1,则结果ans*=a,具体的原理应该挺好想的。
附一道例题 求root(N, k)
代码如下:

#include<iostream>
using namespace std;
long long fastexp(int x,int y,int k){
    int t=1;
    x=x%(k-1);
    while(y!=0){
        if(y%2==1){
            t*=x;
            t%=k-1;
        }
        x*=x;
        x%=k-1;
        y>>=1;
    }
    return t==0?k-1:t;//想想为什么有这一步
}

int main(){
    int x,y,k;
    while(cin>>x>>y>>k){
        int n=fastexp(x,y,k);
        cout<<n<<endl;
    }

思路如下:
在这里插入图片描述

矩阵与矩阵快速幂

  • 矩阵加减乘
  • 矩阵快速幂(与上类似)

字符串(长整数)计算

高精度数的计算,但是Java有BigDecimal,这也太不公平了!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值