在 Ruby 中迭代无限序列

发布于 2024-11-15 21:30:21 字数 903 浏览 4 评论 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 是第一个超过 5 的三角形数 除数。 第一个大于 5 的三角形数的值是多少 一百因数?

这是我使用 Ruby 提出的解决方案:

triangle_number = 1
(2..9_999_999_999_999_999).each do |i|
  triangle_number += i
  num_divisors = 2 # 1 and the number divide the number always so we don't iterate over the entire sequence
  (2..( i/2 + 1 )).each do |j|
    num_divisors += 1 if i % j == 0
  end
  if num_divisors == 500 then
    puts i
    break
  end
end

我不应该使用像 9_999_999_999_999_999 这样的任意大数字。如果我们有一个像某些函数式语言一样的 Math.INFINITY 序列,那就更好了。如何在 Ruby 中生成惰性无限序列?

I am trying to solve Project Euler problem #12:

The sequence of triangle numbers is generated by adding the natural
numbers. So the 7th triangle number
would be 1 + 2 + 3 + 4 + 5 + 6 + 7 =
28. The first ten terms would be:

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

Let us list the factors of the first seven triangle numbers:

 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

We can see that 28 is the first triangle number to have over five
divisors.
What is the value of the first triangle number to have over five
hundred divisors?

Here's the solution that I came up with using Ruby:

triangle_number = 1
(2..9_999_999_999_999_999).each do |i|
  triangle_number += i
  num_divisors = 2 # 1 and the number divide the number always so we don't iterate over the entire sequence
  (2..( i/2 + 1 )).each do |j|
    num_divisors += 1 if i % j == 0
  end
  if num_divisors == 500 then
    puts i
    break
  end
end

I shouldn't be using an arbitrary huge number like 9_999_999_999_999_999. It would be better if we had a Math.INFINITY sequence like some functional languages. How can I generate a lazy infinite sequence in Ruby?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

半﹌身腐败 2024-11-22 21:30:21

有几个答案很接近,但我实际上没有看到有人使用无限范围。 Ruby 很好地支持它们。

Inf = Float::INFINITY # Ruby ≥ 1.9
Inf = 1.0/0           # Ruby < 1.9
(1..Inf).include?(2305843009213693951)
# => true
(1..Inf).step(7).take(3).inject(&:+)
# => 24.0

在您的情况下,

(2..Inf).find {|i| ((2..( i/2 + 1 )).select{|j| i % j == 0}.count+2)==42 }
=> 2880

您的暴力方法很粗糙,并且可能需要很长时间才能完成。

Several answers are close but I don't actually see anyone using infinite ranges. Ruby supports them just fine.

Inf = Float::INFINITY # Ruby ≥ 1.9
Inf = 1.0/0           # Ruby < 1.9
(1..Inf).include?(2305843009213693951)
# => true
(1..Inf).step(7).take(3).inject(&:+)
# => 24.0

In your case

(2..Inf).find {|i| ((2..( i/2 + 1 )).select{|j| i % j == 0}.count+2)==42 }
=> 2880

Your brute force method is crude and can, potentially, take a very long time to finish.

旧人 2024-11-22 21:30:21

在 Ruby >= 1.9 中,您可以创建一个 Enumerator 对象来生成您喜欢的任何序列。下面是产生无限整数序列的一个:

#!/usr/bin/ruby1.9

sequence = Enumerator.new do |yielder|
  number = 0
  loop do
    number += 1
    yielder.yield number
  end
end

5.times do
  puts sequence.next
end

# => 1
# => 2
# => 3
# => 4
# => 5

Or:

sequence.each do |i|
  puts i
  break if i >= 5
end

Or:

sequence.take(5).each { |i| puts i }

Programming Ruby 1.9< /a>(又名“镐书”),第三。编,页。 83,有一个三角数枚举器的例子。修改上面的枚举器来生成三角数应该很容易。我会在这里这样做,但这会逐字重现示例,可能超出“合理使用”允许的范围。

In Ruby >= 1.9, you can create an Enumerator object that yields whatever sequence you like. Here's one that yields an infinite sequence of integers:

#!/usr/bin/ruby1.9

sequence = Enumerator.new do |yielder|
  number = 0
  loop do
    number += 1
    yielder.yield number
  end
end

5.times do
  puts sequence.next
end

# => 1
# => 2
# => 3
# => 4
# => 5

Or:

sequence.each do |i|
  puts i
  break if i >= 5
end

Or:

sequence.take(5).each { |i| puts i }

Programming Ruby 1.9 (aka "The Pickaxe Book"), 3rd. ed., p. 83, has an example of an Enumerator for triangular numbers. It should be easy to modify the Enumerator above to generate triangular numbers. I'd do it here, but that would reproduce the example verbatim, probably more than "fair use" allows.

緦唸λ蓇 2024-11-22 21:30:21

