素数的求解方法
第一种:试除法
第二种:筛选法
------试除法-------
顾名思义:求一个数X是不是素数,就试用小于x大于1区间的自然数,只要有一个能整除,那么x就不是素数,否则就是。
以输出100—200之间的素数为例
#include <stdio.h>
int main()
{
int i = 1;
int count = 0; //定义一个计数器来统计素数的个数
for (i = 100;i <= 200;i++)
{
int j = 2;
for (j = 2; j < i; j++)
{
if (0 == i % j)
break; //如果i被整除,则跳出循环,它就不是素数
}
if (j >= i) //判断上面的for循环是break跳出来的,还是变量j不满足j < i而跳出来的
{ //如果是break跳出来的,就不是素数并且j < i此if语句进不来;如果是j加加导致不满足循环条件跳出来,代表之间没有一个数把i整除,则是素数,进入此if语句打印素数
count++;
printf("%d ", i);
}
}
printf("\n素数的个数是%d个\n", count);
return 0;
}
上面这个算法时间复杂度很大的,是相当的慢,因为每判断一个数就要用大于1小于这个数区间的其他自然数一一试除。如果这个素数非常大,那么就要除很多很多次才能判断出来。
优化上面程序
想一想,每一个数都可以由它的两个因数相乘得来,那么它的两个因数有什么特点呢?比如x它的两个因数必有一个因数小于x/2。
为什么?你想想x的一半x/2加上它的另一半x/2等于它本身,两个x/2相加等于x,那么两个x/2相乘一定大于或者等于x本身;要想找两个因数相乘等于x,那么必定要有一个因数小于等于x/2,才会满足条件。
那么判断一个数是不是素数我们其实只需要找到它的一个因数就可以证明它不是素数,而不需要找到它的所有因数。我们从2到x/2找到一个即可,找到就是合数,否则是素数。则上面程序的第二个for循环的控制条件可以改成 j<=i/2,第二个if判断条件改成 j>i/2,这样就减少了一半的试除。
#include <stdio.h>
int main()
{
int i = 1;
int count = 0;
for (i = 100;i <= 200;i++)
{
int j = 2;
for (j = 2; j <= i/2; j++)
{
if (0 == i % j)
break;
}
if (j > i/2)
{
count++;
printf("%d ", i);
}
}
printf("\n素数的个数是%d个\n", count);
return 0;
}
再优化
其实一个数由两个因数相乘得来,则必有一个因数小于等于这个数的开平方,这样试除的数比原来x/2又减少了;我们也可以对for(i = 100;i <=200;i++)改进,既然判断素数,那么偶数一定不是素数,我们可以跳过每个偶数判断剩余的数是不是素数,这样要判断的数也减少了一半。
#include <stdio.h>
#include <math.h> //用到sqrt库函数,要引用相应的头文件
int main()
{
int i = 1;
int count = 0;
for (i = 101;i <= 200;i+=1) //跳过每一个偶数
{
int j = 2;
for (j = 2; j <= sqrt(i) ; j++)
{
if (0 == i % j)
break;
}
if (j > sqrt(i))
{
count++;
printf("%d ", i);
}
}
printf("\n素数的个数是%d个\n", count);
return 0;
}
筛选法
公元前250年古希腊的数学家埃拉托塞尼提出一种筛法:
百度百科是这样解释的:
筛选法又称筛法,具体做法是:先把N个自然数按次序排列起来。1不是质数,也不是合数,要划去。第二个数2是质数留下来,而把2后面所有能被2整除的数都划去。2后面第一个没划去的数是3,把3留下,再把3后面所有能被3整除的数都划去。3后面第一个没划去的数是5,把5留下,再把5后面所有能被5整除的数都划去。这样一直做下去,就会把不超过N的全部合数都筛掉,留下的就是不超过N的全部质数。因为希腊人是把数写在涂腊的板上,每要划去一个数,就在上面记以小点,寻求质数的工作完毕后,这许多小点就像一个筛子,所以就把埃拉托斯特尼的方法叫做“埃拉托斯特尼筛”,简称“筛法”。(另一种解释是当时的数写在纸草上,每要划去一个数,就把这个数挖去,寻求质数的工作完毕后,这许多小洞就像一个筛子。)
此处以输出1—100之间的素数为例
(将不是素数的值直接改为0,代表已经筛去)
第一步:先定义并初始化一个数组1—100
第二步:1不是素数,直接筛去,将其赋值为0
第三步:从2开始,2是素数保留,然后依次用2后面的数去除2,能被2整除则不是素数,赋值为0;
第四步:接下来是3,因为上一步没有被2整除,保留,接着第三步的操作,用3后面的数去除3,能被3整除的数则不是素数,赋值为0
第五步:4第二步已经被2整除赋值为0了,不用管,接着是5,保留依次循环上述操作,直到某个数后面没有要筛选的数为止
#include <stdio.h>
#include <math.h>
int main()
{
int count = 0;
int arr[100] = { 0 }; //先将其初始化为0
int i = 0;
for (i = 0; i < 100; i++) //给数组赋值1—100
{
arr[i] = i + 1;
}
//开始筛选
arr[0] = 0; // 1不是素数直接赋值为0,筛选去
for (i = 1; i <= sqrt(100); i++) //其实只需要用2到最大的那个数(这里最大的数是100)开方之间的数去筛选就可以,因为它总有一个因数不会超过它的开平方,前面已经讲过.既然最大的那个数的因数都包含在范围内,那么比它小的那些数因数也一定在范围内。
{
if (0 != arr[i]) //判断是否是前面筛选保留下来的数
{
int j = 0;
for (j = i + 1; j < 100; j++)
{
if (0 != arr[j]) //判断是否已经被筛选过
{
if (0 == arr[j] % arr[i])
arr[j] = 0;
}
}
}
}
//晒选结束,打印素数
for (i = 0; i < 100; i++)
{
if (arr[i] != 0)
{
count++;
printf("%d ",arr[i]);
}
}
printf("\ncount = %d\n", count);
return 0;
}
上述编码不是唯一,如有不对的地方或者疑问,欢迎来扰!