【发布时间】:2015-03-18 06:40:05
【问题描述】:
检查一个数字是否包含 Haskell 中的所有数字的有效方法是什么?修改列表效率极低,因此保留每个数字出现次数的列表是不行的。您可以在递归中保留 10 个 Bool 变量,在其中您可以遍历您的号码的数字列表,但这似乎太丑陋了。
【问题讨论】:
-
你的意思是检查每个字符是否都是数字?或者是否所有可能的数字 0-9 至少出现一次?还是别的什么?
标签: haskell
检查一个数字是否包含 Haskell 中的所有数字的有效方法是什么?修改列表效率极低,因此保留每个数字出现次数的列表是不行的。您可以在递归中保留 10 个 Bool 变量,在其中您可以遍历您的号码的数字列表,但这似乎太丑陋了。
【问题讨论】:
标签: haskell
我的解决方案比user2407038's 快得多。
import Data.Bits
import Data.Char
import Data.List
hasAll10Digits = any (==1023) . scanl setBit (0::Int) . map digitToInt . show
在 ghci 中,它运行 hasAll10Digits (12^223451) 仅需 0.03 秒。
相比之下,user2407038 的代码运行时间约为 11 秒。
【讨论】:
import qualified Data.Set as S; hasAll10Digits = any ((== 10) . S.size) . scanl (flip S.insert) S.empty . show。它只是使用具有不同中间结构的相同算法来存储结果。这也适用于 Data.Map 在大约相同的时间内。
any 和scanl 换成更高阶的foldr。或者您可以手动删除show,无论如何这似乎可能会有所帮助。比较时请务必排除 RTS 启动时间。
map digitToInt . show 对于大数字来说比我能找到的任何东西都快
scanl 在 7.10 之前对列表融合并不是很聪明......您是否尝试过 -fllvm 来加速手动转换? GHC 的本机代码生成器对于已知除数的除法非常愚蠢——我很确定它只能处理 2 的幂。
quotRem 比 Int 更快,但即使使用 -fllvm,你也无法获得较大的 integer 的数字。
这是基于 No_signal 的 excellent approach,但在不分配任何不必要的结构的情况下对运行进行了一些小优化。为了加快速度,请使用-fllvm 进行编译——GHC 的本机代码生成器在优化除数除数方面做得不好。
hasAllDigits :: Int -> Bool
hasAllDigits = go 0x3FF where
go !_set 0 = False
go set n = case (clearBit set r) of
0 -> True
set' -> go set' q
where
(q,r) = n `quotRem` 10
【讨论】:
没有理由说列表对于您的目的来说效率极低。您可以使用集合来跟踪您看到的数字。
import Data.List
import qualified Data.Set as S
digits = foldr S.insert S.empty . unfoldr go where
go 0 = Nothing
go n = let (a,b) = n `divMod` 10 in Just (b,a)
hasAll10Digits = (== (S.fromList [0..9])) . digits
insert for Data.Set 是 O(log n),但是您要插入的集合永远不会超过 10 个元素,因此这些插入基本上是常数时间。
【讨论】: