0

我正在学习 ruby​​ 并通过解决Project Euler中的问题来练习它。

这是我对问题12的解决方案。

# Project Euler problem: 12
# What is the value of the first triangle number to have over five hundred divisors?

require 'prime'

triangle_number = ->(num){ (num *(num + 1)) / 2 }

factor_count = ->(num) do
  prime_fac = Prime.prime_division(num)
  exponents = prime_fac.collect { |item| item.last + 1 }
  fac_count = exponents.inject(:*)
end

n = 2
loop do
  tn = triangle_number.(n)
  if factor_count.(tn) >= 500
    puts tn
    break
  end
  n += 1
end

可以对这段代码进行任何改进吗?

4

3 回答 3

4

正如其他人所说,Rubyists 将比 lambdas 更多地使用方法或块。

RubyEnumerable是一个非常强大的 mixin,所以我觉得在这里构建一个与Prime. 所以:

require 'prime'
class Triangular
  class << self
    include Enumerable
    def each
      sum = 0
      1.upto(Float::INFINITY) do |i|
        yield sum += i
      end
    end
  end
end

这是非常通用的。只需检查它是否有效:

Triangular.first(4) # => [1, 3, 7, 10]

好的。现在您可以使用它来解决您的问题:

def factor_count(num)
  prime_fac = Prime.prime_division(num)
  exponents = prime_fac.collect { |item| item.last + 1 }
  exponents.inject(1, :*)
end

Triangular.find{|t| factor_count(t) >= 500}  # => 76576500

备注

  • Float::INFINITY是 1.9.2 的新功能。要么使用1.0/0require 'backports'要么执行 a loopif 使用早期版本。
  • each可以通过首先检查一个块是否通过来改进;你会经常看到类似的东西:

      def each
        return to_enum __method__ unless block_given?
        # ...
    
于 2010-06-11T20:59:56.983 回答
2

与其一次性解决问题,不如查看问题的各个部分可能会帮助您更好地理解 ruby​​。

第一部分是找出三角形数是多少。由于这使用自然数序列,因此您可以使用 ruby​​ 中的范围来表示它。这是一个例子:

(1..10).to_a => [1,2,3,4,5,6,7,8,9,10]

ruby 中的数组被认为是可枚举的,ruby 提供了许多枚举数据的方法。使用此概念,您可以使用 each 方法迭代此数组并传递一个对数字求和的块。

sum = 0
(1..10).each do |x|
  sum += x
end

sum => 55

这也可以使用另一种称为注入的可枚举方法来完成,该方法将从前一个元素返回的内容传递给当前元素。使用它,您可以在一行中获得总和。在本例中,我使用 1.upto(10),其功能与 (1..10) 相同。

1.upto(10).inject(0) {|sum, x| sum + x} => 55

遍历 this,第一次调用 this,sum = 0, x = 1, 所以 (sum + x) = 1。然后它将 this 传递给下一个元素,所以 sum = 1, x = 2, (sum + x ) = 3. 下一个 sum = 3, x = 3, (sum + x) = 6. sum = 6, x = 4, (sum + x) = 10. 等等。

这只是这个问题的第一步。如果你想以这种方式学习语言,你应该处理问题的每个部分,并了解适合该部分学习的内容,而不是解决整个问题。

重构解决方案(尽管根本没有效率)

def factors(n)
  (1..n).select{|x| n % x == 0}
end

def triangle(n)
  (n * (n + 1)) / 2
end

n = 2

until factors(triangle(n)).size >= 500
  puts n
  n += 1
end

puts triangle(n) 
于 2010-06-11T16:47:01.990 回答
0

看起来您来自编写 Ocaml 或其他函数式语言。在 Ruby 中,您可能希望使用更多def来定义您的方法。Ruby 是关于保持清洁的。但这也可能是个人喜好。

而不是loop do你可以while (faction_count(traingle_number(n)) < 500) do,但对于一些可能对一条线来说太多了。

于 2010-06-11T16:32:36.367 回答