素数问题

mac2022-06-30  19

今天坐在桌前,在思考,以我这种小白的水平怎么去写一个自己的算法呢? 于是我看到了一道题,一个求素数的题。 我想起来第一次的暴力破解,运用了很多循环,简而言之就是一个一个的循环下来,相比那个代码大家也非常的熟悉吧:

#include<stdio.h> int main(){ int i, j, key = 0; printf("2 "); for( i = 3; i < 100000; i++){ for(j = 2; j < i; j++){ if(i%j == 0){ key = 1; } } if(key == 0){ printf("%d ", i); } key = 0; } return 0; }

我们用双层循环嵌套进行,其中,我们需要一个key作为“钥匙”来作为我们确定是否找到了素数的标志~如果key为0,则说明我们没有找到一个可以因数,那么这个i就是我们再找的素数 我们定义一个足够大的数来验证一下 我们所选的数为四十万 我的天,真的没想到会要这么久,七分多钟呢~

接下来就是分析问题了,为什么会耗时如此厉害? 如何解决呢?

首先,我们可以肯定一点,所有的素数,除了2,就不再会有偶数的,对吧~ 那么我们是不是就意识到一个问题,我们其实不必要一个一个的加起来了? 并且还有一个问题,我们也并不用在内部循环一直到i,因为一旦超过i/2以后,那么也就不用考虑了

#include<stdio.h> int main(){ int i, j, key = 0; printf("2 3 ");//加上3\n for( i = 5; i < 400000; i+=2){ //i从5开始,避开3%3=0的问题,i++改为 i+=2 for(j = 3; j < i/2; j+=2){ //j从3开始,因为已经排除了所有偶数 if(i%j == 0){ key = 1; } } if(key == 0){ printf("%d ", i); } key = 0; } return 0; }

在这里,我们在内外都减少了一半的循环,大家可以想象,一般的循环次数是内圈乘以外圈,之前的四百多秒就是20乘以20,那么现在我们的改进后的应该就是10乘以10/2了吧,大概会在50秒左右了~ 看看结果~

结果很明确,还真的是五十多秒,真刺激~

但这还不是我们想要的,我们在看看代码的问题,一般学完分支语句后我们都会接触到一个指令break,一般用作跳出当前模块,对吧~ 那我们细想一下啊,在这个程序中,我们一般只用检测到一个因数,就可以说明当前的数不是质数了,对吧? 于是乎,我们在其中套入一个break吧~

#include<stdio.h> int main(){ int i, j, key = 0; printf("2 3 "); for( i = 5; i < 400000; i+=2){ for(j = 3; j < i; j+=2){ if(i%j == 0){ key = 1; break; //加入break } } if(key == 0){ printf("%d ", i); } key = 0; } return 0; }

break的插入迅速减少了内部循环的次数,而究竟多少次,这个我也无从知晓,应该会很快的

二十秒! 一个break居然为我们减少了这么多的时间!!! 真的很不可思议! 那么到现在究竟还有什么方法可以继续为我们服务,来降低他的时间呢?

学C语言的小伙伴们应该清楚,C语言有着其他的函数库,这些函数库中就存在着一些非常有用的函数,今天我们需要用到的函数就是<math.h>这个头文件中的sqrt 他是一个开方用的函数~ 那么它可以用来干么呢?在这道题目中,我们还能用它优化哪呢?

举个例子:36的因数有哪些? 2, 3, 4, 6, 9, 12, 18 我们可以发现其实找到6就足够了!因为超过6的数后面的数,其实是和6前面的数一对儿的! 2和18 3和12 4和9

那么如果一个数只要检测到他的平方根的时候发现没有因数,那么后面也不会有了,所以它就是质数了!

#include<stdio.h> #include<math.h> int main(){ int i, j, key = 0; printf("2 3 "); for( i = 5; i < 400000; i+=2){ for(j = 3; j < sqrt(i); j+=2){ if(i%j == 0){ key = 1; break; } } if(key == 0){ printf("%d ", i); } key = 0; } return 0; }

来瞧瞧运行结果?

3.667秒,真棒! 事到如今,也就差不多了~ 才怪!

一直到这里,其实都还算不上算法! 那么这道题是否还可以更加的简便? 当然可以!

我们需要思考一个问题,质数是什么?能用来干什么?

举个例子,还是36,我们再来分解他~ 这一次我们分解的更加彻底! 36 = 2233 2是质数么?3是质数么? 换一个数试试122 112=2222*7 2和7依旧是质数 是的! 所有的数分解以后都是由质数组成的! 那么我们接下来只需要将内部循环的数全部改成质数就可以了!但别忘了,我们需要将质数全部储存起来,在这里我们只能牺牲空间换取时间了。于是我重新写了一遍代码,经行测试!

#include<stdio.h> int main(){ int i, j, t = 0, k[35000], key = 0; k[0] = 2; for(i = 3; i < 400000; i+=2){ j = 0; for(j = 0; j < t+1; j++){ if(i%k[j] == 0){ key++; break; } } if(key == 0){ t++; k[t] = i; } key = 0; } for(i = 0; i < t; i++){ printf("%d ", k[i]); } return 0; }

真不错,我们再来运行一下吧 哇哦!3.188秒! 虽然这并没有快多少,但却是也是略有进步,并且虽然牺牲了空间,但从计数和其他方面也存在一定的优势哦~ 好啦,今天就写到这里了~

最新回复(0)