【问题标题】:Ruby Maths Function MemoizationRuby 数学函数记忆
【发布时间】:2010-12-04 12:19:29
【问题描述】:

我写了一些看起来像这样的代码:

def get(x, y)
   @cachedResults.set(x,y, Math.hypot(x, y)) if @cachedResults.get(x,y).nil?
   @cachedResults.get(x,y)
end

@cachedResults 包含我写的一个二维数组类(在几分钟内),这个函数的目的是确保我永远不必为任何给定的 (x,y) 调用 Math.hypot 两次。 [这可以使用对称性和其他东西进一步优化,但无论如何]

所以我调用了这个函数,让它运行了 160000 次;它只用了 15 秒多一点。然后,为了看看它比非 Memoized 版本快多少,我将代码更改为:

def get(x, y)
   Math.hypot(x, y)
end

而且,令我惊讶的是,再次运行只用了 15 秒多一点。完全相同的时间。所以我的问题是,ruby 中的数学函数是自然记忆的吗?如果是这样,ruby Memoized 的程度如何?

(如果不是,那你为什么认为我一直得到这个结果?)

【问题讨论】:

    标签: ruby math function memoization


    【解决方案1】:

    为你做任何事情 160000 次需要大约 15 秒吗?在您的系统上对它进行基准测试,只返回 x;很可能hypot操作(用C实现)比解释器开销可以忽略不计。

    对于带有 khell 的 memoized get 方法的 ruby​​ 1.8.7,在 get 方法中调用函数,并在 get 方法中返回 x,迭代次数为 100000:

    peregrino:$ time ruby​​ src/memoized_hypot.rb 真实0m1.714s 用户 0m1.436s 系统 0m0.080s peregrino:$ time ruby​​ src/plain_hypot.rb 实际0m0.495s 用户 0m0.364s 系统 0m0.060s peregrino:$ time ruby​​ src/empty_hypo.rb 实际0m0.369s 用户 0m0.220s 系统 0m0.068s

    kheill 的 memoization 在每次调用时都会创建一个字符串,这比在每次调用时调用 C 库的 hypot 函数要昂贵得多。

    调用hypot和只返回x之间的区别表明hypot只贡献了25%的运行时间。这不是您应该优化的代码 - 如果可以,请尝试内联对库的调用,而不是将其包装在另一个方法中。

    peregrino:$ time ruby​​ src/inline_hypot.rb 实际0m0.365s 用户 0m0.236s 系统 0m0.044s

    这是

    100000.times{ |i| Math.hypot(i,6) }   
    

    而不是

    100000.times{ |i| foo.get(i,6) }   
    

    其中 foo 是一个带有已发布方法的对象。


    这些时间是在上网本 (Asus eeepc 900) 上的,速度不是很快,所以它们比你的时间快得多有点奇怪。所以其他东西可能会主导你的结果。

    【讨论】:

    • +1 - 在确定代码实际花费时间之前从不优化。
    • 我赞同莎拉的说法。我只是想使用原生记忆而不是创建一个新的。
    • 我同意莎拉的说法。我知道这一点,但我认为无论如何我都会很聪明。不是一个聪明的举动。而且您的故障非常有意义,因此我将其标记为答案。我对红宝石太陌生了,我需要多玩一点,直到我能正确判断它。不过,谢谢。
    • 哦,我特意选择了一台我知道运行代码很慢的计算机,以试图使结果更明显。
    【解决方案2】:

    试试这个:

    def initialize
      @cachedResults = {}
    end
    
    def get(x, y)
       @cachedResults["#{x}:#{y}"] ||= Math.hypot(x, y)
    end
    

    【讨论】:

    • 这里的重点是您希望缓存查找尽可能快。原生 Hash 类可能比您在几分钟内编写的 2D Array 类快很多!
    • 同意,很好,我是 ruby​​ 新手,仍在学习常用的做事方式。即便如此,我应该知道使用内置的散列函数。泰,我会从这个错误中吸取教训。 (+1)
    【解决方案3】:

    在这种情况下,我不认为这里的记忆会提高很多

    Math.hypot 所做的只是sqrt(x**2 + y**2)。这不是对已计算值的递归调用。

    【讨论】:

    • 是的,我只是自己看了一下,只是为了确定。谢谢。
    猜你喜欢
    • 2014-06-02
    • 2019-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-20
    • 2020-11-07
    • 2018-09-18
    • 1970-01-01
    相关资源
    最近更新 更多