素数问题的标准求解模式----求反对者:

cao_jialian 2009-12-01 05:21:14
素数问题是一个经典问题,但有无数的初学者有着无穷的烦恼--不是初学者的问题,是教育者的问题,他(她)们没有教这类问题的基本方法:
问题描述:素数m是仅能被1和m整除的正整数,特别地,1不是素数。
换个说法就是在2至m-1范围(这个范围再讨论)内至少存在着一个整数因子k能整除m,只要能在指定的范围内找到任一(或几)个整数因子k,就可以下结论“m不是素数”。
也就是说否定“一个整数m是素数”是容易的,找这个整数因子k--“一票否决!”,但要肯定“一个整数m是素数”是困难的,一定要在指定的范围内无否决票!--不把这个范围内的整数因子都试除一次是不行的!
下一个问题就是这个范围就是从2到m-1吗?设q=m/k,r=m%k,(不必多解释了吧?q是整除的商、r是整除的余数)。当r为0时,k是一个否决因子,同时q也是一个否决因子!也就是说一次求余运算(%)就蕴含着可能求出两个否决因子k和q!由于k的试除是由小到大的,则q的出现一定是由大到小的--所以找一下k与q会合的地方?当m是个平方数时k==q!m不是平方数呢?k大于sqrt(m)后是不是到达了q的范围了?!

解题步骤:
对于任意给定的整数m,
1.当m<2,则m一定不是素数!
2.假设m是素数
3.取k从2至sqrt(m)
3.1.若m%k==0,否定假设--m一定不是素数,结束第3步
4.若假设没有被否定过,则m是素数,否则m不是素数。

代码设计:
int m,k;
int assume=1; /*假定m是素数*/
/*获取或生成m*/
if(m<2)
assume=0; /*m不是素数*/
else
for(k=2; k<=sqrt(m); k++)
if(m%k == 0)
{
assune=0;
break;
}
if(assume == 1)
/*m是素数的处理*/
else
/*m不是素数的处理*/
==================代码优化就不做了:“first make it work, then make it fast!"======

assume称作标志量----因为这个素数问题是否决容易,所以该标志量一开始假设为“是”(assume=1),然后通过一个循环过程尝试着去“否决”它(assume=0)。
k称作侯选项,需要 在一个范围内一一列举,又称作穷举侯选项。
这个方法称作穷举法,在程序设计的入门阶段是一个最基本的方法,并且在程序设计的每一个阶段,当没有想出更好的方法时,穷举法也是打开局面的一种方法。试一试这种方法吧----关键是假设“是”呢?还是假设“不是”呢?

顺便点评一下书上的代码
for(k=2; k<=sqrt(m);k++)
if(m%k==0)
break;
if(k>sqrt(m))
/*m是素数*/
else
/*m不是素数*/
这是利用k==sqrt(m)+1表示for循环是完整做完了而不是提前break出来的,间接认定m%k==0从没有实现过-->“m是素数”没有被否决过。这个法子表面上“节省”了一个变量assume,但用间接法子去判定一个事实--实在不是一个好法子--至少对初学者不是个好法子(不过若干年前可有人拼命叫好)。

由于素数的题目太多,求证素数的过程最好设计为一个函数
int isprime(int m)
{
int k;
if(m<2)
return 0; /*小于2的都不是素数*/
if(m%2 ==0)
return 0; /*偶数都不是素数*/
for(k=3; k*k<=m; k+=2) /*k从3开始,只选奇数做穷举侯选项*/
if(m%k == 0)
return 0; /*函数中的return 比break更干脆,退出循环并退出函数*/
return 1;
} /*没有使用标志量,一定蕴含着“m是素数”这一假设*/
...全文
277 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
cao_jialian 2009-12-01
  • 打赏
  • 举报
回复
18楼,謹受教。
循环不变量外提估计不必讨论
sqrt精度问题没有测试过,等待测试
在素数范围上我是没有考虑过。
jackyjkchen 2009-12-01
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 cao_jialian 的回复:]
楼上 几位,k*k在循环中是效率低些--此处是介绍方法,将sqrt(m)从循环中拉出则不必啦,循环不变量外提是编译器的基本功能,我不用sqrt的理由是double sqrt(double)可能在较大的值转换为整型量时会产生XXX.999999999之类的小数部分不被入整--看一下[美]Eric S.Roberts的《C语言的科学和艺术》(机工版)他专门谈了sqrt
[/Quote]
养成先测试再提建议的习惯,你的方法比起书上其实好不到哪去,相反k*k所造成的效率损失却是极其严重的!

sqrt外不外提不是你能控制的,编写代码最重要的一点就是不能依赖编译器。

sqrt的精度问题,你可以测试一下,在32位整数范围内,是不会有任何问题的。

如果我说你的程序在大于2^32时就不行了,算不算比sqrt更大的缺陷呢?
jackyjkchen 2009-12-01
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 cao_jialian 的回复:]
楼上有几位将输入形参定义为unsigned会有问题的,我调用时传个实参为负怎么办,隐式转换为无符号数计算?
[/Quote]
建议你先看一看整数的内存(IDE里就能看),有符号数和无符号数内存里没什么区别,0xFFFFFFFF在有符号数下是-1,无符号数就是2^32-1,所谓的隐式转换做的就是这一步。

负数是不存在素数这个概念的。
cao_jialian 2009-12-01
  • 打赏
  • 举报
