如何将机器int用于Haskell中设置的有限操作位?
我想用一个machine int来表示一个包含零个或多个数字1..9的集合。这是为了满足一种愚蠢的痴迷,即我可以用Haskell构建一个数独求解算法的速度有多快,但这一切都是为了扩展我的Haskell教育。我发现,除了我能找到的书本练习之外,我还有一个真正的问题需要解决。对于一个“非常难”但不需要猜测就可以解决的难题,我将它缩短到几毫秒,但我希望下一代的性能可以用微秒来衡量:) 我需要进行以下操作:如何将机器int用于Haskell中设置的有限操作位?,haskell,Haskell,我想用一个machine int来表示一个包含零个或多个数字1..9的集合。这是为了满足一种愚蠢的痴迷,即我可以用Haskell构建一个数独求解算法的速度有多快,但这一切都是为了扩展我的Haskell教育。我发现,除了我能找到的书本练习之外,我还有一个真正的问题需要解决。对于一个“非常难”但不需要猜测就可以解决的难题,我将它缩短到几毫秒,但我希望下一代的性能可以用微秒来衡量:) 我需要进行以下操作: 初始化包含所有成员的集合,对应于十进制数字511或0b111111111。我在想s1=MyBi
- 初始化包含所有成员的集合,对应于十进制数字511或0b111111111。我在想s1=MyBitSet(511)之类的东西,但是任何不需要单独设置每个位的方法都可以
- 设置减法-类似于0b111111111-0b10101=>111101010。在按位运算中,我相信这将是s1或补码(s2)
- 成员-像s2={2,4,6,7,8,9}
你能给我指出正确的方向吗?根据上面@user2407038的建议,我制定了以下模块,似乎很好地完成了这项工作,并让我有机会第一次在脑海中直接了解哈斯克尔的一些概念:
--
-- A simple, hopefully very fast Sudoku markup set implementation
--
module MarkupSet where
import Data.Bits ((.&.), complement, setBit, testBit)
import Data.Word (Word)
import Test.HUnit
type MarkupSet = Word
-- initialized MarkupSet value, 511 decimal, 0b111111111 binary
defaultMarkupSet :: MarkupSet
defaultMarkupSet = 511
-- an empty set is also handy in some cases
emptyMarkupSet :: MarkupSet
emptyMarkupSet = 0
-- construct a MarkupSet from an Int (Word) value, e.g. markupSet 42
markupSet :: Word -> MarkupSet
markupSet x = x
-- construct a MarkupSet from list of values, e.g. [1,3,5]
fromValues :: [Int] -> MarkupSet
fromValues = foldl (\acc n -> setBit acc (n-1)) emptyMarkupSet
-- based on comment from @dfeuer, updated from:
-- fromValues [] = markupSet 0
-- fromValues (n:ns) = setBit (fromValues ns) (n - 1)
-- a list of the integer members 1..9 in a MarkupSet, e.g. [1,3,5]
enumMembers :: MarkupSet -> [Int]
enumMembers s = [ i+1 | i <- [0..8], testBit s i]
-- set difference, aka complement set: members of s not in s'
-- e.g difference {1,3,5,8} {1,2,3} -> {5,8}
difference :: MarkupSet -> MarkupSet -> MarkupSet
difference s s' = s .&. (complement s')
-- 9-digit binary string representation, e.g. "001011010"
showBits :: MarkupSet -> String
showBits s = [ if testBit s i then '1' else '0' | i <- [8,7..0] ]
-- ===================================================================
-- Tests
-- ===================================================================
case_default_has_all_members = TestCase $ assertEqual
"defaultMarkupSet should have all bits set"
"111111111"
(showBits defaultMarkupSet)
case_construct_from_int = TestCase $ assertEqual
"can construct from Int literal"
"000101010"
(showBits $ markupSet 42)
case_construct_from_values = TestCase $ assertEqual
"can construct from list of ints in range [1..9]"
297
(fromValues [1,4,6,9])
case_enum_int_members = TestCase $ assertEqual
"can enumerate its integer members"
[2,4,6]
(enumMembers $ markupSet 42)
case_subtract_sets = TestCase $ assertEqual
"can calculate set difference"
[1,3,5,7,8,9]
(enumMembers $ difference defaultMarkupSet (markupSet 42))
tests =
[ case_default_has_all_members
, case_construct_from_int
, case_construct_from_values
, case_enum_int_members
, case_subtract_sets
]
main = do
runTestTT $ TestList tests
--
--一个简单、希望非常快速的数独标记集实现
--
模块标记在哪里
导入数据位((.&.)、补码、setBit、testBit)
导入数据。Word(Word)
导入测试.HUnit
键入MarkupSet=Word
--初始化的MarkupSet值,511十进制,0b111111111二进制
defaultMarkupSet::MarkupSet
defaultMarkupSet=511
--空集在某些情况下也很方便
emptyMarkuper::markuper
EmptyMarkuper=0
--从Int(Word)值构造MarkupSet,例如MarkupSet 42
markupSet::Word->markupSet
标记x=x
--根据值列表构造一个标记,例如[1,3,5]
fromValues::[Int]->MarkupSet
fromValues=foldl(\acc n->setBit acc(n-1))清空标记
--根据@dfeuer的评论,更新自:
--fromValues[]=0
--fromValues(n:ns)=setBit(fromValues ns)(n-1)
--标记中整数成员1..9的列表,例如[1,3,5]
enumMembers::MarkupSet->[Int]
enumMembers=[i+1 | i早期优化是程序员的堕落。分析您的代码,找到热点,我敢打赌,您会发现并不是您的整数表示最让您慢。您知道“集”减法和“集”吗成员资格可以通过按位操作写入。嗯,这看起来很有趣@user2407038,我来看看那个模块,我想它可能会起作用,谢谢:)我被困在Data.BitSet上,错过了那一个:)@bheklillr是的,你当然是对的,通常我不会在没有证据的情况下追求这种事情这是一个根本原因。它是it’现在任何能引起我兴趣的方向都是好的,都会激发新的探索:)我会警惕自己养成坏习惯:)@scanny单纯地把它作为一种学习练习是完全不同的,在这种情况下,我鼓励你努力追求它!我只是警告你不要这样做,因为我正是被它咬了一口在过去一周的工作中,我认为使用C会比Python给我带来巨大的速度提升,但对于这个特别的问题,NumPy已经尽我所能让它变得最快了。fromValues
是一个折页,你应该把它写成一个折页,以利用折页的特殊优化规则。但是当你当然,我想把它构造成一个左折叠。我不认为这对一个数独解算器来说有什么意义,但是如果你想学习基本的Haskell优化,试试看。谢谢@dfeuer,我会的!:)