【问题标题】:Ruby: enumerator can't be coerced to Fixnum; struggling with Project Euler #5Ruby:不能将枚举器强制为 Fixnum;与 Project Euler #5 斗争
【发布时间】:2012-05-18 02:26:52
【问题描述】:

挑战是找到最小的整数foo

foo % 1..20 == 0

我目前的尝试是蛮力

until foo % 1.upto(20) == 0 do
    foo++
end

这会输出错误unexpected keyword end。但是,如果我不将 end 关键字放入 irb 代码将永远不会运行,因为该块没有关闭。

我做了一个空的测试用例来看看我的错误在哪里

until foo % 1.upto(20) == 0 do
end

这会引发一个新错误:enumerator can't be coerced to a fixnum。我想这意味着你不能直接在一个范围内执行模数并期望整个范围内有一个简洁的布尔结果。但我不知道从这里去哪里。

我的第一次尝试不是蛮力,而是尝试更有效/优雅/中肯的东西,如下所示:

foo = 1
1.upto(20) {|bar| foo *= bar unless foo % i == 0}

给出了错误的答案。我不明白为什么,但我也对为什么感兴趣

foo = 1
20.downto(1) {|bar| foo *= bar unless foo % i == 0}

输出不同的答案。

编辑:我会使用 for 循环(我在 ActionScript 中编程时弄湿了我的脚),但它们在 ruby​​ 中无法正常工作。

【问题讨论】:

  • foo++ 在 Ruby 中无法正常工作。你需要写 foo += 1
  • @David:这是我最不喜欢的语言功能...
  • @sarnold 最不喜欢它是因为您希望它是用 Ruby 编写的,还是不喜欢用其他语言编写的?这在 Ruby(或一般的 OO)中并没有真正意义,因为数字不是可变的,++ 因此会涉及隐藏分配。
  • @Andrew: 不要试图用 reasoning 来安抚我 :) -- 我刚刚习惯了foo++ 在我使用的每一种语言中 -- 然后是 Ruby咬我。
  • @sarnold 不,我明白,我也习惯了,当我开始使用 Ruby 时感到震惊。但是看到 for 循环在 Ruby 中不像在那些语言中那样存在,我不会太想念 ++

标签: ruby fixnum


【解决方案1】:

您的第一个解决方案是错误的,因为1.upto(20) 是一个 枚举器,即本质上是一个 1 到 20 的迭代器,它不能用作模数或与另一个比较的数字数字,因为它本身不是数字。

你真的需要两个“循环”:

foo = 1
foo += 1 until (1..20).all? { |i| foo % i == 0 }

第一个循环是until,然后all? 是另一个循环,因为它确保块({ |i| foo % i == 0 })对于调用它的范围内的每个元素(@ 987654327@)。请注意,我使用的是单行“向后”语法(这也适用于 ifunlesswhile,...)——以上等价于:

foo = 1
until (1..20).all? { |i| foo % i == 0 } do
  foo += 1
end
# foo => 232792560

此外,这非常低效,Euler 项目通常涉及比编程更多的数学,非暴力解决方案可能涉及更多数学但速度要快得多。

【讨论】:

  • 尽管我想把这些问题的解决方案做得“优雅”,但到目前为止我只能处理“凌乱”。在发布问题后,我想出了一个不同方法的想法,我正试图将一些代码片段打造成形状。这至少比蛮力高出一步,我认为它会起作用,但我需要所有能帮助我理解哪里出错的帮助。有趣的部分是,在花了几个小时之后,这对我的头造成了多大的伤害,增加了我的基本能力。启发性的部分是之后阅读其他人的(更好的)解决方案。感谢您为此提供的帮助。
【解决方案2】:

试试这个:

until 1.upto(20).reject{|i| foo % i == 0 }.empty? do
  foo += 1
end

【讨论】:

  • 因为它必须能被 20 整除,所以你可以从 foo = 20 开始,每次加 20 而不是 1
  • 好主意,因为我放弃了foo=17740946。 :)
  • 我不是为了解决 ProjectEuler 问题而试图改进答案 - 我只是为尝试的解决方案提供正确的语法
  • 所以拒绝方法跳过满足该条件的值的迭代,并在达到最大值时为空结束迭代?既然 20 可以因式分解,你真的能找到最小的公倍数吗?我正在尝试比蛮力更好的解决方案,但它将依赖于首先消除迭代值之间的公因子,除了具有最多问题因子副本的值。从那里,倍增。我至少会尝试这个解决方案。良好的做法,即使结果错误且过于复杂,但至少可能比蛮力更有效。谢谢!
【解决方案3】:

我知道这不是直接的 OP 问题,但这更容易实现:

puts (1..20).reduce(:lcm)

它是如此简单,以这种方式解决它似乎不公平,但这正是为什么 Ruby 是我为 Project Euler 选择的语言。

另见this question

【讨论】:

    【解决方案4】:

    如果是我,我会定义一个函数来测试条件:

    def mod_test(num)
      test = (1..20).map {|i| num % i == 0}
      test.all? # all are true
    end
    

    然后循环尝试不同的值:

    foo = 20
    until mod_test(foo) do
      foo += 20
    end
    

    (感谢Dylan+= 20 的加速。)

    我确信有一个聪明的方法可以使用foo % 10 == 0 的知识来暗示foo % 5 == 0foo % 2 == 0,并且只对120 之间的素数执行测试,并且可能甚至可以使用该信息直接构造数字——但我的代码运行得足够快。

    【讨论】:

    • 您实际上可以只测试 11..20。如果 20 个有效,那么 10 个也有效;如果 18 个有效,那么 9 个也有效;等
    • @Dylan:一个很好的观点,几乎可以将运行时间缩短一半。 (函数调用会阻止它恰好是一半。)很好。 :)
    • @Dylan 你有有趣的优化。我已经完成了我的工作,以了解每个人的意见。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    • 1970-01-01
    相关资源
    最近更新 更多