C++ 11新標準實現POJ No.1001-Exponentiation

Exponentiation(高精度冪計算)(標籤:鏈表,字符串,快速冪計算)

題目描述

對數值很大、精度很高的數進行高精度計算是一類十分常見的問題。好比,對國債進行計算就是屬於這類問題。
如今要你解決的問題是:對一個實數R( 0.0 < R < 99.999 ),要求寫程序精確計算 R 的 n 次方(R n),其中n 是整數而且 0 < n <= 25。

Inputhtml

T輸入包括多組 R 和 n。 R 的值佔第 1 到第 6 列,n 的值佔第 8 和第 9 列。

Outputgit

對於每組輸入,要求輸出一行,該行包含精確的 R 的 n 次方。輸出須要去掉前導的 0 後不要的 0 。若是輸出是整數,不要輸出小數點。

Sample Inputgithub

95.123 12
0.4321 20
5.1234 15
6.7592  9
98.999 10
1.0100 12

Sample Output算法

548815620517731830194541.899025343415715973535967221869852721
.00000005148554641076956121994511276767154838481760200726351203835429763013462401
43992025569.928573701266488041146654993318703707511666295476720493953024
29448126.764121021618164430206909037173276672
90429072743629540498.107596019456651774561044010001
1.126825030131969720661201

解題思路數組

對於此類高精度計算,必定不能用常規的+、-、*、/運算符運算,因其會致使溢出問題,因此考慮將原數據的每一位分開,而且順序放入線性表中,模擬豎式的計算過程進行計算。在選擇線性表的種類時,根據實際須要選擇數組或鏈表。在C++ STL標準模板庫中,以數組實現的線性表的容器是vector,其容量能夠隨數據大小變化,即在開始時會根據初始數據的大小開闢一個比數據的長度稍大的空間,好比初始數組中只有一個數,可是容器會先開闢8個數據的容量。若後續對容器進行數據量的增長,會填滿這8個空位,那麼容器會自動判斷是否有空位,容量不夠的話,會從新開闢一塊更大容量的空間,而且將原數組中的內容拷貝到新的空間。這就致使,在此問題中若用數組實現線性表時,會浪費更多的程序運行時間,因此此處選擇鏈表形式的線性表,在STL中,使用線性表須要添加頭文件:函數

#include <list>

將題目中輸入的s轉存到list當中,在轉存時,爲了方便操做,將高位與低位倒置放在鏈表中(由於模擬豎式運算時,須要從低位向高位計算)。而且同時記錄原始數據中小數點的位置。spa

list<int> origin, res(1, 1); //origin爲轉換爲鏈表以後存儲底數的變量
        int dot_pos = 0, res_dot = 0; //dot_pos爲原底數小數點位置, res_dot記錄結果的小數點位置
        for (int i = s.size() - 1; i >= 0; --i) { if (s[i] == '.') dot_pos = s.size() - i - 1; //記錄是否在原底數中有小數點的存在,記錄逆序小數點位置
            else origin.push_back(s[i]-'0'); //轉化char爲int類型
        }

 接下來運用快速冪計算的算法思路來求解s的n次冪。普通的冪計算是用遞歸(遞推)方式求解,計算n次冪須要n次遞歸計算,時間複雜度爲O(n)。而快速冪算法的簡單思路爲:若計算s^n,將n表示爲二進制形式,如5可表示爲101,即1+4,則,s^5=s^(1+4)=s^1*s^4=1*s^1*s^4。根據以上所述,依次計算s^1, s^2, s^4, ...,將s^1與s^4累乘到1上,便可獲得結果。時間複雜度爲O(logn)。詳細代碼以下:.net

/*使用快速冪運算求高精度冪*/
while (n > 0) { if (n & 1) { multiple(res, origin); res_dot += dot_pos; //更新結果的小數點位置
 } multiple(origin, origin); if (dot_pos > 0) dot_pos <<= 1; n >>= 1; } 

其中,multiple計算兩個存儲在鏈表中的兩個數的乘積,結果經過第一個參數的引用返回。multiple函數的定義以下:指針

/*計算存儲在兩個鏈表中的兩個數的乘積,結果經過Num1的引用返回*/
void multiple(list<int>& num1, list<int> num2) { //模擬乘法豎式進行計算,規定num2爲乘數,num1爲被乘數
    list<int> temp, origin_num1(num1); //temp存儲乘數與被乘數其中一位的臨時結果,origin_num1爲num1的拷貝,以防後續將num1清空致使原數據丟失
    int key_index = 0; //記錄兩位數相乘時被乘數的左移位數
 num1.erase(num1.begin(), num1.end()); for (auto key : origin_num1) { temp.erase(temp.begin(), temp.end()); for (int i = 0; i < key_index; ++i) temp.push_back(0); multiple_one(num2, key, temp); add(num1, temp); ++key_index; } }

注意,由於傳入的兩個數num1與num2多是同一個變量,因此此處實現時,爲了函數體中可以保留num2原參數值,沒有傳入引用。傳入引用時,C++編譯器直接對實參進行操做,相似指針,而不加&符號時,C++在初始化函數後,拷貝一份實參的實體,並對這個新的實體進行操做,關於C++引用的詳細內容,可參考這裏code

上述代碼中的multiple_one與add函數分別計算一位數與多位數相乘以及兩個多位數相加,實現思想與multiple函數相同,即模擬豎式計算。其函數原型以下:

/*計算一個一位整數與存儲在鏈表L-H中的數的乘積,結果存儲在result的引用返回*/
void multiple_one(list<int>& num, int one, list<int>& result); //模擬豎式計算多位乘以一位/*計算存儲在鏈表L-H中的兩個數的和,結果存儲在num1*/
void add(list<int>& num1, list<int>& num2); //模擬豎式計算兩個數相加

 

完成快速冪計算後,程序的主體已基本完成,最後的步驟就是根據題目要求格式化輸出,即去掉前導與末尾的0。此處實現格式化時得出最後結果,對結果進行操做。也能夠直接對原數據進行去零操做。對於程序運行時間來講,後者效果更好。

源代碼

~~~Code~~~

相關文章
相關標籤/搜索