Индукция для нетривиальной функции списка - PullRequest
3 голосов
/ 09 февраля 2020

Вот математическое упражнение (взято с стр. 2 - на русском языке):

Существует 100 визуально неразличимых монет трех типов: золото, серебро и медь (каждая тип встречается хотя бы один раз). Известно, что золото весит 3 грамма каждый, серебро весит 2 грамма каждый, медь весит 1 грамм каждый. Как определить тип всех монет весом не более 101 на двухпластинчатых весах без гирь?

(Примечание. Полагаю, что упражнение неверное и требуется не более 102 взвешиваний). Однако это не имеет значения)

Решение состоит в следующем:

  1. Возьмите монеты одну из одной из списка монет и сравните каждую монету с предыдущей
    • Если монеты имеют одинаковый вес, то мы назначаем их одной группе и продолжаем весить дальше
    • Если мы нашли более тяжелую монету c j , чем предыдущая, то go с шагом 2
    • Если мы нашли более легкую монету c i , чем предыдущая, продолжайте взвешивать монеты, пытаясь найти монету c j тяжелее c i
      • Если вместо этого мы нашли более легкую монету, то c 0 > c i > c j и мы знаем вес этих монет: 3> 2> 1. Go с шагом 3
  2. Ke еп сравнение монет
    • Если мы нашли более тяжелую монету c k , чем c j , то c i j k (и веса 1 <2 <3) </li>
    • Если мы нашли более легкую монету c k чем c j , затем сравните c i и c k
      • Если c i k , затем веса c i , c j , c k равны 1, 3, 2
      • Если c i > c k , то веса c i , c j , c k - 2, 3, 1
      • Если c i = c k , затем сравните c i + c k с c j
        • Если c i + c k j , затем веса c i , c j , c k равны 1, 3, 1 (в этом случае у нас нет серебряной монеты, поэтому мы на этапах 3 и 4 вместо двух медных монет)
        • Если c i + c k > c j , тогда веса c i , c j , c k равны 2, 3, 2
        • Если c i + c k = c j , затем веса c i , c j , c k 1, 2, 1
  3. Сравнить остальные монеты с серебряной монетой (или двумя медными монетами)
    • легкие монеты медные
    • такие же монеты серебряные
    • более тяжелые монеты золотые
  4. Если на шаге 1 мы сначала нашли более легкую монету вместо более тяжелой, то нам нужно сравнить первые тяжелые монеты с серебряной монетой, чтобы определить их вес (это может быть 102-е взвешивание в зависимости от набора монет)

Вот пример списка монет:

c<sub>0</sub>  c<sub>i</sub>    c<sub>j</sub>  c<sub>k</sub>
3 3 2 2 2 3 3 1 1 2 1 3
|_| |___| |_|
 i    j    k

Вот решение в Изабель HOL:

datatype coin = GC | SC | CC

datatype comp = LT | EQ | GT

primrec coin_weight :: "coin ⇒ nat" where
  "coin_weight CC = 1"
| "coin_weight SC = 2"
| "coin_weight GC = 3"

primrec sum_list where
  "sum_list f [] = 0"
| "sum_list f (x # xs) = f x + sum_list f xs"

definition weigh :: "coin list ⇒ coin list ⇒ comp" where
  "weigh xs ys = (
    let xw = sum_list coin_weight xs in
    let yw = sum_list coin_weight ys in
    if xw < yw then LT else
    if xw > yw then GT else EQ)"

definition std_weigh :: "coin list ⇒ coin ⇒ nat" where
  "std_weigh xs ys ≡ (case weigh xs [ys] of LT ⇒ 3 | GT ⇒ 1 | EQ ⇒ 2)"

definition gen_weights :: "coin list ⇒ coin ⇒ coin list ⇒ nat ⇒ nat ⇒ nat ⇒ nat ⇒ nat ⇒ nat ⇒ nat list" where
  "gen_weights cs c⇩0 std i j k w⇩j w⇩k w ≡
    ― ‹Optional heavy coins (\<^term>‹c⇩0›...)›
    replicate i (std_weigh std c⇩0) @
    ― ‹Light coins (\<^term>‹c⇩i›...)›
    replicate j w⇩j @
    ― ‹Heavy coins (\<^term>‹c⇩j›...)›
    replicate k w⇩k @
    ― ‹A light coin (\<^term>‹c⇩k›)›
    [w] @
    ― ‹Rest coins›
    map (std_weigh std) cs"

primrec determine_weights where
  "determine_weights [] c⇩0 c⇩i c⇩j i j k = None"
| "determine_weights (c⇩k # cs) c⇩0 c⇩i c⇩j i j k = (
    case weigh [c⇩j] [c⇩k]
      of LT ⇒ Some (gen_weights cs c⇩0 [c⇩j] i j (Suc k) 1 2 3)
       | GT ⇒ Some (
          case weigh [c⇩i] [c⇩k]
            of LT ⇒ gen_weights cs c⇩0 [c⇩k] i j (Suc k) 1 3 2
             | GT ⇒ gen_weights cs c⇩0 [c⇩i] i j (Suc k) 2 3 1
             | EQ ⇒ (
                case weigh [c⇩i, c⇩k] [c⇩j]
                  of LT ⇒ gen_weights cs c⇩0 [c⇩i, c⇩k] i j (Suc k) 1 3 1
                   | GT ⇒ gen_weights cs c⇩0 [c⇩k] i j (Suc k) 2 3 2
                   | EQ ⇒ gen_weights cs c⇩0 [c⇩j] i j (Suc k) 1 2 1))
       | EQ ⇒ determine_weights cs c⇩0 c⇩i c⇩j i j (Suc k))"

