高精度算法
提问时间来喽(0v0!):
问题(1):
什么是高精度算法?为什么需要高精度算法?高精度算法可以用来干什么?
问题(2):
如何将一个很大很大很大的数字存储下来呢?
问题(3):
怎么能将他们加起来呢?减去?相乘?相除呢?----->模拟小学数学!
通用知识点来喽(ovo!):
知识点(1):
针对问题(1):
在C++中当你用int、long long,甚至是unsigned long long 都无法处理的超级巨大数字,你会感到无比痛苦甚至到绝望,那么我们此时就只有一种方法了——高精度算法。
高精度算法:属于处理大数字的数学计算方法,是用计算机对于超大数据的一种模拟加,减,乘,除等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。
c语言知识回顾:
知识点(2):
使用c++ stl库中的vector数组(在这里只做了简单的vector的用法,具体底层的原理和容器的相关知识,有兴趣的同学自行学习)
使用vector来存储大数是因为vector的用法基本和数组没有区别,而且他还更加智能,聪明!他不像数组那么笨(~ ̄(OO) ̄)ブ,需要提前开设数组的大小,它可以随时需要使用的时候随时开一块新的空间提供给新的数据,而且它的方法也更多,方便我们进行计算!
vector小聪明来喽:
C++中的vector是一个可以改变大小的数组,当解题时无法知道自己需要的数组规模有多大时可以用vector来达到最大节约空间的目的。使用时需要包含vector头文件。
定义一个一维动态数组的语法为
vector<int> a; //int为该动态数组的元素数据类型,可以为string、double等
[1,2]
定义一个二维动态数组的语法为
vector<int*> a; //三维数据类型为int**,以此类推。
C++中vector的基本操作有:
这里举一个简单的例子:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> a;
vector<int> b;
for (int i = 0; i < 10; i++) //向数组a依次添加0~9
a.push_back(i);
a.swap(b); //将数组a元素与数组b元素交换
cout << a.size() << " " << b.size() << endl; //此时应当输出 0 10
for (vector<int>::iterator it = b.begin(); it != b.end(); it++)//从第一个元素开始遍历数组元素
cout << *it << " "; //依次输出0~9
cout << endl;
b.erase(b.begin() + 1); //删除位置1的元素,即元素1.
cout << b.size() << endl; //由于删除了一个元素,此时输出应当为8
for (vector<int>::reverse_iterator rit = b.rbegin(); rit != b.rend(); ++rit)//逆向输出数组元素
cout << *rit << " "; //应当输出9 8 7 6 5 4 3 2 0
cout << endl;
b.resize(9); //将数组空间设定为9,相当于比之前多了1个位置
b.push_back(20);//在尾部添加元素20
for (vector<int>::iterator it = b.begin(); it != b.end(); it++)
cout << *it << " "; //应当输出0 2 3 4 5 6 7 8 9 20
return 0;
}
思路过程来喽(=v=!):
加法:
重点分析:
- 一个很大的数字在数组中是倒着存储的,这是因为一旦发生进位,我们需要开辟一块新空间,将进位得到的数字填写进去,如果一个数的最大的位在数组的第一位,那么它产生进位后我们需要将整个数组向后移动才能腾出来新的空间,所以我们干脆将最大的一位放在最后,这样可以有效解决进位问题
- 加法的实质:A[i] ( 数组A的第i位 )+B[i] (数组B的第i位) +t(进位)
减法:
重点分析:
-
字符串的比较和字符串数字比较的区别:
字符串:
如(James和Jan)作比较,取决于ASCLL值,两个字符串,前两个字符相同,第三个字符n的Ascll码值比m大,所以Jan>James
字符串数字:
1235和12345作比较,在人的印象中肯定是12345比较大,但是计算机中1235这个字符串却比12345大,原因是因为第四个字符的ASCLL码的大小影响导致。所以在比较数字字符串大小的时候,不能单纯的使用a.compare(b)这个函数,要我们手动写一个函数!
数字字符串大小判断函数:
bool cmp(string a,string b){ if(a.size()!=b.size())return a.size()>b.size(); for(int i=0;i<a.size();i++){ if(a[i]!=b[i])return a[i]>b[i]; } return true; }
-
A-B大小分析
A-B>=0—>A-B
A-B<0—>-(B-A)
-
实质:
t=A[i]-B[i]-t;
若t>=0 C[i]=t; t=0;
若t<0 C[i]=t+10; t=1;
-
前导零问题:
有时候会在相减过程中出现前导零
则使用
while(C.size()>1&&C.back()==0)C.pop_back();
乘法:(大正整数*小正整数)
重点分析:
-
实质:
t+=A[i]*b;
C .push _ back(t%10);
t/=10;
-
进位问题
当乘法全部做完后,若发现最后t>0这时候代表着,我们被乘数的最高位和我们的乘数相乘得到了一个大于10的数,这时候我们进行继续进位
通过:
while(t){ C.push_back(t%10); t/=10; }
-
前导0 (0*123456)
相同的公式
while(C.size()>1&&C.back()==0)C.pop_back();
除法:
重点分析:
-
实质:
t=t*10+A[i];
C .push_back(t/b);
t=t%b;
-
注意除法是从高位向低位计算
for(int i=A.size()-1;i>=0;i--);
在做完运算后,通过reverse倒转数组,记得添加#include < algorithm > 头文件
reverse(C.begin(),C.end());
-
注意处理前导0
while(C.size()>1&&C.back()==0)C.pop_back();
代码模板来喽(o!):
加法模板:
#include<iostream>
#include<vector>
using namespace std;
vector<int> add(vector<int> &A,vector<int> &B){//使用引用直接调用原有地址,修改原有数组,而不需要开辟新空间,节约时间
if(A.size()<B.size())return add(B,A); //因为两个数字的长度有大有小,我们算法设计的前提是根据打的数字来考虑的,所以颠倒一下就好了!
vector<int> C;//答案数组
int t=0;//进位标志
for(int i=0;i<A.size();i++){//此时A数组一定比B数组大,根据大的数组来设计的for循环
t+=A[i];//相当于t+A
if(i<B.size())t+=B[i];//如果B还没加完,就相当于t(进位)+A(数字A)+(数字B)
C.push_back(t%10);//通过t%10得到的余数为当前位置该存放的数字
t/=10;//t/=10得到进了多少位
}
if(t) C.push_back(t);//全部结束后,如果t!=0表示最大位置还有进位,此时将进位结果新建一位给与
return C;
}
int main(){
string a,b;//因为a,b可能会巨大无比!所以要用字符串来存哦!
vector<int> A,B;//这里相当于数组,字符串的每一位代表数组的每一位,我们选用vector来存储字符串
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');//将a字符串中的每一位,倒序存储在A中
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');//如上句话一样
vector<int> C=add(A,B);//调用加法运算函数
for(int i=C.size()-1;i>=0;i--)cout<<C[i];
return 0;
}
减法模板:
#include<iostream>
#include<vector>
using namespace std;
bool cmp(string a,string b){
if(a.size()!=b.size()) return a.size()>b.size();
for(int i=0;i<a.size();i++){
if(a[i]!=b[i])return a[i]>b[i];
}
return true;
}
vector<int> sub(vector<int> &A,vector<int> &B){
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++){
t=A[i]-t;
if(i<B.size())t-=B[i];
if(t>=0){
C.push_back(t);
t=0;
}else{
C.push_back(t+10);
t=1;
}
}
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
int main(){
string a,b;
cin>>a>>b;
vector<int> A,B;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
if(cmp(a,b)){
vector<int> C=sub(A,B);
for(int i=C.size()-1;i>=0;i--)cout<<C[i];
}else{
vector<int> C=sub(B,A);
cout<<"-";
for(int i=C.size()-1;i>=0;i--)cout<<C[i];
}
}
乘法模板:
#include<iostream>
#include<vector>
using namespace std;
vector<int> mul(vector<int> &A,int b){
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++){
t+=A[i]*b;
C.push_back(t%10);
t/=10;
}
while(t){
C.push_back(t%10);
t/=10;
}
return C;
}
int main(){
string a;
int b;
cin>>a>>b;
vector<int> A;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
vector<int> C=mul(A,b);
for(int i=C.size()-1;i>=0;i--)cout<<C[i];
return 0;
}
除法模板:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//r=r*10+A[i]
vector<int> div(vector<int> &A,int b,int &r){
vector<int> C;
for(int i=A.size()-1;i>=0;i--){
r=r*10+A[i];
C.push_back(r/b);
r%=b;
}
reverse(C.begin(),C.end());
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
int main(){
string a;
int b;
cin>>a>>b;
vector<int> A;
int r=0;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
vector<int> C=div(A,b,r);
for(int i=C.size()-1;i>=0;i--)cout<<C[i];
cout<<endl<<r;
return 0;
}