Aula 8 Metodologia de Dijkstra para desenvolvimento de ciclos Ciclos Parte importante da escrita de algoritmos Impossível demonstrar correcção com testes Condições invariantes importantes para demonstrar correcção 2 Verdadeiras do início ao fim dos ciclos Fortes! Referem todas instâncias envolvidas Introdução à Programação 2003/2004 potênciaDe() /** Devolve a potência n de x. @pre 0 ≤ n. @post potênciaDe = xn. */ double potênciaDe(double const x, int const n) { assert(0 <= n); int i = 0; double potência = 1.0; while(i != n) { potência *= x; x; potência *= ++i; ++i; } Inicialização Acção Passo Progresso return potência; } 3 Introdução à Programação 2003/2004 Diagrama de actividade inic {PC: 0 ≤ n} int i = 0; double potência = 1.0; [i = n] {0 ≤ n 0 ≤ i < n potência = xi} acção {0 ≤ n 0 < i ≤ n potência = xi} {0 ≤ n i = 0 potência = 1} passo [i ≠ n] potência *= x; {0 ≤ n i = n potência = xn} ++i; prog 4 Introdução à Programação 2003/2004 Condição invariante CI: 0 ≤ i ≤ n potência = xi. Condição invariante explica funcionamento do ciclo 5 Durante todo o ciclo potência contém xi, estando sempre i entre 0 e n Como i varia entre 0 e n, e termina com n, o objectivo é atingido Introdução à Programação 2003/2004 Metodologia de Dijkstra Edsger Dijkstra Fundador da programação disciplinada Programação é uma ciência Metodologia Baseia-se na condição objectivo • Programação é actividade orientada pelos objectivos 6 Condição invariante retirada da condição objectivo Introdução à Programação 2003/2004 Soma dos primeiros n ímpares inteiros int somaDosPrimeirosÍmpares(int const n) { int r = ?; ... return r; } 7 Introdução à Programação 2003/2004 Raiz inteira int raizInteiraDe(int const x) { int r = ?; ... return r; } 8 Introdução à Programação 2003/2004 1 Especificar o problema Escrever o Pré-condição o Condição objectivo somaDosPrimeirosÍmpares() /** Devolve a soma dos primeiros n naturais ímpares. @pre 0 ≤ n. @post somaDosPrimeirosÍmpares = (S j : 0 ≤ j < n : 2j + 1). */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int r = ?; ... // CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 10 Introdução à Programação 2003/2004 raizInteiraDe() /** Devolve a melhor aproximação por defeito de x1/2. @pre 0 ≤ x. @post 0 ≤ raizInteiraDe ≤ x1/2 < raizInteiraDe + 1. */ int raizInteiraDe(int const x) { assert(0 <= x); // 0 ≤ x. int r = ?; ... // CO: 0 ≤ r ≤ x1/2 < r + 1. assert(0 <= r and r * r <= x and x < (r + 1) * (r + 1)); return r; } 11 Introdução à Programação 2003/2004 2 Será necessário um ciclo? Verificar se ciclo é melhor abordagem o Análise do problema o Intuição! o Experiência… somaDosPrimeirosÍmpares() /** Devolve a soma dos primeiros n naturais ímpares. @pre 0 ≤ n. @post somaDosPrimeirosÍmpares = (S j : 0 ≤ j < n : 2j + 1). */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int r = ?; while(...) { ... } // CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 13 Introdução à Programação 2003/2004 raizInteiraDe() /** Devolve a melhor aproximação por defeito de x1/2. @pre 0 ≤ x. @post 0 ≤ raizInteiraDe ≤ x1/2 < raizInteiraDe + 1. */ int raizInteiraDe(int const x) { assert(0 <= x); // 0 ≤ x. int r = ?; while(...) { ... } // CO: 0 ≤ r ≤ x1/2 < r + 1. assert(0 <= r and r * r <= x and x < (r + 1) * (r + 1)); return r; } 14 Introdução à Programação 2003/2004 3 Obtenção da condição invariante Programar é actividade orientada pelos objectivos o Enfraquecer a CO para obter a CI somaDosPrimeirosÍmpares() Substituição de constante na CO por variável inic int i = ?; int r = ?; Não é equivalente a CO. CO: r = (S j : 0 ≤ j < n : 2j + 1). r = (S j : 0 ≤ j < i : 2j + 1). CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. 16 Introdução à Programação 2003/2004 somaDosPrimeirosÍmpares() /** Devolve a soma dos primeiros n naturais ímpares. @pre 0 ≤ n. @post somaDosPrimeirosÍmpares = (S j : 0 ≤ j < n : 2j + 1). */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int i = ?; int r = ?; while(...) { ... } // CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. // que implica CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 17 Introdução à Programação 2003/2004 somaDosPrimeirosÍmpares() CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. Enfraquecendo as restrições relativas a i CI: r = (S j : 0 ≤ j < i : 2j + 1) ? ≤ i ≤ ?. CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. 18 Introdução à Programação 2003/2004 somaDosPrimeirosÍmpares() /** Devolve a soma dos primeiros n naturais ímpares. @pre 0 ≤ n. @post somaDosPrimeirosÍmpares = (S j : 0 ≤ j < n : 2j + 1). */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int i = ?; int r = ?; // CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. while(...) { ... } // CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. // que implica CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 19 Introdução à Programação 2003/2004 raizInteiraDe() ABCD Factorização da condição objectivo Parte é negação da guarda Parte restante é condição invariante A CO: A B C D CI: A C B AC D C Sabendo que no final do ciclo se quer CI ¬G CO ¬G: B D 20 Logo, CI ¬G = CO Introdução à Programação 2003/2004 raizInteiraDe() CO: 0 ≤ r ≤ x1/2 < r + 1, ou seja 0 ≤ r r2 ≤ x x < (r + 1)2 . Começando a procurar em 0 (zero) e terminando quando se sabe que o valor seguinte já não serve… CI: 0 ≤ r r2 ≤ x. ¬G: x < (r + 1)2. 21 Introdução à Programação 2003/2004 raizInteiraDe() /** Devolve a melhor aproximação por defeito de x1/2. @pre 0 ≤ x. @post 0 ≤ raizInteiraDe ≤ x1/2 < raizInteiraDe + 1. */ int raizInteiraDe(int const x) { assert(0 <= x); // 0 ≤ x. int r = ?; // CI: 0 ≤ r r2 ≤ x. while(...) { ... } // CO: 0 ≤ r ≤ x1/2 < r + 1. assert(0 <= r and r * r <= x and x < (r + 1) * (r + 1)); return r; } 22 Introdução à Programação 2003/2004 4 Determinação da guarda Procurar guarda que leve ao resultado o CI ¬G CO somaDosPrimeirosÍmpares() CI ¬G: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n ¬G. CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. Qual a guarda mais simples que garante a verificação da CO? ¬G: i = n. ou seja, G: i ≠ n. CI ¬G: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n i = n. CI ¬G: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ n i = n. que implica CO. 24 Introdução à Programação 2003/2004 somaDosPrimeirosÍmpares() /** Devolve a soma dos primeiros n naturais ímpares. @pre 0 ≤ n. @post somaDosPrimeirosÍmpares = (S j : 0 ≤ j < n : 2j + 1). */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int i = ?; int r = ?; // CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. while(i != n) { ... } // CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. // que implica CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 25 Introdução à Programação 2003/2004 raizInteiraDe() CO: 0 ≤ r r2 ≤ x x < (r + 1)2 . CI: 0 ≤ r r2 ≤ x. Logo, sabendo que CI ¬G = CO ¬G: x < (r + 1)2 ou seja, G: (r + 1)2 ≤ x. 26 Introdução à Programação 2003/2004 raizInteiraDe() /** Devolve a melhor aproximação por defeito de x1/2. @pre 0 ≤ x. @post 0 ≤ raizInteiraDe ≤ x1/2 < raizInteiraDe + 1. */ int raizInteiraDe(int const x) { assert(0 <= x); // 0 ≤ x. int r = ?; // CI: 0 ≤ r r2 ≤ x. while((r + 1) * (r + 1) <= x) { ... } // CO: 0 ≤ r ≤ x1/2 < r + 1. assert(0 <= r and r * r <= x and x < (r + 1) * (r + 1)); return r; } 27 Introdução à Programação 2003/2004 5 Determinação da inicialização Escolher inicialização tão simples quanto possível: o Sabendo que PC é verdadeira o Garantir verificação da CI somaDosPrimeirosÍmpares() /** Devolve a soma dos primeiros n naturais ímpares. @pre 0 ≤ n. @post somaDosPrimeirosÍmpares = (S j : 0 ≤ j < n : 2j + 1). */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int i = ?; int r = ?; // CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. while(i != n) { ... } // CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. // que implica CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 29 Introdução à Programação 2003/2004 somaDosPrimeirosÍmpares() /** Devolve a soma dos primeiros n naturais ímpares. @pre 0 ≤ n. @post somaDosPrimeirosÍmpares = (S j : 0 ≤ j < n : 2j + 1). */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int i = 0; int r = 0; // CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. while(i != n) { ... } // CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. // que implica CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 30 Introdução à Programação 2003/2004 raizInteiraDe() /** Devolve a melhor aproximação por defeito de x1/2. @pre 0 ≤ x. @post 0 ≤ raizInteiraDe ≤ x1/2 < raizInteiraDe + 1. */ int raizInteiraDe(int const x) { assert(0 <= x); // 0 ≤ x. int r = ?; // CI: 0 ≤ r r2 ≤ x. while((r + 1)*(r + 1) <= x) { ... } // CO: 0 ≤ r ≤ x1/2 < r + 1. assert(0 <= r and r * r <= x and x < (r + 1) * (r + 1)); return r; } 31 Introdução à Programação 2003/2004 raizInteiraDe() /** Devolve a melhor aproximação por defeito de x1/2. @pre 0 ≤ x. @post 0 ≤ raizInteiraDe ≤ x1/2 < raizInteiraDe + 1. */ int raizInteiraDe(int const x) { assert(0 <= x); // 0 ≤ x. int r = 0; // CI: 0 ≤ r r2 ≤ x. while((r + 1)*(r + 1) <= x) { ... } // CO: 0 ≤ r ≤ x1/2 < r + 1. assert(0 <= r and r * r <= x and x < (r + 1) * (r + 1)); return r; } 32 Introdução à Programação 2003/2004 6 Escolha de um progresso Um ciclo tem de terminar o Num número finito de passos o Rapidamente? Pode valer a pena investigar progressos complexos somaDosPrimeirosÍmpares() /** ... */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int i = 0; int r = 0; // CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. while(i != n) { acção ++i; } // CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. // que implica CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 34 Introdução à Programação 2003/2004 raizInteiraDe() /** ... */ int raizInteiraDe(int const x) { assert(0 <= x); // 0 ≤ x. int r = 0; // CI: 0 ≤ r r2 ≤ x. while((r + 1)*(r + 1) <= x) { acção ++r; } // CO: 0 ≤ r ≤ x1/2 < r + 1. assert(0 <= r and r * r <= x and x < (r + 1) * (r + 1)); return r; } 35 Introdução à Programação 2003/2004 7 Determinação da acção Escolher acção tal que // CI e G. acção prog // CI. Garante veracidade de CI apesar do progresso somaDosPrimeirosÍmpares() // CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. while(i != n) { // CI G: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n i ≠ n. // CI G: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i < n. acção // r = (S j : 0 ≤ j < i + 1 : 2j + 1) 0 ≤ i + 1 ≤ n. // r = (S j : 0 ≤ j < i + 1 : 2j + 1) -1 ≤ i < n. ++i; // CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. } Falta um termo ao somatório. 37 Introdução à Programação 2003/2004 somaDosPrimeirosÍmpares() /** ... */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); // 0 ≤ n. int i = 0; int r = 0; // CI: r = (S j : 0 ≤ j < i : 2j + 1) 0 ≤ i ≤ n. while(i != n) { r += 2 * i + 1; ++i; } // CO’: r = (S j : 0 ≤ j < i : 2j + 1) i = n. // que implica CO: r = (S j : 0 ≤ j < n : 2j + 1). return r; } 38 Introdução à Programação 2003/2004 raizInteiraDe() // CI: 0 ≤ r r2 ≤ x. while((r + 1)*(r + 1) <= x) { // CI G: 0 ≤ r r2 ≤ x (r + 1) 2 ≤ x. // CI G: 0 ≤ r (r + 1) 2 ≤ x. acção // 0 ≤ r + 1 (r + 1) 2 ≤ x. // -1 ≤ r (r + 1) 2 ≤ x. ++r; // CI: 0 ≤ r r2 ≤ x. Não é necessária } acção! 39 Introdução à Programação 2003/2004 raizInteiraDe() /** ... */ int raizInteiraDe(int const x) { assert(0 <= x); // 0 ≤ x. int r = 0; // CI: 0 ≤ r r2 ≤ x. while((r + 1)*(r + 1) <= x) ++r; // CO: 0 ≤ r ≤ x1/2 < r + 1. assert(0 <= r and r * r <= x and x < (r + 1) * (r + 1)); return r; } 40 Introdução à Programação 2003/2004 8 E se alguma coisa falhar? Voltar atrás acção ou inic demasiado complexas CI menos boa ... Será que na soma dos ímpares o ciclo é necessário? Inteiros na base 1 1: * 2: ** 3: *** Impares 1: 3: 5: 4: **** 5: ***** ... 42 7: Soma dos primeiros 4 ímpares * 1+3+5+7 * ** * * *** = * * * * **** Introdução à Programação 2003/2004 Será que na soma dos ímpares o ciclo é necessário? Inteiros na base 1 1: * 2: ** 3: *** Impares 1: 3: 5: 4: **** 5: ***** ... 43 7: Soma dos primeiros 4 ímpares * 1+3+5+7 * ** = * * *** ** ** * * * **** Introdução à Programação 2003/2004 Será que na soma dos ímpares o ciclo é necessário? Inteiros na base 1 1: * 2: ** 3: *** Impares 1: 3: 5: 4: **** 5: ***** ... 44 7: Soma dos primeiros 4 ímpares * 1+3+5+7 * ** * * *** = *** *** *** * * * **** Introdução à Programação 2003/2004 Será que na soma dos ímpares o ciclo é necessário? Inteiros na base 1 1: * 2: ** 3: *** Impares 1: 3: 5: 4: **** 5: ***** ... 45 7: Soma dos primeiros 4 ímpares * 1+3+4+7 * ** * * *** * * * **** Introdução à Programação = **** **** **** **** 2003/2004 somaDosPrimeirosÍmpares() /** Devolve a soma dos primeiros n naturais ímpares. @pre 0 ≤ n. @post somaDosPrimeirosÍmpares = (S j : 0 ≤ j < n : 2j + 1). */ int somaDosPrimeirosÍmpares(int const n) { assert(0 <= n); return n * n; } 46 Introdução à Programação 2003/2004 Aula 8: Sumário Importância da especificação do problema Noção de invariante de um ciclo Condição invariante, inicialização e guarda de um ciclo Metodologia de Dijkstra para desenvolvimento de ciclos: Obtenção da condição invariante Obtenção da guarda Inicialização Progresso Acção Importância da metodologia: Base formal para desenvolvimento Controlo dos erros Redução de surpresas Melhor raciocínio Maior eficiência Prática da construção de ciclos: usar ou não usar metodologia? 47 Introdução à Programação 2003/2004