Ruby项目Euler#12效率

时间:2013-03-10 18:06:27

标签: ruby

处理Euler项目的问题12:

  

通过添加自然数来生成三角数的序列。所以第7个三角形数字是1 + 2 + 3 + 4 + 5 + 6 + 7 = 28.前十个术语是:

     

1,3,6,10,15,21,28,36,45,55,......

     

让我们列出前七个三角形数字的因子:

 1: 1
 3: 1,3
 6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28
     

我们可以看到28是第一个超过五个除数的三角形数。

     

第一个三角形数的值超过500个除数是多少?

这就是我所拥有的:

require 'reusable'

# The idea here is that 2^n is the smallest number with n factors, 
# according to their definition, so it's a good place to start.
# It also happens to be a HUGE number, so I'm worried I'm thinking 
# about this wrong. Did 4999 instead of 5000, just to make sure 
# I didn't overshoot.
start = 2 * 4999

# The faster way to calculate the nth Triangle number
def nthTriangle(n)
 n * (n + 1) / 2
end

def answer(num)
    i = startingTriangle(num)
    while true
        triangle = i*(i+1)/2
        puts triangle
        factors = numFactors(triangle)
        return "#{triangle} is triangle number #{i}, with #{factors} factors." if factors > num
        i += 1
    end
end

# Basic reversal of the nthTriangle thing to figure
# out which n to start with in the answer function.
def startingTriangle(n)
    power = n - 2
    sqrt(power * 2).to_i - 1
end

puts answer(5000)

那个必需的文件(我试图把方法放在一堆Euler问题中):

def primesUpTo(n)
  nums = [0, 0] + (2..n).to_a
  (2..sqrt(n).to_i+1).each do |i|
    if nums[i].nonzero?
      (i**2..n).step(i) {|m| nums[m] = 0}
    end
  end
  nums.find_all {|m| m.nonzero?}
end

def prime?(n)
    test = primesUpTo(sqrt(n).to_i)
    test.each do |i|
        if n % i == 0
            return false
        end
    end
    true
end

# Just for faster, more intuitive (to me) array summing
def sum(array)
    array.inject(0) {|s, n| s + n }
end

# Ditto
def product(array)
    array.inject(1) {|p, n| p * n}
end

# I don't like typing the 'Math.'
def sqrt(n)
    Math.sqrt(n)
end

# Returns an array of arrays of the prime factors of num
# Form [[factor1, power1],[factor2, power2]]
# Ex: primeFactors(12) == [[2,2],[3,1]]
def primeFactors(n)
    array = []
    # 2 3 
    primesUpTo((n/2).to_i+1).select{ |i| n % i == 0 }.each do |p|
        pcount = 1
        n = n / p
        while n % p == 0
            pcount += 1
            n = n / p
        end
        array << [p, pcount]
    end
    array
end

# Returns the number of factors a number has
# INCLUDING both the number itself and 1
# ex: numFactors(28) = 6
def numFactors(n)
    return 2 if prime?(n)
    product = 1
    primeFactors(n).each do |i|
        product *= i[1] + 1
    end
    product
end

我的问题是我的代码非常慢。如果我从1开始而不是我的开始编号,它需要一分钟+才能达到200000(不到2 ^ 4999)。但是除了废除库素数解决方案并将所有素数添加到一个数组中我一直指的是 - 我觉得它只会使它更快一点 - 我想不出如何更快地制作它。它需要更快。

我是否认为这一切都错了?有什么建议吗?

同样有用的是如何提高我的任何库方法的效率,我可能会一次又一次地使用它。我想从头开始制作它们,所以我理解它们,但我担心它们的效率非常低。

1 个答案:

答案 0 :(得分:2)

从你的代码:

  

这里的想法是2 ^ n是具有n个因子的最小数字

从所述的Project Euler任务:

  

我们可以看到28是第一个超过五个除数的三角形数。

我不确定为什么你认为2 ^ n是具有n个因子的最小数字,但问题中给出的例子清楚地证明了你的假设是错误的,因为2 ^ 5 = 32,它大于28。

我的解决方案从1开始搜索并且效率相当高。我根本不使用素数。

附录:为了完整起见,另一个大问题除了从一个数字太高的数字开始搜索超过5000的除数而不是大于500,正如你所注意到并指出的那样评论。