1. 程式人生 > >#數論# 歐幾里德演算法 、擴充套件歐幾里德演算法 、費馬小、逆元求解(ing)

#數論# 歐幾里德演算法 、擴充套件歐幾里德演算法 、費馬小、逆元求解(ing)

歐幾里德求gcd(輾轉相除法):

定理:

gcd(a, b) = gcd(b, a % b)
兩個正整數a和b(a>b),它們的最大公約數等於a除以b的餘數c和b之間的最大公約數

證明:

a可以表示成a = kb + r,則r = a % b;

  1. 假設d是a, b的一個公約數,則有a % d = 0,b % d = 0,由於r = a - kb,因此r % d = 0,證明充分性;
  2. 假設d 是(b, a % b)的公約數,則b % d = 0,r % d = 0,由於a = kb + r,因此a % d = 0,證明必要性;

實現程式碼:

int gcd(int a, int
b) { //遞迴形式 if(b == 0) return a; //當 b == 0 時,(a,0)的最大公約數是a return gcd(b, a % b); } int gcd(int a, int b) { //非遞迴形式 int tmp = a; while(b) { a = b; b = tmp % b; } return a; }

複雜度:O( log(max(a, b)) )

擴充套件歐幾里德演算法:

用來在已知a, b求解一組x,y使得 a * x + b * y = Gcd(a, b),擴充套件歐幾里德常用在求解橫線性方程及方程組中。

核心程式碼:

//if(a < 0) a = -a, c = -c;  
int exgcd(int a, int b, int &x, int &y) { //a必須大於0
    if(b == 0) {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

主要應用於以下三個方面:

1. 求解不定方程:ax +by = c

  • 對於不定整數方程 ax + by = c,若 c mod gcd(a, b) = 0,則該方程存在整數解,否則不存在整數解。
  • 在找到 ax + by = gcd(a, b) 的一組解 x0,y0 後,對於 ax + by = c 的整數解,只需將 ax + by = gcd(a, b) 的每個解乘上 c / gcd(a, b) 即可。
  • ax + by = gcd(a, b) 的其他整數解滿足: x1 = x0 + k * b / gcd(a, b) ,y1 = y0 - k * a / gcd(a, b) (其中 k 為任意整數)
  • 如果要保證x最小,可以令 x % (b / gcd),同理,保證y最小就需要令 y % (a / gcd)。

經典例題1:Romantic


The Sky is Sprite.
The Birds is Fly in the Sky.
The Wind is Wonderful.
Blew Throw the Trees
Trees are Shaking, Leaves are Falling.
Lovers Walk passing, and so are You.
…………………………..Write in English class by yifenfei
Girls are clever and bright. In HDU every girl like math. Every girl like to solve math problem!
Now tell you two nonnegative integer a and b. Find the nonnegative integer X and integer Y to satisfy X*a + Y*b = 1. If no such answer print “sorry” instead.

Code:

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if(b == 0) {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}
int main()
{
    LL a, b;
    while(cin >> a >> b) {
        LL x, y;
        LL gcd = exgcd(a, b, x, y);
        if(1 % gcd) cout << "sorry" << endl;
        else { //由於gcd == c == 1所以直接對方程ax + by = gcd(a,b)求解即可
            while(x < 0) { //題目要求x必須大於0
                x += b / gcd;
                y -= a / gcd;
            }
            cout << x << " " << y << endl;
        }
    }
    return 0;
}

2. 求解模線性方程(線性同餘方程):

同餘方程 ax ≡ b (mod n) 對於未知數 x 有解,當且僅當 gcd(a,n) | b。且方程有解時,方程有 gcd(a, n) 個解,求解方程 ax ≡ b (mod n) 相當於求解方程 ax+ ny = b(x, y為整數)。
每個解之間的間隔dx = n / d。

經典例題2:青蛙的約會


兩隻青蛙在網上相識了,它們聊得很開心,於是覺得很有必要見一面。它們很高興地發現它們住在同一條緯度線上,於是它們約定各自朝西跳,直到碰面為止。可是它們出發之前忘記了一件很重要的事情,既沒有問清楚對方的特徵,也沒有約定見面的具體位置。不過青蛙們都是很樂觀的,它們覺得只要一直朝著某個方向跳下去,總能碰到對方的。但是除非這兩隻青蛙在同一時間跳到同一點上,不然是永遠都不可能碰面的。為了幫助這兩隻樂觀的青蛙,你被要求寫一個程式來判斷這兩隻青蛙是否能夠碰面,會在什麼時候碰面。
我們把這兩隻青蛙分別叫做青蛙A和青蛙B,並且規定緯度線上東經0度處為原點,由東往西為正方向,單位長度1米,這樣我們就得到了一條首尾相接的數軸。設青蛙A的出發點座標是x,青蛙B的出發點座標是y。青蛙A一次能跳m米,青蛙B一次能跳n米,兩隻青蛙跳一次所花費的時間相同。緯度線總長L米。現在要你求出它們跳了幾次以後才會碰面。

Solution:

求餘方程問題
假設青蛙跳了t次,根據題意列出方程:(x + t * m) % L = (y + t * n) % L ,轉化為(m - n) * t + k * L = y - x,那麼現在已經符合ax + by = c方程了,設a = m-n,b = L,c = y-x,由於ax + by = gcd(a, b)而不是c,因此只有當c | gcd(a,b)時才有解。
此時方程兩邊同時乘c / gcd(a,b),就得到 c / gcd(a, b) * (ax + by) = c 方程了

程式碼:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef vector<LL> vec;
typedef vector<vec> mat;
const int MaxN = 2e3 + 5;
const int MaxM = 1e5 + 5;
const int Mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if(b == 0) {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

int main()
{
    LL x, y, m, n, l;
    scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &l);
    LL a = m - n, b = l, c = y - x;
    if(a < 0) a = -a, c = -c;
    LL gcd = exgcd(a, b, x, y);
    if(c % gcd) printf("Impossible\n"); 
    else {
        x = x * c / gcd; 
        int base = b / gcd;
        while(x < 0) x += base; //x必須是正數
        printf("%lld\n", x % base);  //保證x最小
    }
    return 0;
}

3. 求解模的逆元:

對於正整數a,如果有 ax ≡ 1 (mod n),那麼把這個同餘方程中的最小正整數解x叫做a模x的逆元。
對於同餘方程 ax ≡ 1(mod n), gcd(a, n) = 1 的求解就是求解方程ax + ny = 1。

逆元:

求解逆元的三種方法:

1. 費馬小定理求逆元:

費馬小定理:(m為素數)
推導過程:

為a在模m下的逆元(用快速冪求即可)。但有限制條件:m必須為質數,且a,m互質。

2. 拓展gcd求逆元:(a,m互質)

將a,m代入擴充套件gcd公式ax + by = gcd(a, b) 得出公式ax + my = gcd(a, m),令a,m互質,則gcd(a, m) = 1,得出ax +my = 1,即,此時x就是a的逆元。

3. 尤拉定理:

相關推薦

#數論# 演算法 擴充套件演算法 求解ing

歐幾里德求gcd(輾轉相除法): 定理: gcd(a, b) = gcd(b, a % b) 兩個正整數a和b(a>b),它們的最大公約數等於a除以b的餘數c和b之間的最大公約數 證明: a可以表示成a = kb + r,則r = a %

演算法擴充套件演算法乘法

最近看了一本書《程式設計師》裡面說的一個面試題: 求兩個數的最大公約數: SoEasy的題目看過C 的人都知道怎麼寫這個程式 1.傳統方法:窮舉 #include <math.h>int main(){int m=1970,n=1066,p=0;p=m<n?m:n;for(;p>=1

poj 1061 青蛙的約會 數論擴充套件

青蛙的約會Description兩隻青蛙在網上相識了,它們聊得很開心,於是覺得很有必要見一面。它們很高興地發現它們住在同一條緯度線上,於是它們約定各自朝西跳,直到碰面為止。可是它們出發之前忘記了一件很重要的事情,既沒有問清楚對方的特徵,也沒有約定見面的具體位置。不過青蛙們都是

【未完成】除法取模擴充套件演算法

1.+,-,*都可以直接取模,但是除法不可以(模素數相當於換了數域,因為數域變成了有限域,有限域上沒有除法,要換成乘以逆元)。 2.除法取模要變成乘它的逆元。 a * x MOD m == 1則稱X為A關於模m的乘法逆元,其中a和m必須互素。 3.當m為素數時可以使用

演算法學習——演算法&擴充套件演算法

最大公約數/歐幾里德演算法(gcd) 歐幾里德演算法又稱輾轉相除法,證明可以度娘。 個人簡單腦部就是a和b兩個數的模還是a和b的最大公約數 int型別  int gcd(int a, int b) {return a%b==0?b:gcd(b,a%b);} long l

各種密碼學演算法的GUI程式設計實現DESAESPresent擴充套件演算法素性檢測

encryption-algorithm 各種密碼學演算法的 C# GUI程式設計實現,包含: DES AES Present 擴充套件歐幾里得演算法 素性檢測 最終的結果 DES加密 DES解密

乘法擴充套件演算法二元一次方程a的n次方取餘

知識點:乘法逆元,逆元的求法,二元一次方程求通解,a的n次方求餘數 一,乘法逆元 乘法逆元的概念類似於倒數(ax=1,a−1=x),不過是在取餘數的情況下的倒數。 如果(a×x)%p=1,則稱x是a模p的逆元。另一種記法:ax=1(modp),即等

gcd演算法/extgcd擴充套件演算法以及在解不定方程中的應用

這個應該是我在noip前就應該會的東西 ,但是當時也許只是記下了程式碼吧 ,現在有諸多的不理解。後來藉著書和幾篇部落格弄懂了並小證了一下,鑑於網上有些部落格關於這個的寫的真的不好看,所以自己來總結一下,順帶以後也能看。 順帶一提,gcd(a,b)表示a,b的最

【專題】演算法擴充套件乘法

1.歐幾里得 用途 最大公因數和最小公倍數 定理:  gcd(a,b)=gcd(b,a%b)   證明: 我們令c=gcd(a,b) 令a=n∗c , b=m∗c a%b=a−k

【結論】【數論】拓展演算法定理

1、歐幾里得原理 (1)歐幾里得演算法 int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } (2)、拓展歐幾

輾轉相除法 定理 尤拉定理 擴充套件演算法簡介

證明:對於(2),p是素數,a是整數且gcd(a,p)=1即他們的最大公約數是1。由於a, 2a, 3a, ……,(p-1)a 模p的餘數都不相同。否則若(i*a) mod p=(j*a) mod p其中 1 =< i < j <= p-1 則p|(j-i)*a, 而gcd(a,p)=1,那

演算法複習——擴充套件演算法擴充套件得,,整除

①歐幾里得演算法 就是求gcd的有趣的輾轉相除法,不再贅述啦0v0 程式碼: int gcd(int a,int b) { if(b==0) return a; else return gcd(b,a%b); } ②擴充套件歐幾里得演算法 需要解決這樣的問題:兩個非0整數a,b

求組合數取模楊輝三角打表 & 求擴充套件定理尤拉定理線性求法 & Lucas

    在acm競賽中,組合數取模的題目還是經常會見到的,所以這是有必要掌握的一個演算法。我本人就因為這個東西而被坑了很多次了= =之前的部落格也都扯過了,就不多說了,下面進入正題。 (1)楊輝三角求組合數     楊輝三角這個東西應該都不陌生,三角的兩邊始終為一,之後向

演算法擴充套件演算法的簡單例子

歐幾里得演算法: #include <cstdio> #include <cstdlib> /* * 挑戰。。。p113 */ struct point{ //格點

淺析擴充套件得和莫比烏斯反演填坑ing

逆元 扯一點沒有多大用的東西 在數論裡面,我們不把倒數叫做倒數,而叫做逆元(純屬裝逼) 逆元的作用很大,先來看點easy的栗子 某些性質 a+b≡amodp+bmodp(modp)a+b≡amodp+bmodp(modp) a−b≡am

乘法詳解【定理+擴充套件演算法

乘法逆元 何為乘法逆元? 對於兩個數a,pa,p若gcd(a,p)=1gcd(a,p)=1則一定存在另一個數bb,使得ab≡1(modp)ab≡1(modp),並稱此時的bb為aa關於11模pp的乘法逆元。我們記此時的bb為inv(a)inv(a)或a−1a

LG 的數學計劃 ---- 第三步 演算法擴充套件

於是,我們在完成神奇的前兩步之後,來到了這個神奇的地方——歐幾里得演算法和擴充套件歐幾里得演算法。 那麼,這是用來幹什麼的演算法呢? 算最大公約數(GCD)~~~ 好吧,考慮到有一些同學可能還不知道這是怎樣一種神奇的東西,那麼我就把這個東西的定義放到下面來

51Nod 1119 機器人走方格 擴充套件得++求組合數

M * N的方格,一個機器人從左上走到右下,只能向右或向下走。有多少種不同的走法?由於方法數量可能很大,只需要輸出Mod 10^9 + 7的結果。 收起 輸入 第1行,2個數M,N,中間用空格隔開。(2 <= m,n <= 1000000) 輸出 輸出走法的數量 Mo

除法取模(定理+擴充套件得)

除法取模 以下只說這兩個方法 1)費馬小定理 a^(p-1)==1(mod)p     a*a^(p-1)=1%M a/b%mod=a*b^(mod-2)%mod; 只有p,mod為素數時,而

種求法擴充套件得,定理或尤拉定理,特例,打表等

乘法逆元 對於縮系中的元素,每個數a均有唯一的與之對應的乘法逆元x,使得ax≡1(mod n) 一個數有逆元的充分必要條件是gcd(a,n)=1,此時逆元唯一存在 逆元的含義:模n意義下,1個數a