大整數類的實現(4)(原創)

2021-04-15 08:41:34 字數 3635 閱讀 8340

5.helper.h

namespace dozerg

return base;

}bool dividestringbytwo(std::string & value,bool & iszero)

std::string::size_type p(value.find_last_not_of(char(0))+1);

iszero=!p;

value.erase(p);

return carry;

}void mutistringwithtwo(std::string & value,int addnumber)

if(carry)

value.push_back(carry);        //this requires 0<=carry<=9

}std::string & reversestring(std::string & value,int addvalue = 0)

return value;

}}//namespace stringhelper

}//namespace dozerg

————————————————————————————————————

這個檔案內的函式完成2個重要的任務:把10進製數字字串轉換成2進製位元位,把2進製位元位轉換成10進製數字字串。

首先是checkbasefromstring。它接受乙個8進製的、10進製的或16進製表示的數字字串,按照c語言標準,8進製數字以'0'開頭,後接小於8的數字序列;16進製制數字以"0x"或"0x"開頭,後接'0'-'9,'a'-'f','a'-'f'的字串行。數字前面允許乙個'-'或'+'表示數值的符號。

checkbasefromstring按照這個標準來判斷乙個字串是否是正確的數字表示,並得到它的基數(8,10,16)。它的實現原理則使用了有限狀態機原理,每本《編譯原理》都會講到的東西。具體的流程我就不想多講了,只要用乙個例子走一遍就清楚了。

checkbasefromstring的返回值中,只有8,10,16是表示數值的真正基數,返回2表示數值為0,其他的數值(0,1,3,-1)都表示字串不是正確的數字表示。

然後我想介紹排在最後的函式reversestring。這個函式從名字就可以看出來,它反轉傳入的字串引數value,並返回value的引用。不過值得一提的是它的第二個引數addvalue,是需要加到value的每乙個字元ascii碼上去的。

具體怎麼樣呢,看看**會更清楚。

std::string::size_type l=value.length();

得到value的長度l。

if(l&1)

value[l>>1]+=addvalue;

如果l是奇數,那麼把中間的那個字元ascii碼加addvalue。比如l=5,那麼就先把value[2]加addvalue,顯然在後面的交換過程中,是不會處理到value[2]的,所以這裡先單獨處理了。

然後的迴圈就是首尾交換字元了,

value[i]^=value[l-i-1];

value[l-i-1]^=value[i];

value[i]^=value[l-i-1];

這3行**交換value[i]和value[l-i-1]的值,如果你不懂怎麼做的,那麼看看《離散數學》吧。

下面2行**則給每個字元加addvalue。

這個函式其實非常簡單,只要是稍微有經驗的coder,一眼就能看出來。所以對不起,我囉嗦了!

好了,該到精彩部分了。

下面我講dividestringbytwo。它接受2個引數:

std::string & value,

bool & iszero

第乙個引數value是乙個表示10進製數字的字串,但是不是簡單的"123"。如果value表示的是123,那麼它包含的3個字元是

char[3] = = ;

是不是看到了reversestring的影子?不錯,從"123"到上面的形式的轉換正是reversestring的工作,當然它也負責相反方向的轉換。

那麼究竟為什麼用這種形式來表示數字123呢?讓我們溫習一下10進製數轉2進製數的過程。

比如數字123,它轉化為2進製是(1111011),計算過程如下:

123 / 2 = 61 餘 1

61 / 2 = 30  餘 1

30 / 2 = 15  餘 0

15 / 2 = 7   餘 1

7 / 2 = 3    餘 1

3 / 2 = 1    餘 1

1 / 2 = 0    餘 1

0            結束

於是我們把餘數從下往上排列,就是(1111011)。

好了,現在我可以描述dividestringbytwo的具體作用了,它執行上面的一次除法和求餘的工作。

同樣用上面的例子,假定輸入的字串value為,**:

for(std::string::reverse_iterator v=value.rbegin();v!=value.rend();++v)

對value執行除以2的操作,於是value變成了。**:

std::string::size_type p(value.find_last_not_of(char(0))+1);

iszero=!p;

value.erase(p);

得到最後乙個非0字元(6)的下乙個位置(0),並移除,為什麼?顯然實際上表示的61,那麼應該用就夠了,所以這樣做的原因就是緊縮value的長度,顯然dividestringbytwo的時間複雜度是與value長度有關的。

於是這裡又可以說明另乙個設計決定——把123倒置成來表示——的好處了:每次只需要在末尾erase元素。

iszero雖然是作為乙個引數傳進來了,實際上的作用卻是返回value是否已經成了0,這一點通過!p來判斷。因為std::string::npos通常都是-1,所以如果「最後乙個非0字元」不存在,p的值會是0。

最後就是函式的返回值。如果你看懂了上面除以2的**,就應該知道此時carry的值表示了最後是否有餘數(1為true,0為false),所以整個函式的返回值也就表示了「divide string by two」之後的餘數是多少。

在把10進製數字字串轉化成2進製位元位的時候,dividestringbytwo是最關鍵的呼叫函式之一。

最後我們來看函式mutistringwithtwo。它的作用與dividestringbytwo正好相反,它把字串表示的數字乘以2。為了知道它怎麼工作的,我們再來溫習一下2進製數怎麼轉化成10進製數。

以上面的2進製序列(1111011)為例,轉化過程如下:

1111011

111011 結果 1

11011 結果 1 + 1 * 2 = 3

1011 結果 1 + 3 * 2 = 7

011 結果 1 + 7 * 2 = 15

11 結果 0 + 15 * 2 = 30

1 結果 1 + 30 * 2 = 61

結果 1 + 61 * 2 = 123

上面的過程非常清晰,每次把10進製數乘以2,加上2進製序列的最高位。mutistringwithtwo完成的就是每一步乘2加x的操作,其中addnumber是需要加的數值。

**我想不用講解了,如果弄懂了上面的過程,看**就像看**一樣容易。

同樣在這個過程中,採用的方式表示123是有優勢的,最高位在後面,進製的時候只要push_back就行了。

大整數C 類的實現

include include include includeusing namespace std class bigint ostream operator ostream out,bigint bint 該函式得作用是將向量中的每乙個數字轉化為字元 數字 0 就相當於將數字轉化為字元 stri...

大整數類c 實現

在日常使用c 的過程中,經常會遇到數字太大越界的情況,對於這樣的大整數運算,我們可以用模擬比算的方法來實現,但是這樣每次運算都要實現這樣的演算法會帶來一定的不方便,我們希望能像int這樣的內建型別一樣使用大整數,所以我們實現乙個大整數struct 感謝劉汝佳老師的演算法競賽入門經典一書 struct...

大整數類 實現加減法

上次寫了乙個 無符號大整數加法 是比較容易的,這次實現了完整的大整數的加減法,支援有符號的!不過實現起來感覺不是很順暢,感覺可以優化的地方還很多,先貼一下 日後再來優化。另,思路主要是模擬手算的過程,計算方式在注釋裡有說清楚。biginteger.h ifndef big integer h def...