Infinity 在 Float 上定义 (Ruby 1.9)

a = Float::INFINITY
puts a #=> Infinity
b = -a
puts a*b #=> -Infinity, just toying

1.upto(a) {|x| break if x >10; puts x}

Infinity is defined on Float (Ruby 1.9)

a = Float::INFINITY
puts a #=> Infinity
b = -a
puts a*b #=> -Infinity, just toying

1.upto(a) {|x| break if x >10; puts x}
來不及說愛妳 2024-11-22 21:30:21

当前版本的 Ruby 大力支持生成器:

sequence = 1.step

Currrent versions of Ruby support generators heavily:

sequence = 1.step
小…楫夜泊 2024-11-22 21:30:21

在 Ruby 2.6 中,这变得更加容易:

(1..).each {|n| ... }

来源:https://bugs.ruby-lang.org/问题/12912

In Ruby 2.6 this becomes much easier:

(1..).each {|n| ... }

Source: https://bugs.ruby-lang.org/issues/12912

无法回应 2024-11-22 21:30:21

这最好作为一个简单的循环。

triangle_number = 1
i  = 1
while num_divisors < 500
  i += 1
  triangle_number += i
  # ...
end
puts i

This would be best as a simple loop.

triangle_number = 1
i  = 1
while num_divisors < 500
  i += 1
  triangle_number += i
  # ...
end
puts i
苄①跕圉湢 2024-11-22 21:30:21

正如 Amadan 提到的,您可以使用闭包:

triangle = lambda { t = 0; n = 1; lambda{ t += n; n += 1; t } }[]
10.times { puts triangle[] }

不要真的认为它比循环慢得多。您也可以在类对象中保存状态,但您需要更多的输入:

class Tri
  def initialize
    @t = 0
    @n = 1
  end

  def next
    @t += n
    @n += 1
    @t
  end
end

t = Tri.new
10.times{ puts t.next }

添加:

对于那些喜欢 longjmps 的人:

require "generator"

tri =
  Generator.new do |g|
    t, n = 0, 1
    loop do
      t += n
      n += 1
      g.yield t
    end
  end

puts (0..19).map{ tri.next }.inspect

As Amadan mentioned you can use closures:

triangle = lambda { t = 0; n = 1; lambda{ t += n; n += 1; t } }[]
10.times { puts triangle[] }

Don't really think it is much slower than a loop. You can save state in class object too, but you will need more typing:

class Tri
  def initialize
    @t = 0
    @n = 1
  end

  def next
    @t += n
    @n += 1
    @t
  end
end

t = Tri.new
10.times{ puts t.next }

Added:

For those who like longjmps:

require "generator"

tri =
  Generator.new do |g|
    t, n = 0, 1
    loop do
      t += n
      n += 1
      g.yield t
    end
  end

puts (0..19).map{ tri.next }.inspect
酒绊 2024-11-22 21:30:21

以 Wayne 的出色答案为基础,并本着用最少字符数做事的 Ruby 精神,这里有一个稍微更新的版本:

sequence = Enumerator.new { |yielder| 1.step { |num| yielder.yield num } }

显然,它不能解决原始的欧拉问题,但有利于生成无限的整数序列。绝对适用于 Ruby > 2.0。享受!

Building on Wayne's excellent answer and in the Ruby spirit of doing things with the least number of characters here is a slightly updated version:

sequence = Enumerator.new { |yielder| 1.step { |num| yielder.yield num } }

Obviously, doesn't solve the original Euler problem but is good for generating an infinite sequence of integers. Definitely works for Ruby > 2.0. Enjoy!

星光不落少年眉 2024-11-22 21:30:21

2018 年圣诞节,Ruby 引入了 无限范围,为这个问题提供了一种简单的新方法。

这是通过省略范围中的最后一个字符来实现的,例如:

(1..)
(1...)
(10..)
(Time.now..)

或者使用 Jonas Elfström 的解决方案进行更新:

(2..).find { |i| ((2..( i / 2 + 1 )).select { |j| i % j == 0 }.count + 2) == 42 }

希望这对某人有用!

On Christmas Day 2018, Ruby introduced the endless range, providing a simple new approach to this problem.

This is implemented by ommitting the final character from the range, for example:

(1..)
(1...)
(10..)
(Time.now..)

Or to update using Jonas Elfström's solution:

(2..).find { |i| ((2..( i / 2 + 1 )).select { |j| i % j == 0 }.count + 2) == 42 }

Hope this proves useful to someone!

狼性发作 2024-11-22 21:30:21

我相信纤维(我相信是在 Ruby 1.9 中添加的)可能接近你想要的。请参阅此处了解一些信息或者只是搜索红宝石纤维

I believe that fibers (added in Ruby 1.9 I believe) may be close to what you want. See here for some information or just search for Ruby Fibers

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文