原文: http://blog.youkuaiyun.com/aegoose/article/details/7075997
看到 http://blog.youkuaiyun.com/program_think/article/details/7032600 讲了一些求质数的简单算法逻辑, 最近也用ruby玩一些编码实现,想到用ruby实现质数的也是挺有趣.
本文尝试用ruby实现一些简单的求质数算法的简单逻辑, 没有考虑过太多的性能是否能得到真优化问题, 以后有时间再慢慢优化优化. 本人只是通过ruby尝试一下算法的简单逻辑.
1. 算法背景
给定一个整数N, 求N以内的所有质数.
比如: 当 n = 10, 能得到 2 3 5 7的质数.
2. 试除法
即通过简单的除法计算来算出所有质数.
2.1 遍历除因子求质数
遍历每个整数x, x<n, 通过x除以 [ 2..x-1 ]的所有数xi, 如果x不能整数所的xi, 则x为质数.
# divide all fronts
#
# Check 2 to "x-1" is the divisor or not for a number "x"
#
def by_divide_fronts(n)
(2..n).inject([]) do |primes, x|
the_one = false
(2..x-1).each do |xi|
if (x % xi == 0)
the_one = xi
break
end
end
primes << x unless the_one
primes
end
end
2.2 折半遍历因子求质数
遍历每个整数x, x<n, 通过x除以 [ 2..x/2 ]的所有数xi, 如果x不能整数所的xi, 则x为质数.
# divide half fronts
#
# Only check 2 to "x/2" is the divisor or not for a number "x"
#
# If the number "x" have a divisor, it should less that "x/2"
# Because the minimal divisor should be 2
#
def by_half_divide_fronts(n)
(2..n).inject([]) do |primes, x|
the_one = false
(2..x/2).each do |xi|
if (x % xi == 0)
the_one = xi
break
end
end
primes << x unless the_one
primes
end
end
2.3 折半遍历奇数因子求质数
遍历每个整数x, x<n, 通过x除以 [ 2..x/2 )的所有奇数xi, 如果x不能整数所的xi, 则x为质数.
# divide odd fronts
#
# Only check the odd number from 2 to "x/2" is the divisor or not for a number "x"
#
# If the number "x" have a divisor, it should less that "x/2"
#
# All the divisor should be odd number except 2
#
def by_half_divide_odd_fronts(n)
(2..n).select { |x| x % 2 != 0 }.inject([2]) do |primes, odd_x|
the_one = false
(2..odd_x/2).each do |xi|
if (odd_x % xi == 0)
the_one = xi
break
end
end
primes << odd_x unless the_one
primes
end
end
2.4 平方根遍历奇数因子求质数
遍历每个整数x, x<n, 通过x除以 [ 2..平方根(x) )的所有奇数xi, 如果x不能整数所的xi, 则x为质数.
每个数都可以差成两个因数的乘积, 如自然数x, x可能可以有因数组合:
1*x, 2*x1, 3*x1, ... (平方根(x)-1)*xi, 平方根(x)*平方根(x),xj*平方根(x), ... , xi*3, xi*2
根据序列可以知道最大的因数为平方根(x).
# divide max square root fronts
#
# Only check the odd number from 2 to "square root" is a divisor or not for a number "x"
#
# If the number "x" have a divisor, it should less that "square root"
# Because
# there are always pairs divisor with number x:
# x1 * x2 = x
# when:
# x1 = 1 then x2 = x;
# x1 = 2 then x = x/2,
# ... ...
# x1 = sqrt(x) then x2 = sqrt(x)
# ... ...
# And if x1 > sqrt(x) then x2 < sqrt(x)
# So the max divisor should always smaller than square root
#
# All the divisor should be odd number except 2
#
def by_square_root(n)
(2..n).select { |x| x % 2 != 0 }.inject([2]) do |primes, odd_x|
the_one = false
(2..Math.sqrt(odd_x)).each do |xi|
if (odd_x % xi == 0)
the_one = xi
break
end
end
primes << odd_x unless the_one
primes
end
end
2.5 平方根结合子质数遍历因子求质数
遍历每个整数x, x<n, 通过x除以 [ 2..平方根(x) )的所有质数xi, 如果x不能整数所的xi, 则x为质数.
每个自然数x, 如果有因数, 都必然可能差分成几个质数的乘积, 如 x = xi*xj*xk, 其中xi,xj, xk均为质数.
# divide number by primes fronts
#
# Only check the odd number from 2 to "square root" is a divisor or not for a number "x"
#
# If the number "x" have a divisor, it should less that "square root"
#
# All the divisor should be odd number except 2
#
# All the divisor should be primes number
# Because
# other no-primes divisor can be combine by two or more primes number divisor, such as
# 3 is divisor of x, then 3*3, or 3*5 may the divisor of x
#
def square_root_and_primes(n)
(2..n).select { |x| x % 2 != 0 }.inject([2]) do |primes, odd_x|
the_one = false
primes[1..-1].each do |xi|
break if xi > Math.sqrt(odd_x)
if (odd_x % xi == 0)
the_one = xi
break
end
end
primes << odd_x unless the_one
primes
end
end
2. 筛法

# sieve all divisor
#
# I have numbers [from,to]
# 1. all divisors in {2*xi, xi>=2} should be deleted, and 2 should be saved
# 2. the rest begin with 3, all divisors {x = 3*xi, xi>=2} should be deleted, and 3 should be saved
# 3. the rest begin with a primer "k", delete all {k*xi, xi>=2}, save the "k"
# ... ...
# Notice that mid = to/2 is the max divisor to try
# Because mid * 2 >= n, and (the_one_bigger_than_mid) * 2 > n
#
def sieve(n)
arr = (3..n).to_a
x = 2
primes = [2]
mid = n/2
while x <= mid
arr = arr.select { |xi| xi % x != 0 }
primes << (x = arr[0])
end
primes += arr
primes
end
3. 总结:
通过取N = 15000, 对各个方法进行测试, 得到以下的参数结果.
不过可以看出, 筛选法并没有在性能上得到提高. 这个源于个人在ruby函数的使用上用了太多的数组的内部函数, 如select, 对数组的操作过于频繁导致. 如果用C语言再加一些代码逻辑上的优化, 可能性能会不一样.
再考虑针从存储和读写上优化一下ruby代码,应该可以得到更好的优化.