階乘之計算從入門到精通-大數的表示
1.大數,這裡提到的大數指有效數字非常多的數,它可能包含少則幾
十、幾百位十進位制數,多則幾百萬或者更多位十進位制數。有效數字這麼多的數隻具有數學意義,在現實生活中,並不需要這麼高的精度,比如銀河系的直徑有10萬光年,如果用原子核的直徑來度量,31位十進位制數就可使得誤差不超過乙個原子核。
2.大數的表示:
2.1定點數和浮點數
我們知道,在計算機中,數是存貯在記憶體(ram)中的。在記憶體中儲存乙個數有兩類格式,定點數和浮點數。定點數可以精確地表示乙個整數,但數的範圍相對較小,如乙個32位元的無符號整數可表示0-4294967295之間的數,可精確到9-10位數字(這裡的數字指10進製數字,如無特別指出,數字一律指10進製數字),而乙個8位元組的無符號整數則能精確到19位數字。浮點數能表示更大的範圍,但精度較低。當表示的整數很大的,則可能存在誤差。乙個8位元組的雙精度浮點數可表示2.22*10^-308到 1.79*10^308之間的數,可精確到15-16位數字.
2.2日常生活中的數的表示:
對於這裡提到的大數,上文提到的兩種表示法都不能滿足需求。為此,必需設計一種表示法來儲存大數。我們以日常生活中的十進位制數為例,看看是如何表示的。如乙個數n被寫成"12345",則這個數可以用乙個陣列a來表示,a[0]=1, a[1]=2, a[2]=3, a[3]=4, a[4]=5,這時數n= a[4]*10^0 +a[3]*10^1 +a[2]*10^2 +a[1]*10^3 +a[0]*10^4, (10^4表示10的4次方,下同),10^i可以叫做權,在日常生活中,a[0]被稱作萬位,也說是說它的權是10000,類似的,a[1]被稱作千位,它的權是1000。
2.3 大數在計算機語言表示:
在日常生活中,我們使用的阿拉伯數字只有0-9共10個,按照書寫習慣,乙個字元表示1位數字。計算機中,我們常用的最小資料儲存單位是位元組,c語言稱之為char,多個位元組可表示乙個更大的儲存單位。習慣上,兩個相鄰位元組組合起來稱作乙個短整數,在32位的c語言編譯器中稱之為short,彙編語語言一般記作word,4個相鄰的位元組組合起來稱為乙個長整數,在32位的c語言編譯器中稱之為long,組合語言一般記作dword。在計算機中,按照權的不同,數的表示可分為兩種,2進製和10進製,嚴格說來,應該是2^k進製和10^k進製,前者具占用空間少,運算速度快的優點。後者則具有容易顯示的優點。我們試舉例說明:
例1:若乙個大數用乙個長為len的short型陣列a來表示,並採用權從大到小的順序依次存放,數n表示為a[0] * 65536^(len-1)+a[1] * 65536^(len-2)+...a[len-1] * 65536^0,這時65536稱為基,其進製2的16次方。
例2:若乙個大數用乙個長為len的short型陣列a來表示並採用權從大到小的順序依次存放,數n=a[0] * 10000^(len-1)+a[1] * 10000^(len-2)+...a[len-1] * 10000^0,這裡10000稱為基,其進製為10000,即:10^4,陣列的每個元素可表示4位數字。一般地,這時陣列的每乙個元素為小於10000的數。類似的,可以用long型陣列,基為2^32=4294967296來表示乙個大數; 當然可以用long型組,基為1000000000來表示,這種表示法,陣列的每個元素可表示9位數字。當然,也可以用char型陣列,基為10。最後一種表示法,在新手寫的計算大數階乘程式最為常見,但計算速度卻是最慢的。使用更大的基,可以充分發揮cpu的計算能力,計算量將更少,計算速度更快,占用的儲存空間也更少。
2.4 大尾序和小尾序,我們在書寫乙個數時,總是先寫權較大的數字,後寫權較小的數字,但計算機中的數並不總是按這個的順序存放。小尾(little endian)就是低位位元組排放在記憶體的低端,高位位元組排放在記憶體的高階。例如對於乙個4位元組的整數0x12345678,將在記憶體中按照如下順序排放, intel處理器大多數使用小尾(little endian)位元組序。
address[0]: 0x78
address[1]: 0x56
address[2]: 0x34
address[3]:0x12
大尾(big endian)就是高位位元組排放在記憶體的低端,低位位元組排放在記憶體的高階。例如對於乙個4位元組的整數0x12345678,將在記憶體中按照如下順序排放, motorola處理器大多數使用大尾(big endian)位元組序。
address[0]: 0x12
address[1]: 0x34
address[2]: 0x56
address[3]:0x78
類似的,乙個大數的各個元素的排列方式既可以採用低位在前的方式,也可以採用高位在前的方式,說不上那個更好,各有利弊吧。我習慣使用高位在前的方式。
2.5 不完全精度的大數表示:
儘管以上的表示法可準確的表示乙個整數,但有時可能只要求計算結果只精確到有限的幾位。如用 windows自帶的計算器計算1000的階乘時,只能得到大約32位的數字,換名話說,windows計算器的精度為32位。1000的階乘是乙個整數,但我們只要它的前幾位有效數字,象windows計算器這樣,只能表示部分有效數字的表示法叫不完全精度,不完全精度不但占用空間省,更重要的是,在只要求計算結果為有限精度的情況下,可大大減少計算量。大數的不完全精度的表示法除了需要用陣列儲存有數數字外,還需要乙個數來表示第乙個有效數字的權,10的階乘約等於4.023872600770937e+2567,則第乙個有效數字的權是10^2567,這時我們把2567叫做階碼。在這個例子中,我們可以用乙個長為16的char型陣列和乙個數來表示,前者表示各位有效數字,陣列的各個元素依次為:4,0,2,3,8,7,2,6,0,0,7,7,0,9,3,7,後者表示階碼,值為2567。
2.6 大數的鏈式儲存法
如果我們搜尋大數階乘的源**,就會發現,有許多程式採用鍊錶儲存大數。儘管這種儲存方式能夠表示大數,也不需要事先知道乙個特定的數有多少位有效數字,可以在運算過程中自動擴充套件鍊錶長度。但是,如果基於運算速度和記憶體的考慮,強烈
不建議採用這種儲存方式,因為:
1.這種儲存方式的記憶體利用率很低。基於大數乘法的計算和顯示,一般需要定義雙鏈表,假如我們用1個char表示1位十進位制數,則可以這樣定義鍊錶的節點:
struct _node ;
當編譯器採用預設設定,在通常的32位編譯器,這個結構體將占用12位元組。但這並不等於說,分配具有1000個節點的鍊錶需要1000*12位元組。不要忘記,作業系統或者庫函式在從記憶體池中分配和釋放記憶體時,也需要維護乙個鍊錶。實驗表明,在vc編譯的程式,乙個節點總的記憶體佔用量為 sizeof(struct _node) 向上取16的倍數再加8位元組。也就是說,採用這種方式表示n位十進位制數需要 n*24位元組,而採用1個char型陣列僅需要n位元組。
2採用鍊錶方式表示大數的執行速度很慢.
2.1如果乙個大數需要n個節點,需要呼叫n次malloc(c)或new(c++)函式,採用動態陣列則不要用呼叫這麼多次malloc.
2.2 訪問陣列表示的大數比鍊錶表示的大數具有更高的cache命中率。陣列的各個元素的位址是連續的,而鍊錶的各個節點在記憶體中的位址是不連續的,而且具有更大的資料量。因此前者的cache的命中率高於後者,從而導致執行速度高於後者。
2.3對陣列的順序訪問也比鍊錶快,如p1表示陣列當前元素的位址,則計算陣列的下乙個位址時一般用p1++,而對鍊錶來說則可能是p2=p2->next,毫無疑問,前者的執行速度更快。
階乘之計算從入門到精通 大數的表示
1.大數,這裡提到的大數指有效數字非常多的數,它可能包含少則幾 十 幾百位十進位制數,多則幾百萬或者更多位十進位制數。有效數字這麼多的數隻具有數學意義,在現實生活中,並不需要這麼高的精度,比如銀河系的直徑有10萬光年,如果用原子核的直徑來度量,31位十進位制數就可使得誤差不超過乙個原子核。2.大數的...
階乘之計算從入門到精通 近似計算之一
在 階乘之計算從入門到精通 菜鳥篇 中提到,使用double型數來計算階乘,當n 170,計算結果就超過double數的最大範圍而發生了溢位,故當n 170時,就不能用這個方法來計算階乘了,果真如此嗎?no,只要肯動腦筋,辦法總是有的。通過windows計算器,我們知道,171!1.24101807...
大數階算之計算從入門到精通 序
摘要 本系列文章將深入 大數階乘之計算的各種演算法和實現,從最簡單的大家最容易想到的演算法,到使用各種大數乘法的版本,包括硬乘法 分治法 快速數論變換 fnt 和快速傅利葉變換 fft 的版本,甚至還有使用組合語言寫成的迷你版,使用斯特林公式的極速版。這些系列文章不僅分析和講述演算法思想,而且給出一...