【问题标题】:Proof by induction with multiple lists用多个列表进行归纳证明
【发布时间】:2015-08-05 02:23:18
【问题描述】:

我正在关注 Coursera 上的 Scala 函数式编程讲座,在视频 5.7 的结尾,Martin Odersky 要求通过归纳证明以下等式的正确性:

(xs ++ ys) map f = (xs map f) ++ (ys map f)

当涉及多个列表时,如何通过归纳处理证明?

我检查了 xs 为 Nil 和 ys 为 Nil 的基本情况。 我已经通过归纳证明,当 xs 被 x::xs 替换时,等式成立,但是我们是否还需要检查 ys 被 y::ys 替换的等式?

在那种情况下(不会过多地破坏练习......无论如何都没有评分)你如何处理:(xs ++ (y::ys)) map f

这是我在类似示例中使用的方法,以证明

(xs ++ ys).reverse = ys.reverse ++ xs.reverse

证明(省略基本情况和简单的 x::xs 情况):

(xs ++ (y::ys)).reverse
= (xs ++ (List(y) ++ ys)).reverse         //y::ys = List(y) ++ ys
= ((xs ++ List(y)) ++ ys).reverse         //concat associativity
= ys.reverse ++ (xs ++ List(y)).reverse   //by induction hypothesis (proven with x::xs)
= ys.reverse ++ List(y).reverse ++ xs.reverse //by induction hypothesis
= ys.reverse ++ (y::Nil).reverse ++ xs.reverse //List(y) = y :: Nil
= ys.reverse ++ Nil.reverse ++ List(y) ++ xs.reverse //reverse definition
= (ys.reverse ++ List(y)) ++ xs.reverse //reverse on Nil (base case)
= (y :: ys).reverse ++ xs.reverse         //reverse definition

这样对吗?

【问题讨论】:

  • 查看++map的定义。有 2 个列表,但它们分解了哪一个? ;)
  • 当然,但是定义并不能证明...我们是否可以说“它已通过对表达式的两个列表之一进行归纳证明是正确的,因此整个表达式是正确”...我倾向于认为我们不能。

标签: scala functional-programming induction proof-of-correctness equational-reasoning


【解决方案1】:

该属性涉及多个列表,但++ 仅在其左侧参数上递归。这是一个提示,你可以通过对左论证的归纳来证明。一般来说,当证明一个关于某个递归函数的命题时,你首先要尝试的是对函数递归的同一个参数进行归纳

我给你做一个例子:

声明(xs ++ ys) map f = (xs map f) ++ (ys map f)

证明:对xs进行归纳。

  • 基本情况:xs = Nil

    • lhs = (Nil ++ ys) map f = ys map f

      (由++定义)

    • rhs = (Nil map f) ++ (ys map f) = Nil ++ ys map f = ys map f

      (由map 定义,然后由++ 定义)

    • 因此 lhs = rhs
  • 归纳案例:xs = z :: zs

    • 假设(zs ++ ys) map f = (zs map f) ++ (ys map f)
    • 目标((z :: zs) ++ ys) map f = ((z :: zs) map f) ++ (ys map f)
    • lhs = (z :: (zs ++ ys)) map f = f(z) :: ((zs ++ ys) map f) (1)

      (由map定义)

    • rhs = ((z :: zs) map f) ++ (ys map f) = (f(z) :: (zs map f)) ++ (ys map f)

      (由map定义)

    • 反过来,rhs = f(z) :: ((zs map f) ++ (ys map f)) (2)

      (由++定义)

    • 来自 假设(1)(2),我们已经证明了目标

因此,我们已经证明,xsysf 的声明是真实的。

【讨论】:

    【解决方案2】:

    正如@Phil 的评论所说,首先是对++:: 在列表中所做的方法的一个很好的理解,更好的方法是documentation

    我们如何证明列表程序的性质? 答案是结构归纳法! 通过结构归纳证明列表属性 P(xs) 的证明规则:

    P(Nil)(基本情况) 对于所有 x,xs : P(xs) => P(x::xs) (归纳步骤)

    对于所有 xs:P(xs)(结果)

    归纳步骤中的P(xs)称为归纳假设

    因为唯一重要的是 xs,ys 是用长度 l 修复正确的 List,在证明 xs 之后你可以证明 ys,或者看到它是可交换的

    让我们应用归纳和函数的定义

    P(xs): (xs ++ ys) 映射 f = (xs 映射 f) ++ (ys 映射 f)

    我们将 xs 替换为 nil

    (nil ++ ys) map f [definition of ++ ] 
    ys map f  on the other hand 
    (xs map f) ++ (ys map p) [apply map over NIL] 
    (NIL) ++ (ys map p) [definition pf ++] 
    ys map p
    

    感应步骤

    ((x::xs) ++ ys) map f [definition ++]
    (x:: (xs ++ ys)) map f [definition map]
    f(x) :: ((xs ++ ys) map f) [induction hypothesis]
    f(x) :: ((xs map f) ++ (ys map f)) [definition ++]
    (f(x) :: (xs map f)) ++ (ys map f) [definition map]
    (x::xs) map f ++ ys map f
    

    q.e.d

    例如 scala 工作表中的另一个案例

    import scala.util.Random
    
    // P : length ( append(as,bs) )) = length ( as ) + length (bs)
    
    def length[T](as: List[T]): Int = as match {
        case Nil => 0
        case _::xs => 1 + length(xs)
    }
    
    def append[T](as: List[T], bs: List[T]): List[T] = as match {
      case Nil => bs
      case x :: xs => x :: append(xs, bs)
    }
    
    // base case  we substitute Nil for as in P
    
    val a:List[Int] = Nil
    val n = 10
    val b:List[Int] = Seq.fill(n)(Random.nextInt).toList
    
    length((append(a,b)))
    
    length(a)
    
    length(b)
    

    导入 scala.util.Random

    length: length[T](val as: List[T]) => Int
    
    
    
    
    append: append[T](val as: List[T],val bs: List[T]) => List[T]
    
    
    
    
    
    
    a: List[Int] = List()
    n: Int = 10
    b: List[Int] = List(1168053950, 922397949, -1884264936, 869558369, -165728826, -1052466354, -1696038881, 246666877, 1673332480, -975585734)
    
    res0: Int = 10
    
    res1: Int = 0
    
    res2: Int = 10
    

    here你可以找到更多例子

    【讨论】:

    • 感谢您的回答。我的问题可能不是很清楚,您的答案与我已经证明的部分相对应。我的问题是你如何从“归纳一个列表”到“整个表达式被证明是正确的”。
    • @Gael 你只需要在一个列表中引入并不透明地引用另一个列表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-20
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    相关资源
    最近更新 更多