求质数算法的ruby实现

原文: 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. 筛法

    从n个数中不断筛选掉所有质数的倍数, 最终剩下的就均为质数.
    比如:
       1) 保存质数2, 再筛掉所有可以被2的所有倍数2*n, 剩下的数将以质数3开始;
       2) 保存质数3, 再筛掉所有可以被3的所有倍数3*n, 剩下的数将以质数5开始;
       3) 以此类推导,不断重复筛选.

    可以参考wiki上的详细说明: 埃拉托斯特尼筛法 以及下图: 


# 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代码,应该可以得到更好的优化.


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值