【问题标题】:How to create a Prouhet–Thue–Morse sequence in Haskell?如何在 Haskell 中创建 Prouhet-Thue-Morse 序列?
【发布时间】:2011-11-24 09:15:50
【问题描述】:

我是 Haskell 的菜鸟,但对 ActionScript 3.0 Object Orientated 有一些经验。因此,致力于重大的编程过渡。我已经阅读了有关 Haskel 的基本知识,例如算术。我可以编写简单的函数。

作为一项实际任务,我必须在 Haskell 中通过计算机生成名为 tms1 的Thue-Morse sequence。所以应该是这样的:

>tms1 0
0
>tms1 1
1
>tms1 2
10
>tms1 3
1001
>tms1 4
10010110

等等...根据维基百科,我应该使用公式。

t0 = 0
t2n = tn
t2n + 1 = 1 − tn

我不知道如何在 Haskell 中实现这个公式。你能指导我创建一个吗? 这是我到目前为止得到的:

module ThueMorse where
tms1 :: Int -> Int
tms1 0 = 0
tms1 1 = 1
tms1 2 = 10
tms1 3 = 1001
tms1 x = tms1 ((x-1)) --if x = 4 the output will be 1001, i don't know how to make this in a recursion function

我在互联网上做了一些研究,发现了这段代码。

来源: http://pastebin.com/Humyf6Kp

代码:

module ThueMorse where
tms1 :: [Int]
tms1 = buildtms1 [0] 1
    where buildtms1 x n 
        |(n `rem` 2 == 0) = buildtms1 (x++[(x !! (n `div` 2))]) (n+1)
        |(n `rem` 2 == 1) = buildtms1 (x++[1- (x !! ((n-1) `div` 2))]) (n+1)

custinv []  = []
custinv x   = (1-head x):(custinv (tail x))

tms3 :: [Int]
tms3 = buildtms3 [0] 1
    where buildtms3 x n = buildtms3 (x++(custinv x)) (n*2)

intToBinary :: Int -> [Bool]
intToBinary n   | (n==0) = []
                | (n `rem` 2 ==0) = intToBinary (n `div` 2) ++ [False]
                | (n `rem` 2 ==1) = intToBinary (n `div` 2) ++ [True]

amountTrue :: [Bool] -> Int
amountTrue [] = 0
amountTrue (x:xs)   | (x==True) = 1+amountTrue(xs)
                    | (x==False) = amountTrue(xs)

tms4 :: [Int]
tms4= buildtms4 0
    where buildtms4 n
        |(amountTrue (intToBinary n) `rem` 2 ==0) = 0:(buildtms4 (n+1))
        |(amountTrue (intToBinary n) `rem` 2 ==1) = 1:(buildtms4 (n+1))

但是这段代码没有给出想要的结果。非常感谢任何帮助。

【问题讨论】:

  • 问题似乎是,您丢弃了前导零。使用列表而不是数字也可以避免一次又一次地转换数字。我一直在想:如果我用大量代码解决一个小问题,那我就做错了。
  • 您可以写:a `rem` 2 == 0/1even aodd acustinv x 可以定义为map (\a -> 1-a) x,甚至是custinv = map (1-)amountTrue x = sum (fromEnum x)amountTrue x = length (filter id x).

标签: function haskell recursion


【解决方案1】:

我建议为您的代码使用布尔值列表;那么您不需要显式转换数字。我使用这样定义的序列:

0
01
0110
01101001
0110100110010110
01101001100101101001011001101001
...

注意前导零非常重要!

递归定义现在很容易:

morse = [False] : map step morse where step a = a ++ map not a

这是可行的,因为我们从不访问尚未定义的元素。打印列表作为练习留给读者。

这里是另一个定义,利用这样一个事实,可以通过将1 替换为100 替换为01 来获得下一步:

morse = [False] : map (concatMap step) morse where step x = [x,not x]

编辑

这里是 sdcvvc 使用函数 iterate 更简单的定义。 iterate f x返回fx的重复应用列表,从没有应用开始:

iterate f x = [x,f x,f (f x),f (f (f x)),...]

以下是定义:

morse = iterate (\a -> a ++ map not a) [False]
morse = iterate (>>= \x -> [x,not x]) [False]

【讨论】:

  • 使用迭代:第一个定义:morse = iterate (\a -> a ++ map not a) [False] 第二个定义:morse = iterate (>>= \x -> [x,not x]) [False]
【解决方案2】:

您对序列的定义似乎是位序列的序列:

0  1  10 1001 10010110 ... etc.
t0 t1 t2 t3   t4

但维基百科页面将其定义为单个位序列:

0  1  1  0  1  ... etc
t0 t1 t2 t3 t4

这是维基百科中的定义所引用的公式。有了这些知识,你提到的递归关系的定义就更容易理解了:

t0 = 0
t2n = tn
t2n + 1 = 1 − tn

用英文可以这样表述:

  • 第零位为零。
  • 对于偶数非零索引,该位与索引一半处的位相同。
  • 对于奇数索引,位是 1 减去一半的位(索引减一)。

棘手的部分是从下标 2n2n+1 到奇数和偶数,并理解 n 在每种情况下的含义。一旦完成,就可以直接编写一个计算序列的第 *n* 位的函数:

lookupMorse :: Int -> Int
lookupMorse 0 = 0;
lookupMorse n | even n    =     lookupMorse (div  n    2)
              | otherwise = 1 - lookupMorse (div (n-1) 2)

如果您想要整个序列,请将lookupMorse 映射到非负整数上:

morse :: [Int]
morse = map lookupMorse [0..]

这是无限的 Thue-Morse 序列。为了展示它,take 其中一些,将它们变成字符串,然后连接生成的序列:

>concatMap show $ take 10 morse
"0110100110"

最后,如果要使用“位序列的序列”定义,则需要先从序列中删除一些位,然后再取一些。除了零索引的情况外,要丢弃的数字与要取的数字相同:

lookupMorseAlternate :: Int -> [Int]
lookupMorseAlternate 0 = take 1 morse
lookupMorseAlternate n = take len $ drop len morse
    where
        len = 2 ^ (n-1)

这产生了另一种序列定义:

morseAlternate :: [[Int]]
morseAlternate = map lookupMorseAlternate [0..]

你可以这样使用:

>concatMap show $ lookupMorseAlternate 4
"10010110"
>map (concatMap show) $ take 5 morseAlternate
["0", "1", "10", "1001", "10010110"]

【讨论】:

    【解决方案3】:

    像这样简单:

    invertList :: [Integer] -> [Integer]
    invertList [] = []
    invertList (h:t) 
        |h == 1 = 0:invertList t
        |h == 0 = 1:invertList t
        |otherwise = error "Wrong Parameters: Should be 0 or 1"
    
    thueMorse :: Integer -> [Integer]
    thueMorse 1 = [0]
    thueMorse n = thueMorse (n - 1) ++ invertList (thueMorse (n - 1))
    

    【讨论】:

    • 我认为这里的所有结果都是一步到位的。如果您将 thueMorse 1 = [0] 更改为 thueMorse 0 = [0] 则此方法有效。
    猜你喜欢
    • 1970-01-01
    • 2014-09-27
    • 2011-09-05
    • 2011-09-29
    • 2019-08-14
    • 2011-01-03
    • 2020-09-28
    • 1970-01-01
    • 2021-05-21
    相关资源
    最近更新 更多