GMP庫使用方法
目錄
Useful Macros and Constants 12
Combined Initialization and Assignment Functions 14
Division Functions (除法、模運算) 16
Logical and Bit Manipulation Functions 20
Linux 下安裝GMP庫
下面是一些常用的大數運算庫的地址(有些雖然不是專門的大數運算庫,但是帶有相關的庫)
Crypto++:http://www.eskimo.com/~weidai/cryptlib.html
MIRACL:https://github.com/CertiVox/MIRACL
GNU MP:http://www.swox.com/gmp/
Piologie: http://www.hipilib.de/pidownload.htm
cryptlib:http://www.cs.auckland.ac.nz/~pgut001/cryptlib/
RSAEuro:http://www.rsaeuro.com/products/RSAEuro/
OpenSSL:http://www.openssl.org/
RSARef:http://download.gale.org/rsaref20.tar.Z
GInt:http://triade.studentenweb.org/GInt/gint.html(Delphi)
sudo apt-get install m4
如果要增加C++支援,./configure的時候加上--enable-cxx引數。
使用configure的時候要加上 --enable-cxx命令,否則不能使用c++庫gmpxx.h
新建test.cpp檔案
#include <gmpxx.h>
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
mpz_t a,b,c;
mpz_init(a);
mpz_init(b);
mpz_init(c);
gmp_scanf("%Zd%Zd",a,b);
mpz_add(c,a,b);
gmp_printf("c= %Zd\n",c);
return 0;
}
編譯
g++ test.cpp -o test -lgmp
下面我們以求10000!為例說明如何使用gmp。要使用gmp必須先包含gmp的標頭檔案:
#include <gmp.h>
求10000!我們需要的資料型別是整數,當然需要的是多精度整數,定義一個多精度整數(multiple precision integer)變數可以用:
mpz_t num;
現在我們需要定義三個變數:
mpz_t z_i, z_s, z_o;
分別用來迭代1..10000之間的數字、儲存結果、儲存1這個數字以使得z_i自增。可以用字串來給多精度數字初始化為一個大數:
mpz_init_set_str(z_i, "1", 10);
mpz_init_set_str(z_s, "1", 10);
mpz_init_set_str(z_o, "1", 10);
mpz_init_set_str的原型是:
int mpz_init_set_str (mpz_t rop, char *str, int base)
這三個引數分別是多精度整數變數,字串,進位制。
現在我們迴圈10000次並進行乘法和加法,乘法和加法的函式分別是mpz_mul,mpz_add,原型分別是:
void mpz_add (mpz_t rop, mpz t op1, mpz t op2)
效果為:rop = op1 + op2
void mpz_mul (mpz_t rop, mpz t op1, mpz t op2)
效果為:rop = op1 * op2
我們的程式可以寫為:
int i;
for (i = 0; i < 10000; i++)
{
mpz_mul(z_s, z_s, z_i);
mpz_add(z_i, z_i, z_o);
}
然後我們按大整數的格式來輸出結果,因為是mpz_t型別,不能用一般的printf,只能用gmp_printf:
gmp_printf("%Zd\n", z_s);
最後我們釋放這幾個大整數所佔的空間:
mpz_clear(z_i);
mpz_clear(z_s);
mpz_clear(z_o);
程式就完畢了。運算結果非常大,顯示了幾頁但是速度卻非常快,幾乎是一秒不到就做完了還包括了在控制檯列印時間。
完整的程式如下:
1 #include <gmp.h>
2 #include <string.h>
3 int main(int argc, const char *argv[])
4 {
5 mpz_t z_i, z_s, z_o;
6 mpz_init_set_str(z_i, "1", 10);
7 mpz_init_set_str(z_s, "1", 10);
8 mpz_init_set_str(z_o, "1", 10);
9 int i;
10 for (i = 0; i < 10000; i++)
11 {
12 mpz_mul(z_s, z_s, z_i);
13 mpz_add(z_i, z_i, z_o);
14 }
15 gmp_printf("%Zd\n", z_s);
16 mpz_clear(z_i);
17 mpz_clear(z_s);
18 mpz_clear(z_o);
19 getchar();
20 return 0;
21 }
GMP Basics
Gmp6.2.0文件
使用GMP時,不要使用本文件未說明的函式、巨集、資料型別,否則保證你的程式不與未來版本的GMP相容。
Headers and Libraries
使用GMP所需要的一切宣告都彙集到了標頭檔案gmp.h裡,此標頭檔案同時適用於C和C++。
GMP中有些函式接受FILE*引數,只有當同時包含了stdio.h時,這些函式才是可用的。
類似的還有:stdarg.h對應可變引數函式(如gmp_vprintf()),obstack.h對應使用struct obstack引數的函式(gmp_obstack_prif())。
編譯的時候要連結gmp庫gcc prog.c -lgmp。C++函式單獨存放在另外的庫gmpxx中g++ prog.cpp -lgmpxx -lgmp。
Nomenclature and Types
Nomenclature 命名法
整數(integer)是指高精度整數(multiple precision integer),資料型別mpz_t。
有理數(rational number)是指高精度分數(multiple precision fraction),型別mpq_t。
浮點數(Floating point number,Float)由一個任意精度的尾數(mantissa)和一個極限精度的指數(limited precision exponent)構成。
浮點數函式用mp_exp_t型別接受和返回指數,此型別一般是long,有些機器上是int(為了效率)。
高精度數中等於一個機器字長的部分稱為一個limb(漢語意思為肢,四肢的肢)。型別為mp_limb_t。通常一個limb是32位或64位。
一個高精度數包含的limb的數目,用mp_size_t型別儲存,此型別一般般是long,某些系統上是int,將來可能是long long。
一個高精度數有的位數用mp_bitcnt_t型別儲存,當前此型別總是unsigned long,將來某些系統上可能會變成unsigned long long。
隨機狀態用型別gmp_randstate_t表示。(Random state means an algorithm selection and current state data.)。
mp_bitcnt_t用於位(bit)的計數以及表示範圍;size_t用於給位元組和字元計數。
Analogy比喻
GMP函式一般把輸出引數放在輸入引數之前,此約定是受賦值運算子的啟發;相容與BSD MP 的函式是例外:輸出引數放在最後。
在一次函式呼叫中可以把同一個變數同時用作輸入輸出引數。gmz_mul(x,x,x);此呼叫計算整數x的平方然後把計算結果再存入x中。
注意:一般自己寫的函式不能這麼幹,要支援這樣需要程式碼特地為此繞一下
使用GMP變數之前,需要先初始化,使用完畢要釋放變數。初始化和釋放是通過專門的函式呼叫完成的。
一個變數只需要初始化一次,如果非要多次初始化,在每次初始化操作之間釋放變數。初始化後的變數可以進行任意次賦值。
一般在函式的開頭初始化變數,函式末尾釋放變數;為了效率,應避免過多的變數初始化和釋放操作。
Parameter Conventions
mpz_t實際上是長度為1的結構體陣列,結構體內部的成員變數僅供庫內部使用,如果在程式中訪問了結構體成員,在新版GMP庫中很可能不相容。
Gmp中對變數的操作是使用指標,因此不要對變數進行copy!就算是用copy,也只讀,不要修改,否則原來的變數也會被修改
把變數作為函式引數傳遞時,實際上傳遞的是指標。
gmp型別變數的本質是長度為1的結構體陣列,變數的值儲存在地址 _mp_d 對應的值中
如果自己編寫的函式要返回結果,應該仿照GMP的庫函式,使用輸出引數(即函式的引數有部分引數用於輸出值);如果直接使用return語句,返回的僅僅是個指標。
上面foo函式的輸入引數param設定為const
Memory Management
GMP自己會管理自己申請的記憶體。mpz_t和mpq_t在型別的變數需要時會申請更大的記憶體,但從不會減小記憶體,為了效率
mpf_t型別的變數使用固定的記憶體,記憶體大小根據初始化時的精度設定確定。
GMP預設使用malloc系列函式進行記憶體分配,但這是可設定的。
Reentrancy
GMP是可重入的且是執行緒安全的,除了如下例外情況:
編譯時使用了--enable-alloca=malloc-notreentrant選項
函式mpf_set_default_prec()和mpf_init()使用全域性變數儲存精度
函式mpz_random()以及其它舊隨機數生成函式使用全域性變數儲存隨機狀態,故是不可重入的。新的隨機函式可用,這些函式使用gmp_randstate_t引數
gmp_randinit()(已過時)在全域性變數中返回錯誤碼,所以不可重入;使用新函式gmp_randinit_default()或gmp_randinit_lc_2exp()
mp_set_memory_funcitons()使用全域性變數儲存記憶體分配函式
如果mp_set_memory_functions()設定的記憶體分配函式是不可重入的,那麼GMP也將是不可重入的。預設的malloc()函式是不可衝入的
如果標準IO函式不可重入,則GMP的IO函式也不可重入
多個執行緒同時讀一個GMP變數是安全的;但多執行緒一個讀一個寫、或多個同時寫入都是不安全的;多執行緒使用同一個gmp_randstate_t變數生成隨機數也是不安全的
Useful Macros and Constants
const int mp_bits_per_limb 一個limb的位數
const char * const gmp_version x.y.z 格式的GMP版本;例如“6.2.1”
Integer Functions
Initialization Functions
void mpz_init(mpz_t x) 初始化x並設其初值為0
void mpz_inits(mpz_t x, ...) 初始化引數列表中的變數,初值設為0. 最後一個引數用NULL,表示結束。
void mpz_clear(mpz_t x), void mpz_clears(mpz_t x, ...) 釋放變數
使用mpz_clears更方便!把多個變數一次clear 如mpz_clears(a,b,c,...,NULL);
Assignment Functions
對浮點數進行截斷再賦值
Combined Initialization and Assignment Functions
初始化和賦值組合
Don’t use an initialize-and-set function on a variable already initialized!
Conversion Functions
這些函式把GMP整數(mpz_t)轉換成標準C語言型別。C型別轉換到GMP整數,使用賦值函式和輸出輸出函式。
這一類函式都有mpz_get_字首,用si、ui、d、str對應signed long、unsigned long、double、char *str型別;返回值都是對應的結果型別。
轉成整數、浮點數,如果超出了其表示範圍,轉換過來就沒有意義了。可以用mpz_fit_ulong_p(mpz_t op)一族的函式判斷一下,在取值範圍內,則返回1,否則返回0.
可以用這個方法來獲取大整數的bit位!!!
使用NULL時需要手動釋放記憶體,因為GMP預設使用malloc、realloc、free函式來操作動態記憶體,因此釋放記憶體時使用free函式即可
Arithmetic Functions
加、減、乘、取相反數、取絕對值。返回值都是void。add\sub\mul\neg\abs
void mpz_add(mpz_t rop, mpz_t x, mpz_t y) // x+y ⇒ rop
void mpz_sub(mpz_t rop, mpz_t x, mpz_t y) // x-y ⇒ rop
void mpz_mul(mpz_t rop, mpz_t x, mpz_t y) // x*y ⇒ rop
void mpz_neg(mpz_t rop, mpz_t op) // -op ⇒ rop
void mpz_abs(mpt_t rop, mpz_t op) // |op| ⇒ rop
而且加減乘都有與signed long和unsigned long對應的變體
void mpz_add_ui(mpz_t rop, mpz_t x, unsigned long y) // x+y ⇒ rop
void mpz_sub_ui(mpz_t rop, mpz_t x, unsigned long y) // x-y ⇒ rop
void mpz_ui_sub(mpz_t rop, unsigned long x, mpz_t y) // x-y ⇒ rop // 留意,減法有兩個變體。
void mpz_mul_ui(mpz_t rop, mpz_t x, unsigned long y) // x*y ⇒ rop
把ui換成si就是另外四個變體。
Division Functions (除法、模運算)
除法除不盡的時候要考慮舍入,GMP的整數除法運算有三種攝入模式:向上取整(ceil)、向下取整(floor)、向零取整(也叫截斷,trunct),分別對應cidv,fdiv和tdiv。
基本的除法函式,返回值都是void。
void mpz_cdiv_q(mpz_t q, mpz_t n, mpz_t d)
void mpz_cdiv_r(mpz_t r, mpz_t n, mpz_t d)
void mpz_cdiv_qr(mpz_t q, mpz_t r, mpz_t n, mpz_t d)
其中,n是被除數,d是除數(divisor),q是商(quote),r是餘數(remainder)
q僅計算商,r僅計算餘數,qr同時計算商和餘數,qr函式中,q和r不能使用同一個變數,否則行為不定。
基本型別的變體有ui字尾(unsigned long)一種,ui指的是使用unsigned long作為除數。ui變體中 ,餘數作為函式返回值返回,因為ui不能表示負數,所以返回的實際是餘數的絕對值。
fdiv和tdiv的除法函式與之類同。
向上取整的除法,餘數的符號和d相反;向下取整的符號,餘數的符號和d相同;向零取整的除法,餘數符號和被除數相同。
三種取整方式都滿足n = d*q + r, |r| <= |d|。
Exponentiation Functions
Exp引數還支援負數(模逆存在時指數可為負數)
Root Extraction Functions
Number Theoretic Functions
Comparison Functions
op1 小於、等於、大於op2的時候分別返回小於零、等於零、大於零的數字。(沒說-1,+1)。
而且si、ui、d三個變體實際是巨集,所以會對運算元多次求值。
比較兩個運算元的絕對值得大小。
Logical and Bit Manipulation Functions
普通的位運算,與或非(補)
Op為正數時,返回二進位制表示中1的個數
Op為負數時,
Formatted Output
函式名,stdio裡的格式化輸入輸出加 gmp_ 字首就是了。gmp_printf gmp_scanf
Format string只翻譯成單位元組字元,多位元組字元無法識別,未來可能會支援
Custom Allocation
Internals
內部的實現原理
Integer Internals
未賦值時為0,為負數時,表示在_mp_d對應的值前加個負號
指向 limbs陣列的指標,limbs陣列的值為變數的絕對值大小,使用小端儲存的方式
Limbs陣列的長度