AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 76924944
Accepted
Carl
Carl
Asked: 2023-08-18 04:33:43 +0800 CST2023-08-18 04:33:43 +0800 CST 2023-08-18 04:33:43 +0800 CST

Com Dafny, verifique a função para contar elementos do conjunto de inteiros menores que um limite

  • 772

Estou aberto a qualquer forma de escrever CountLessThan que seja verificável. Aqui está o que eu tenho:

function SetLessThan(numbers: set<int>, threshold: int): set<int>
{
  set i | i in numbers && i < threshold
}
method CountLessThan(numbers: set<int>, threshold: int) returns (count: int)
  ensures count == |SetLessThan(numbers, threshold)|
{
  count := 0;
  var shrink := numbers;
  var grow := {};
  while |shrink | > 0
    decreases shrink
    invariant shrink + grow == numbers
    invariant count == |SetLessThan(grow, threshold)|
  {
    var i: int :| i in shrink;
    shrink := shrink - {i};
    grow := grow + {i};
    if i < threshold {
      count := count + 1;
    }
  }
}

method Main()
{
  var s: set<int> := {1, 2, 3, 4, 5};
  var c: int := CountLessThan(s, 4);
  print c;
  // assert c == 3;

}

Este é um pequeno passo para definir um conjunto classificado, depois um mapa classificado e, por fim, um conjunto de intervalos.

dafny
  • 1 1 respostas
  • 14 Views

