提高功能性能

2023-12-29

我正在编写一个小程序来检查以下问题的解决方案布罗卡的问题 http://en.wikipedia.org/wiki/Brocard's_problem或所谓的棕色数字我首先用 ruby​​ 创建了一个草稿:

class Integer
  def factorial
    f = 1; for i in 1..self; f *= i; end; f
  end
end

boundary = 1000
m = 0

# Brown Numbers - pair of integers (m,n) where n factorial is equal with square root of m

while m <= boundary

    n = 0

    while n <= boundary
        puts "(#{m},#{n})" if ((n.factorial + 1) == (m ** 2)) 
        n += 1
    end

    m += 1
end

但我发现 Haskell 更适合做数学运算,因此我问了一个question https://stackoverflow.com/questions/24862938/ruby-while-loop-translated-to-haskell之前,我很快就得到了关于如何将 ruby​​ 代码转换为 Haskell 的答案:

results :: [(Integer, Integer)] --Use instead of `Int` to fix overflow issue
results =  [(x,y) | x <- [1..1000], y <- [1..1000] , 1 + fac x == y*y]
    where fac n = product [1..n]

我稍微改变了这一点,这样我就可以从我想要的任何数字开始执行相同的操作,因为上面的操作将从1 up to 1000或任何硬编码的数字,但我希望能够决定它应该经历的时间间隔,因此:

pairs :: (Integer, Integer) -> [(Integer, Integer)]
pairs (lower, upper) =  [(m, n) | m <- [lower..upper], n <- [lower..upper], 1 + factorial n == m*m] where factorial n = product [1..n]

如果可能的话,我想要一些关于优化的示例或指针以提高操作速度,因为此时如果我运行此操作一段时间间隔,例如[100..10000]这需要很长时间(我在45分钟后停止了)。

PS性能优化将应用于计算的 Haskell 实现(pairs函数),而不是 ruby​​ 函数,以防有人想知道我在谈论哪个函数。


那么,您将如何加快 ruby​​ 实施速度?尽管它们是不同的语言,但可以应用类似的优化,即记忆和更智能的算法。

1. 记忆

记忆可以防止您一遍又一遍地计算阶乘。

这是您的配对版本:

pairs :: (Integer, Integer) -> [(Integer, Integer)]
pairs (lower, upper) =  [(m, n) | m <- [lower..upper], n <- [lower..upper], 1 + factorial n == m*m]
    where factorial n = product [1..n]

阶乘多久被调用一次?好吧,我们可以说它至少被调用了upper - lower次,尽管我们可能不记得之前调用的值。在这种情况下,我们需要(upper - lower)²阶乘的调用。尽管阶乘计算起来相当简单,但它并不是免费的。

如果我们生成一个无限的阶乘列表并简单地选择正确的阶乘会怎样?

pairsMem :: (Integer, Integer) -> [(Integer, Integer)]
pairsMem (lower, upper) =  [(m, n) | m <- [lower..upper], n <- [lower..upper], 1 + factorial n == m*m]
    where factorial  = (factorials!!) . fromInteger
          factorials = scanl (*) 1 [1..]

Now factorials是列表[1,1,2,6,24,…], and factorial只需查找相应的值即可。两个版本比较如何?

你的版本

main = print $ pairs (0,1000)


