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...