【问题标题】:Code Optimization - Generating Prime Numbers代码优化 - 生成素数
【发布时间】:2016-09-02 13:59:43
【问题描述】:

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

输入

输入以单行中的测试用例数量 t (t

输出

对于每个测试用例打印所有质数 p 使得 m

示例输入:

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 循环、普通循环、创建数组然后打印它。 任何建议。

【问题讨论】:

  • "花费了很多时间" 给我们数字。有多慢?
  • 您的代码中有一个潜在的错误,与 i 用于内部循环的重用有关。为任一循环使用不同的变量可能会很好。我将在下面的代码示例中进行更改。
  • @mast 我没有时间......但它比 6 秒慢,因为这是 spoj.com 上的一个挑战,当我提交我的解决方案时,它说超过了时间限制。限时6秒

标签: ruby algorithm primes


【解决方案1】:

Ruby 比其他一些语言慢,这取决于您将它与哪种语言进行比较;肯定比 C/C++ 慢。但是您的问题不是语言(尽管它会影响运行时行为),而是您找到素数的方式。有许多更好的算法可以找到素数,例如Sieve of EratosthenesSieve of Atkin。您也可以阅读 Wikipedia 上的“Generating Primes”页面并点击那里的链接。

顺便说一句,对于埃拉托色尼筛法,甚至还有一个现成的piece of code on Stackoverflow。我敢肯定,在谷歌上搜索一下也会找到其他算法的实现。

由于您的问题是在特定范围内寻找素数,因此这是在上述链接中找到的 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”的变化。

【讨论】:

  • better_sieve_upto(1, 10) 返回[3, 4, 5, 6, 7, 8, 9],因此您的解决方案有些问题。
  • 这个功能不起作用。您将不得不改回第 2 行:s = (2..last).to_a 然后更改倒数第二行:s.compact! 然后在倒数第二行添加:s.reject! {|x| x &lt; first}
  • 你说的很对;这里有些不对劲。我已经更新了。
【解决方案2】:

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

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

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

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 解释的那样,这会快得多,但仍然不是很快。

以下是使用 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 方法应该越快。

【讨论】:

  • 也尝试过这样做。还是不够快
  • 请注意,这仍然是低效的,因为它检查每个数字是否是素数。您可能已经摆脱了一个循环,但您实际上可以通过执行 Prime.each(100) {|prime| p prime } 来保存 两个 循环,正如 ruby​​ 文档所解释的那样:ruby-doc.org/stdlib-2.3.1/libdoc/prime/rdoc/Prime.html
  • @Technaton 我只是替换了prime? 函数。如果 OP 的代码中还有其他低效之处,我不知道您为什么要在我的答案的 cmets 中而不是在您自己的答案中解决它们。话虽如此,OP 正在尝试在给定范围内查找素数,但 Prime.each 枚举从 2 开始的素数,所以我认为它不能在这里使用。
  • @Technaton 如果您建议我使用 Prime::EratosthenesGenerator 预先计算素数,我已经完成了。谢谢。
  • 它们都可以工作,但我不知道为什么,但是当我在 spoj.com 上提交代码时,它总是说超出时间限制。无论如何,在这个问题上花了我一整天的时间后,我继续前进。谢谢mwp @Technaton。今天真的学到了很多。我什至从未听说过这些筛选算法。将阅读更多关于它们和此类算法的信息。你们可以为这些东西推荐任何好的阅读场所。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-30
  • 1970-01-01
  • 2015-02-20
  • 1970-01-01
  • 2021-10-26
相关资源
最近更新 更多