初見 | 基礎算法 | 高精度


更改
2021.2.6
更改了文末統計字數的方式

1 高精度算法

正經的定義是:

高精度算法(High Accuracy Algorithm)是處理大數字的數學計算方法。在一般的科學計算中,會經常算到小數點后幾百位或者更多,當然也可能是幾千億幾百億的大數字。一般這類數字我們統稱為高精 度數,高精度算法是用計算機對於超大數據的一種模擬加,減,乘,除,乘方,階乘,開方等運算。對於非常龐大的數字無法在計算機中正常存儲,於是,將這個數字拆開,拆成一位一位的,或者是四位四位的存儲到一個數組中, 用一個數組去表示一個數字,這樣這個數字就被稱為是高精度數。高精度算法就是能處理高精度數各種運算的算法,但又因其特殊性,故從普通數的算法中分離,自成一家。

但是,作為一個剛入門的,我個人的理解就是

用數組以按照數位拆分一個大於double/long long等范圍的數據,然后進行處理。
至於好處,我認為,就是對於超大(高精度)數據的使用
當然肯定比處理一般的數據麻煩一點,要不然單列出來學干嘛

2 高精度加法

①算法分析

上來一看,這個數據被拆分成了一個個位,作為計算能力非常之差的憨批,我很容易就想到了早在小學就學習過的最基礎的加法計算方法:列 豎 式