1 respostas

  • Voted
  1. Best Answer
    James Wilcox
    2023-08-18T05:35:11+08:002023-08-18T05:35:11+08:00

    Você tem um exercício clássico de depuração de verificação pela frente :)

    A abordagem básica para a depuração de verificação é adicionar assertinstruções para restringir onde está o seu problema.

    Escrevi uma transcrição completa de uma sessão de depuração idealizada. Sinta-se à vontade para pular para o final se quiser apenas o código funcional.

    O código em sua postagem relata o seguinte erro na invariante do loop:

    while |shrink | > 0
      decreases shrink
      invariant shrink + grow == numbers
    
      invariant count == |SetLessThan(grow, threshold)|
        // Error: might not be maintained
    {
      var i: int :| i in shrink;
      shrink := shrink - {i};
      grow := grow + {i};
      if i < threshold {
        count := count + 1;
      }
    }
    

    Isso significa que Dafny não conseguiu restabelecer a invariante na parte inferior do loop. Vamos converter isso em uma afirmação.

    while |shrink | > 0
      decreases shrink
      invariant shrink + grow == numbers
      invariant count == |SetLessThan(grow, threshold)|
    {
      var i: int :| i in shrink;
      shrink := shrink - {i};
      grow := grow + {i};
      if i < threshold {
        count := count + 1;
      }
      assert count == |SetLessThan(grow, threshold)|;  // Error: might not hold
    }
    

    Na verdade, não fizemos nada, mas observe que o erro na invariante não é mais relatado, portanto, há apenas um total de erros. Isso é Dafny dizendo "se você pudesse provar a afirmação, o restante do código também seria verificado".

    Agora começamos a mover a afirmação para trás no loop. A maneira mais fácil de movê-lo para uma instrução if unilateral é introduzir artificialmente uma elseramificação vazia e, em seguida, fazer uma cópia da afirmação em ambos os lados.

    while |shrink | > 0
      decreases shrink
      invariant shrink + grow == numbers
      invariant count == |SetLessThan(grow, threshold)|
    {
      var i: int :| i in shrink;
      shrink := shrink - {i};
      grow := grow + {i};
      if i < threshold {
        count := count + 1;
        assert count == |SetLessThan(grow, threshold)|;  // Error: might not hold
      } else {
        assert count == |SetLessThan(grow, threshold)|;  // Error: might not hold
      }
      assert count == |SetLessThan(grow, threshold)|;
    }
    

    Observe que o erro na parte inferior assertdesapareceu. Temos dois erros, mas se pudermos corrigi-los, estaremos resolvidos. Estamos progredindo.

    Na primeira ramificação, agora movemos a assertparte de trás da atribuição. Para fazer isso, substituímos as ocorrências de countpor count + 1em assert:

    while |shrink | > 0
      decreases shrink
      invariant shrink + grow == numbers
      invariant count == |SetLessThan(grow, threshold)|
    {
      var i: int :| i in shrink;
      shrink := shrink - {i};
      grow := grow + {i};
      if i < threshold {
        assert count + 1 == |SetLessThan(grow, threshold)|; // Error: might not hold
        count := count + 1;
        assert count == |SetLessThan(grow, threshold)|;
      } else {
        assert count == |SetLessThan(grow, threshold)|;  // Error: might not hold
      }
      assert count == |SetLessThan(grow, threshold)|;
    }
    

    Observe ainda apenas dois erros.

    Agora precisamos extrair o assert do topo do arquivo if. A maneira mais fácil de fazer isso é com uma ifexpressão -.

    while |shrink | > 0
      decreases shrink
      invariant shrink + grow == numbers
      invariant count == |SetLessThan(grow, threshold)|
    {
      var i: int :| i in shrink;
      shrink := shrink - {i};
      grow := grow + {i};
      assert if i < threshold
        then (count + 1 == |SetLessThan(grow, threshold)|)  // Error
        else count == |SetLessThan(grow, threshold)|;  // Error
      if i < threshold {
        assert count + 1 == |SetLessThan(grow, threshold)|;
        count := count + 1;
        assert count == |SetLessThan(grow, threshold)|;
      } else {
        assert count == |SetLessThan(grow, threshold)|;
      }
      assert count == |SetLessThan(grow, threshold)|;
    }
    

    Dafny ainda mostra erros separados para os dois ramos, mas agora todos fazem parte de uma assertdeclaração novamente, mais progresso.

    Em seguida, voltamos na atribuição para grow. Substituímos grow + {i}no growassert.

    while |shrink | > 0
      decreases shrink
      invariant shrink + grow == numbers
      invariant count == |SetLessThan(grow, threshold)|
    {
      var i: int :| i in shrink;
      shrink := shrink - {i};
    
      assert if i < threshold
        then (count + 1 == |SetLessThan(grow + {i}, threshold)|)
        else count == |SetLessThan(grow + {i}, threshold)|;
      // Error: might not hold
    
      grow := grow + {i};
      assert if i < threshold
        then (count + 1 == |SetLessThan(grow, threshold)|)
        else count == |SetLessThan(grow, threshold)|;
      if i < threshold {
        assert count + 1 == |SetLessThan(grow, threshold)|;
        count := count + 1;
        assert count == |SetLessThan(grow, threshold)|;
      } else {
        assert count == |SetLessThan(grow, threshold)|;
      }
      assert count == |SetLessThan(grow, threshold)|;
    }
    

    Nós movemos o erro novamente com sucesso.

    Poderíamos continuar retrocedendo na shrinkatribuição, mas como não é relevante para esta afirmação, não importa, então vamos ficar aqui.

    Neste ponto, eliminamos todas as "coisas imperativas" no problema de verificação e só temos "coisas lógicas" com as quais nos preocupar. Vamos provar esta afirmação.

    Primeiro, podemos reescrever a afirmação para retirar ==o arquivo if-then-else. Coloque isso assertacima do anterior.

    assert (if i < threshold then count + 1 else count) ==
      |SetLessThan(grow + {i}, threshold)|;
    

    Este é agora o nosso único erro, por isso ainda estamos progredindo. Dafny provará o resto se pudermos provar isso.

    Podemos retirar a ocorrência de counttambém. Coloque esta afirmação acima da anterior.

    assert count + (if i < threshold then 1 else 0) == 
      |SetLessThan(grow + {i}, threshold)|;
    

    Vamos provar isso com um calcbloco de ulação. Coloque este bloco acima da afirmação anterior.

    calc {
      count + (if i < threshold then 1 else 0);
      |SetLessThan(grow + {i}, threshold)|;  // Error: might not equal previous line
    }
    

    Temos um loop invariante sobre count, então vamos substituí-lo em.

    calc {
      count + (if i < threshold then 1 else 0);
      |SetLessThan(grow, threshold)| + (if i < threshold then 1 else 0);
      |SetLessThan(grow + {i}, threshold)|;  // Error
    }
    

    Ainda apenas um erro, então Dafny concorda com a primeira etapa do nosso cálculo.

    Para preencher a lacuna entre a segunda e a terceira linhas, precisamos conectar os conjuntos SetLessThan(grow + {i}, threshold)e SetLessThan(grow, threshold). Como eles estão conectados? Bem, se i < thresholdentão o primeiro é o último mais i, caso contrário, eles são iguais. Vamos dizer isso.

    assert SetLessThan(grow + {i}, threshold) ==
      SetLessThan(grow, threshold) + (if i < threshold then {i} else {});
    calc {
      count + (if i < threshold then 1 else 0);
      |SetLessThan(grow, threshold)| + (if i < threshold then 1 else 0);
      |SetLessThan(grow + {i}, threshold)|;  // Error
    }
    

    Observe que não há erro no assert no topo! Portanto, Dafny gosta de nossa igualdade definida, mas ainda não pode provar o fato de cardinalidade.

    Vamos tentar preencher a lacuna entre a segunda e a terceira linhas puxando o operador de cardinalidade para fora do sinal de mais.

    calc {
      count + (if i < threshold then 1 else 0);
      |SetLessThan(grow, threshold)| + (if i < threshold then 1 else 0);
      |SetLessThan(grow, threshold) + (if i < threshold then {i} else {})|;  // Error: might not equal previous line
      |SetLessThan(grow + {i}, threshold)|;
    }
    

    Ah! O erro mudou. Agora, a penúltima e a penúltima linhas são iguais, mas as duas linhas do meio não. O que da?

    Bem, a cardinalidade de uma união nem sempre é a soma das cardinalidades. Isso só é verdade se eles forem disjuntos. Esses conjuntos são disjuntos? Sim! Por que? porque saímos ido psiquiatra e growsomos shrinkdisjuntos. Mas Dafny não pode dizer porque precisamos de outro loop invariante.

    Em Dafny, A !! Bsignifica Ae Bsão disjuntos.

    Adicionamos este loop invariante:

    invariant grow !! shrink
    

    Todos os erros sumiram!!! Wooo. Nós celebramos.

    E agora nós limpamos. Adicionamos uma tonelada de afirmações desnecessárias que agora podemos excluir e descobrir o que realmente importa. Apenas tente excluir coisas e ver o que ainda verifica.

    Acontece que as únicas coisas que importam são o loop invariante e um assert. Aqui está o loop final

    while |shrink | > 0
      decreases shrink
      invariant shrink + grow == numbers
      invariant grow !! shrink
      invariant count == |SetLessThan(grow, threshold)|
    {
      var i: int :| i in shrink;
      shrink := shrink - {i};
      assert SetLessThan(grow + {i}, threshold) ==
        SetLessThan(grow, threshold) + if i < threshold then {i} else {};
      grow := grow + {i};
      if i < threshold {
        count := count + 1;
      } 
    }
    
    • 3

