Haskell算法建议和替代解决方案的建议

sk谐的

我将在下面保留对问题的描述,但是我正在去掉所有不相关的部分。

1)多亏了dfeuer,我得以从该程序中节省了近整整一秒钟的时间。我从10.1下降到9.2秒。

2)由于对SO用户应用程序发布的内容进行了修改,因此我能够将时间缩短到7.0秒,从而使IO非常高效。

3)感谢在这里找到的groupBy的其他版本,我现在是6.2秒。

4)重新实现构建器输出,现在是5.8秒。

import qualified Data.ByteString.Builder    as BB  
import qualified Data.ByteString.Lazy.Char8 as DB
import qualified Data.Function              as DF

import Control.Applicative
import Data.Monoid
import System.IO

groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy rel []     = []
groupBy rel (x:xs) = (x:ys) : groupBy rel zs
    where (ys,zs) = groupByAux x xs
          groupByAux x0 (x:xs) 
              | rel x0 x = (x:ys, zs)
              where (ys,zs) = groupByAux x xs
          groupByAux y xs = ([], xs)

filterLengthBy :: (Int -> Bool) -> [[a]] -> [[a]]
filterLengthBy fn = filter (flb fn)
    where flb fn xs = fn $ length xs  

buildOutput x y =  BB.intDec x <> BB.char7 ' ' <> BB.intDec y <> BB.char7 '\n'

tensAndHundreds :: [[[Int]]] -> [BB.Builder]
tensAndHundreds [] = []
tensAndHundreds (x:xs) 
    | length x /= 10 = tens x ++ tensAndHundreds xs
    | otherwise      = buildOutput (head $ head x) 100 : tensAndHundreds xs

tens :: [[Int]] -> [BB.Builder]
tens = foldr (\x acc -> buildOutput (head x) 10 : acc) []

dbIntVals :: DB.ByteString -> [Int]
dbIntVals xs = 
    case (DB.readInt xs) of
         Just (x', xs') -> x' : dbIntVals (DB.tail xs')
         Nothing        -> if xs == DB.empty
                           then []
                           else dbIntVals (DB.tail xs)

generateResults :: [Int] -> [BB.Builder]
generateResults xs = tensAndHundreds 
                   $ groupBy ((==) `DF.on` (`quot` 100) . head) 
                   $ filterLengthBy (== 10)
                   $ groupBy ((==) `DF.on` (`quot` 10)) xs

displayResults :: [BB.Builder] -> IO ()
displayResults xs = BB.hPutBuilder stdout $ mconcat xs

main :: IO ()
main = DB.getContents >>= 
       displayResults . generateResults . dbIntVals

我在原始帖子底部的两个问题是:

  1. 考虑到我上面发布的方法,是否有任何我想念的东西可以提高该程序的效率并因此提高其性能?空间分配是最后的瓶颈吗?换句话说,groupBy方法是否正常运行?

  2. 在解决传统意义上的计数器问题的同时,我还可以通过哪些其他方式来解决这个会提高效率的问题?我还没有Haskell先进功能的经验,但是请随时提供指示。

这是原始问题。我离开了代码的第一个版本,但删除了所有探查器内容

我再次开始学习Haskell,并决定尝试一些发现的随机问题。我在Arch Linux论坛上得出的一个问题是:

给定一组唯一的,升序的(但不一定是连续的整数),如果该子范围具有所有100个元素,则从0开始的每个潜在子范围100(从0-99、100-199、1300-1399等) ,然后打印head值,后跟100:

0 100
145600 100

否则,对于从0(0-9、10-19、15430-15439等)开始的相同范围内的每10个连续集,请打印该范围的开头,然后打印10:

30 10
70 10
145620 10 
145650 10

来自小型测试集的示例输出如下所示:

0 10
19000 100
20320 10
54540 100

我的主要测试文件有56,653,371个整数,其中有290,197套(十个)和四套(100个)。该测试机具有AMD 1090T处理器和8 GB RAM,并且在64位Linux上运行GHC 7.8.3。唯一使用的编译器标志是-O2 -fflvm。

有一件重要的事情要注意:我故意避免使用在编程中经常使用的计数器。例如,具有跟踪10和100的计数器:“如果计数器1 == 100,则...”之类的东西。我想避免跟踪这些计数器的状态,而只使用可用的功能/ HOF。

