-
什么是素数?
- 素数又叫质数。 素数,指的是大于1的整数中,只能被1和这个数本身整除的数。有素数自然也有合数,合数,指的是在大于1的整数中除了能被1和本身整除外,还能被其他数(0除外) 整除的数。
- 接下来是几种求素数的方法
一 .基础法
int isPrime(int n)
{
if (n <= 1)
return 2;
for (int i = 2; i <= n; ++i)
{ // 遍历大于1且小于n的整数
if (n % i == 0)
{ // 如果n可以被某数整除,则为合数
return 0;
}
}
return 1; // 遍历结束,则为素数
}
int main() {
int n = 0;
scanf("%d", &n);
if (isPrime(n) == 1)
printf("%d是素数", n);
else if (isPrime(n) == 2)
printf("%d不是素数也不是合数", n);
else
printf("%d是合数", n);
return 0;
}//基础方法 时间复杂度为o(n)
这边是最易想到的求素数方法,时间复杂度较高,接下来我们对其进行优化
二.试除法
int isPrime2(int n)
{
if (n <= 1)
return 2;
for (int i = 2; i <= sqrt(n); ++i)
{ // 遍历大于1且小于根号n的整数
if (n % i == 0)
{ // 如果n可以被某数整除,则为合数
return 0;
}
}
return 1; // 遍历结束,则为素数
}
int main() {
int n = 0;
scanf("%d", &n);
if (isPrime2(n) == 1)
printf("%d是素数", n);
else if (isPrime2(n) == 2)
printf("%d不是素数也不是合数", n);
else
printf("%d是合数", n);
return 0;
}//试除法 时间复杂度为o(根号n)
此方法较基础法的优化是显而易见的,在遍历时将n次改成了√n,为什么能这样改捏?细微思考可以发现我们压根不需要遍历到n才能发现这个数是不是素数,举例:4是不是素数我们只需要遍历到到2即可得知 9是不是素数我们只需遍历到3即可得知。试除法:给定一个 合数 n(这里,n是待分解的整数),试除法看成是用小于等于n的每个素数去试除待分解的整数。 如果找到一个数能够整除除尽,这个数就是待分解整数的因子。 试除法一定能够找到n的因子。 因为它检查n的所有可能的因子,所以如果这个算法“失败”,也就证明了n是个素数。
虽然这样已经很快了,但仍然不是最优的,尤其是当我们需要寻找大量素数的时候,仍会消耗大量的时间。那么有没有什么办法可以批量查找素数呢?
那必然是有的,咱们接着往下看!
三.埃式筛
void eratosthenes(int* nums, int numsSize) {
for (int i = 2; i < numsSize; i++) {
if (nums[i]) {
for (int j = 2; i * j < numsSize; j++)
nums[i * j] = 0;
}
}
}
int main() {
int n = 0;
scanf("%d", &n);
if (n < 2) return 0; //如果小于2,代表没有质数直接返回
n++; //0~n有n+1个数,所以数组大小加1
int* nums = (int*)malloc(sizeof(int) * (n));//动态开辟数组
nums[0] = nums[1] = 0; //0 和 1 不是质数
//假设全都是质数,1为质数,0为非质数
for (int i = 2; i < n; i++)
nums[i] = 1;
//进行埃氏筛法
eratosthenes(nums, n);
//打印
for (int i = 2; i < n; i++) {
if (nums[i]) {
printf("%d ", i);
}
}
return 0;
}//时间复杂度是O(nloglogn)
//埃式筛法的思路非常简单,就是用已经筛选出来的素数去过滤所有能够被它整除的数。
//这些素数就像是筛子一样去过滤自然数,最后被筛剩下的数自然就是不能被前面素数整除的数,
//根据素数的定义,这些剩下的数也是素数。
这便是埃式筛,为什么叫埃式筛捏?是埃及人发明的吗?漏漏漏,埃式筛全称是埃拉托斯特尼算法。这个名字念起来非常拗口,这是一个古希腊的名字。此人是个古希腊的大牛,是大名鼎鼎的阿基米德的好友。他虽然没有阿基米德那么出名,但是也非常非常厉害,在数学、天文学、地理学、文学、历史学等多个领域都有建树。并且还自创方法测量了地球直径、地月距离、地日距离以及黄赤交角等诸多数值。要知道他生活的年代是两千五百多年前,那时候中国还是春秋战国时期,可以想见此人有多厉害。
这个看起来很高级的算法到底有多厉害呢? 举个例子吧如果要算20以内的素数正常方法应该是循环60次左右但是埃式筛只需要大概30次左右,已经非常优秀了,那要是求10000以内的素数,此算法的优化就不言而喻了。
其实对于如此优秀的埃式筛还有优化。埃式筛在对于例如38,它有2和19这两个素因数,那么它就会被认为是两次合数,这就带来了额外的开销,如果对于每一个合数我们只更新一次怎么样保证每个合数只被更新一次呢?这里要用到一个定理,就是每个合数分解质因数只有的结果是唯一的。既然是唯一的,那么一定可以找到最小的质因数,如果我们能够保证一个合数只会被它最小的质因数认定为合数,那么整个优化就完成了。总之就是保证每个合数只会被它最小的质因数消除。看我说了这么多去优化到极致,也就可以看出这边是用空间换时间了。
结尾:
一个小小的求素数居然有如此多的学问,在一遍一遍的优化后也许优化效果甚微,但人们对这种极致的追求是十分宝贵的,愿我们在以后的问题中也能优化出自己的“筛”。