1、1 数论与数论算法 一、 质数 与质因 数 分解 1. 质数 : 也称素数, 指除 1 和它本身以外 没有其他因数的正整数 。 素数有无穷多 个。 1 不 是素数。 2. 质因 数分解 : 任意一个大于 1 的正整数都可以唯一分解成若干个质数相乘的形式。即: 3. 【 例题 1 】 已知正整数 N 是两个不同质数的乘积, 请求出较大的那个质数 (洛谷 1075) 。 提示:若 P、Q 为质数, 令 N = P * Q ,则 N 除 1 和它本身 以外,有且仅有 P、 Q 两个因 数。这也是目前主流的“非对称加密算法”R S A 算法的实现原理。 a 2 *3 *5 * 7 .(1b c d =
2、 0 ) bcd A Aa 为 大于 的正整数,其中 、 、 、 #include using namespace std; int main() i n tn=0 ; i n ti=2 ; cin n; / 找出n 的其中一个质因数 w h il e(n%i! =0) i+; i n tk=n/i ;/ / 求出n 的另一个质因数 / 求较大的那个质因数 i f(ki) k=i; cout k; return 0; 2 4. 【例题 2 】 对 N !进行质因数分解, 并求出每个质因数的个数 ( N int cnt10001 = 0; / 定义数组,保存每个质因数的个数 / 对给定的整数d
3、进行质因数分解 void seprate(int d) int factor = 2; while( d = factor) i f(d%f a ct or! =0) factor +; continue; cntfactor +; d=d/f a ct or ; int main() i n tn=0 ; scanf(“%d“, / 从2 到n 依次进行质因数分解 / 因为n ! =2*3*4* . .* n f o r(i n ti=2 ;i0) printf(“%d %dn“, i, cnti); return 0; 3 二、 质数 筛选( 求 1 到 N 的所 有质 数 ) 1. 【
4、试除法】: 使用 2 到 s q r t ( x ) 试除 x ,如 果 x 不存 在因 数则表 示 x 是质 数; 如果 x 有 因数则表示 x 不是质数。时间复杂度为:() Onn 2. 【 例题 1 】求第 k 小的素数 #include #include int main() i n tn=0 ,f=0 ; scanf(“%d“, for ( int x = 2; x = n; x+ ) f=1;/ / 将标记变量f 设为1 ,默认x 是质数 for ( int i = 2; i = sqrt(x); i+ ) i f(x%i= =0) / 如果找到x 有因数,则表明x 不是质数,将标
5、记变量设为0 ,跳出循环 f=0; break; i f(f= =1) printf(“%d “, x); return 0; / 部 分代 码已省 略 i n tx=2 ,n=1 ;/ /n 表示当前已求出的素数是第几个,x 表示当前被试除的整数 w h il e(nk) x+ ; f=1; for ( int i = 2;i = sqrt(x);i+) i f (x%i= =0 ) f=0; break; i f(f= =1) n +; printf(“%d“,x);4 3. 【 埃氏筛选法 】:由 质因数分 解定理, 可以得出 ,任意合 数都可以 分解成至 少两个质数 的乘积, 所以质数
6、是不能 被再分的 。 如果 x 是质数, 那么 2 * x、 3*x、 4*x、 必然都 不是质数。我们可利用此规律筛选出 N 以内的素数。时间复杂度为: (l o g l o g) On n 提示:假设 N 的值为 11,其对应的筛选过程如下: 第 1 轮筛选:2、3、4 、5、6 、7、8 、9、1 0 、1 1 (标记 4、6、8、1 0 不是素数) 第 2 轮筛选:2、3、4 、5、6 、7、8 、9 、1 0 、1 1 (标记 9 不是素数) 第 3 轮筛选:2、3、4 、5、6 、7、8 、9 、1 0 、1 1 (标记 10 不是素数) 因此输出结果为:2、3、5、7、1 1 4
7、. 【 练习 1 】线性筛素数(洛谷 3383) 5. 【 练习 2 】素数个数(洛谷 3912) #include #include #include int f10000010 = 0; / 素数标记数组, 若fi != 0, 则表示i 是素数 int main() i n tn=0 ; scanf(“%d“, memset( / 1 、假设n 以内的所有整数都是素数 f 1=0 ;/ /2 、整数1 不是素数 for ( int i = 2; i = sqrt(n); i+ ) i f(f i= =0) continue; / 3 、素数的整数倍必然不是素数 f o r(i n tj=2
8、* i;j =n ;j + =i) fj = 0; / 4 、输出所有的素数 for ( int i = 2; i = n; i+ ) i f(f i! =0) printf(“%d “, i); return 0; 5 三、 同余 模运算 1 . 模运算的基本规则如下: 加 法 : ( a+b )%p=( a%p+b%p )%p 减 法 : ( a-b )%p=( a%p-b%p )%p 乘 法 : ( a*b )%p=( a%p*b%p )%p 幂运算:a b %p=( ( a%p ) b )%p 交换率:( a + b) %p=( b+a )%p ( a*b )%p=( b*a )%p
9、结合率:( ( a + b ) %p+c )%p=( a+( b + c )%p )%p ( ( a * b )%p*c )%p=( a*( b * c )%p )%p 分配率:( ( a + b)%p*c )%p=( ( a*c )%p+( b*c )%p )%p 2. 【 例题 1 】给定一个整数 N,请求出 N 模 K 的余数。1 #include int main() char n100 = 0; l o ngl o ngk=0 ; scanf(“%s“, n); scanf(“%lld“, int len = strlen(n); l o ngl o nga n s=0 ,d=1 ;
10、 for ( int i = len 1 ;i =0 ;i ) d=d%k ;/ / 避免数据溢出 ans += (ni 0 )*d%k ; ans = ans % k; / 避免数据溢出 d=d*1 0 ; printf(“%lld“, ans); return 0; 6 四、 整数 的二进 制 分解 与快速幂 1. 整数 的二进 制分 解:任意整数都可以唯一分解成若干个二次幂的和。即: 2. 【 例题 1 】求 A*B 模 P,1 #include using namespace std; int main() l o ngl o nga=0 ,b=0 ,p=0 ,t=0 ; c i n
11、a b p ; t=a ; long long ans = 0; w h il e(b! =0) i n tx=b/ / 判断b 以二进制形式表示时 ,最末一位是否为1 i f(x= =1 ) ans = (ans + k)%p; t=( t*2 )%p ;/ / 后一项是前一项的两倍:t ( n)=2*t ( n 1) b=b 1 ;/ / 移除最末一位 ,将倒数第2 位变成最末一位 cout a n s % p ; retu rn 0 ; 7 参考代码如下: 4. 【 练习 】NOIP2013TGD1T 1-转圈游戏(洛谷 1 9 6 5 ) 五、 欧几 里德与 扩 展欧 几里德算 法 1
12、. 欧几里德算 法:又 称辗转相 除法,通 常用于 快速求解两个正整 数 a ,b 的最 大公因数 。 计算公式:g c d ( a , b ) = g c d ( b , a % b )。算法思路如下: 第 1 步:输入整数 a、b;若 a int main() l o ngl o nga=0 ,b=0 ,p=0 ; scanf(“%lld %lld %lld“, long long ans = 1%p; / 若b 的值为0, 则ab 的值为1 l o ngl o ngt=a ; w h il e(b) i f(b / t(n) = (t(n 1)2 / p 和a 的最大值都为109, 所以
13、t*t 不会超过long long 的存储范围 t = (t * t)%p; b =1 ; printf(“%lldn“, ans%p); return 0; 8 欧几里德算法的递归实现如下: 欧几里德算法非递归实现如下: #include long long gcd(long long a, long long b) i f(b= =0) return a; return gcd(b, a%b); int main() l o ngl o ngm=0 ,n=0 ; scanf(“%lld %lld“, / 输入m ,n ;求它们的最大公因数 i f(m int main() l o ngl
14、o ngm=0 ,n=0 ; scanf(“%lld %lld“, / 输入m ,n ;求它们的最大公因数 i f(mn) long long tmp = m; m=n; n = tmp; l o ngl o ngr=m%n ; w h il e(r! =0) m=n; n=r; r=m%n; printf(“%lld“, n); return 0; 9 2. 扩展 欧几里 德算 法: 对于任意不完全为 0 的非负整数 a、 b, 必然存在整数 x、 y, 满足: 证明过程如下: ( 1 )、若 b 等于 0,显然存在 x = 1 、y = 0 ,使得 ( 2 )、若 b 0 ,由欧几里德算法
15、可得: ( 3 )、假设存在整数 x、y 满足: ( 4 )、又因为: 令: 则就可以得到: 对欧几里德算法的递推过程运用数学归纳法,可得定理成立。 , , % gcd a b gcd b a b * * , ax by g c dab *1 0 * 0 , 0 ag c d a * % * , % bxabyg c dbab * % * b* x +(a - b* / )*y * % * = a * y - b * ( x - / *) bx a by ab bx a by aby /* xy y xaby * gcd( , ) axby ab 10 3. 【 扩欧 算法练 习 1 】扩展欧
16、几里德,参考代码如下: 4. 【 扩欧 算法练 习 2 】同余方程(洛谷 1 0 8 2 ) #include / a * x + b*y = gcd(a, b) / 递推过程:x(n) = y(n 1) ;y(n) = x(n 1) (a/b)*y(n 1) long long exgcd(long long a, long long b, long long y=0; return a; long long d = exgcd(b, a%b, x, y); long long t = x; x=y; y=t (a/b) * y; return d; int main() long long a = 0, b = 0, x = 0, y = 0; scanf(“%lld %lld“, exgcd(a, b, x, y); printf(“%lld %lld“, x, y); return 0;