我一直在尝试从 Haskell 的 acm.timus.ru解决问题 1330 。基本上,它归结为: 1) 从标准输入中读取一个长度为 N (N < 10^4) 和 M 对整数 (M < 10^5) 的数组 A;2) 对于每个 (from, to) 对,将子数组 A[from..to] 的总和打印到标准输出。
由于 SO 不允许我在此问题中发布超过 2 个 URL,因此我将参考下面我的Github 存储库中的文件。
我想出了两个解决方案,它们共享大部分代码。第一个(1330_slow.hs)使用 Prelude 函数(getLine/read/words)并且有点慢:
$ ./bench.sh slow_hs
slow_hs
Time inside the program: 2.18
MD5 (output.slow_hs.txt) = 89bcf8fd69a7fce953595d329c8f033a
另一个解决方案 (1330.hs) 抛弃了这些函数,用它们的 Data.ByteString.Char8 等效项 (B.getLine/B.readInt/B.words) 替换它们,并且表现得很好:
$ ./bench.sh hs
hs
Time inside the program: 0.27
MD5 (output.hs.txt) = 89bcf8fd69a7fce953595d329c8f033a
这个问题的时间限制是 500 毫秒,所以虽然 270 毫秒已经足够快(并且可以与我在其他语言中的解决方案相媲美,例如 C++ 和 Go),但 2180 毫秒并没有减少它。那么为什么我的第一个解决方案如此缓慢呢?即使按照 Real World Haskell 的分析技巧,我仍然无法理解这一点(我所能弄清楚的是大部分时间都花在了 readIntPair 函数上,这并没有太大帮助)。
如果你想自己做一些测试,我有一个 Python 输入生成器 (gen_test.py) 和一个预生成的输入文件 (input.txt),以防你没有安装 Python。以及两种解决方案之间的差异(slow_fast_diff.txt)。