> ghc --make SO.hs -O2 -rtsopts > /dev/null
> ./SO.hs +RTS -s
[(5,4),(11,5),(71,7)]
 204,022,149,768 bytes allocated in the heap
     220,119,948 bytes copied during GC
          41,860 bytes maximum residency (2 sample(s))
          20,308 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0     414079 colls,     0 par    2.39s    2.23s     0.0000s    0.0001s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0001s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time   67.33s  ( 67.70s elapsed)
  GC      time    2.39s  (  2.23s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time   69.72s  ( 69.93s elapsed)

  %GC     time       3.4%  (3.2% elapsed)

  Alloc rate    3,030,266,322 bytes per MUT second

  Productivity  96.6% of total user, 96.3% of total elapsed
  

大约68秒。

pairsMem

main = print $ pairsMem (0,1000)


> ghc --make -O2 -rtsopts SO.hs > /dev/null
> ./SO.hs +RTS -s
[(5,4),(11,5),(71,7)]
     551,558,988 bytes allocated in the heap
         644,420 bytes copied during GC
         231,120 bytes maximum residency (2 sample(s))
          71,504 bytes maximum slop
               2 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      1159 colls,     0 par    0.00s    0.01s     0.0000s    0.0001s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    2.17s  (  2.18s elapsed)
  GC      time    0.00s  (  0.01s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    2.17s  (  2.18s elapsed)

  %GC     time       0.0%  (0.3% elapsed)

  Alloc rate    253,955,217 bytes per MUT second

  Productivity 100.0% of total user, 99.5% of total elapsed
  

大约两秒或只有原始时间的 3%。对于一个几乎微不足道的改变来说还不错。然而,正如您所看到的,我们使用了两倍的内存。毕竟,我们将阶乘保存在列表中。然而,分配的字节总数是非记忆变体的 0.27%,因为我们不需要重新生成product.

pairsMem (100,10000)

如果数字很大呢?你说的是(100,1000)45 分钟后你停了下来。记忆版本有多快?

main = print $ pairsMem (100,10000)


> ghc --make -O2 -rtsopts SO.hs > /dev/null
> ./SO.hs +RTS -s
… 20 minutes later Ctrl+C…
  

这仍然需要太长时间。我们还能做什么?

2. 更聪明的配对

让我们回到绘图板。您正在检查 (lower,upper) 中的所有对 (n,m)。这合理吗?

事实上,不,因为阶乘增长得非常快。所以对于任何自然数让f(m)是最大的自然数,使得f(m)! <= m。现在,对于任意m,我们只需要检查f(m)第一个阶乘 - 所有其他阶乘都会更大。

只是为了记录,f(10^100) is 70.

现在策略很明确了:我们根据需要生成尽可能多的阶乘,然后简单地检查是否m * m - 1在阶乘列表中:

import Data.Maybe (isJust)
import Data.List (elemIndex)

pairsList :: (Integer, Integer) -> [(Integer, Integer)]
pairsList (lower, upper) = [(m, fromIntegral ret) 
                           | m <- [lower..upper], 
                             let l = elemIndex (m*m - 1) fs,
                             isJust l,
                             let Just ret = l
                           ]
    where fs = takeWhile (<upper*upper) $ scanl (*) 1 [1..]

这个版本的表现如何pairsMemLim?

main = print $ pairsList (1, 10^8)


> ghc --make -O2 -rtsopts SO.hs > /dev/null
> ./SO +RTS -s
[(5,4),(11,5),(71,7)]
  21,193,518,276 bytes allocated in the heap
       2,372,136 bytes copied during GC
          58,672 bytes maximum residency (2 sample(s))
          19,580 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0     40823 colls,     0 par    0.06s    0.11s     0.0000s    0.0000s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0001s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time   38.17s  ( 38.15s elapsed)
  GC      time    0.06s  (  0.11s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time   38.23s  ( 38.26s elapsed)

  %GC     time       0.2%  (0.3% elapsed)

  Alloc rate    555,212,922 bytes per MUT second

  Productivity  99.8% of total user, 99.8% of total elapsed
  

好吧,降到40岁。但是,如果我们使用提供更有效查找的数据结构呢?

3. 使用正确的数据结构

由于我们想要有效的查找,我们将使用Set。功能几乎保持不变,但是fs将是Set Integer,并且查找是通过完成的lookupIndex:

import Data.Maybe (isJust)
import qualified Data.Set as S

pairsSet :: (Integer, Integer) -> [(Integer, Integer)]
pairsSet (lower, upper) = [(m, 1 + fromIntegral ret) 
                          | m <- [lower..upper], 
                            let l = S.lookupIndex (m*m - 1) fs,
                            isJust l,
                            let Just ret = l
                          ]
    where fs = S.fromList . takeWhile (<upper*upper) $ scanl (*) 1 [1..]

这里的表现是pairsSet:


 > ./SO +RTS -s
[(5,4),(11,5),(71,7)]
  18,393,520,096 bytes allocated in the heap
       2,069,872 bytes copied during GC
          58,752 bytes maximum residency (2 sample(s))
          19,580 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0     35630 colls,     0 par    0.06s    0.08s     0.0000s    0.0001s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0001s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time   18.52s  ( 18.52s elapsed)
  GC      time    0.06s  (  0.08s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time   18.58s  ( 18.60s elapsed)

  %GC     time       0.3%  (0.4% elapsed)

  Alloc rate    993,405,304 bytes per MUT second

  Productivity  99.7% of total user, 99.5% of total elapsed
  

我们的优化之旅到此结束。顺便说一句,我们已经降低了复杂性????(n³) to ????(n log n)因为我们的数据结构为我们提供了对数搜索。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

提高功能性能 的相关文章

随机推荐

  • 使用 InProcessPipelineRunner 执行时,PubsubReader 失败并出现 NullPointerException

    我有简单的管道 仅执行读取 PubsubIO Read subscription 在消耗大约 200 个元素后 每次运行都会失败 但有以下例外 error run main 0 java lang RuntimeException java
  • 如何使用 Wix 3.11 检查 .net Framework 4.7.1

    我正在尝试通过条件检查 Wix 3 11 的 net 版本 这在 4 5 之前都可以正常工作 如下所示
  • 如何使用 PDO 在 PHP 中打印 MySQL 数据库表

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想打印表格上的所有行 每一行都是论坛中问题的答案 用户可以删除行 我可以获取数据库中的整个表 但我不知道如何获得每一行 控制器 fo
  • android studio onMapReady 未调用

    我想将地图视图集成到我的一个视图中 我已经生成了一个新的地图片段 它以不同的视角出现 并且像魅力一样发挥作用 然后 我尝试将代码集成到正常活动中 带有操作栏等 它有点有效 在屏幕上显示得很好 但 onMapReady 在那种环境中永远不会被
  • django中的自定义用户模型不允许在admin中设置密码

    我创建了一个自定义用户模型 并在我的应用程序中成功使用了该模型 问题是 在管理中 在用户编辑屏幕上 我显示当前密码哈希 而不是用于设置密码的非常有用的界面 我在 Python 2 7 上使用 Django 1 5b1 为了管理用户界面 如何
  • 如何在 Java 8 中从有限流构建无限重复流?

    我怎样才能转动有限的事物流Stream
  • 更改 ionic 2 应用程序中的 iOS 状态栏颜色

    我正在按照 ionic 2 文档设置 iOS 状态栏颜色 但它不起作用 状态栏文本是白色的 这意味着在我的白色背景上它是不可见的 我在应用程序构造函数中放入的代码是 StatusBar overlaysWebView true Status
  • 从 Access DB 发送包含动态名称附件的电子邮件

    我不知道如何让这个东西继续工作 下面的代码发送一封电子邮件 其中包含 MS Access 2010 的附件 问题是 如果它需要固定的文件名 那么当我使用每个文件末尾的日期时 我的文件名会发生变化 示例 green 12 04 2012 cs
  • 使用 AWK 中的第一个字段作为文件名

    该数据集是一个包含三列的大文件 一个部分的 ID 一些不相关的内容和一行文本 示例可能如下所示 A01 001 This is a simple test A01 002 Just for exemplary purpose A01 003
  • 将 NServiceBus 与 Asp.Net MVC 2 结合使用

    有没有办法将 NServiceBus 与 Asp Net MVC 2 一起使用 我想将请求消息从 Asp Net MVC2 应用程序发送到服务 该服务处理该消息并回复响应消息 有没有办法清楚地做到这一点 NServiceBus 仅支持注册状
  • Jquery 冲突导致错误

    从事具有多种功能的项目 例如 谷歌翻译 图像滑块 使用画廊 弹出窗口 使用阴影框 JavaScript 水平菜单栏 Now we are getting jquery conflict in it and error message suc
  • 从 Docker 容器获取 Mac 地址

    是否可以从Docker容器中获取主机的MAC地址并将其写入文本文件中 docker inspect
  • GCS - Python 下载具有目录结构的 blob

    我使用 GCS python SDK 和 google API 客户端的组合来循环启用版本的存储桶并根据元数据下载特定对象 from google cloud import storage from googleapiclient impo
  • 计算负载并避免光标

    给出下面的表结构 它表示乘客通过门磁上下车的公交路线 而且 有一个人坐在那辆公共汽车上 手里拿着一个记着点数的剪贴板 CREATE TABLE BusLoad ROUTE CHAR 4 NOT NULL StopNumber INT NOT
  • 从 Powershell 调用 AppDomain.DoCallback

    这是基于 Stack Overflow 问题 如何在新的 AppDomain 中将程序集加载为仅反射 https stackoverflow com questions 35249342 how to load an assembly as
  • 选择 Plsql 中的第二行

    假设我有下表 SomeTable id price 如何从此表中选择价格第二高的行 注意 这必须在 Pl SQL 中以与数据库无关的方式完成 是否可以在没有任何循环的情况下做到这一点 我知道这是如何使用 Oracle 结构来完成的 例如ro
  • “不要在设计中使用抽象基类;但在建模/分析中”

    虽然我在 OOAD 方面有一些经验 但我是 SOA 的新手 SOA 设计的指导原则之一是 仅使用抽象类进行建模 从设计中省略它们 抽象的使用有助于建模 分析阶段 在分析阶段 我提出了一个 BankAccount 基类 从它派生的专门类是 F
  • 将 Java 7 与官方 Google Appengine Maven 插件结合使用

    我在使用时遇到问题官方 Maven 插件 https developers google com appengine docs java tools maven以及带有 Google Appengine 的 Java 7 配置 我的项目配置
  • 优先级队列数据结构

    假设我有一个优先级队列 它按升序删除元素 并且存储在该队列中的是元素1 1 3 0 1 递增的顺序是0 then 1 then 3 但是有三个元素1s 当我打电话时remove它会首先删除0 但如果我打电话remove它会再次删除所有三个吗
  • 提高功能性能

    我正在编写一个小程序来检查以下问题的解决方案布罗卡的问题 http en wikipedia org wiki Brocard s problem或所谓的棕色数字我首先用 ruby 创建了一个草稿 class Integer def fac