primrec find_heavier where
  "find_heavier [] c⇩0 c⇩i i j alt = None"
| "find_heavier (c⇩j # cs) c⇩0 c⇩i i j alt = (
    case weigh [c⇩i] [c⇩j]
      of LT ⇒ determine_weights cs c⇩0 c⇩i c⇩j i (Suc j) 0
       | GT ⇒ alt cs c⇩j (Suc j)
       | EQ ⇒ find_heavier cs c⇩0 c⇩i i (Suc j) alt)"

primrec weigh_coins where
  "weigh_coins [] = Some []"
| "weigh_coins (c⇩0 # cs) =
    find_heavier cs c⇩0 c⇩0 0 0
      (λcs c⇩i i. find_heavier cs c⇩0 c⇩i i 0
        (λcs c⇩j j. Some (gen_weights cs c⇩0 [c⇩i] 0 i j 3 2 1)))"

Я могу доказать, что раствор Это справедливо для конкретного случая:

definition "coins ≡ [GC, GC, SC, SC, SC, GC, GC, CC, CC, SC, CC, GC]"

value "weigh_coins coins"

lemma weigh_coins_ok:
  "cs = coins ⟹
   weigh_coins cs = Some ws ⟹
   ws = map coin_weight cs"
  by (induct cs; auto simp: coins_def weigh_def gen_weights_def std_weigh_def)

lemma weigh_coins_length_ok:
  "cs = coins ⟹
   weigh_coins cs = Some ws ⟹
   length cs = length ws"
  by (induct cs; auto simp: coins_def weigh_def gen_weights_def std_weigh_def)

Однако я не знаю, как это доказать для общего случая:

lemma weigh_coins_ok:
  "weigh_coins cs = Some ws ⟹
   ws = map coin_weight cs"
proof (induct cs)
  case Nil
  then show ?case by simp
next
  case (Cons c cs)
  then show ?case

qed

Я не могу вызвать более cs, потому что Мне нужно доказать, что

weigh_coins (c # cs) = Some ws ⟹ ∃ws. weigh_coins cs = Some ws

Это не имеет места. Я могу определить веса для [CC, SC, GC], но не могу сделать это для [SC, GC].

Альтернативный подход - доказать эти леммы для особых случаев:

[CC, CC, ...] @ [SC, SC, ...] @ [GC, GC, ...] @ ...
[CC, CC, ...] @ [GC, GC, ...] @ [SC, SC, ...] @ ...
[SC, SC, ...] @ [GC, GC, ...] @ [CC, CC, ...] @ ...
...

А затем чтобы доказать, что список дел является исчерпывающим.

Например:

lemma weigh_coins_length:
  "cs = [CC] @ replicate n CC @ [SC, GC] ⟹
   weigh_coins cs = Some ws ⟹
   length cs = length ws"
  apply (induct n arbitrary: cs ws)
  apply (auto simp: weigh_def gen_weights_def std_weigh_def)[1]

Однако я не могу доказать даже эту лемму.

Вопросы:

  1. Не могли бы вы предложить, как можно доказать такую ​​лемму или как переформулировать функции, чтобы сделать леммы доказуемыми?
  2. Как сформулировать лемму о том, что функция weigh используется самое большее n + 2 раз в алгоритм, где n количество монет?

1 Ответ

3 голосов
/ 21 февраля 2020

Некоторые общие советы:

У вас есть три рекурсивные функции: determine_weights, find_heavier, weigh_coins.

Для каждой рекурсивной функции попробуйте express соотношение между входы и результаты без использования рекурсии (вместо этого используйте квантификаторы). Свойство, которое вы доказываете для более ранних функций, должно быть достаточно сильным, чтобы доказать свойства для более поздних. Также свойство не должно фиксировать ни один из параметров. Например, find_heavier всегда изначально вызывается с j = 0, но свойство должно работать для всех значений j, чтобы его можно было использовать во время индукции.

Также попытайтесь сформулировать и доказать высокий уровень шаги в вашем описании: Например, покажите, что эта функция находит серебряную монету или две медные монеты.

По вопросу 2:

Я бы попытался изложить проблему так, чтобы ее не было. можно обмануть. Например:

datatype strategy =
    Return "coin list"
  | Weigh "nat list" "nat list" "comp ⇒ strategy"  ― ‹Weigh coins based on positions›

definition "select indexes coins ≡ map (nth coins) indexes"

fun runStrategy where
  "runStrategy coins _ (Return r) = Some r"
| "runStrategy coins 0 _ = None"
| "runStrategy coins (Suc n) (Weigh xs ys cont) = (
      if distinct xs ∧ distinct ys ∧ set xs ∩ set ys = {} then 
        runStrategy coins n (cont (weigh (select xs coins) (select ys coins)))
      else None)"

lemma "∃strategy. ∀coins. 
    length coins = 100 ∧ (∀c. c ∈ set coins)
    ⟶ runStrategy coins 101 strategy = Some coins"

Здесь runStrategy вызывает weigh не более 101 раза, и стратегия не может ничего узнать о монетах, кроме результата сравнения, переданного в продолжение Weigh.

...