10.1数论初步

1.欧几里得算法(辗转相除法)和唯一分解定理:

①唯一性分解定理:

算术基本定理,又称为正整数的唯一分解定理,即:每个大于1的自然数均可写为质数的积,而且这些素因子按大小排列之后,写法仅有一种方式。

算术基本定理的内容由两部分构成:

分解的存在性;

分解的唯一性,即若不考虑排列的顺序,正整数分解为素数乘积的方式是唯一的。

②辗转相除法:

是求最大公约数的算法。

辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数求余数的最大公约数。在这个过程中,较大的数缩小了,所以继续进行同样的计算可以不断缩小这两个数直至余数变为零。这时的除数就是所求的两个数的最大公约数。

辗转相除法处理大数时非常高效,它需要的步骤不会超过较小数的位数(十进制下)的五倍。

③例题(除法表达式):

【描述】给出如下除法表达式E:X1/X2/X3/..../Xk,其中Xi是正整数并且Xi<=2 000 000 000(1<=i<=k,k<=10 000)。除法表达式应当按照从左到右的顺序求结果。

例如:表达式1/2/1/2的值是1/4。现在让你可以在表达E中嵌入括号以改变计算顺序,例如表达式(1/2)/(1/2)的值是1。

现在给你一个除法表达式E,要求告诉是否能够通过加括号(或者不加)得到表达式E‘ ,E‘的值为整数。

【思路】首先优化算法,表达式的值一定可以写成A/B的形式:A是一些Xi的乘积,而B是其他数的乘积,不难发现,X2必须放在分母位置,其他数随意,因为本题是存在性求解问题,所以只要极端情况下存在解即可,极端情况是只有X2在分母上,其他数都在分子上的情况,这样能保证分母最大,分子最小,以下是算法的优化过程:

1.进行高精度运算,k次乘法+1次除法,明显会杯具的TLE

2.利用唯一性分解定理:把x2写成若干个素数相乘的形式:X2 = p1^a1*p2^a2*p3^a3*……,依次判断每个pi^ai是否是X1X2X3X4……的约数,只需要把Xi中的pi的指数加起来,如果比ai打,说明pi约掉了,E是整数,以下是部分代码实现,my ugly code:

#include<stdio.h>
#include<iostream>
using namespace std;
struct num
{
    int p,a;
};
num n[999999];
int main()
{
    int x2,zhizhen = 0;cin>>x2;
    for(int i = 0;i < 999999;i++)
    {
        n[i].p = 0;n[i].a = 0;
    }
    while(1)
    {
        if(x2 == 1) break;
        for(int i = 2;i <= x2;i++)
            if(x2 % i == 0)
            {
                int flag = 0;
                for(int j = 0;j < zhizhen;j++)
                    if(n[j].p == i)
                        {n[j].a++;flag =1;break;}
                if(!flag) {n[zhizhen].p = i;n[zhizhen++].a = 1;}
                x2 /= i;break;
            }
    }
    for(int i = 0;i < zhizhen;i++) printf("%d  %d\n",n[i].p,n[i].a);
    return 0;
}

3.直接约分,每次约掉Xi和X2的最大公约数,约到X2=1时,就能证明E就是整数了,代码实现如下:

#include<stdio.h>
#include<string>
#include<sstream>
#include<string.h>
#include<iostream>
using namespace std;
int gcd(int a,int b)  //最大公约数
{
    return b == 0 ? a: gcd(b,a%b);
}
int main()
{
    string str;
    while(cin>>str)
    {
        int num[10005],x2;
        memset(num,0,sizeof(num));
        char temp;
        stringstream s(str);
        s>>num[0];
        s>>temp;
        s>>x2;
        int zhizhen = 1;
        while(s>>temp)
        {s>>num[zhizhen++];}
        for(int i = 0;i <zhizhen;i++)
        {
            x2 /= gcd(num[i],x2);
            if(x2 == 1) break;
        }
        if(x2 == 1) printf("YES\n");
        else printf("NO\n");

    }
    return 0;
}

2.Eratosthenes筛法:

①素数(质数):

一个大于1的自然数,除了1和它本身外,不能被其他自然数整除(除0以外)的数称之为素数(质数);否则称为合数。

不包含0、1,从2开始,其中1既不是质数,也不是和数

②素数定理:

π(x)表示不超过x的素数的个数,注意是约等于,只能说比较接近。

③程序实现:

#include<stdio.h>
#include<string>
#include<sstream>
#include<string.h>
#include<iostream>
using namespace std;
int main()
{
    int n,shu[99999];  //在机房的电脑上最多能开这么大的数组了
    cin>>n;
    memset(shu,1,sizeof(shu));
    shu[1] = 0;
    for(int i = 2;i <= n;i++)
        for(int j = 2;j * i <= n;j++)
            shu[i * j] = 0;
    for(int i = 1;i <= n;i++)
        if(shu[i]) printf("%d\n",i);
    return 0;
}

