• Nenhum resultado encontrado

Calculando d´ıgito verificador

No documento Algoritmos e Estruturas de Dados I (páginas 116-121)

9.3 Alguns exemplos

9.3.1 Calculando d´ıgito verificador

Vamos fazer um programa que recebe um n´umero de N d´ıgitos, sendo o ´ultimo deles o “d´ıgito verificador” do n´umero formado pelosN−1 primeiros. Devemos calcular se o d´ıgito verificador fornecido pelo usu´ario est´a correto segundo o esquema de c´alculo seguinte: cada d´ıgito do n´umero, come¸cando da direita para a esquerda (menos sig-nificativo para o mais sigsig-nificativo) ´e multiplicado, na ordem, por 1, depois 2, depois 1, depois 2 e assim sucessivamente. O n´umero de entrada do exemplo ´e 261533-4.

+---+---+---+---+---+---+ +---+

| 2 | 6 | 1 | 5 | 3 | 3 | - | 4 | +---+---+---+---+---+---+ +---+

| | | | | |

x2 x1 x2 x1 x2 x1

| | | | | |

=4 =6 =2 =5 =6 =3

+---+---+---+---+---+-> = 26

Como 26 tem dois d´ıgitos, vamos repetir o processo acima at´e gerarmos um n´umero de um ´unico d´ıgito. Assim:

+---+---+

| 2 | 6 | +---+---+

| | x2 x1

| |

=4 =6 +---+ = 10

Como 10 ainda tem dois d´ıgitos, o algoritmo roda ainda mais uma vez:

+---+---+

| 1 | 0 | +---+---+

| | x2 x1

| |

=2 =0 +---+ = 2

Assim, o d´ıgito verificador calculado (2) difere daquele fornecido (4) e o programa deveria acusar o erro. O programa da figura 9.7 ilustra uma poss´ıvel solu¸c˜ao.

9.3. ALGUNS EXEMPLOS 117

program digitoverificador ; var numero, n: longint ;

dv, dv correto : integer; procedure l e (var n: longint ) ;

procedure separa numero e dv (n: longint ; var num: longint ; var dv: integer) ; begin

num:= n div 10;

dv:= nmod 10;

end;

function calcula dv correto (n: longint ) : integer; var soma, mult , ultimo : integer;

begin

separa numero e dv (numero, n, dv) ; dv correto:= calcula dv correto (n) ; i f dv correto <>dv then

writeln (’digito verificador invalido.’) end.

Figura 9.7: Calculando d´ıgito verificador.

O importante para se observar neste c´odigo ´e a clareza do algoritmo no programa principal. O leitor pode acompanhar este trecho e perceber claramente as diversas etapas em uma linguagem de bastante alto n´ıvel: leitura do n´umero, separa¸c˜ao deste em duas partes, uma contendo os primeiros d´ıgitos a outra contendo o dv de entrada.

Em seguida o c´alculo do d´ıgito verificador correto e finalmente a compara¸c˜ao dos dados calculados com o de entrada, gerando a mensagem final.

No programa principal pode-se ignorar completamente como s˜ao feitas todas as opera¸c˜oes nas fun¸c˜oes e procedumentos: n˜ao importa como os dados s˜ao lidos, nem como os d´ıgitos s˜ao separados, e muito menos como ´e feito o c´alculo do d´ıgito verifi-cador correto. No programa principal o importante ´e o algoritmo em alto n´ıvel.

E claro que em algum momento ser´´ a necess´ario escrever c´odigo para cada uma das fun¸c˜oes e procedimentos, mas quando isto for feito o programador estar´a resolvendo um subproblema de cada vez, o que facilita muito a constru¸c˜ao do c´odigo para o problema global.

Por exemplo, a leitura poderia ser feita em uma interface gr´afica ou textual. Foi es-colhida nesta vers˜ao uma interface textual, mas que permite testes de consistˆencia dos dados de entrada. Isto, feito separadamente, mant´em o c´odigo global independente.

No caso, o programador usou diretivas do compilador para alterar o comportamento padr˜ao que aborta o programa se o usu´ario digitar um tipo n˜ao esperado. Trata corre-tamente o erro e quando tudo est´a certo, termina o procedimento. Importante notar que, para leitura de dados, o parˆametro tem que ser passado por referˆencia, e n˜ao por valor, sen˜ao o valor seria lido em uma c´opia da vari´avel do programa principal.

Considera¸c˜oes similares s˜ao feitas para a outra fun¸c˜ao e o procedimento, ambos possuem c´odigo pr´oprio e fazem com que a aten¸c˜ao do programador fique voltada exclusivamente para o subproblema da vez. Desde que o prot´otipo da fun¸c˜ao ou pro-cedimento esteja corretamente especificado, n˜ao h´a problema em se alterar o c´odigo interno. Notamos tamb´em que no caso da fun¸c˜ao, foram usadas vari´aveis locais para aux´ılio no c´alculo do d´ıgito verificador. Tudo para o bem da clareza do c´odigo prin-cipal. Se um dia mudarem a defini¸c˜ao de como se calcula o d´ıgito verificador, basta alterar a fun¸c˜ao que o programa principal continuar´a a funcionar corretamente.

Na ´ultima fun¸c˜ao ´e importante notar a passagem de parˆametros por valor. Isto permitiu o la¸co que controla o loop interno usar o pr´oprio parˆametro como condi¸c˜ao de parada, pois ele ´e dividido por 10 a cada itera¸c˜ao. Se o parˆametro fosse passado por referˆencia isto n˜ao poderia ser feito, pois estar´ıamos estragando o valor da vari´avel que ser´a ainda usada no programa principal.

