【C++天梯计划】1.12 高精度算法(High Accuracy Algorithm)

文章目录

    • 什么是高精度算法?
    • 高精度算法结构
    • 例题1:求2的n次方
        • 题目描述
        • 输入
        • 输出
        • 样例
        • 思路
        • 代码:
    • 例题2:破译密码
        • 题目描述
        • 输入
        • 输出
        • 样例
        • 思路
        • 代码:


今天我要开启一个新计划----【C++天梯计划】
目的是通过天梯计划,通过题目和知识点串联的方式,完成C++复习与巩固。

什么是高精度算法?

高精度算法(High Accuracy Algorithm)是处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。

高精度算法结构

#include
#include
#include
using namespace std;
struct Wint:vector<int>//用标准库vector做基类,完美解决位数问题,同时更易于实现
{
    //将低精度转高精度的初始化,可以自动被编译器调用
    //因此无需单独写高精度数和低精度数的运算函数,十分方便
    Wint(int n=0)//默认初始化为0,但0的保存形式为空
    {
        push_back(n);
        check();
    }
    Wint& check()//在各类运算中经常用到的进位小函数,不妨内置
    {
        while(!empty()&&!back())pop_back();//去除最高位可能存在的0
        if(empty())return *this;
        for(int i=1; i<size(); ++i)
        {
            (*this)[i]+=(*this)[i-1]/10;
            (*this)[i-1]%=10;
        }
        while(back()>=10)
        {
            push_back(back()/10);
            (*this)[size()-2]%=10;
        }
        return *this;//为使用方便,将进位后的自身返回引用
    }
};
//输入输出
istream& operator>>(istream &is,Wint &n)
{
    string s;
    is>>s;
    n.clear();
    for(int i=s.size()-1; i>=0; --i)n.push_back(s[i]-'0');
    return is;
}
ostream& operator<<(ostream &os,const Wint &n)
{
    if(n.empty())os<<0;
    for(int i=n.size()-1; i>=0; --i)os<<n[i];
    return os;
}
//比较,只需要写两个,其他的直接代入即可
//常量引用当参数,避免拷贝更高效
bool operator!=(const Wint &a,const Wint &b)
{
    if(a.size()!=b.size())return 1;
    for(int i=a.size()-1; i>=0; --i)
        if(a[i]!=b[i])return 1;
    return 0;
}
bool operator==(const Wint &a,const Wint &b)
{
    return !(a!=b);
}
bool operator<(const Wint &a,const Wint &b)
{
    if(a.size()!=b.size())return a.size()<b.size();
    for(int i=a.size()-1; i>=0; --i)
        if(a[i]!=b[i])return a[i]<b[i];
    return 0;
}
bool operator>(const Wint &a,const Wint &b)
{
    return b<a;
}
bool operator<=(const Wint &a,const Wint &b)
{
    return !(a>b);
}
bool operator>=(const Wint &a,const Wint &b)
{
    return !(a<b);
}
//加法,先实现+=,这样更简洁高效
Wint& operator+=(Wint &a,const Wint &b)
{
    if(a.size()<b.size())a.resize(b.size());
    for(int i=0; i!=b.size(); ++i)a[i]+=b[i];
    return a.check();
}
Wint operator+(Wint a,const Wint &b)
{
    return a+=b;
}
//减法,返回差的绝对值,由于后面有交换,故参数不用引用
Wint& operator-=(Wint &a,Wint b)
{
    if(a<b)swap(a,b);
    for(int i=0; i!=b.size(); a[i]-=b[i],++i)
        if(a[i]<b[i])//需要借位
        {
            int j=i+1;
            while(!a[j])++j;
            while(j>i)
            {
                --a[j];
                a[--j]+=10;
            }
        }
    return a.check();
}
Wint operator-(Wint a,const Wint &b)
{
    return a-=b;
}
//乘法不能先实现*=,原因自己想
Wint operator*(const Wint &a,const Wint &b)
{
    Wint n;
    n.assign(a.size()+b.size()-1,0);
    for(int i=0; i!=a.size(); ++i)
        for(int j=0; j!=b.size(); ++j)
            n[i+j]+=a[i]*b[j];
    return n.check();
}
Wint& operator*=(Wint &a,const Wint &b)
{
    return a=a*b;
}
//除法和取模先实现一个带余除法函数
Wint divmod(Wint &a,const Wint &b)
{
    Wint ans;
    for(int t=a.size()-b.size(); a>=b; --t)
    {
        Wint d;
        d.assign(t+1,0);
        d.back()=1;
        Wint c=b*d;
        while(a>=c)
        {
            a-=c;
            ans+=d;
        }
    }
    return ans;
}
Wint operator/(Wint a,const Wint &b)
{
    return divmod(a,b);
}
Wint& operator/=(Wint &a,const Wint &b)
{
    return a=a/b;
}
Wint& operator%=(Wint &a,const Wint &b)
{
    divmod(a,b);
    return a;
}
Wint operator%(Wint a,const Wint &b)
{
    return a%=b;
}
//顺手实现一个快速幂,可以看到和普通快速幂几乎无异
Wint pow(const Wint &n,const Wint &k)
{
    if(k.empty())return 1;
    if(k==2)return n*n;
    if(k.front()%2)return n*pow(n,k-1);
    return pow(pow(n,k/2),2);
}
//通过重载运算符,还可以实现++、--、^、!、逻辑运算符等很多运算,十分简单,此处都不写了
int main()//你完全可以像int一般便捷地使用Wint,如下
{
    Wint a,b;
    //可以把b改成int型,仍能正常使用
    cin>>a>>b;
    cout<<(a<b)<<endl
        <<(a==b)<<endl
        <<a+b<<endl
        <<a-b<<endl
        <<a*b<<endl
        <<a/b<<endl
        <<a%b<<endl
        <<pow(a,b);
}