LRJ说这份代码可以改进,以下是改进的代码:

#include<stdio.h>
#include<string>
#include<sstream>
#include<math.h>
#include<string.h>
#include<iostream>
using namespace std;
int main()
{
//    freopen("out.txt","w",stdout);
    int n,shu[99999];
    cin>>n;
    int m = sqrt(n + 0.5);  //一个数<span lang="EN-US">n</span>如果是合数,那么它的所有的因子不超过<span lang="EN-US"> sqrt(n),因为</span>如果m能被2~m-1之间任一整数整除,其二个因子必定有一个小于或等于√m,另一个大于或等于√m。
    memset(shu,1,sizeof(shu));
    shu[1] = 0;
    for(int i = 2;i <= m;i++)  //i是因子
        if(shu[i])  //只有素数才判断,已经不是素数的其倍数肯定也已经不是了,真他妈好算法啊
        for(int j = i * i;j<= n;j += i)  //i*i之前的都已经判断完成
            shu[j] = 0;
    for(int i = 1;i <= n;i++)
        if(shu[i]) printf("%d\n",i);
    return 0;
}

3.拓展欧几里得算法:

①应用:线性方程ax+by=c ,已知a,b,c,求解x,y。

②思路:

ax+by=c有解 => c=k*gcd(a,b)=kd(因为d=gcd(a,b)=>d|(ax+by))

设 a>b。

⑴当 b=0,gcd(a,b)=a。此时 x=1,y=0;

⑵当ab != 0 时

设 ax+by=gcd(a,b);bx‘+(a mod b)y’=gcd(b,a mod b);

根据朴素的欧几里德原理有 gcd(a,b)=gcd(b,a mod b);

则:ax+by=bx‘+(a mod b)y’;

即:ax+by=bx‘+(a-[a/b]*b)y’=ay‘+bx’-[a/b]*by‘;

也就是ax+by==ay+b(x’-[a/b]*y);

根据系数相等得:x=y‘; y=x‘-[a/b]*y’;

这样我们就得到了求解 x,y 的方法:x,y 的值基于 x’,y‘.

上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。

③由一组解生成剩余的解:

任取另外一组解(x2,y2),则ax1+by1=ax2+by2(都等于gcd(a,b)),变形得:a(x2-x1) = b(y2-y1)。假设gcd(a,b) = g,方程两边同除以g,得a‘(x2-x1) = b‘(y1-y2)。此时a‘和b‘互素,因此x2-x1一定是b‘的整数倍。设它为kb‘,计算得:y2-y1 = ka‘。

其任意解可以基于x1,y1  任意整数解可以写成(x1+kb‘,y1-ka‘),其中a‘=a/gcd(a,b)  b‘=b/gcd(a,b)。

若ax+by=g,(g=gcd(a,b),即g是a,b的最大公约数)有整数解;则ax+by=c(c是g的倍数)有整数解

④代码实现:

以下是LRJ的思路,表示没看懂。

#include<stdio.h>
#include<string>
#include<sstream>
#include<math.h>
#include<string.h>
#include<iostream>
using namespace std;
void gcd(int a,int b,int &d,int&x,int&y)
{
    if(!b){d = a;x = 1;y = 0;}
    else {gcd(b,a%b,d,y,x);y = y - x*(a/b);}
}
int main()
{
    int x,y,d;
    gcd(15,6,d,x,y);
    printf("%d %d\n",x,y);
    return 0;
}

4.同余与模算术:

①模算术公式:

(a+b)%n=(a%n+b%n)%n

(a-b)%n=((a%n)-(b%n)+n)%n  (由于a%n可能小于b%n,故需要在结果上加上n)

ab%n=((a%n)(b%n))%n

②同余:

定义:两个整数除以同一个整数,若得相同余数,则二整数同余。给定一个正整数n,如果两个整数a和b满足a-b能被n整除,即n|(a-b),那么就称整数a与b对模n同余

注意:当b=1时,ax=1(mod n)的解成为a关于模n的逆,它类似于实数运算中的“倒数”概念,要求ax-1=ny要有解,1必须是gcd(a,n)的倍数因此a和n必须互素,即gcd(a,n)=1,在满足这个条件的前提下,ax=1(mod n)只有唯一解。否则,方程无解!

性质:(ax-b)mod n=0即存在y,使:ax-b=ny成立。

10.1数论初步

时间: 2024-10-28 15:00:19

10.1数论初步的相关文章

