【问题标题】:Why is my Ruby code for Project Euler #10 so slow?为什么我的 Project Euler #10 的 Ruby 代码这么慢?
【发布时间】:2013-08-11 06:41:51
【问题描述】:

我对 Ruby 比较陌生,但就语言而言,它似乎很简单。我正在使用 Ruby 完成 Euler 项目,但我在以下方面遇到了很大的速度问题:

10 以下的素数之和为 2 + 3 + 5 + 7 = 17。 求两百万以下的所有素数之和。

我的代码:

beginning_time = Time.now
(1..10000).each { |i| i }

def isPrime(num)
    factors = 0
    primecount = 1
    while primecount <= num
        if (num%primecount == 0)
            factors += 1
        end
        if (factors > 2)
            return false
        end
        primecount += 1
    end
    return true 
end

def make_sieve(num)
    sieve = Array.new(num)
    summation = 0
    for i in 1..num
            if(isPrime(i) == true)
                summation += i
                puts i
                for x in i..num
                    if x%i == 0
                    # Go through entire array and make all multiples of i False
                        sieve[x] = false
                    else
                        sieve[i] = true                     
                    end
                end
            else
                # If i is NOT prime, move to the next number. in the For Loop
                next
            end
    end
    puts summation
end

make_sieve(2000000)


end_time = Time.now
puts "Time elapsed #{(end_time - beginning_time)*1000} milliseconds"

我认为我对筛子的想法是正确的,但我真的不知道是什么让这个程序运行如此缓慢。我用 20,000 运行它,大约需要 15 秒,这似乎仍然很慢,尽管输出的速度比我输入 2,000,000 时快得多。

我是否在逻辑上或语法上或两者兼而有之?

【问题讨论】:

  • 这是一个更惯用的解决方案:require 'prime'; puts Prime.each(2_000_000).reduce(:+) # 142913828922

标签: ruby performance primes sieve-of-eratosthenes


【解决方案1】:

您的isPrime() 测试在素数上非常慢;但你甚至不需要它。筛选的关键是,最初所有的数字都被标记为素数;然后对于每个素数,我们标记出它的所有倍数。因此,当我们到达筛子中的某个条目时,我们已经知道它是否是素数——它是否标记为true 表示素数,或者它标记为false 表示复合(一些较小的倍数素数)。

根本没有必要测试它是否是素数。

为了找到倍数,我们只计算:对于 5,它是它之后的每个第 5 个条目; 7 - 每 7 个。无需使用% 运算符对其进行测试,只需立即设置为false。无需将它们中的任何一个设置为true,因为所有数字在开始时都设置为true

【讨论】:

  • 对于 5,从 25 (= 5^2) 开始就足够了; 10、15 和 20 已被标记为复合。对于素数 p,您可以在 p^2 处开始标记复合材料。
  • 我没有意识到的是,一旦 2 被标记为素数,它的复合标记为非素数,下一个数字是素数,当该数字的复合标记为非素数时,下一个数字是素数和以此类推。
  • @nucleogenesis exactamundo!.. 接受? (你知道,旁边的大空 V 标志)
【解决方案2】:

您似乎在用 Ruby 编写 JavaScript 代码,却忽略了使 Ruby 如此优雅的细微之处。你应该看一下Ruby Best Practices 之类的东西,它读起来很轻松,但处理的是使用 Ruby 习语而不是强加另一种语言的概念。

如前所述,Eratosthenes 筛子的全部意义在于您只需从列表中删除所有复合数,只留下素数。无需检查每个元素的素数。

这是一个 Rubyish 解决方案。它运行大约 1.5 秒。数组元素N-1代表数字N有点复杂,所以(i+i+1 .. num).step(i+1)等价于(n * 2 .. num).step(n)

def make_sieve(num)

  sieve = Array.new(num, true)

  sieve.each_with_index do |is_prime, i|
    next if i == 0 or not is_prime
    (i+i+1 .. num).step(i+1) { |i| sieve[i] = false }
  end

  puts sieve.each_index.select { |i| sieve[i] }.map { |i| i+1 }.inject(:+)

end

make_sieve(2_000_000)

输出

142913828923

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-18
    • 1970-01-01
    • 2010-12-31
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    相关资源
    最近更新 更多