首先了解概念:质数,也被称为素数,是指除了1跟它本身没有其他因数的数。1既不是质数也不是合数,2是最小的质数。
问题:列举小于任意数Num的质数
可以把这样一个问题分解为两个功能模块,List_Prime(Num)和is_Prime(n),其中is_Prime(n)嵌套在List_Prime(Num)中。
算法1:首先从2开始迭代累加到num,该部分是List_Prime(Num)。对于任意一个数n,需要判断它是不是质数,即is_Prime(n)。对2到n-1取余(取模),如果发现有余数为0的情况,则说明有因数存在,n不是质数。否则,n是质数。
我们发现,所有质数都是奇数,所以在从2迭代到num的过程中,每次累加2而不是1。
算法2: List_Prime(Num)进行改进,从2迭代到num的过程中每次增加2,这样每次只判断奇数是不是质数,is_Prime(n)与算法1思路相同。可以提高50%的效率。
我们也发现,任何一个合数都是由质因子组成的,比如10=2*5,12=2*2*3。所以在算法2的基础上,我们可以改进is_Prime(n)。
算法3:List_Prime(Num)与算法2相同。对于is_Prime(n),我们不需要考察从2到n-1的所有的数,只需要考察之前得到的质数即可。比如对于7,我们只需要看2,3,5是不是7的因子,而不需要看2,3,4,5,6。可以在算法2的基础上提高约50%的效率。
对于任何一个数num,一定需要考察所有比num小的质数是不是其因子吗?比如对于7,需要考察7能不能被2、3、5整除么?其实不需要,因为任何一个合数都至少可以分解成两个数,而其中一个必然可以是质数。(任何一个合数都可以转化为若干质数相乘的形式)所以对于num,在所有小于它的质数中,我们只需要判断“前一半”(严格说是前一部分)是不是num的因子即可,因为另外的“后一半”要依靠前一半存在。
比如对于11,我们判断2、3都不是其因子,5和5以后就不需要了,因为若因子有5,一定存在另一个大于5的因子y使得5*y=11,但11比25都小,所以更不可能有5*y=11了。所以我们可以用质数的平方来作判断条件,如果比当前质因子平方大或相等就继续判断,如果小则返回true。比如对于num=11
2*2<num, 考察2是不是num的因子,不是,继续;
3*3<num, 考察3是不是num的因子,不是,继续;
5*5>num, 返回true,num是质数。因为不可能有5和5以后的质数作为num的因子了,若有则必有小于5的质数作为num的另一个因子,但之前判断它们都不是。
算法4:List_Prime(Num)与算法3相同。对于is_Prime(n),我们不需要考察之前得到的所有质数即可,只考察平方小于等于n的质数是不是其因子。
Ruby的代码如下
$arr=[]
$arr[0]=2
def add_prime(n)
3.step(n,2){|num|$arr<<numifis_prime?num}
end
def is_prime?(number)
j=0
while $arr[j]*$arr[j]<=number
return falseifnumber%$arr[j]==0
j+=1
end
return true
end
add_prime(50)
print $arr.join(","),"\n"