代码优化 - 生成素数

时间:2016-09-02 13:59:44

标签: ruby algorithm primes

我正在尝试为以下问题编写代码:

输入

输入以单行中的测试用例的数量t开始(t <= 10)。在下一个t行的每一行中,有两个数字m和n(1 <= m <= n <= 1000000000,n-m <= 100000)由空格分隔。

输出

对于每个测试用例,打印所有质数p,使得m <= p <= n,每行一个数,由空行分隔的测试用例。

示例输入:

2
1 10
3 5

示例输出:

2
3
5
7

3
5

我的代码:

def prime?(number)
    return false if number == 1
    (2..number-1).each do |n|            
        return false if number % n == 0             
    end 
    true    
end

t = gets.strip.to_i
for i in 1..t
    mi, ni = gets.strip.split(' ')
    mi = mi.to_i
    ni = ni.to_i

    i = mi
    while i <= ni
        puts i if prime?(i)
        i += 1
    end


    puts "\n"
end

代码运行正常,我遇到的唯一问题是,与其他编程语言相比,在运行大输入范围时需要花费大量时间。

我在这里做错了吗?这段代码可以进一步优化以加快运行时间吗?

我尝试过使用for循环,普通循环,创建数组然后打印它。 任何建议。

2 个答案:

答案 0 :(得分:2)

Ruby比其他语言慢,取决于你比较它的语言;肯定比C / C ++慢。但是你的问题不是语言(尽管它会影响运行时行为),而是你找到素数的方法。有许多更好的算法可用于查找素数,例如Sieve of EratosthenesSieve of Atkin。您还可以阅读维基百科上的“Generating Primes”页面,并按照其中的链接进行操作。

顺便说一下,对于Eratosthenes的筛子,甚至还有一个即用型piece of code on Stackoverflow。我相信谷歌的一些谷歌搜索也会出现其他算法的实现。

由于您的问题是在某个范围内找到素数,因此在上面的链接中找到的Sieve of Eratosthenes代码经过修改以适合您的特定问题:

def better_sieve_upto(first, last)
  sieve = [nil, nil] + (2..last).to_a
  sieve.each do |i|
    next unless i
    break if i*i > last
    (i*i).step(last, i) {|j| sieve[j] = nil }
  end
  sieve.reject {|i| !i || i < first}
end

注意从“sieve.compact”到具有相应条件的复杂“sieve.reject”的更改。

答案 1 :(得分:0)

如果数字为2则返回true,如果数字可被2整除,则返回false。

开始迭代3,而不是2.使用两步。

迭代到数字的平方根,而不是数字减1。

def prime?(number)
  return true if number == 2
  return false if number <= 1 or number % 2 == 0
  (3..Math.sqrt(number)).step(2) do |n|
    return false if number % n == 0
  end
  true
end

@Technation解释说,这会更快,但仍然不会很快。

以下是使用Sieve of Eratosthenes built into Ruby的方法。您需要预先计算所有素数到最大值,这将非常快,然后选择每个范围内的素数。

require 'prime'

ranges = Array.new(gets.strip.to_i) do
  min, max = gets.strip.split.map(&:to_i)
  Range.new(min, max)
end

primes = Prime.each(ranges.map(&:max).max, Prime::EratosthenesGenerator.new)

ranges.each do |range|
  primes.each do |prime|
    next if prime < range.min 
    break if prime > range.max
    puts prime
  end

  primes.rewind
  puts "\n"
end

以下是各种解决方案在50000 200000范围内的表现:

  • 你的原始素数?功能:1m49.639s
  • 我修改过的素数?功能:0m0.687s
  • Prime :: EratosthenesGenerator:0m0.221s

正在处理的范围越多,Prime :: EratosthenesGenerator方法应该越快。