고정밀 알고리즘
1. 소개
컴퓨터를 사용하여 수치 계산을 수행할 때 때때로 다음과 같은 문제에 직면합니다. n! 의 정확한 결과는 무엇입니까?
n이 30보다 작을 때는 물론 컴퓨터에 딸린 계산기를 이용해 계산할 수 있습니다. 하지만 100!을 만나면 정확한 결과를 직접 계산할 수 있는 방법이 없습니다. 또 다른 예로, 두 개의 20,000자리 숫자의 합을 구합니다.
그렇다면 정밀도 누락 문제를 해결하는 방법은 무엇입니까?
고정확도 알고리즘은 큰 숫자를 처리하기 위한 수학적 계산 방법입니다.
- 일반적인 과학적인 계산에서는 소수점 이하 몇 백 자리 이상까지 계산이 이루어지는 경우가 많으며, 물론 수천억에서 수백억에 이르는 큰 숫자일 수도 있습니다. 일반적으로 우리는 이러한 유형의 숫자를 고정밀 숫자라고 부릅니다. 고정밀 알고리즘은 컴퓨터를 사용하여 매우 큰 데이터에 대한 덧셈, 뺄셈, 곱셈, 나눗셈, 지수화, 계승, 제곱근 및 기타 연산을 시뮬레이션합니다. 매우 큰 숫자는 컴퓨터에 정상적으로 저장할 수 없습니다.
- 그래서 이 숫자를 한 자리 또는 네 자리로 나누어 배열로 저장하고, 배열을 이용하여 숫자를 표현하므로 이 숫자를 고정밀도 숫자라고 합니다.
- 고정밀도 알고리즘은 고정밀도 숫자에 대한 다양한 연산을 처리할 수 있는 알고리즘이지만 그 특수성으로 인해 일반 숫자 알고리즘과 분리되어 자체적인 알고리즘이 됩니다.
- 고정밀 처리는 실제로 수계산을 시뮬레이션하는 시뮬레이션 방법으로 그 원리는 수직 계산을 사용할 때와 동일하지만 처리 과정에서 고정밀 데이터 읽기, 변환 및 저장에 주의해야 합니다. , 데이터 계산 및 결과 비트 숫자 계산 및 출력 과 같은 여러 문제 .
2. 고정밀도 + 저정밀도
999999999999999999와 같이 큰 숫자가 있고, 6666과 같이 작은 숫자가 있습니다. 이 두 숫자를 어떻게 합치나요?
고정밀 추가 아이디어
-
큰 숫자를 문자열에 저장합니다.
-
문자열의 각 문자 번호는 ASCII 변환을 통해 배열에 저장됩니다.
배열의 시작 부분에 하위 비트가 있어야 합니다. a[i] = s[len-i-1]-'0'; -
덧셈과 올림 계산:
① a[i+1] += a[i]/10,
② a[i] %= 10; -
숫자 오버플로, 길이 +1;
-
역방향 출력 결과;
C++ 언어 프로그래밍을 예로 들어 어떻게 수행하는지 살펴보겠습니다.
#include<iostream>
#include<string>
using namespace std;
string s1;
int a[1000],b;
int main(){
cin>>s1>>b; // 1.输入数值
코드에서 s1 배열은 큰 숫자를 저장하고 b 정수는 소수를 저장합니다.
① 1234 + 66
② 123456 + 99
수학적 덧셈 연산에 따르면 단위의 자리와 단위의 자리가 먼저 더해지게 되는데, 즉
① s1[3] + 6
② s1[5] + 9
위에서부터 가 가능하다. 단위 숫자 + 단위 숫자의 색인 첨자가 일치하지 않아 프로그래밍이 어려워지는 것을 확인했습니다. 그런 다음 배열을 역순으로 저장하는 것을 고려할 수 있습니다! 한 자리 숫자는 배열의 시작 부분인 s1[0]에 위치하며, 값이 아무리 크더라도 배열 첨자는 s1[0]으로 시작하여 덧셈을 수행합니다.
// s1存到数组a里面,记得转为整数
int len1 = s1.size(); //获取长度
for(int i=0;i<len1;i++){
a[i] = s1[len1-i-1]-'0';
}
s1은 문자열이고 ASCII 코드 테이블을 통해 정수로 변환해야 하므로 -'0'을 빼야 합니다.
좋아, 위에서 우리는 배열에 큰 숫자를 저장하는 것을 완료했으며 그런 다음 추가 작업을 수행해야 합니다.
- a[0] 위치에 먼저 소수점을 추가합니다.
a[0] +=b;
예: 1234 + 89 =》a[0] = 1323; - 그런 다음 새 한 자리를 [0]에 그대로 두고 다른 숫자에 대해 캐리 연산을 수행합니다.
a[0+1] += a[0] /10; // a[1] = 3+132 = 135
a[0] = a[0] % 10; // a[0] = 3
- 비유하자면 십의 자리가 업데이트되고 다른 자리가 수행됩니다.
//3.进行加法运算。
a[0]+=b; // 5+9999 10004
//4.进位操作
for(int i=0;i<len1;i++){
a[i+1] += a[i] / 10;
a[i] = a[i] % 10;
}
덧셈 연산 후에는 숫자 오버플로 상황을 고려해야 합니다(예: 999 +11 == 1010에는 천 자리가 추가됩니다). 이 문제의 해결 방법은 간단합니다. 가장 높은 비트가 0이 아닌지 확인하고, 조건이 만족되면 캐리 연산을 다시 수행하면 됩니다!
//5.考虑到数字溢出
while(a[len1]){
a[len1+1] += a[len1]/10;
a[len1] %= 10;
len1++;
}
결과를 출력할 때, 결과를 반대로 하는 것을 기억하세요. 이전에는 a[0]이 가장 낮은 비트이고 출력은 왼쪽에서 오른쪽으로 높은 비트에서 낮은 비트로 이루어지기 때문입니다.
//6.反向输出
for(int i=len1-1;i>=0;i--){
cout<<a[i];
}
고정밀도 + 저정밀도의 전체 코드는 다음과 같습니다.
#include<iostream>
#include<string>
using namespace std;
string s1;
int a[1000],b;
int main(){
cin>>s1>>b;
// s1存到数组a里面,记得转为整数
int len1 = s1.size();
for(int i=0;i<len1;i++){
a[i] = s1[len1-i-1]-'0';
}
//3.进行加法运算。
a[0]+=b; // 5+9999 10004
//4.进位操作
for(int i=0;i<len1;i++){
a[i+1] += a[i] / 10;
a[i] = a[i] % 10;
}
//5.考虑到数字溢出
if(a[len]){
len++;
}
//6.反向输出
for(int i=len1-1;i>=0;i--){
cout<<a[i];
}
}
3. 고정밀도 + 고정밀도
위와 비슷한 단계입니다.
고정밀 추가 아이디어:
- 큰 숫자를 문자열에 저장합니다.
- 문자열의 각 문자 번호는 ASCII 변환을 통해 배열에 저장됩니다.
배열의 시작 부분에 하위 비트가 있어야 합니다. a[i] = s[len-i-1]-'0'; - 최대 숫자 길이를 얻으십시오: max(len1,len2);
- 덧셈과 올림 계산:
① a[i+1] += a[i]/10,
② a[i] %= 10; - 숫자 오버플로, 길이 +1;
- 역방향 출력 결과;
#include<iostream>
#include<string>
using namespace std;
string s1,s2;
int a[10000],b[10000],c[100001];
int main(){
// 1.输入值,长度
cin>>s1>>s2;
int len1 = s1.size();
int len2 = s2.size();
// 2.把字符转为整数存到数组
// 注意要个位存到数组开头
for(int i=0;i<len1;i++){
a[i] = s1[len1-i-1]-'0';
}
for(int i=0;i<len2;i++){
b[i] = s2[len2-i-1]-'0';
}
두 큰 숫자 모두 문자열로 저장한 다음 정수로 변환해야 합니다. 그러면 배열 첨자는 자릿수에 따라 순차적으로 추가됩니다. 즉, a[i]+b[i]입니다. 추가를 중지할 시기는 길이가 가장 큰 숫자에 따라 결정되므로 추가하기 전에 가장 큰 숫자 길이가 필요합니다.
// 3.获取最大的数。
int len = max(len1,len2);
// 对各个位数进行相加并把最新的值存到输出C里面。
for(int i=0;i<len;i++){
c[i]=a[i]+b[i];
}
c[i] = a[i]+b[i]를 통해 10보다 큰 c[0] = 11과 같은 상황이 있을 수 있으며 캐리가 필요합니다!
//4.进位
for(int i=0;i<len;i++){
c[i+1] += c[i]/10;
c[i] %= 10;
}
여전히 마찬가지입니다.캐리 이후 오버플로 문제를 고려하여 출력이 반전됩니다.
//6.考虑到数字溢出
if(a[len]){
len++;
}
//7.反向输出
for(int i=len-1;i>=0;i--){
cout<<a[i];
}
고정밀 + 고정밀 전체 코드:
/*
高精度的加法思想
1.把大数存到字符串;
2.字符串的每个字符数字都通过ASCII转换存到数组,
注意的是要低位存在数组开头:a[i] = s[len-i-1]-'0';
3.获取最大的数长度:max(len1,len2) ;
4.把a,b值加入到c数组: c[i] = a[i]+b[i];
5.c数组加法进位的算式:
① c[i+1] += c[i]/10;
② c[i] %= 10;
6.数字溢出,长度+1;
7.反向输出结果;
*/
#include<iostream>
#include<string>
using namespace std;
string s1,s2;
int a[10000],b[10000],c[100001];
int main(){
// 1.输入值,长度
cin>>s1>>s2;
int len1 = s1.size();
int len2 = s2.size();
// 2.把字符转为整数存到数组
// 注意要个位存到数组开头
for(int i=0;i<len1;i++){
a[i] = s1[len1-i-1]-'0';
}
for(int i=0;i<len2;i++){
b[i] = s2[len2-i-1]-'0';
}
// 3.获取最大的数。
int len = max(len1,len2);
// 对各个位数进行相加
for(int i=0;i<len;i++){
c[i]=a[i]+b[i];
}
//4.进位
for(int i=0;i<len;i++){
c[i+1] += c[i]/10;
c[i] %= 10;
}
//5.溢出
while(c[len]==0 && len>0){
len--;
}
if(c[len]>0){
len++;
}
//6.反向输出
for(int i=len-1;i>=0;i--){
cout<<c[i];
}
return 0;
}
4. 고정밀도 뺄셈
이런 식으로 뺄셈이 필요한데, 두 숫자의 뺄셈이 <0인 경우 음수 '-' 기호가 출력되어야 합니다!
고정밀 뺄셈의 아이디어:
-
두 개의 큰 숫자를 입력하십시오.
-
크기를 결정하려면 s1이 항상 s2보다 크도록 수정하세요.
-
길이를 구하세요.
-
문자를 정수로 변환: a[i] = s1[len1-i-1]-'0';
-
뺄셈 연산:
① if(a[i]<b[i]){ a[i+1]–; //상위 비트 – a[i]+=10; //하위 비트+10 } ② c[i] = a[i]-b[i]; -
선행 0을 제거하십시오.
-
역방향 출력;
자, 값을 입력해 보세요. 처음 입력하는 것은 감수, 두 번째는 감수를 나타냅니다. 뺄셈에는 음수가 나올 것을 알기 때문에 감수 < 감수인 상황을 고려해야 합니다. 즉, 감수의 길이 < 피감의 길이이거나, 길이가 같으면 감수의 길이 < 피감의 값입니다. 그런 다음 '-' 기호를 출력하고 두 값을 교환합니다. 감수는 항상 피감보다 크다는 것을 영구적으로 인식하십시오! ! !
#include<iostream>
#include<string>
using namespace std;
string s1,s2;
int a[10000],b[10000],c[10000];
int main(){
// 1.输入值
cin>>s1>>s2;
// 2.判断大小,固定s1恒大于s2
if(s1.size()<s2.size() || s1.size()==s2.size() && s1<s2){
swap(s1,s2); //交换值
cout<<"-";
}
// 3.获取长度
int len1 = s1.size();
int len2 = s2.size();
// 4.字符变整数
for(int i=0;i<len1;i++){
a[i] = s1[len1-i-1]-'0';
}
for(int i=0;i<len2;i++){
b[i] = s2[len2-i-1]-'0';
}
정수로 변환하여 배열에 저장한 후 뺄셈 연산을 수행하는데, 뺄셈 규칙에 따라 뺄 수가 없으면 +10을 빌려야 하고, 빌려 오면 빼야 합니다. 1. 예를 들어 1234-66입니다. a[0] - b[0] < 0이면 +10, 즉 a[0] + 10을 빌린 다음 b[0]을 빼야 하며, 그러면 a[0+1]–;
//5.减法运算
for(int i=0;i<len1;i++){
if(a[i]<b[i]){
a[i+1]--; //被借位--
a[i]+=10; // 本位+10
}
c[i] = a[i]-b[i]; //相减结果存到数组c
}
123 -120 = 003이면 선행 0을 제거해야 합니다. 그러면 역방향으로 출력됩니다.
//6.去除前导零
while(c[len1-1]==0 && len1>1){
len1--;
}
//7.反向输出
for(int i=len1-1;i>=0;i--){
cout<<c[i];
}
고정밀 뺄셈을 위한 완전한 코드:
/*
高精度减法的思想
1.输入大数;
2.判断大小,固定s1恒大于s2:
if(s1.size()<s2.size() || s1.size()==s2.size() && s1<s2){
swap(s1,s2); //交换值
cout<<"-";
}
3.获取长度;
4.字符变整数:a[i] = s1[len1-i-1]-'0';
5.减法运算:
if(a[i]<b[i]){
a[i+1]--; //上位--
a[i]+=10; // 本位+10
}
c[i] = a[i]-b[i];
6.去除前导零;
while(c[len1-1]==0 && len1>1){
len1--;
}
7.反向输出;
*/
#include<iostream>
#include<string>
using namespace std;
string s1,s2;
int a[10000],b[10000],c[10000];
int main(){
// 1.输入值
cin>>s1>>s2;
// 2.判断大小,固定s1恒大于s2
if(s1.size()<s2.size() || s1.size()==s2.size() && s1<s2){
swap(s1,s2); //交换值
cout<<"-";
}
// 3.获取长度
int len1 = s1.size();
int len2 = s2.size();
// 4.字符变整数
for(int i=0;i<len1;i++){
a[i] = s1[len1-i-1]-'0';
}
for(int i=0;i<len2;i++){
b[i] = s2[len2-i-1]-'0';
}
//5.减法运算
for(int i=0;i<len1;i++){
if(a[i]<b[i]){
a[i+1]--; //上位--
a[i]+=10; // 本位+10
}
c[i] = a[i]-b[i];
}
//6去除前导零
while(c[len1-1]==0 && len1>1){
len1--;
}
//7.反向输出
for(int i=len1-1;i>=0;i--){
cout<<c[i];
}
return 0;
}