(圖源度娘 相信大家都對這陌生了,但是我兩位數加兩位數天天用
這個的基礎就是兩個數對應的每個數位相加,但是這樣就涉及一個問題:進 位
不過這個還是挺簡單的,兩個一位數相加最多也就進1,於是我們馬上就可以愉快地開始用代碼實現了。

②代碼實現

首先是理一下思路:

  1. 錄入數據:用字符串/字符數組接收,再逆序存到int/float/double數組,以便於計算
    (這個地方倒序的原因是如果牽扯到進位,正序的話進位,下標要-1,要是第一個進位的話......那數組會非常的開心,我也是。於是用倒序,進位的時候下標+1,只要數組開的夠長就好了,最后逆序輸出就好了)
  2. 進行加法運算:根據剛才算法分析中的用while循環完成就可以了(雖然我個人比較喜歡用for,但是那樣有麻煩了億點
  3. 輸出數據:因為是逆序輸入,所以逆序輸出。

以下是代碼

害怕自己以后看不懂所以加了很多很Dai的注釋

//高精度加法 
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int a[201],b[201],c[201];//三個int數組來儲存數據進行運算 
int main()
{
	char na[201],nb[201];
	//兩個用來接收數據的字符串(字符數組) 
	gets(na);
	gets(nb); 
	//接收數據
	int la=strlen(na);
	int lb=strlen(nb);
	//計算長度 
	for(int i=0;i<la;i++){
		a[la-i]=na[i]-'0';
	}
	for(int i=0;i<lb;i++){
	b[lb-i]=nb[i]-'0';
	}
	//將數據倒序存入 
	int lc=1,x=0;
	//這里的x是進位的數 ,初始為0 
	while(lc<=la||lc<=lb){//這里算到不包括最后一次進位的最高位
		c[lc]=a[lc]+b[lc]+x;//這里是a+b+進位數 
		x=c[lc]/10;//讓進位數等於結果的十位 
		c[lc]%=10;//讓此位結果變為初步結果的個位 
		lc++;//要計算的位數+1 
	}		
	//加法計算的核心,這里算到不包括最后一次進位的最高位 
	c[lc]=x;
	//如果最后一次計算有進位數,讓進位數等於新高位
	if(c[lc]==0)
		lc--;
	//如果沒有進位,那么把lc--以便輸出
	for(int i=lc;i>0;i--)
	cout<<c[i];
	cout<<endl;
	//輸出,收尾(	
	return 0;
}

學的時候沒覺出來,自己敲的時候發現還是得注意一些小細節的,比如要判斷最后的lc這一位是否等於0
當然應該是我太菜了才會把這些東西當成細節

3 高精度減法

①算法分析

和加法一樣,用列 豎 式的方法,這里只需要考慮不 夠 減 要 借 位的問題。

(圖源某小學課件 又是大家非常陌生而我每次兩位數減一位數的時候都要用的東西
那么具體的解決方法就是不夠減的時候本位+10,上一位-1,非常簡單

②代碼實現

還是理一下思路:

  1. 錄入數據:用字符串/字符數組接收,再逆序存到int/float/double數組,以便於計算
  2. 進行減法運算
    1. 判斷被減數是否小於減數,如果是的話交換順序並先輸出一個負號
    2. while+判斷完成算法分析中分析到的借位問題
  3. 輸出數據:因為是逆序輸入,所以逆序輸出。
    應該沒有人發現我是復制的

以下是代碼

很Dai的注釋更多了,各位看着笑笑就好

//高精度減法 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a1[256]={0},a2[256]={0},a[256]={0};
//聲明來存數的數組 
int main()
{
	char n1[256],n2[256],m[256];//用於接收數據的字符串(字符數組),以及m這個后面用來交換用的中間量 
	gets(n1); 
	gets(n2);
	//接收數據,這里默認是n1-n2
	if((strlen(n1)<strlen(n2))||(strlen(n1)==strlen(n2)&&strcmp(n1,n2)<0))
	//判斷被減數是否小於減數
	//第一個判斷條件很好懂,是被減數的位數少於減數
	//第二個是當兩者位數相同時,使用strcmp進行比較
	/*strcmp:
	
	strcmp(n1,n2)就是從n1[0]和n2[0]開始比較(以ASCⅡ里的順序來比較)
	若前者大,則返回大於0的數 
	反之,返回小於0的數
	若相同,則返回0 
	
	(這樣理解,可能不准確,於是去問了下度娘:)
	
	strcmp函數是string compare(字符串比較)的縮寫,
	用於比較兩個字符串並根據比較結果返回整數。
	基本形式為strcmp(str1,str2),若str1=str2,則返回零;
	str1<str2,則返回負數;若str1>str2,則返回正數。
	(嘛,還是差不多的) 
	*/ 
	{
		strcpy(m,n1);
		strcpy(n1,n2);
		strcpy(n2,m);
		/*
		strcpy就是用后者賦值給前者,復制貼貼 
		
		怕自己這個蒟蒻看不懂於是又去了一趟度娘(蒟蒻的日常) 
		
		strcpy,即string copy(字符串復制)的縮寫。
		strcpy是C++語言的一個標准函數 ,
		strcpy把含有'\0'結束符的字符串復制到另一個地址空間,
		返回值的類型為char*。
		*/ 
		cout<<"-";//先輸出一個負號,這樣后面就可以安心的運算了(蒟蒻發言) 
	} 
	int l1=strlen(n1),l2=strlen(n2);
	
	for(int i=0;i<l1;i++)
	a1[l1-i]=int(n1[i]-'0');
	for(int i=0;i<l2;i++)
	a2[l2-i]=int(n2[i]-'0');
	//將數據存入int類型數組,不用多說
	int x=1;
	//x代表要計算的是第幾位數 
	while(x<=l1||x<=l2)
	{
		if(a1[x]<a2[x])
		{
			a1[x]+=10;
			a1[x+1]--;
		}//這里的意思就是如果不夠減就向高位借10,高一位-1,此位+10 
		a[x]=a1[x]-a2[x];
		x++;	
	}
	//減法核心程序
	int l=x; 
	while(a[l]==0&&l>1)
	l--;
	//如果最高位是0,那么x--以免最高位是0 
	for(int i=l;i>=1;i--)
	cout<<a[i];
	cout<<endl;
	//收尾(來自蒟蒻的喜悅,前幾次沒成功竟然是因為最后輸出的時候打成i++了,果然是Dai) 
	return 0;
 } 

也就我能把這么基礎的東西打75行了,自卑

4 高精度乘法

①算法分析

個人認為這是本次學習最難的地方了(畢竟菜)
但是如果前面的高精度加減法沒有問題的話,這個理解起來也比較簡單。
和前面的思路差不多,我還是從列 豎 式入手

相信各位現在或許還時常能見到這個東西。
我們根據豎式來算的話,就是用a[i]去乘b[1],b[2],b[3]...,然后每次i在變化的時候需要錯位,這個還是比較好想的,讓接收答案的數組c的下標是c[內層循環變量+外層循環變量-1]即可。(當然我最一開始想的時候沒太想通,於是舉了個例子,在這次的高精度學習之中,我個人覺得舉例子是個非常助於理解的方法)
還有一點就是,在進位時的問題,這個也比較好解決。參照剛才加法的方法,可以聲明一個變量x來代表每次進位的數,只不過這里不只是進位1了,而是最多進位9,我們還是可以用當前結果/10的方法來完成計算進位的多少。
實際上分析完這些,乘法也沒有難很多。

②代碼實現

  1. 輸入數據,和之前一樣。

  2. 進行乘法運算

    1. 得到當前的數據,即本次乘積+上次計算結果(最初默認為0)+進位(默認為0)
    2. 用本次數據求出本次產生進位本次數據最終結果(即%10)
  3. 逆序輸出,和之前一樣

這次分析的多了點,上代碼

多理解還是很有用的(至少對我來說),理解了加和減,乘法也能觸類旁通的亞子

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a[256],b[256],c[256000],x=0;
int main()
{
	char s1[256],s2[256];
	scanf("%s",s1);
	scanf("%s",s2);
	int l1=strlen(s1),l2=strlen(s2);
	for(int i=0;i<=l1-1;i++)
	a[l1-i]=s1[i]-'0';
	//沒想到啊,查了15分鍾錯誤,原來是這一行的s1[i]-0 打成了s1[i]=0 
	for(int i=0;i<=l2-1;i++)
	b[l2-i]=s2[i]-'0';
	//前邊的就和高精度加減法一樣 
	for(int i=1;i<=l1;i++)
	{
		x=0;
		for(int p=1;p<=l2;p++)
		{
			c[i+p-1]=c[i+p-1]+x+(a[i]*b[p]);
		  //上次處理的數+進位+當前乘積 
			x=c[i+p-1]/10;
			//x是進位數 
			c[p+i-1]%=10;
			//當前結果取余10 
		}
		c[l2+i]=x;
		//當內循環結束后,當前進位是下一位 
	}
	//這里是乘法核心程序,注解都在上面了,實際上結合高精度的加減法來說理解起來並不難 
	int l3=l1+l2;
	while(c[l3]==0&&l3>1)
	l3--;
	for(int i=l3;i>=1;i--)
	cout<<c[i];
	cout<<endl; 
	//收尾>o< 
	return 0;
}

5 總結

本次學習的新知識點

  • 對於高精度算法的進一步了解,以及了解其使用的范圍
  • 掌握高精度算法的加減乘如何實現

本次學習的感悟

  • 要耐心理解,現在知識點的難度不和之前一樣了

本次學習欠缺

  • 還沒練題

End

2021.1.28
2915詞
5555字符


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM