Аксиомы бесконечного множества Дафни и доказательство леммы НОД

Вот цель. Я надеюсь доказать, что по двум взаимно простым числам a и b я могу показать, что существуют некоторые x и y, такие как 1 = a*x+b*y. Я скопировал реализацию НОД из KRML 279

lemma GcdLinearCombination(a: pos, b: pos)
    ensures exists x,y :: Gcd(a,b) == a*x+b*y
{

Глядя на стандартное доказательство этого факта в учебниках, доказательство использует бесконечное множество, утверждая, что некоторое наименьшее d в этом множестве является линейной комбинацией a и b, а также НОД. Он включает в себя деление a на d и демонстрацию того, что остаток r также имеет ту же форму, линейную комбинацию a и b, но поскольку d является наименьшей комбинацией этой формы, это противоречие, поэтому r должно быть равно 0. Можем ли мы действительно построить подобное доказательство в Дафни?

function combinationSet(a: pos, b: pos): set<pos> 
{
    set x: nat,y: nat | 0 <= x <= a && 0 <= y <= b && a*x+b*y > 0 :: a*x+b*y
}

function icombinationSet(a: pos, b: pos): iset<pos> 
{
    iset x: nat,y: nat | a*x+b*y > 0 :: a*x+b*y
}

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

Попытка реализовать Min для бесконечного набора терпит неудачу, потому что iset не уменьшается.

  function iMin(s: iset<pos>): pos
        requires s != iset{}
        ensures forall x | x in s :: iMin(s) in s && iMin(s) <= x
    {
        var x :| x in s;
        if s == iset{x} then
            x
        else
            var y := iMin(s - iset{x}); //infinite set size is not decreasing as expected
            assert forall z | z in s :: z == x || (z in (s - iset{x}) && y <= z);
            if x < y then x else y
    }
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
103
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Следующее, кажется, работает для бесконечных множеств.

   lemma WellOrderingPrinciple(s: iset<pos>)
        requires s != iset{}
        ensures exists min :: min in s && forall x | x in s && x != min :: min < x; 

    function iMin(s: iset<pos>): pos
        requires s != iset{}
        ensures forall x | x in s && x != iMin(s) :: iMin(s) in s ==> iMin(s) < x
    {
        WellOrderingPrinciple(s);
        var min :| min in s && forall x | x in s && x != min :: min < x; 
        min
    }
Ответ принят как подходящий

Я использовал nat вместо pos, но его легко портировать на pos.

lemma MinExist(s: set<nat>)
  requires s != {}
  ensures exists x :: x in s && forall y :: y in s ==> x <= y
{
    var x := FindMin(s);   
}

function FindMin(s: set<nat>): (min: nat)
  requires s != {}
  ensures min in s && forall y :: y in s ==> min <= y
{
  var x :| x in s;
  if s == {x} then
    x
  else
    var s' := s - {x};
    assert s == s' + {x};
    var y := FindMin(s');
    if x < y then x else y
}
 
function ISetMin(s: iset<nat>): nat
 requires s != iset{}
{
   ISetMinExists(s);
   var x :| x in s && forall y :: y in s ==> x <= y;
   x
}    

lemma ISetMinExists (s: iset<nat>)
  requires s != iset{}
  ensures exists x :: x in s && forall y :: y in s ==> x <= y
{
    var x :| x in s;
    var rs := s - iset{x};
    if rs == iset{}
    {
      forall i | i in s ensures x <= i {
         if i == x {}
         else {
          assert s == iset{x};
          assert i in iset{x};
          assert false;
         }
      }
    }
    else {
      var ss := set y | y < x ;
      var tt := set y | y in ss && y in s;
      if |tt| == 0 {
        forall i  | i in s ensures x <= i {
          if i < x {
            assert forall z :: z < x ==> z in ss;
            assert i in ss;
            assert i in tt;
          }
        }
      }
      else {
         var y := FindMin(tt);
         forall i | i in s ensures y <= i {
            if i < x {
               assert i in tt;
            }
         }
      }
    }
}

Я не закончил, но вот пробный набросок. Обратите внимание, что я усилил постусловие iMin, и есть несколько предположений, которые необходимо доказать. Надеюсь это поможет !

lemma WellOrderingPrinciple(s: iset<nat>)
    requires s != iset{}
    ensures exists min :: min in s && forall x | x in s && x != min :: min < x; 

function iMin(s: iset<nat>): nat
    requires s != iset{}
    // Change of the postcondition to be stronger
    ensures iMin(s) in s  && forall x | x in s && x != iMin(s) :: iMin(s) < x
{
    WellOrderingPrinciple(s);
    var min :| min in s && forall x | x in s && x != min :: min < x; 
    min
}

predicate IsGCD(a: nat, b: nat, g: nat) {
  g > 0 && (a % g) == 0 && (b % g) == 0 &&
  (forall x: nat | x > 0 && a % x == 0 && b % x == 0 ::
   x <= g
  )
}

function euclidianDivision(a: nat, d: nat): (result: (nat, nat))
  requires d > 0
  ensures result.0 == (a - (a % d))/d
  ensures result.1 == a % d
  ensures
    calc {
      result.0 * d + result.1;
      { assert result.0 == (a - (a % d))/d; }
      ((a - (a % d))/d)*d + result.1;
      ((a - (a % d))/d)*d + (a % d);
      { assume (a - (a % d)) % d == 0; } // TODO: Prove this
      a - (a % d) + (a % d);
      a;
    }
    result.0 * d + result.1 == a && 0 <= result.1 < d
{
  ((a - (a % d))/d, a % d)
}



lemma GcdLinearCombination(a: nat, b: nat, g: nat)
    requires IsGCD(a, b, g)
    requires a > 0 && b > 0
    ensures exists x,y :: g == a*x+b*y
{
  var allCombinations :=
    iset x: int, y: int | a*x+b*y > 0 :: var j: nat := a*x+b*y; j;
  assert allCombinations != iset{} by {
    var x := 1;
    var y := 1;
    assert a*x+b*y > 0;
    var j: nat := a*x+b*y;
    assert j in allCombinations;
  }
  var d := iMin(allCombinations);
  // Definition of d. Place where you need.
  //assert d in allCombinations && (forall x <- allCombinations | x != d :: d < x);

  assert d == g by {
    assert exists x: int, y: int :: a*x+b*y == d;
    var x, y :| a*x+b*y == d;
    assert forall x | x in allCombinations && x != d :: d in allCombinations ==> d < x;
    assert d > 0;
    
    if a % d != 0 {
      var (q, r) := euclidianDivision(a, d);
      assert r != 0;
      assert q * d + r == a;
      var x' := 1-q*x;
      var y': int := 0 - q*y;
      calc <==> {
        a == q * d + r;
        a == q * (a*x + b*y) + r;
        a - q * (a*x + b*y) == r;
        r == a - q * (a*x + b*y);
        r == a - q*(a*x) - q*(b*y);
        r == a - q*(x*a) - q*(y*b);
        r == a - (q*x)*a - (q*y)*b;
        r == (1 - (q*x))*a - (q*y)*b;
        r == x' * a + y' * b;
        r == a* x' + b * y';
      }
      var j: nat := a* x' + b * y';
      assert j in allCombinations;
      assert j == r;
      assert false;
    }
    if b % d != 0 {
      var (q, r) := euclidianDivision(b, d);
      assert r != 0;
      assert q * d + r == b;
      var y' := 1-q*y;
      var x': int := 0 - q*x;
      calc <==> {
        b == q * d + r;
        b == q * (a*x + b*y) + r;
        b == q * (b*y + a*x) + r;
        b - q * (b*y + a*x) == r;
        r == b - q * (b*y + a*x);
        r == b - q*(b*y) - q*(a*x);
        r == b - q*(b*y) - q*(a*x);
        r == b - (q*y)*b - (q*x)*a;
        r == (1 - (q*y))*b - (q*x)*a;
        r == y' * b + x' * a;
        r == b* y' + a * x';
        r == a * x' + b * y';
      }
      var j: nat := a* x' + b * y';
      assert j in allCombinations;
      assert j == r;
      assert false;
    }
    // Ok, so now we proved that d divides a and d divides b
    assert (a % d) == 0 && (b % d) == 0;
    var (x', r0) := euclidianDivision(a, d);
    var (y', r1) := euclidianDivision(b, d);
    assert x' * d == a;
    assert y' * d == b;
    // Now, is it the biggest?
    assert (forall x: nat | x > 0 && a % x == 0 && b % x == 0 :: x <= d) by {
      forall x: nat | x > 0 && a % x == 0 && b % x == 0 
        ensures x <= d
      {
        assume false; // TODO
      }
    } 
  }
  assert g in allCombinations;
}

Я не знал, что вы можете сделать расчет в операторе обеспечения в евклидовом дивизионе. Я предполагаю, что это означает, что вы также можете вызвать лемму в гарантии?

Hath995 18.01.2023 00:33

Да, действительно, вы можете использовать вызов леммы перед любым выражением, используя ";"; однако убедитесь, что вы заключаете все результирующее выражение в круглые скобки, чтобы избежать ";" досрочно закрыть пункт о гарантиях или любой другой пункт, например. function double(i: int): (r: int) ensures (thelemma(); r % 2 == 0) { thelemma(); 4*(thelemma(); i)+2 }

Mikaël Mayer 18.01.2023 00:46

Другие вопросы по теме