回复
楼上有几位将输入形参定义为unsigned会有问题的,我调用时传个实参为负怎么办,隐式转换为无符号数计算?
cao_jialian 2009-12-01
  • 打赏
  • 举报
回复
楼上 几位,k*k在循环中是效率低些--此处是介绍方法,将sqrt(m)从循环中拉出则不必啦,循环不变量外提是编译器的基本功能,我不用sqrt的理由是double sqrt(double)可能在较大的值转换为整型量时会产生XXX.999999999之类的小数部分不被入整--看一下[美]Eric S.Roberts的《C语言的科学和艺术》(机工版)他专门谈了sqrt
cao_jialian 2009-12-01
  • 打赏
  • 举报
回复
诸位,我写了一句:
==================代码优化就不做了:“first make it work, then make it fast!"======

我并没有要求是最高效的,我是针对初学者的困惑--教程序设计为什么不教算法提出的。
honghu069 2009-12-01
  • 打赏
  • 举报
回复

if(m%2 ==0) ----> if(m&1==0)

其实用Rabin-Miller素数检测算法效率比较高
jernymy 2009-12-01
  • 打赏
  • 举报
回复
mark先
mstlq 2009-12-01
  • 打赏
  • 举报
回复
呵呵,不小心遗漏^_^

int isprime(unsigned int m) //肯定是检查非负数^_^
{
unsigned int k,sqr;
if(m <2)
return 0; /*小于2的都不是素数*/
if (m==2)
return 1;
if(m%2 ==0)
return 0; /*偶数都不是素数*/
sqr = sqrt((double)m);
for(k=3; k <=sqr; k+=2) /*k从3开始,只选奇数做穷举侯选项*/
if(m%k == 0)
return 0; /*函数中的return 比break更干脆,退出循环并退出函数*/
return 1;
} /*没有使用标志量,一定蕴含着“m是素数”这一假设*/
jackyjkchen 2009-12-01
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 mstlq 的回复:]
乘法,少算一点是一点……
C/C++ codeint isprime(int m)
{int k,sqr;if(m<2)return0;/*小于2的都不是素数*/if(m%2==0)return0;/*偶数都不是素数*/
sqr= sqrt((double)m);for(k=3; k<=sqr; k+=2)/*k从3开始,只选奇数做穷举侯选项*/if(m%k==0)return0;/*函数中的return 比break更干脆,退出循环并退出函数*/return1;
}/*没有使用标志量,一定蕴含着“m是素数”这一假设*/
[/Quote]
2也是素数,不要忘了……
lovesi3344 2009-12-01
  • 打赏
  • 举报
回复
偶数2是素数哦!!

[Quote=引用 5 楼 mstlq 的回复:]
乘法,少算一点是一点……
C/C++ codeint isprime(int m)
{int k,sqr;if(m<2)return0;/*小于2的都不是素数*/if(m%2==0)return0;/*偶数都不是素数*/
sqr= sqrt((double)m);for(k=3; k<=sqr; k+=2)/*k从3开始,只选奇数做穷举侯选项*/if(m%k==0)return0;/*函数中的return 比break更干脆,退出循环并退出函数*/return1;
}/*没有使用标志量,一定蕴含着“m是素数”这一假设*/
[/Quote]
晴天1999 2009-12-01
  • 打赏
  • 举报
回复
新手哈!多多指教哟!
jackyjkchen 2009-12-01
  • 打赏
  • 举报
回复
修改下,效率更高


int TestPrime(unsigned long n)
{
unsigned long i = 0;
unsigned long sq = 0;
if(n == 1)
return false;
else if(n == 2)
return true;
if(!(n % 2))
return false;
sq = (unsigned long)sqrt((double)n);
for(i=3; i<=sq; i+=2)
{
if(!(n % i))
return false;
}
return true;
}
zhengjiankang 2009-12-01
  • 打赏
  • 举报
回复
楼主。。。。
mstlq 2009-12-01
  • 打赏
  • 举报
回复
乘法,少算一点是一点……

int isprime(int m)
{
int k,sqr;
if(m <2)
return 0; /*小于2的都不是素数*/
if(m%2 ==0)
return 0; /*偶数都不是素数*/
sqr = sqrt((double)m);
for(k=3; k <=sqr; k+=2) /*k从3开始,只选奇数做穷举侯选项*/
if(m%k == 0)
return 0; /*函数中的return 比break更干脆,退出循环并退出函数*/
return 1;
} /*没有使用标志量,一定蕴含着“m是素数”这一假设*/
jackyjkchen 2009-12-01
  • 打赏
  • 举报
回复
for(k=3; k*k <=m; k+=2)
for(k=2; k <=sqrt(m); k++)

你的解法最大的问题在这一句,每次循环都得重新计算k*k或sqrt(m),时间代价很大。

应该吧sqrt拉出来。

我的常用函数


int TestPrime(unsigned long n)
{
unsigned long i = 0;
unsigned long sq = 0;
if(n == 1)
return false;
else if(n == 2)
return true;
if(!(n % 2))
return false;
sq = (unsigned long)sqrt((double)n);
for(i=2; i<=sq; i++)
{
if(!(n % i))
return false;
}
return true;
}

Style_2009 2009-12-01
  • 打赏
  • 举报
回复
友情帮顶
z569362161 2009-12-01
  • 打赏
  • 举报
回复
写的很好.


还有我只差5分就两个小三角了,能不能给5 分啊!谢谢!
rlxtime 2009-12-01
  • 打赏
  • 举报
回复
沙发, 哥德巴赫

69,335

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