数论初步(费马小定理) - Happy 2004

Description Consider a positive integer X,and let S be the sum of all positive integer divisors of 2004^X. Your job is to determine S modulo 29 (the rest of the division of S by 29). Take X = 1 for an example. The positive integer divisors of 2004^1

数论初步之素数判断

基本数论-素数判断 一.暴力求解 1.一个共识       x = a*b且x = sqrt(x)*sqrt(x) => a==b==sqrt(x)或者a<sqrt(x) 且 b > sqrt(x),即要么a==b要么一个大于根号x一个小于根号x    且a = x/b,那么我们只用判断小于sqrt(x)的数是否可以整除x即可. 2.暴力求法(O(n^(2/3))) bool isPrime(int x){ for(int i=2;i<=sqrt(x);i++){ if(x%i==

数论初步

求两个数的最大公约数 1.高精度运算 2.唯一分解定理:将两个数分解为素数的 n 次方的形式,然后依次计算: 3.欧几里得算法: 1 int gcd(int a,int b) { 2 return b==0 ? a : gcd(b,a%b); 3 } 最小公倍数 = a * b / gcd(a,b),注意精度: ax+by+c = 0 直线上有多少个整点 (x,y) 满足 x 属于 [x1,x2],y 属于 [y1,y2].这是扩展欧几里得算法: 首先解决扩展欧几里得 ax + by = gcd

数论初步-欧几里得算法

1 int judge(int* X) { 2 X[2] /= gcd(X[2], X[1]); 3 for(int i = 3; i <= k; i++) X[2] /= gcd(X[i], X[2]); 4 return X[2] == 1; 5 } 这个算法称为欧几里得算法.不会溢出,因为gcd函数的递归层数不超过4.785lgN + 1.6723,其中N=max{a,b}. 让gcd递归层数最多的是gcd(Fn,Fn-1).利用gcd还可以求出两个整数a和b的最小公倍数lcm(a,b).

数论初步——欧几里得算法和唯一分解定理

具体内容参见紫书p310-p312 一.辗转相除法 恒等式:gcd(a,b) = gcd(b,a%b) 边界条件:gcd(a,0) = a 辗转相除法的关键(恒等式)和边界条件一起构成了下面的程序: int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 这个算法称为欧几里得算法!!!! 二.求lcm 公式:gcd(a,b) * lcm(a,b) = a * b 正确的写法(先除后乘):lcm(a,b) = a / gcd(a,b)

数论初步——同余与模算术

具体内容见紫书p314-p316 一.a mod b a mod b:a除以b的余数,C语言表达式是a % b,且b!=0 二.模线性方程组 题目:输入正整数a,b,n,解方程ax ≡ b(mod n) .a,b,n<=109. 新记号:同余 "≡" a ≡ b(mod n):a和b关于模n同余,即a%n = b%n a ≡ b(mod n)的充要条件:a-b是n的整数倍 特殊情况,当b=1时,ax ≡ 1(mod n) 的解称为a关于模n的逆,它类似于实数运算中"倒数

数论初步——扩展欧几里得算法

具体内容见紫书p313-p314 一.扩展欧几里得算法 思想:找出一对整数(x,y),使得ax+by=gcd(a,b) 举例:当"a=6,b=15"时,gcd(6,15)=3,故可以得到解"x=3,y=-1",当然还有其他解"x=-2,y=1". 程序: /* 扩展欧几里得算法 */ void gcd(int a, int b, int& d, int& x, int& y) { if(b == 0){ //边界,因为 a

《算法竞赛入门经典(第二版)》pdf

下载地址:网盘下载 内容简介  · · · · · · <算法竞赛入门经典(第2版)>是一本算法竞赛的入门与提高教材,把C/C++语言.算法和解题有机地结合在一起,淡化理论,注重学习方法和实践技巧.全书内容分为12 章,包括程序设计入门.循环结构程序设计.数组和字符串.函数和递归.C++与STL入门.数据结构基础.暴力求解法.高效算法设计.动态规划初步.数学概念与方法.图论模型与算法.高级专题等内容,覆盖了算法竞赛入门和提高所需的主要知识点,并含有大量例题和习题.书中的代码规范.简洁.易懂,不

复杂状态的动态规划

紫皮各种…… 1.最优配对问题 d(i,S) = min{|Pi,Pj| + d(i-1,S-i-j) | j在S中}; 空间 n 个点,配成n/2对使两点的距离之和尽量少 for(int i = 1;i <= n;i++){ for(int S = 0;S < (1 << n);S++){ d[i][S] = INF; for(int j = 0;j < n;j++){ if(S & (1<< j)){ /// 如果j能参与配对 d[i][S] = mi