簡體   English   中英

在 Ruby 中迭代無限序列

[英]Iterate over an infinite sequence in Ruby

我正在嘗試解決 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 個除數的三角形數。 第一個有超過 500 個除數的三角形數的值是多少?

這是我使用 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 中生成惰性無限序列?

在 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

或者:

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

或者:

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

Ruby 1.9 編程(又名“鎬書”),第 3。 編,第。 83,有一個三角形數的枚舉器的例子。 修改上面的 Enumerator 來生成三角形數應該很容易。 我會在這里做,但這會逐字復制示例,可能超出“合理使用”所允許的范圍。

幾個答案很接近,但我實際上沒有看到有人使用無限范圍。 Ruby 很好地支持它們。

Inf = Float::INFINITY # Ruby 1.9
Inf = 1.0/0 # Ruby before 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

您的蠻力方法很粗糙,可能需要很長時間才能完成。

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}

當前版本的 Ruby 非常支持生成器:

sequence = 1.step

在 Ruby 2.6 中,這變得更容易了:

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

來源: https : //bugs.ruby-lang.org/issues/12912

這最好作為一個簡單的循環。

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

正如 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

基於 Wayne 的出色回答,本着用最少字符數做事的 Ruby 精神,這里有一個稍微更新的版本:

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

顯然,它不能解決原始的 Euler 問題,但可以很好地生成無窮大的整數序列。 絕對適用於 Ruby > 2.0。 享受!

在 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 }

希望這對某人有用!

我相信纖維(我相信在 Ruby 1.9 中添加)可能接近您想要的。 請參閱此處了解一些信息或僅搜索 Ruby Fibers

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM