Number Theory -- Solve problems on reminders

写一篇关于解同余方程的文章。

其实这个东西我也一直很晕,只知道算法,具体的东西从来没仔细推过......(太懒了)

这两天才仔细把这个问题研究了一下,为了防止以后忘掉,记下来好了。

Part I 最大公约数和辗转相除法

所有的问题都是从求两个数的最大公约数开始的。

两个整数的公约数定义如下:d是a , b的公约数,当且仅当d | a && d | b。

a , b的最大公约数就是a和b公约数里最大的一个,记作gcd(a , b)或(a , b)。

最大公约数的一种非常流行的求法就是欧几里德辗转相除法。

算法基于这样一个事实

gcd(a , b) = gcd(b , a % b)

具体一些来说,我们要求(a0 , b0)的最大公约数,那么设

a0 = q0 * b0 + b1 ... (1),其中q0是a0除以b0的商,那么0 <= b1 < b0。

设d = (a0 , b0),那么观察等式(1),d | a0 , d | q0 * b0 ,所以d | b1,所以gcd(a0 , b0) = gcd(b0 , b1)

同样的,我们可以得到

b0 = q1 * b1 + b2 ... (2)

b1 = q2 * b2 + b3 ... (3)

...

bk = qk+1 * bk+1  ... (k+1)

不断的迭代下去,一定存在一个等式,最后一项为0,不妨设是第k + 1个等式,为什么呢?

0 = bk+1 < bk < bk-1 < ... < b1 < b0,由于小于b0的正整数是有限的,所以必存在bk+1 = 0。

那么(a0 , b0) = (b0 , b1) = ... = (bk , bk+1) = bk+1

这样就得到了gcd(a0 , b0)。

写个简单的程序

int gcd(int a , int b)
{
    if (!b) 
        return a ;
    else
        return gcd(b , a % b)
}

写成迭代的形式,效率会更高一些。

Part II ax + by = (a , b)

一个新的问题:若(a , b) = d,必存在整数x , y满足ax + by = d,并求出x , y。

可以仿照辗转相除法迭代求x , y出来。

现在我们在迭代的过程中,知道了如下事实,我们设返回的最大公约数为d:

B = q * C + D ... (1) , x * B + y * C = d ... (2)

我们要求的就是x' , y'满足

A = q' * B + C ... (3) , x' * A + y' * B = d ... (4)

由(2)和(4) 有:

x' * A + y' * B = x * B + y * C = d ... (5)

将(3)代入,得到:

x' * (q' * B + C) + y' * B = x * B + y * C

x' * q' * B + x' * C + y' * B = x * B + y * C

现在这样构造,令:

x' = y

则有

y' * B = x * B - x' * q' * B

y' = x - x' * q' = x - y * q'

这样我们得出新的x' , y'

x' = y

y' = x - y * [A / B]

这样我们在迭代中就求出了x和y。

int extended_gcd(int a , int b , int& x , int& y)
{
    if (!b)
    { x = 1 ; y = 0 ; return a ; }
    else
    { int res = extended_gcd(b , a % b , y , x) ; y -= x * (a / b) ; return res ; }
}

这里有一个问题,我们在分析的时候,余数为0的最后一层,并不是我们的程序中的递归的最后一层。按照我们的分析,最后一层x = 0 , y = 1,而我们的程序判断的时候,是b = 0为最后的出口,所以x , y要交换一下,变成x = 1 , y = 0。

这个答案不是唯一的,当x+=b , y-=a,同样满足。

Part III 线性同余方程ax ≡ b(mod n)

我们的新问题又来了:解一个单独的线性同余方程ax ≡ b(mod n)

显然,它等价于求出这样的x,使得存在整数y,使

ax - by = b ... (1)

记d = (a , n) , a = a' * d , n = n' * d,那么必有d | b,否则方程无解。

这样方程就变成了

a'x - n'y = b/d ... (2),(a' , n') = 1

我们在Part II中得到的x0 , y0是方程a'x0 - n'y0 = 1 ... (3)的解,

观察(2)和(3),于是得到

x = x0 * (b / d) , y = y0 * (b / d)

注意的是,还要对n取一下模。

这个x同样不是唯一的,所有解属于同一个模n'剩余系(注意是n')。

void modular_linear_equation(int a , int b , int n)
{
    int e , x , y , d = extended_gcd(a , n , x , y) , ans[] ;
    if (b % d) 
        report_no_solution ;
    else
    {
        e = x * (b / d) % n ;
        for (int i = 0 ; i < n ; i++)
            ans[i] = (e + i * n / d) % n ;
    }
}

Part IV 线性同余方程组

我们这里只考虑两个方程的情况。(再多我就不会了)

x ≡ a1 (mod m1) ... (1)
x ≡ a2 (mod m2) ... (2)

有解的充要条件是(m1 , m2) | a1 - a2。如果其成立,则方程组仅有一个小于m的非负整数解。

(1) 等价于

x = a1 + m1y

(2) 等价于

x = a2 + m2z

于是:

a1 + m1y = a2 + m2z

设c = (m1 , m2),则:

(a1 - a2) / c = m2 / c * z - m1 / c * y

只要求得p , q使得:

(m2 / c) * p + (m1 / c) * q = 1

只需令:

z = p * (a1 - a2) / c

就得到一个解:

x = a2 + m2z

Part V 中国剩余定理

设m1 , m2 , ... , mk两两互素,则下面的方程组:

x ≡ a1 (mod m1)
x ≡ a2 (mod m2)
...
x ≡ ak (mod mk)

在0 <= x < M = m1m2...mk内有唯一解。

记Mi = M / mi,则(Mi , mi) = 1,故有pi , qi,使得Mipi + miqi = 1,记ei = Mipi,那么必有

ei ≡ 0(mod mj) (j != i)
ei ≡ 1(mod mi)

这样就可以看出,e1a1 + ... + ekak是一个解,不过不一定是最小的。

加减M的整数倍可以得到其他解。

int ChineseReminderTheory(int a[] , int m[] , int k)
{
    int M , Mi , i , answer , pi , qi ;
    for (M = 1 , i = 0 ; i < k ; i++) M *= m[i] ;
    for (answer = i = 0 ; i < k ; i++)
    {
        Mi = M / m[i] ;
        extended_gcd(Mi , m[i] , pi , qi) ;
        answer = (answer + Mi * pi * a[i]) % M ;
    }
    if (answer < 0) answer += M ;
    return answer ;
}

Postscript

posted @ 2008-12-11 12:15  jesonpeng  阅读(131)  评论(0)    收藏  举报