【问题标题】:Recursive Multiplier in ruby红宝石中的递归乘法器
【发布时间】:2018-10-18 02:31:12
【问题描述】:

我正在尝试在 ruby​​ 中创建一个递归乘法器。

@j = 0

def multiplier(x, y, z)
    count = 0
    if x > 0
        if z > 0
            @j += y
            z -= 1
            count += 1
            multiplier(x, y, z)
        else
            x -= 1
            z = count
            p z
            multiplier(x, y, z)
        end
    else
        return @j
    end
end


def main
    puts "Calculation is: " + multiplier(3, 10, 4).to_s
end

main

X 是乘法发生的次数

Y 是我们要相乘的数字

Z 是我们乘以的数

代码应该输出120和那里的变量

我在让 Z 保持我需要的样子时遇到问题。另外,我更愿意在没有全局变量的情况下这样做

类似 x*(y*z) 但没有时间符号

【问题讨论】:

  • 我很困惑...x * y * z 总是得到正确的解决方案吗? (顺便说一句,这与x * (y * z) 相同).. 因为这似乎与您对变量的解释不一致。
  • x*(y*z) 总会得到正确答案。获取代码中存在的变量。 x: 3, y: 10, z: 4 ==> 3*(10*4) ==> 3*(40) ==> 120。你觉得有什么不同?
  • 好的,我明白你的要求了。我认为关键是你想在没有时代符号的情况下做到这一点,也许你应该更明确地说这是要求的一部分。
  • 我虽然递归乘法总是没有时间符号。我也在我的问题结束时发表了评论,没有时间符号
  • 不知道为什么递归很重要,但我们可以避免像这样的时间符号Array.new(x,Array.new(y,z)).flatten.reduce(:+)

标签: ruby recursion


【解决方案1】:

您的代码的主要问题是count 是一个局部变量,它不会在递归调用之间保存。此外,如果您想避免使用全局变量,请将变量作为附加参数传递给函数。在 FP 中我们称之为累加器

def multiplier(x, y, z, j = 0)
  if z > 0
    multiplier(x, y, z - 1, j + y)
  elsif z.zero? # done with z, jump to x × (y × z)
    # z = -1 to explicitly mark we are done with z
    multiplier(x, j, -1, 0)
  elsif y.zero? # yay, we are all set, reducing is done!
    j
  else
    # still doing x × result step
    multiplier(x, y - 1, -1, j + x)
  end
end

multiplier(3, 10, 4)
#⇒ 120

上面肯定缺少对输入有效性的必要检查,但我敢打赌你明白了。

【讨论】:

  • 谢谢。这很棒
  • 我个人会省略第 6 行中的 0multiplier(x, j, -1)。当然,它在功能上是相同的。我认为你暗示的这个答案的关键点是这个函数是 pure - 即没有副作用,所以对于给定的输入,你总是会得到相同的结果 - - 不像 OP 的原始版本,它操纵一个“全局”变量来累积结果。
【解决方案2】:

recursive answer above 的基础上,这里有一个更通用的函数,它可以获取一个任意长的正整数列表并将它们乘以递归加法:

def multiplier(*integers, accum: 0)
  if integers.size == 1
    # We are finished!
    integers[0]
  elsif integers[-1].positive?
    # "Multiply" the last two integers, by recursive addition
    integers[-1] -= 1
    multiplier(*integers, accum: accum + integers[-2])
  else
    # The above multiplication is complete; set the last integer to its result
    integers[-2] = accum
    multiplier(*integers[0..-2])
  end
end

