无限列表中的周期性(Fibonacci mod序列)Haskell

时间:2018-02-11 08:24:36

标签: haskell

我需要在Haskell中创建一个函数,其工作方式如下

periodicity ::[Integer] ->[Integer]
periodicity [1,2,3,3,4,1,2,3,3,4...] = [1,2,3,4]
periodicity [0,1,2,2,5,4,3,3,0,1,2,5,4...] = [0,1,2,5,4,3]

也就是说,从列表中你可以获得总是重复的部分,数学科学中的内容将被称为函数的周期。

我已经尝试了这个,但是我的工作并不像我想要的那样,因为我希望与infinites列表一起工作

periodicty :: Eq a => [a] -> [a]
periodicity xs = take n xs
    where l = length xs
          n = head [m | m <- divisors l, 
                        concat (replicate (l `div` m) (take m xs)) == xs]

我发现这个函数给了我一段时间的长度,我本可以解决问题,但我不明白代码在哪里:

periodo 1 = 1
periodo n = f 1 ps 0
  where
   f 0 (1 : xs) pi = pi
   f _ (x : xs) pi = f x xs (pi + 1)
   ps = 1 : 1 : zipWith (\u v -> (u + v) `mod` n) (tail ps) ps 

2 个答案:

答案 0 :(得分:5)

正如你所说,你想要的功能是不可能的 1

但是既然你说你真的追随的是Pisano时期,那么足以注意到两个连续的数字足以确定斐波纳契序列的其余部分(mod n或其他)。所以你真的在寻找相邻对的第一次重现,例如

0, 1, 1, 2, 0, 2, 2, 1, 0, 1, 1, 2, 0, 2, 2, 1, 0, 1, 1, 2, 0, 2, 2, 1, 0
^^^^                    ^^^^
[--------- 8 -----------)

我不是为他们编码人们的问题,但我可以草拟我解决这个问题的方式。要记住的一件事是周期性可能有一个不重复的前缀 - 我不知道这是否实际发生在Fibonacci序列mod n中,但它通常发生。所以我们需要准备好丢弃一个前缀。

首先,zip列表及其尾部以获取相邻对的列表

    [ 0,     1,     1,     2,     0,     2,    2,     1 ...]
 -> [(0,1), (1,1), (1,2), (2,0), (0,2), (2,2), (2,1), ... ]

从这里开始,在列表中折叠一个Data.Map,键入该对,其中值是它首次出现的索引。您可以使用foldr执行此操作,但我可能只使用带累加器的递归函数。对于上面的示例,每个步骤的地图如下所示:

{(0,1): 0}
{(0,1): 0, (1,1): 1}
{(0,1): 0, (1,1): 1, (1,2): 2}
{(0,1): 0, (1,1): 1, (1,2): 2, (2,0): 3}
...

当您到达列表中已存在该键的位置时,您可以从地图中的一个点中减去当前索引,并且这是您的句点。

1 这是一个证明。让我们假设您有图灵机的规范,并列出其执行步骤的列表steps。如果它停止,该列表将是有限的,否则无限。现在构建这个列表:

bad = zipWith const (cycle [1,2,3]) steps ++ cycle [1,2,3,4]

只要机器运行,此列表将循环使用第3个周期,之后使用第4个周期。因此,如果图灵机停止,periodicity bad = 4,否则periodicity bad = 3。也就是说,periodicity可以决定暂停问题,这是不可能的。

答案 1 :(得分:2)

对于一个任意的无限列表,你所要求的是不可能的。我们只能在有限的时间内检查一个有限的子列表,而我们所知道的,列表的下一个元素可能会破坏模式。

在您的评论中,您澄清您确实正在寻找Fibonacci序列的周期性部分,模 m 。在这种特殊情况下,如果我理解正确的话,就有可能。

Fibonacci序列(mod m )在某个点之后是周期性的,如果相同的值重复三次:前两个值都等于它们的前任,所以函数变为周期性的如果两个或多个数字的任何序列重复一次,那么它也是在某一点之后的周期性,因为我们知道该值及其前身是 k 和<的重复em> k -1个术语之前,该函数将使用句点 k 再次生成相同的子序列。没有更短的时间,或者我们会检测到它,从左到右。

此外,任何无限重复的序列将首先重复一次,因此这将检测所有此类序列。

因此,比我最初写的更好的计算方法是在列表的前面搜索当前的数字及其前身。 (您可以使用luqui构建连续对列表的策略,或者递归搜索相同的数据结构而不是构建新的数据结构。)如果存在匹配,则保证序列的重复周期等于两者之间的距离。同一对的出现。

由于您从头开始搜索每个初始子序列,因此非周期性初始子序列的长度需要时间二次。要在线性时间内执行 m ²+ 2步的上限:我们知道只有 m 可能的值,这意味着只有 m ²可能的值对,一系列 k 数字包含 k -1个连续的数字对,因此根据鸽子原理,第一个 m ²+ 2个序列的元素必须在两个不同的位置包含一对连续的值,并从该对的第一个实例开始变为周期性的。因此,搜索固定长度的初始子序列就足够了,我们可以构建一个列表中的每个 n ²潜在对的索引(如果有的话),直到我们遇到第一个副本。 (也就是说,我们需要使用一个可变阵列,因此我们牺牲了速度或功能纯度。)

这类似于lugui的算法,但查找速度更快。

猜想

如果0:1出现多次,则序列是周期性的。如果每个Fibonacci序列(mod m )都是周期性的,那么周期就是第二次出现[0,1]的位置。

0:1只会由之前的-1:1生成,前一个-3:2将由之前的-8:5生成,前一个0:1会生成该m-1:1,依此类推。 [..., - 8,5,-3,2,-1,1,0]正是斐波纳契序列,向后,交替的符号,mod m ,如果有任何两个连续的数字出现在原始序列中,它是周期性的。因此,iff [0,1,1]将由此模式生成,它最终将在Fibonacci序列mod m 中生成1:m-1。如果f m -1并且在Fibo mod m 中连续出现1,则会发生这种情况。

两个特例

如果Fibo mod m i 位置包含1:-1,则序列的周期为 i +2,如果包含1,4,序列的句点为2 i +4。 (如果序列包含{{1}},则下一个位置是 i +2,下一个 i +2步骤为:{0,-1,-1, -2,-3,-5,..., - 1,1})。所以这让我们快捷一点;当我们在Fibo mod 5的第8位看到{{1}}时,我们知道序列的周期为20.在这种特殊情况下,扫描平均需要的元素少于一半,其上限为 m ²/ 2 + 1个要扫描的元素,以便将案例排除在外,并使用常量内存。