数论--整除&素数

这一篇里的符号如未详细说明则都是整数。

带余除法#

小学时候我们学过加减乘除,我们知道两个整数相加、减、乘得到的结果都是整数,只有除法不一定。那时候我们还没学过小数,我们的解决办法是使用余数。

比如:5/2=2...1,读作五除以2等于2余1

这里面五是被除数,二是除数,结果中的二是商,省略号后面的一是余数。

我们把它用一般化的式子表示,这也是带余除法定理。

a÷b=c..ra=b×c+r0r<|b|

除和除以#

直到大二我也没分清除和除以的关系,导致我第一次看这章的时候云里雾里的。

除和除以都是谓语,代表一个动作,除的主语要是除数,而除以的主语要是被除数。

例如上面的例子,你可以说是五除以二或者二除五,你不能说是二除以五,那样就反了。

整除符号#

假如a除以b的余数为零,也就是说满足a=bc的关系,那么就说明a能被b整除,记作b|a

如果无法整除记作ba

一些性质#

  • b|ac|b那么c|a

    b|a,c|ba=kb,b=mcba=(mk)cc|a

  • a|b,a|c那么a|(b+c)

    a|b,a|cb=ka,c=mab+c=ka+ma=a(m+k)a|a(m+k)a|(b+c)

  • a|b,那么对所有c都有a|bc

素数#

如果一个数只能被1和它本身整除,这个数就是素数,否则就是合数。1既不是素数也不是合数。

2是素数里唯一一个偶数,也是最小的素数。其他偶数都可以被2整除。

最小因数#

我们发现,一个数的最小因数一定是一个素数(除了1)。我们不妨列出几个数看看。

4 - 1,2,4
6 - 1,2,3,6
8 - 1,2,4,8
9 - 1,3,9
12 - 1,2,3,4,6,12

我们可以看到这些因数里除了1和它本身外,最小的都是素数。

证明一下。用反证法吧。

a=bc,bcba1b=mn,m<b,m|bb|a,m|bm|a,m<bb

商拆分#

一个合数肯定能拆成若干个素数的乘积。

假如a是和数,一定能找出一个纯素数数列m1...mn乘积为a。

这个证明方式和上面的差不多,不证明了。其实也非常容易想出来。

举个例子

12=22318=233

寻找1-N之间的素数#

学编程语言的时侯我们大家基本上都写过这个程序。大部分是这样写的:

void get_prime_nums_traditional(){
    prime_nums.push_back(2);
    for (int i = 3; i <= N; i++) {
        int j=2;
        for (; j <= i-1; j++) {
            if (i % j==0)
                break;
        }
        if (j==i)
            prime_nums.push_back(i);
    }
}

这个写法没错,而且在教学中是一个很经典的双循环示例。但是我们学了数论之后可就不能这么写了。我们已经探究了素数的很多性质,就应该写出更快速高效的代码。

上面的代码把3~n中的所有数遍历一遍,并且使用一个内循环,再把2到当前数i减1的所有数和i做余数运算,如果在2~i-1中有任何一个数字能整除i,就证明它不是素数。如果没找到整除的数字就是素数。

我们大可不必如此费力。

判断是否是素数#

假设给我们一个数a,我们怎么判断它是否是素数?

我们知道,这个数肯定能写成a=bc的形式,假设bc那么就一定有ba,因为b大于根号a了c也一定大于根号a,两个大于根号a的数相乘一定大于a。

我们还知道一个合数的最小因数一定是素数,所以我们只需要遍历[2,a]之间的每一个素数,用这个素数去除a,判断是否能整除,如果能,就证明这个数不是素数,如果都不能整除,就证明这个数是素数。

快在哪?#

  • 缩小搜索边界,从[2,a1]变成了[2,a]
  • 内循环不用遍历每个正数,而只需要遍历素数列表,遍历的次数又少了

实现代码#

bool is_prime(int inputNum){
    for (int i = 0; i < prime_nums.size() && prime_nums[i] <= sqrt(inputNum); i++){
        if (inputNum%prime_nums[i]==0)
            return 0;
    }
    return 1;
}
void get_prime_nums(){
    prime_nums.push_back(2);
    for (int i = 3; i <= N; i++) {
        if (is_prime(i))
            prime_nums.push_back(i);
    }
}

快了多少#

生成1~100以内的素数,在内循环里添加一个计数器查看内循环执行了多少次。

Traditional : 1133 times
New Algorithm : 181 times

未改进的算法执行了1133次内循环,心算法只执行了181次。

时空互换#

其实对于这个问题,采用时空互换的办法会更好。也就是空间换时间。

因为我们计算机中的数据类型大小是有限制的,比如int类型会有一个能表示的最大值。我们在编写代码时把这个范围内的所有素数都硬编码进程序,这样即可达到O(1)的时间复杂度。

posted @   yudoge  阅读(784)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
主题色彩