如何在Agda中实现Floyd的Hare和Tortoise算法?

Aad*_*hah 7 haskell functional-programming theorem-proving agda floyd-cycle-finding

我想将以下Haskell代码转换为Agda:

import Control.Arrow (first)
import Control.Monad (join)

safeTail :: [a] -> [a]
safeTail []     = []
safeTail (_:xs) = xs

floyd :: [a] -> [a] -> ([a], [a])
floyd xs     []     = ([], xs)
floyd (x:xs) (_:ys) = first (x:) $ floyd xs (safeTail ys)

split :: [a] -> ([a], [a])
split = join floyd
Run Code Online (Sandbox Code Playgroud)

这使我们能够有效地将列表拆分为两个:

split [1,2,3,4,5]   = ([1,2,3], [4,5])
split [1,2,3,4,5,6] = ([1,2,3], [4,5,6])
Run Code Online (Sandbox Code Playgroud)

所以,我试图将此代码转换为Agda:

floyd : {A : Set} ? List A ? List A ? List A × List A
floyd xs        []        = ([] , xs)
floyd (x :: xs) (_ :: ys) = first (x ::_) (floyd xs (safeTail ys))
Run Code Online (Sandbox Code Playgroud)

唯一的问题是Agda抱怨说我错过了这个案子floyd [] (y :: ys).但是,这种情况永远不应该出现.我怎样才能向Agda证明这种情况永远不会出现?


以下是此算法如何工作的可视示例:

+-------------+-------------+
|   Tortoise  |     Hare    |
+-------------+-------------+
| ^ 1 2 3 4 5 | ^ 1 2 3 4 5 |
| 1 ^ 2 3 4 5 | 1 2 ^ 3 4 5 |
| 1 2 ^ 3 4 5 | 1 2 3 4 ^ 5 |
| 1 2 3 ^ 4 5 | 1 2 3 4 5 ^ |
+-------------+-------------+
Run Code Online (Sandbox Code Playgroud)

这是另一个例子:

+---------------+---------------+
|    Tortoise   |      Hare     |
+---------------+---------------+
| ^ 1 2 3 4 5 6 | ^ 1 2 3 4 5 6 |
| 1 ^ 2 3 4 5 6 | 1 2 ^ 3 4 5 6 |
| 1 2 ^ 3 4 5 6 | 1 2 3 4 ^ 5 6 |
| 1 2 3 ^ 4 5 6 | 1 2 3 4 5 6 ^ |
+---------------+---------------+
Run Code Online (Sandbox Code Playgroud)

当野兔(第二个参数floyd)到达列表的末尾时,乌龟(第一个参数floyd)到达列表的中间.因此,通过使用两个指针(第二个指针移动速度是第一个指针的两倍),我们可以有效地将列表拆分为两个.

use*_*465 6

Twan van Laarhoven在评论中提出的相同但是与Vecs.他的版本可能更好.

open import Function
open import Data.Nat.Base
open import Data.Product renaming (map to pmap)
open import Data.List.Base
open import Data.Vec hiding (split)

?-step : ? {m n} -> m ? n -> m ? suc n
?-step  z?n     = z?n
?-step (s?s le) = s?s (?-step le)

?-refl : ? {n} -> n ? n
?-refl {0}     = z?n
?-refl {suc n} = s?s ?-refl

floyd : ? {A : Set} {n m} -> m ? n -> Vec A n -> Vec A m -> List A × List A
floyd  z?n            xs       []          = [] , toList xs
floyd (s?s  z?n)     (x ? xs) (y ? [])     = x ? [] , toList xs
floyd (s?s (s?s le)) (x ? xs) (y ? z ? ys) = pmap (x ?_) id (floyd (?-step le) xs ys)

split : ? {A : Set} -> List A -> List A × List A
split xs = floyd ?-refl (fromList xs) (fromList xs)

open import Relation.Binary.PropositionalEquality

test? : split (1 ? 2 ? 3 ? 4 ? 5 ? []) ? (1 ? 2 ? 3 ? [] , 4 ? 5 ? [])
test? = refl

test? : split (1 ? 2 ? 3 ? 4 ? 5 ? 6 ? []) ? (1 ? 2 ? 3 ? [] , 4 ? 5 ? 6 ? [])
test? = refl
Run Code Online (Sandbox Code Playgroud)

此外,函数喜欢?-refl?-step在标准库中的某个地方,但我懒得搜索.


这是我喜欢做的傻事:

open import Function
open import Data.Nat.Base
open import Data.Product renaming (map to pmap)
open import Data.List.Base
open import Data.Vec hiding (split)

floyd : ? {A : Set}
      -> (k : ? -> ?)
      -> (? {n} -> Vec A (k (suc n)) -> Vec A (suc (k n)))
      -> ? n
      -> Vec A (k n)
      -> List A × List A
floyd k u  0            xs = [] , toList xs
floyd k u  1            xs with u xs
... | x ? xs' = x ? [] , toList xs'
floyd k u (suc (suc n)) xs with u xs
... | x ? xs' = pmap (x ?_) id (floyd (k ? suc) u n xs')

split : ? {A : Set} -> List A -> List A × List A
split xs = floyd id id (length xs) (fromList xs)

open import Relation.Binary.PropositionalEquality

test? : split (1 ? 2 ? 3 ? 4 ? 5 ? []) ? (1 ? 2 ? 3 ? [] , 4 ? 5 ? [])
test? = refl

test? : split (1 ? 2 ? 3 ? 4 ? 5 ? 6 ? []) ? (1 ? 2 ? 3 ? [] , 4 ? 5 ? 6 ? [])
test? = refl
Run Code Online (Sandbox Code Playgroud)

这部分是基于下面评论中的Benjamin Hodgson建议.

  • 您通常可以通过要求一个`Vec`的长度为"某个加上"另一个的长度来省去"≤"的证明:`forall {m} - > Vec A n - > Vec A(n + m) - > ......` (3认同)
  • @BenjaminHodgson然后你必须证明你可以将`n`分成'ceil(n/2)+ floor(n/2)`来启动算法.弗洛伊德似乎首先在做什么. (2认同)