【讨论】:

    【解决方案3】:

    我会说 ruby​​ 的惯用方式是使用迭代器而不是递归/循环

    def multiplier(x, y, z)
      x.times.map do
        y.times.map do
          z
        end.reduce(:+)
      end.reduce(:+)
    end
    

    def multiplier(x, y, z)
      x.times.map do
        y.times.map do
          z
        end
      end.flatten.reduce(:+)
    end
    

    或者如果唯一的操作是inc

    def multiplier(x, y, z)
      j = 0
      x.times do
        y.times do
          z.times do
            j += 1
          end
        end
      end
      j
    end
    

    输出是一样的

    multiplier(3, 10, 4)
    # 120
    

    对于任意数量的参数,我们必须使用递归

    def multiplier(*xs, res: 0)
      return res if xs.empty?
      xs[0].times do
        res += 1 if xs.size == 1
        res = multiplier(*xs.drop(1), res: res)
      end
      res
    end
    

    def multiplier(*xs, res: 0)
      head, *tail = xs
      head.to_i.times do
        res += 1 if tail.empty?
        res = multiplier(*tail, res: res)
      end
      res
    end
    

    【讨论】:

    • 可怕的空间复杂度,虽然:)(前两个)
    • 第一个是O(max(x,y)),第二个是O(x*y),最后一个是O(1),我想。值得考虑的好事,谢谢@SergioTulentsev
    • @SergioTulentsev times.maptimes.lazy.map 到处都是O(1) :shrug:
    • @AlekseiMatiushkin 如果性能是一个问题,那么最后一个应该是最好的选择,否则我会选择第一个或第二个
    • @SergioTulentsev 很好的解构
    【解决方案4】:

    我先写一个递归地乘以两个数字的方法:

    def multiply_2(a, b)
      return 0 if a.zero?
      b + multiply_2(a - 1, b)
    end
    
    multiply_2(3, 4)
    #=> 12
    

    并在该方法的基础上乘以三个数字:

    def multiply_3(a, b, c)
      multiply_2(multiply_2(a, b), c)
    end
    
    multiply_3(3, 4, 10)
    #=> 3
    

    并最终将其扩展到处理 n 个数字:

    def multiply_n(a, b, *more)
      result = multiply_2(a, b)
      return result if more.empty?
      multiply_n(result, *more)
    end
    
    multiply_n(3, 4, 10, 2)
    #=> 240
    

    请注意,对于大量数字,您可能会遇到SystemStackError。这可以通过创建 multiply_2 tail-recursive 来避免(作为练习,这并不难)并启用 Ruby 的 :tailcall_optimization

    【讨论】:

      【解决方案5】:

      可以这样写。

      def multiplier(*args)
        prod = recurse(*args.map(&:abs))
        args.count { |n| n < 0 }.even? ? prod : -prod
      end
      
      def recurse(first, *rest)
        first.zero? || rest.empty? ? first : ([recurse(*rest)]*first).sum
      end
      
      multiplier(3,  10,  4)        #=>  120
      multiplier(3,  10,  4, 2, 3)  #=>  720
      multiplier(3, -10,  4)        #=> -120
      multiplier(3, -10, -4)        #=>  120
      multiplier(3,   0,  4)        #=>    0
      multiplier(3,   0, -4)        #=>    0
      

      假设我们希望计算multiplier(3, -4)recurse(3, 4) 被调用,在哪里

      first = 3
      rest = [4]
      first.zero? #=> false
      rest.empty? #=> false
      

      所以我们计算

      ([recurse(4)]*3).sum
      

      recurse(4)

      first = 4
      rest = []
      

      由于rest.empty #=&gt; truerecurse 返回first #=&gt; 4,所以

      ([recurse(4)]*3).sum]
        #=> ([4]*3).sum => [4,4,4].sum => 12
      

      返回到multiplier。由于[3, -4] 包含奇数个负值,因此乘数返回-12

      【讨论】:

      • 因此,作为避免可怕的时代符号(尽管Array#* 不是Integer#*),可以使用[recurse(*rest)].cycle(first).sum 另一个好处是这不需要构建额外的Arrays
      • @engineersmnky,我从未见过cycle 与参数一起使用。我必须记住这一点。哎呀,我希望 OP 不打算让答案避免所有使用星号。
      猜你喜欢
      • 1970-01-01
      • 2019-09-21
      • 1970-01-01
      • 2014-09-25
      • 2013-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多