Ainda uma ´ultima observa¸c˜ao, no procedimento que separa o n´umero de seu d´ıgito verificador, atentar para a sintaxe, emPascal, que se usa quando se quer passar dois parˆametros do mesmo tipo, um por valor e outro por referˆencia. No caso, eles devem ser separados pelo s´ımbolo do ponto-e-v´ırgula, o segundo deles contendo a palavravar para indicar referˆencia.

9.4. C ´ALCULO DO MDC PELA DEFINIC¸ ˜AO 119

9.4 C´ alculo do MDC pela defini¸ c˜ ao

Nesta se¸c˜ao vamos revisitar o c´alculo do MDC pela defini¸c˜ao estudado na se¸c˜ao 8.5.

Deixamos propositalmente em aberto a quest˜ao de que aquele c´odigo continha trechos de c´odigo similares que tinham que ser repetidos por causa de um n´umero de entrada variante no programa.

De fato, naquele c´odigo exibido na figura 8.15 o c´alculo para saber se o n´umeroa era par difere do c´alculo para saber se o n´umerob´e par apenas por conta do valor de a oub. O mesmo ocorre para o c´odigo da figura 8.16 no caso de n´umeros ´ımpares.

Na verdade, os quatro trechos de c´odigo est˜ao ali apenas para se saber quantas vezes um dado n´umero n pode ser dividido por um n´umero primo p, seja ele o 2 ou qualquer ´ımpar primo.

Este c´alculo pode ser feito em um subprograma que recebe os n´umeros de entrada de alguma maneira e que devolva o valor correto tamb´em segundo uma conven¸c˜ao.

Esta conven¸c˜ao, emPascal, ´e dada pelo prot´otipo de uma fun¸c˜ao que ´e constitu´ıdo por trˆes parˆametros: o nome da fun¸c˜ao, os n´umerosn ep envolvidos e, finalmente, o tipo do valor devolvido pelo subprograma, isto ´e, um tipo que contenha o n´umero de vezes em que n ´e dividido por p.

function num vezes que divide(p,n : integer) : integer;

Outro trecho de c´odigo repetido ´e a atualiza¸c˜ao do MDC para receber a menor potˆencia do primo sendo calculado, na primeira vez ´e o 2, nas outras vezes ´e um primo

´ımpar.

Basta criarmos um segundo prot´otipo de fun¸c˜ao que calcule a potˆencia do primo elevado ao valor do menor contador. Este pode ser o seguinte:

function potencia (n, i : integer) : integer;

Estas interfaces nos permitem modificar o programa do c´alculo do MDC pela defini¸c˜ao conforme mostra a figura 9.8.

Observamos que o uso de fun¸c˜oes e procedimentos permite muita flexibilidade ao programador, pois ele pode alterar o c´odigo da fun¸c˜oes, se quiser, tornando-as mais eficientes (caso n˜ao fossem) sem que haja efeito colateral no programa principal. As figuras 9.9 e 9.10 mostram sugest˜oes de c´odigo para as fun¸c˜oes.

Na verdade, este c´odigo n˜ao ´e muito apropriado, pois exige um comportamento n˜ao muito elegante na fun¸c˜ao num vezes que divide, ela tem o efeito colateral de alterar o valor da vari´avel n, o que n˜ao ´e natural dado o nome da fun¸c˜ao. Deveria apenas contar o n´umero de vezes que divide e n˜ao fazer mais nada. O problema n˜ao pode ser resolvido com este mesmo algoritmo sem este efeito colateral. Em todo caso, sabemos que o algoritmo de Euclides ´e mais eficiente, mais simples de implementar e sobretudo mais elegante!

program mdcpeladefinicao ; (∗ pela definicao de mdc ∗) var

a , b, primo , mdc: longint ;

cont a , cont b , menor cont : integer;

function num vezes que divide(p: integer; var n: longint ) : integer; (∗ codigo da funcao num vezes que divide ∗)

function potencia (n,p : integer) : integer; (∗ codigo da funcao potencia ∗)

begin (∗ programa principal ∗) read (a ,b) ;

mdc:= 1;

cont a:= num vezes que divide (2 ,a) ; cont b:= num vezes que divide (2 ,b) ;

i f cont a<= cont b then menor cont:= cont a else

menor cont:= cont b ;

mdc:= mdc potencia (2 ,menor cont) ; writeln (’mdc= ’,mdc) ;

primo:= 3;

while (a<> 1) and (b<> 1) do begin

writeln (primo) ;

cont a:= num vezes que divide(primo , a) ; cont b:= num vezes que divide(primo ,b) ; i f cont a<= cont b then

menor cont:= cont a else

menor cont:= cont b ;

mdc:= mdc potencia (primo , menor cont) ; primo:= primo + 2;

end;

writeln (mdc) ; end.

Figura 9.8: Calcula MDC entre a e b pela defini¸c˜ao usando fun¸c˜oes.

9.4. C ´ALCULO DO MDC PELA DEFINIC¸ ˜AO 121

function num vezes que divide(p: integer; var n: longint ) : integer; var cont : integer;

begin

(∗ descobre quantas vezes o 2 divide as duas entradas ∗) cont:= 0;

writeln (’num_vezes_que_divide= ’, cont) ; num vezes que divide:= cont ;

end;

Figura 9.9: Calcula quantas vezes um n´umero divide outro.

function potencia (n,p : integer) : integer; var pot : longint ;

Figura 9.10: Calcula a potˆencia de um n´umero elevado a outro.

No documento Algoritmos e Estruturas de Dados I (páginas 116-121)