例题1:求2的n次方

题目描述

求 22 的 nn 次方。( 0 \le n \le 1000≤n≤100)

输入

从键盘读入一个整数 nn;

输出

请输出 22 的 nn 次方;

样例

输入
100
输出
1267650600228229401496703205376

思路

准备一个整数数组 aa ,存放 22 的 nn 次方, aa 数组默认存储一个 11 ,代表 22 的 00 次方! 循环 nn 次,每次循环都要将 aa 数组的每一位 \times 2×2 ,并进位,然后判断 aa 数组 \times 2×2 后是否多出一位,如果多出一位, aa 数组位数计数器 k++k++。
逆序输出 aa 数组的 kk 个数。

代码:

#include 
using namespace std;

int a[100] = {1};
//k代表a数组元素的个数,代表了高精度的整数的位数 
int i,j,k = 1,n; 
int main(){
	cin>>n;
	//循环n次,每次都将a数组 * 2
	for(i = 1;i <= n;i++){
		//将a数组的每一位都 * 2 
		for(j = 0;j < k;j++){
			a[j] = a[j] * 2; 
		}
		
		//逐位进位
		for(j = 0;j < k;j++){
			if(a[j] >= 10){
				a[j+1]=a[j+1]+a[j]/10;
				a[j]=a[j]%10; 
			}
		} 
		
		//判断a数组是否多出一位
		if(a[k] != 0){
			k++;
		} 
	} 
	
	//逆序输出a数组的k个数
	for(i = k - 1;i >= 0;i--){
		cout<<a[i];
	} 
}

例题2:破译密码

题目描述

我军拦截敌军传递了2个数字串,后经侦查得知,这两个数字串中包含了一个很重要的密码,这个密码的破译方式为,用这两个数字串中的大数 – 小数,得到结果后将结果转换为16进制(转换时如需使用字母请使用大写字母),就是敌军想要传递的密码。
比如,敌军传递了下列两个数字串:
99999999999999999999999999999999999999973
99999999999999999999999999999999999999999
大数 – 小数的差值 = 26,转换为16进制的结果为1A,也就是1A就是敌军想要传递的密码。
请编程实现破译密码的过程。

输入

输入有2行,分别是2个不超过200位的整数(2个数不存在前导0,也就是不会读入类似00123这样的数),且已知2个整数的差是一个不超过18位的整数。

输出

输出按题意计算出的16进制数。

样例

输入
99999999999999999999999999999999999999973
99999999999999999999999999999999999999999
输出
1A
输入
999999999
3847
输出
3B9ABAF8

思路

1.判断如果s1对应的数字比s2小,交换
2.高精减,得到结果
3.将结果计算为10进制的long long
4.将该整数转换为16进制

代码:

#include 
using namespace std;
string s1,s2;
long long x,t = 1;//t表示10的次方
int a[210],b[210],c[210];
string r,t2 = "0123456789ABCDEF";
int main(){
cin>>s1>>s2;
//1.判断大小
if(s1.size()<s2.size()||(s1.size()==s2.size()&&s1<s2)){
swap(s1,s2);
}

//2.逆序存储
for(int i = 0;i < s1.size();i++){
a[i] = s1[s1.size()-i-1] - '0';
}

for(int i = 0;i < s2.size();i++){
b[i] = s2[s2.size()-i-1] - '0';
}

//3.逐位相减
int len = max(s1.size(),s2.size());

for(int i = 0;i < len;i++){<br />
if(a[i] < b[i]){
a[i+1]--;
a[i]=a[i]+10;
}

c[i] = a[i] - b[i];
}

//4.从下标为0的位置向后计算到倒过来的第1个非0位置
//将结果转换为long long
int p = 0;
for(int i = len - 1;i >= 0;i--){
if(c[i] != 0){
p = i;
break;
}
}

for(int i = 0;i <= p;i++){
x = x + c[i] t;
t = t 10;
}

//cout<
//将x转16进制
if(x == 0){
cout<<0;
return 0;
}

while(x != 0){
r = t2[x%16]+r;
x = x / 16;
}
cout<<r;
return 0;
}

你可能感兴趣的:(#,P1,C++天梯计划,算法,c++,开发语言)