到目前为止,这是我最好的尝试。它将在10.1秒(当前为5.8秒内完成任务从一个角度来看,我之前提到的线程的C版本在9.4秒内完成。我没有写过另一个Haskell版本,它使用计数器完成了8.1秒(当前为4.7)。这就是我的灵感(感谢Xyne并教案例陈述应运而生)。

**编辑**

我忘了提到它的作用了。它使用整数除法将输入数据分割为十个可能的范围,并将它们放入列表列表中(已编辑以阐明所询问的分割量)。所以:

1 4 7 9                                      -> [1,4,7,9]
1 2 3 8                                      -> [1,2,3,8]
0 1 2 3 4 5 6 7 8 9                          -> [0,1,2,3,4,5,6,7,8,9]
7 8 9 10 11 12 13 14 15 16 17 18 19 20 25 27 -> [7,8,9] [10,11,12,13,14,15,16,17,18,19] [20,25,27]

然后filterLengthBy删除长度不为10的所有内容,因为我们不需要它。根据头的整数除法,将结果列表分成多个列表。所以:

[[0,1,2,3,4,5,6,7,8,9],[10,11,12,13,14,15,16,17,18,19],[100,101,102,103,104,105,106,107,108,109]]

变成:

[[[0,1,2,3,4,5,6,7,8,9],[10,11,12,13,14,15,16,17,18,19]],[[100,101,102,103,104,105,106,107,108,109]]]

如果列表的第二级长度为10,则匹配范围为100,并将其输出到解决方案集。否则,该列表包含10个范围的列表,应单独添加到解决方案集中。

旧版本代码

import qualified Data.ByteString.Lazy.Char8  as DB
import qualified Data.Function               as DF
import qualified Data.List                   as DL
import Control.Applicative

groupByEqOn :: Eq b => (a -> b) -> [a] -> [[a]]
groupByEqOn fn = DL.groupBy ((==) `DF.on` fn)

filterLengthBy :: (Int -> Bool) -> [[a]] -> [[a]]
filterLengthBy fn = filter (flb fn)
    where flb fn xs = fn $ length xs   

tensAndHundreds :: [[[Int]]] -> [(Int, Int)]
tensAndHundreds [] = []
tensAndHundreds (x:xs) 
    | length x /= 10 = tens x ++ tensAndHundreds xs
    | otherwise      = (head $ head x, 100) : tensAndHundreds xs

tens :: [[Int]] -> [(Int, Int)]
tens = map (\x -> (head x, 10))

dbEmpty :: DB.ByteString
dbEmpty = DB.empty

dbIntVals :: [DB.ByteString] -> [Int]
dbIntVals [] = []
dbIntVals (x:xs) =
    case (DB.readInt x) of
         (Just (x', dbEmpty)) -> x' : dbIntVals xs
         _                    -> error "*** Error: Non-integer input ***"

generateResults :: [Int] -> [(Int, Int)]
generateResults xs = tensAndHundreds 
                   $ groupByEqOn ((`quot` 100) . head) 
                   $ filterLengthBy (== 10) 
                   $ groupByEqOn (`quot` 10) xs

displayResults :: [(Int, Int)] -> IO ()
displayResults = mapM_ (\(a, b) -> putStrLn (show a ++ " " ++ show b)) 

main :: IO ()
main = dbIntVals <$> DB.lines <$> DB.getContents >>= 
       displayResults . generateResults

探查器令我惊讶的仅有两件事,首先是filterLengthBy有多快。对于所有相关人员,要使其过滤速度如此之快,应引起极大的赞誉。考虑到我向它扔了多少数据,这确实是一件了不起的事情。就这一点而言,令人印象深刻的是我写的东西是如此之快。另一个惊喜是dbIntVals有多慢。我确实实现了正确的错误检查,这使事情变慢了一些,但是我仍然感到惊讶。除此之外,它看起来和我的代码完全一样。

sk谐的

我已经完成了这个线程。我来到这里是为了了解SO Haskell社区必须提供的内容,并且得到了一些有用的答复。为此我很感激。

我发布的代码的最新版本表明,只要有了好的代码,GHC就可以仅使用功能性习语来生成快速,准确的程序。这似乎很明显,但这是我从头开始编写的第一个Haskell程序(带有明显的复制和粘贴groupBy)。

我学到了什么?

从dfeuer身上,我被提醒要小心一些时候,仅依赖于诸如map这样的内置函数不一定是最好的方法。有时结合逻辑会很有帮助。在这种情况下,我获得了非常显着的性能提升。

从应用程序中,我了解了有效的输出。性能的提高是惊人的。

如果有任何Haskell / GHC开发人员正在阅读以下内容:

我认为,此处的groupBy应该包含在标准库中,而不应推送到Hackage。为什么?保持准确性的同时快速。对于数百万个元素的完全相同的操作以及产生完全相同的结果,性能提高了约8.2%。我知道它做的事情并不完全相同;我仍然认为值得一看。

难道实际上使任何意义吗?我发现与上述相同的数据集至少100%的性能提高了10%。我知道'rem'和'mod'的作用不同,并且它们倾向于朝着不同的“方向”四舍五入,但是前者的表现要大得多。我可能不了解其范围,但是10%很难忽略。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

CSS缩放的替代解决方案

来自分类Dev

有关如何自动部署ASP.NET解决方案的建议?

来自分类Dev

谁能为我建议一个更好的ctags解决方案?

来自分类Dev

列表索引错误在Python 2.7中,请在代码中建议选项1的解决方案

来自分类Dev

如果没有适当的解决方案,建议是什么?

来自分类Dev

ASP.Net体系结构解决方案/建议

来自分类Dev

以下提到的勘误的此建议解决方案正确吗?

来自分类Dev

不建议在主线程上使用同步XMLHttpRequest。尝试了许多不同的解决方案

来自分类Dev

Java“ scheduleAtFixedRate”替代解决方案?

来自分类Dev

关于备份解决方案的建议?

来自分类Dev

不建议为什么的AtomicInteger基于流的解决方案?

来自分类Dev

Python-替代迭代解决方案

来自分类Dev

GG情节建议和重点

来自分类Dev

pyhton拆分功能的替代解决方案

来自分类Dev

有关使用Codeigniter处理多维数组的建议或其他可能的解决方案?

来自分类Dev

不建议使用PHP MySQL扩展-是否有开放源代码解决方案?

来自分类Dev

关于备份解决方案的建议?

来自分类Dev

通过WAN备份到NAS的最佳解决方案建议

来自分类Dev

Grsync备份解决方案的建议设置是什么?

来自分类Dev

从列表中选择项目以获得更好解决方案的建议

来自分类Dev

“用户配置文件服务登录失败”-微软建议的解决方案不起作用

来自分类Dev

图中是否有用于子图(路径)匹配的库或建议的解决方案?

来自分类Dev

河内塔-替代解决方案

来自分类Dev

无法显示文森特样本图;没有建议的解决方案

来自分类Dev

适用于Linux的Rotel PC-USB。任何解决方案或建议尝试

来自分类Dev

验证解决方案逻辑时需要的建议

来自分类Dev

需要关于符合 SCORM 的学习解决方案的建议

来自分类Dev

搜索字符串的非蛮力建议解决方案

来自分类Dev

“未找到数据源名称”的建议解决方案不起作用

Related 相关文章

  1. 1

    CSS缩放的替代解决方案

  2. 2

    有关如何自动部署ASP.NET解决方案的建议?

  3. 3

    谁能为我建议一个更好的ctags解决方案?

  4. 4

    列表索引错误在Python 2.7中,请在代码中建议选项1的解决方案

  5. 5

    如果没有适当的解决方案,建议是什么?

  6. 6

    ASP.Net体系结构解决方案/建议

  7. 7

    以下提到的勘误的此建议解决方案正确吗?

  8. 8

    不建议在主线程上使用同步XMLHttpRequest。尝试了许多不同的解决方案

  9. 9

    Java“ scheduleAtFixedRate”替代解决方案?

  10. 10

    关于备份解决方案的建议?

  11. 11

    不建议为什么的AtomicInteger基于流的解决方案?

  12. 12

    Python-替代迭代解决方案

  13. 13

    GG情节建议和重点

  14. 14

    pyhton拆分功能的替代解决方案

  15. 15

    有关使用Codeigniter处理多维数组的建议或其他可能的解决方案?

  16. 16

    不建议使用PHP MySQL扩展-是否有开放源代码解决方案?

  17. 17

    关于备份解决方案的建议?

  18. 18

    通过WAN备份到NAS的最佳解决方案建议

  19. 19

    Grsync备份解决方案的建议设置是什么?

  20. 20

    从列表中选择项目以获得更好解决方案的建议

  21. 21

    “用户配置文件服务登录失败”-微软建议的解决方案不起作用

  22. 22

    图中是否有用于子图(路径)匹配的库或建议的解决方案?

  23. 23

    河内塔-替代解决方案

  24. 24

    无法显示文森特样本图;没有建议的解决方案

  25. 25

    适用于Linux的Rotel PC-USB。任何解决方案或建议尝试

  26. 26

    验证解决方案逻辑时需要的建议

  27. 27

    需要关于符合 SCORM 的学习解决方案的建议

  28. 28

    搜索字符串的非蛮力建议解决方案

  29. 29

    “未找到数据源名称”的建议解决方案不起作用

热门标签

归档