基础的数论知识

来源:互联网 发布:系统修复软件 编辑:程序博客网 时间:2024/05/22 16:01

素数定义

        除了1和该数本身,不能被其他整数整除,1不是素数

素数判定

        素数的判定没有统一的公式,可以通过枚举暴力方法判定,也可以基于费马定理和米勒-拉宾定理进行随机测试判定。

暴力判定

        针对给定的正整数N,枚举1 to sqrt(N), 时间复杂度为O(sqrt(N)).
bool isPrime(long long N){    if(N == 1){        return false;    }    long long bound = sqrt(N);    for(long long i = 2; i < bound; ++i){        if(N % i == 0){            return false;        }    }    return true;}


随机测试判定

        给定一个素数p,满足费马定理或米勒-拉宾定理:

费马定理

        a^p mod p = a

米勒-拉宾定理

        a^(p-1) mod p = 1

        因为这两个定理都是p为素数的必要不充分条件,但若对于整数p,费马定理和米勒-拉宾定理成立,有很大的概率判断p为素数,因此可以随机产生整数a,多次进行测试,如果不满足则p为合数,如果都满足,则很有可能为素数。下面编程实现a^b mod n:
long long pow_mod(long long a, long long b, long long n){    if(!b){        return 1;    }    long long ans = pow_mod(a, b / 2, n);    ans = ans * ans % n;    if(b % 2 == 1){        ans = ans * a % n;    }    return ans;}

素数筛选

        所有的素数能不能由一个统一的公式产生,这一直是无数先哲试图解决的一个问题,迄今为止,仍然没有找到这样的一个万能公式,因此只能通过暴力筛选素数。
const int maxn = 10001;const int maxp = 1000;int vis[maxn]; //vis[i] = 1, 则i是和数; vis[i] = 0,则i是1或者素数int prime[maxp]; //从小到大保存maxn内所有素数//生成素数表,放在prime中,返回素数个数int get_primes(){    //标记素数和合数    int m = sqrt(maxn);    for(int i = 2; i <= m; ++i){        if(!vis[i]){            for(int j = i * i; j <= maxn; j += i){                vis[j] = 1;            }        }    }    //存储素数    int count = 0;    for(int p = 2; p <= maxn; ++p){        if(!vis[p]){            prime[count++] = p;        }    }    return count;}

分解素因子

分解整数

        通常使用试除法,首先构造一个素数表,然后扫描素数表,整除则加入解集。

N!中素因子p的个数

        Leetcode中有一道有N!结果里,结尾0的个数的题目:Factorial Trailing Zeroes,本质就是求素因子5的个数
int Num_p(int N, p){    int num = 0;    while(N){        num += N / p;        N /= p;    }    return num;}

最大公约数(GCD)

        求解两个数的最大公约数和最小公倍数是一个常见的问题,其中最小公倍数需要求最大公约数,因此重要的是求两个数的最大公约数。常见的方法是欧几里得算法和扩展欧几里得算法;

欧几里得算法

        定理:gcd(a, b) = gcd(b, a mod b)
        证明:设a = kb + r; p = gcd(a, b); b = mp; a = np;  则 a = kmp + r = np, r = (n-km)p, 故 p 也是r的约数,得证。代码如下:
int gcd(int a, int b){    if(!b){        return a;    }    return gcd(b, a % b);}

扩展欧几里得算法

        对于不完全为0的非负整数a, b;必然存在整数对x, y, 使得(a,b) = ax + by,其中(a,b)=  gcd(a, b);下面编程实现扩展欧几里得:
int exGcd(int a, int b, int & x, int & y){    if(!b){        x = 1;        y = 0;        return a;    }    int ret = exGcd(b, a % b, y, x);    y = y - a / b * x;    return ret;}
        x,y的迭代更新公式可以容易使用递推归纳证明。这里需要指出的是,这里得到的满足(a,b)= ax + by的整数对x,y并不是唯一的,只是abs(x) + abs(y)取最小值。

求解模线性方程 ax = b(mod n)

        解模线性方程相当与求 ax = b + kn  => ax + n(-k) = b, 这有点类似用扩展欧几里得算法求(a,b)= ax + by.若(a, n)是 b的约数,即可以求解模线性方程有解。现在我们好奇的是如果已知其中一个解x(可以通过扩展欧几里得算法求得),能否求得其他解。
        事实上,如果 ax = b(mod n), 则(x + k * n / (a, n))mod n 也必为解。

贴一道有意思的题目

        1/n = 1/a + 1/b; (0<a<=b), 给定整数n,求出所有满足要求的的整数对a,b;若没有,则返回空集。
        这道题目稍微化简就很明朗了:1/n = 1/(n+k) + k/n(n+k)  =>  1/n = 1/(n+k) + 1/(n^2/k + n), 所有k只要满足为n^2的因子就够了。

0 0