代码优化——生成质数
Code Optimization - Generating Prime Numbers
我正在尝试为以下问题编写代码:
输入
输入以单行中的测试用例数t开始(t<=10)。在接下来的 t 行中,有两个数字 m 和 n(1 <= m <= n <= 1000000000,n-m<=100000),由 space.
分隔
输出
对于每个测试用例打印所有素数 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 循环、普通循环、创建数组然后打印它。
任何建议。
Return 如果数字是 2,则为真,如果数字能被 2 整除,则为假。
从 3 开始迭代,而不是 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 解释的那样,这会快得多,但仍然不是很快。
以下是使用埃拉托色尼筛法 built into Ruby 的方法。您需要预先计算所有素数直到最大最大值,这将非常快,然后 select 每个范围内的素数。
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 方法应该越快。
Ruby 比其他一些语言慢,具体取决于您将其与哪种语言进行比较;肯定比 C/C++ 慢。但是您的问题不是语言(尽管它会影响 运行 时间行为),而是您寻找素数的方式。有很多更好的算法可以找到素数,例如维基百科上的 Sieve of Eratosthenes or the Sieve of Atkin. You might also read the “Generating Primes” 页面,然后跟随 link 那里。
顺便说一下,对于埃拉托色尼筛法,甚至还有现成的 piece of code on Whosebug。我敢肯定,通过谷歌搜索也能找到其他算法的实现。
由于您的问题是在一定范围内找到素数,因此这是在上面找到的埃拉托色尼筛法代码 link 修改以适合您的特定问题:
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" 的变化。
我正在尝试为以下问题编写代码:
输入
输入以单行中的测试用例数t开始(t<=10)。在接下来的 t 行中,有两个数字 m 和 n(1 <= m <= n <= 1000000000,n-m<=100000),由 space.
分隔输出
对于每个测试用例打印所有素数 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 循环、普通循环、创建数组然后打印它。 任何建议。
Return 如果数字是 2,则为真,如果数字能被 2 整除,则为假。
从 3 开始迭代,而不是 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 解释的那样,这会快得多,但仍然不是很快。
以下是使用埃拉托色尼筛法 built into Ruby 的方法。您需要预先计算所有素数直到最大最大值,这将非常快,然后 select 每个范围内的素数。
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 方法应该越快。
Ruby 比其他一些语言慢,具体取决于您将其与哪种语言进行比较;肯定比 C/C++ 慢。但是您的问题不是语言(尽管它会影响 运行 时间行为),而是您寻找素数的方式。有很多更好的算法可以找到素数,例如维基百科上的 Sieve of Eratosthenes or the Sieve of Atkin. You might also read the “Generating Primes” 页面,然后跟随 link 那里。
顺便说一下,对于埃拉托色尼筛法,甚至还有现成的 piece of code on Whosebug。我敢肯定,通过谷歌搜索也能找到其他算法的实现。
由于您的问题是在一定范围内找到素数,因此这是在上面找到的埃拉托色尼筛法代码 link 修改以适合您的特定问题:
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" 的变化。