数论相关

来源:互联网 发布:什么是淘宝众筹 编辑:程序博客网 时间:2024/04/28 22:14

  最近为面试,一直在学习各种各样的技术,但一天下来似乎没有什么成就感,相反感觉并没学到什么,今天在群里(hero)看到一个厉害的同行说到用博客记录下自己学过的知识,仔细想想颇有道理。

  由于笔者才疏学浅,记录下的文字肯定有所不正或纰漏之处,本着不误人子弟的忐忑心态,如果有读者看到笔者的这些文字,能以批判的眼光,提出笔者的不妥之处,大家共同进步,小弟先在此拜谢!

  今天学习《算法导论》,正好看到数论相关内容,取其中笔者理解的一些内容。

基础概念:

  符号d|a("d整除a"):存在某个整数k,使得a = kd;a是d的倍数,d是a的约数

  素数和合数:如果一个整数a>1且只能被平反约数1和它自身整除,则是素数,否则为合数(0,1,所有负整数既不是素数也不是合数)。

  除法定理:对于任何整数a和任何正整数n,存在唯一整数q和r,满足0<= r < n 且a = qn + r。q = a / n(向下取整)称为,r = a mod n 称为余数

  公约数与最大公约数:

      d是a,b的公约数,则有对于任意整数x和y, d|a 且 d|b 蕴涵着 d | (ax + by)。

      两个不同时为0的整数a与b的公约数中最大的称为最大公约数,记做gcd(a,b)。特别的,gcd(a,0) = a,gcd(0,0) = 0

     定理:如果任意整数a和b不都为0,则gcd(a,b)是线性组合集{ax+by: x, y都为整数}中的最小正元素。

 互质数:如果两个整数a和b只有公约数1,即gcd(a,b)=1,则a与b称为互质数

唯一因子分解定理:合数a仅能以一种方式写成如下乘积形式:  a = P1^e1 * P2^e2 * ...*Pr ^er,其中Pi为素数,P1<P2<...<Pr,且ei为正整数

最大公约数:

求两个正整数a,b的最大公约数算法。

1.因子分解求最大公约数

   根据上面提到的唯一因子分解定理可得

   a = P1^e1 *P2^e2 * ...*Pr ^er

   b = P1^f1 *P2^f2 * ...*Pr ^fr

  则 gcd(a,b) = P1^min(e1,f1) *P2^min(e2,f2) * ...*Pr ^min(er,fr)。

  但目前已知最好的因子分解算法也不够理想,所以以上方法不大可能获得高效率。

2.欧几里得算法

  GCD递归定理:对任意非负整数a和任意正整数b,gcd(a,b) = gcd(b, a mod b)

  so,求最大公约数的递归算法的伪代码

  EUCLID(a,b)

    if b == 0 return a

    return   EUCLID(b,a mod b)

  例:EUCLID(30,21) = EUCLID(21,9) = EUCLID(9,3) = EUCLID(3,0) = 3

  当 a < b时,第一次递归调用就会把a,b的位置调转。因为a mod b < b,递归调用过程中b单调递减,因此必定不会无限递归下去。

  好像小学学过的。。。辗转相除?!

元素的幂:

求一个数的幂对另一个数的模运算(a^b mod n),也成为模取幂。在许多素数测试程序和RSA公钥加密系统中,模取幂运算时一种很基本的运算。之所以要取模,也有大部分计算机语言的整数类型在放置a^b的值时容易溢出的原因。

采用b的二进制表示,反复平方法可以高效地解决这个问题。

  MODULAR-EXPONENTIATION(a, b, n)

1 c = 0

2 d = 1

3 let <bk,bk-1,...,b0> be the binary representation of b

4 for i = k to 0

5     c = 2c

6     d = (d * d) mod n

7     if bi == 1

8            c = c + 1

9            d = (d * a) mod n

10 return d

例:a = 7, b = 560, n = 561i9876543210bi1000110000c1248173570140280560d749157526160241298166671

这里的变量c在算法中没有用处,通过c的变化可知以下两部分在循环开始时不变

1.c的值与b的二进制表示的前缀<bk,bk-1,...,bi+1>相同

2.d = a^c mod n

以上的说明,理解起来比较费劲。笔者的理解是:

6行:d = (d * d) mod n 即是 d = (a^i * a^i) mod n = a^2i mod n,而2i即是循环1次,c左移一位

9行:d = (d * a) mod n,由于在6行时d已经变化,因此 d = (((d * d) mod n) * a) mod n,即是 d = (((a^i * a^i) mod n) * a) mod n = (a^i * a^i * a) mod n = a^(2i+1) mod n,而2i+1即是循环1次,左移一位,此二进制位数为1。

以上的算法循环顺序是从b的二进制高位开始,也可以从低位开始实现上述算法,只需要把6行和7~9行的代码调换并且稍加修改即可。

int MODULAR-EXPONENTIATION(int a,int b,int n){
    int r=1;
    while(b){
        if(b&1)r=(r*a)%n;
        a=(a*a)%n;
        b>>=1;       
    }
    return r;
}


为了应用上述知识,可以做一下下面这道练习。

回文字符串(即在字符串中间位置开始两边字符对称的字符串)个数:

给定一个字符串,给定的字符串可以随意排列,求可以组成的回文字符串的个数对10^7+7的模。例如,给定字符串“caabb”,可以组成的回文字符串有“abcba”,"bacab"。

提示1:由n1个a,n2个b,。。。,nk个z组成的n(n=n1+n2+...+nk)个字符的字符串的全排列个数为n!/n1!n2!...nk!

提示2:n! 的唯一因子分解为P1^n1 *P2^n2 *...Pk^nk,其中pi的次数ni= n/Pi + n/Pi^2 + ... + n/Pi^k

来自csdn的在线编程平台——庞果英雄会的一道过往题目


原创粉丝点击