relate perguntas

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    destaque o código em HTML usando <font color="#xxx">

    • 2 respostas
  • Marko Smith

    Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}?

    • 1 respostas
  • Marko Smith

    Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)?

    • 2 respostas
  • Marko Smith

    Por que as compreensões de lista criam uma função internamente?

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 respostas
  • Marko Smith

    Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)?

    • 4 respostas
  • Marko Smith

    Por que o construtor de uma variável global não é chamado em uma biblioteca?

    • 1 respostas
  • Marko Smith

    Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto?

    • 1 respostas
  • Marko Smith

    Somente operações bit a bit para std::byte em C++ 17?

    • 1 respostas
  • Martin Hope
    fbrereto Por que a resolução de sobrecarga prefere std::nullptr_t a uma classe ao passar {}? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 Você pode usar uma lista de inicialização com chaves como argumento de modelo (padrão)? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi Por que as compreensões de lista criam uma função internamente? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A formato fmt %H:%M:%S sem decimais 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python std::views::filter do C++20 não filtrando a visualização corretamente 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute Por que 'char -> int' é promoção, mas 'char -> short' é conversão (mas não promoção)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa Por que o construtor de uma variável global não é chamado em uma biblioteca? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis Comportamento inconsistente de std::common_reference_with em tuplas. Qual é correto? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev Por que os compiladores perdem a vetorização aqui? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan Somente operações bit a bit para std::byte em C++ 17? 2023-08-17 17:13:58 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve