0

处理 Project 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)

还有那个必需的文件(我试图将我将在一堆欧拉问题中重用的方法放入其中):

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)。但除了废弃库素数解决方案并将所有素数添加到我一直提到的数组中——我觉得这只会让它更快一点——我想不出如何让它更快。它需要更快。

我在想这一切都错了吗?有什么建议么?

关于如何提高我的任何库方法的效率的任何建议也很有用,我可能会一次又一次地使用这些方法。我想从头开始制作它们,所以我理解它们,但我担心它们效率很低。

4

1 回答 1

2

从您的代码:

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

从所述的 Project Euler 任务中:

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

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

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

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

于 2013-03-10T18:35:15.507 回答