1. 程式人生 > 實用技巧 >GMP庫使用方法

GMP庫使用方法

目錄

Linux 下安裝GMP庫 2

GMP Basics 6

Headers and Libraries 6

Nomenclature and Types 7

Parameter Conventions 9

Memory Management 10

Reentrancy 11

Useful Macros and Constants 12

Integer Functions 12

Initialization Functions 12

Assignment Functions 13

Combined Initialization and Assignment Functions 14

Conversion Functions 14

Arithmetic Functions 16

Division Functions (除法、模運算) 16

Exponentiation Functions 18

Root Extraction Functions 19

Number Theoretic Functions 19

Comparison Functions 19

Logical and Bit Manipulation Functions 20

Formatted Output 20

Custom Allocation 21

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陣列的長度