Você está na página 1de 13

Java: Algoritmos e programas

O conceito de algoritmo abordado na disciplina apenas de forma informal: uma estratgia para resolver um problema de forma correta em tempo finito. Dado um problema, podemos conceber mais de uma estratgia para resolvlo. Se quisermos que o particular problema seja resolvido com o auxlio de um computador, ento devemos definir estratgias sob a perspectiva da mquina. Como computadores no entendem descries de estratgias em linguagem natural, preciso codificlas em linguagens artificiais compreensveis pelas mquinas. Tais linguagens so denominadas linguagens de programao. Uma codificao de uma estratgia em uma particular linguagem de programao chamada de programa (ou aplicao no jargo Java). Para uma particular estratgia existem mltiplas formas de codificao. O ato de programar, portanto, sempre deve ser precedido por uma atividade de concepo e seleo de estratgias. Se no existe uma estratgia claramente delineada, ento no h como codificar programas corretos. Ao se codificar um programa tambm deve ser considerada, alm do algoritmo claro, a forma de como o usurio vai interagir com o propsito de produzir um programa que seja de fcil uso.

Java: Soma de dois inteiros

Calcular a soma de dois valores inteiros.

Consideraes relevantes: Que valores sero somados? Dois valores fornecidos pelo usurio. Como sero fornecidos tais valores? Via teclado. A soma de dois valores inteiros uma operao primitiva. O que feito com o valor resultante da soma? Ele apresentado ao usurio. Pelo fato da soma de dois valores j ser uma operao primitiva (isto , ela no precisa ser descrita em termos de operaes mais elementares), o algoritmo para o programa bastante simples. Se o processo for encarado sob a tica do computador (j que um algoritmo prescreve os passos a serem executados pelo computador para resolver um particular problema), ento o processo para calcular a soma de um valor representado por a com outro representado por b se restringe ao seguinte: 1. ler um valor do teclado para a e outro para b 2. somar os valores representados por a e b 3. apresentar o valor resultante ao usurio

Aplicao "minimalista"

001 002 003 004 005 006 007 008 009 010 011 012 013

public class SomaDoisInteiros { public static void main(String argv[]) { int a, b, c; a = IO.readInt(); b = IO.readInt(); c = a + b; IO.writeln(c); } } Aplicao mais "usvel"

001 002

public class SomaDoisInteiros2 {

003 004 005 006 007 008 009 010 011 012 013 014 015 016

public static void main(String argv[]) { int a, b, c; IO.write("a: "); a = IO.readInt(); IO.write("b: "); b = IO.readInt(); IO.write("a + b = "); c = a + b; IO.writeln(c); } }

Java: Soma dos n primeiros nmeros naturais verso 1

Dado n, calcular a soma dos n primeiros nmeros naturais.

Podemos resolver o problema com o clculo de uma expresso aritmtica j que o problema um caso particular de uma progresso aritmtica (sn = (a1 + an) * n / 2), onde, no caso especfico a ser resolvido, a1=1 e an=n). A expresso aritmtica a ser calculada, portanto, se reduz a (1 + n) * n / 2 e depende de um valor n a ser fornecido pelo usurio. Um possvel algoritmo (processo de resoluo do problema sob a tica do computador), portanto, pode ser descrito como: 1. ler um valor para n 2. calcular o valor resultante da expresso (1 + n) * n / 2 3. apresentar o resultado para o usurio Observao: O algoritmo pressupe que o usurio ir fornecer um valor vlido, isto , um valor inteiro maior que zero. Se for fornecido um valor menor ou igual a zero o resultado apresentado pelo programa no representa uma resposta vlida.

Aplicao

001 002 003 004 005 006 007 008 009 010 011 012

public class SomaNaturais1 { public static void main(String argv[]) { int n, s; n = IO.readInt(); s = (1 + n) * n / 2; IO.writeln(s); } }

Java: Soma dos n primeiros nmeros naturais verso 2

Dado n, calcular a soma dos n primeiros nmeros naturais.

Se no identificarmos o problema como sendo um caso particular de uma progresso aritmtica, ento uma outra forma de resolvlo poderia ser atravs de somas sucessivas, isto , enquanto enumeramos os naturais de 1 a n, ns vamos somando tais valores conforme vo sendo enumerados. Isto, , a cada ciclo do processo determinado o prximo nmero natural que , ento, somado ao valor das somas j acumuladas dos valores enumerados at ento. Com esta estratgia, se no meio do processo de clculo tivermos atingido, em um particular ciclo, o valor i durante a enumerao, ento j teremos acumulado a soma de todos os valores de 1 a i1 e a tal valor acumulado somado, ento, o valor i para obtermos o valor da soma de todos os valores entre 1 e i. Conseqentemente, ao final do processo, o valor das somas acumuladas representa o valor que procuramos, isto , a soma de todos os valores entre 1 e n. Como iniciamos o processo? Inicialmente definimos o valor da varivel s que utilizamos para representar o valor das somas acumuladas como sendo zero (o elemento neutro da soma), pois nenhum valor ainda foi acumulado, e definimos o valor do i utilizado para representar o valor do nmero natural atingido no processo de enumerao tambm com o valor zero. A cada ciclo do processo incrementamos o valor de i em uma unidade (no primeiro passo i deve passar a valer 1, isto , o primeiro valor da seqncia dos naturais a serem enumerados e, por esta razo, o valor inicial de i dve ser o predecessor de 1) e atualizamos o valor da varivel s que representa a soma dos at ento enumerados valores naturais com um novo valor obtido a partir da soma do valores correntes de s e i. Com tal atualizao (dizemos que um novo valor foi "atribudo" a s) o valor de s fica consistente com o uso pretendido da varivel, isto , manter o valor da soma de todos os naturais j enumerados. Este ciclo de dois passos (determinao do prximo natural e atualizao do valor das somas acumuladas) repetido at i atingir o valor n e este ltimo valor de i estiver agregado ao valor de s. O algoritmo, portanto, se resume a: 1. leia o valor de n 2. faa com que s assuma o valor zero 3. faa com que i assumam o valor zero 4. enquanto i<n faa 1. incremente o valor de i em uma unidade 2. some ao valor em s o valor em i e associe tal valor a s 5. apresente o valor de s ao usurio Observao: Para no precisarmos ser to prolixos na descrio da manipulao e atualizao dos valores de variveis, ns podemos deixar a descrio ainda mais sinttica: 1. leia n 2. s < 0 3. i < 0 4. enquanto i<n faa 1. i < i + 1 2. s < s + i 5. apresente s A instruo s < s + i deve ser lida como "pegue o valor associado a s e o valor associado a i, some os dois e associe o valor resultante a s". No caso particular refraseado, o valor original de s substitudo por outro (isto , o 5

obtido pela operao de soma).

Aplicao

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018

public class SomaNaturais2 { public static void main(String argv[]) { int n, s, i; n = IO.readInt(); s = 0; i = 0; while (i < n) { i = i + 1; s = s + i; } IO.writeln(s); } }

Java: Mximo divisor comum verso 1

Calcular o mximo divisor comum de dois inteiros a e b.

Um algoritmo para determinar o mximo divisor comum baseado em divises sucessivas: a cada ciclo calculado o resto da diviso do maior valor pelo menor e, a seguir, so substitudos o maior pelo menor e o menor pelo resto da diviso efetuada. Esse ciclo repetido at que o valor menor passe a ser zero. Neste caso, o mximo divisor comum o que sobrou como maior valor. Exemplo:
a 195 150 45 15 b 150 45 15 0

Por que este algoritmo funciona? Se k o mximo divisor comum de a e b, ento a=n*k e b=m*k. O resto da diviso a mod b tambm mltiplo de k, pois a mod b = aab*b = n*k((n*k)(m*k))*m*k = n*k(nm)*m*k = (n(nm)*m)*k, onde usado para representar a operao de diviso inteira. O processo forosamente converge para a soluo, pois o maior valor sempre substitudo pelo menor e o menor pelo resto da diviso que sempre menor que o valor do menor do ciclo anterior. Supondo que o usurio fornea inicialmente o valor do maior e depois o valor do menor. Neste caso o algoritmo pode ser descrito como sendo 1. leia a e b 2. enquanto b<>0 faa 1. r < a mod b 2. a < b 3. b < r 3. apresente a E se o usurio inverter a ordem dos valores fornecidos, o que acontece com o processo descrito? Se a representa o valor menor e b o valor maior, ento o resto da diviso a, a passa a assumir o valor maior b e b o resto da diviso que igual ao valor original de a. Feito a troca, os valores esto nas variveis corretas e tudo procede como esperado. Portanto, no h necessidade de tratarmos a ordem invertida como um caso parte.

Aplicao

001 002 003 004 005 006

public class Mdc1 { public static void main(String argv[]) { int a, b, r; a = IO.readInt();

007 008 009 010 011 012 013 014 015 016 017

b = IO.readInt(); while (b != 0) { r = a % b; a = b; b = r; } IO.writeln(a); } }

Java: Mximo divisor comum verso 2

Calcular o mximo divisor comum de dois inteiros a e b.

Um algoritmo baseado em subtraes sucessivas conhecido por "algoritmo de Euclides" e consiste no seguinte: a cada ciclo o maior valor substitudo pelo valor da diferena entre o maior valor e o menor valor at que os dois valores passam a ser iguais; quando passam a ser iguais, o mximo divisor comum procurado um deles (tanto faz qual j que so iguais). Exemplo:
a 195 45 45 45 45 30 15 b 150 150 105 60 15 15 15

Por que este algoritmo funciona? Se k o mximo divisor comum de a e b, ento a=n*k e b=m*k. Se a diferente de b, ento a diferena ab tambm mltiplo do mximo divisor comum, pois ab = n*km*k = (nm)*k. O algoritmo converge para a soluo, pois a cada ciclo o maior valor substitudo pela diferena entre tal valor e o menor valor. A diferena forosamente menor do que o valor a ser substitudo. O algoritmo pode ser descrito sucintamente como sendo: 1. leia a e b 2. enquanto a<>b faa 1. se a>b ento 1. a < a b seno 1. b < b a 3. apresente a

Aplicao

001 002 003 004 005 006 007

public class Mdc2 { public static void main(String argv[]) { int a, b; a = IO.readInt();

008 009 010 011 012 013 014 015

b = IO.readInt(); while (a != b) if (a > b) a = a b; else b = b a; IO.writeln(a); } }

10

Java: Mximo divisor comum verso 3

Calcular o mximo divisor comum de dois inteiros a e b.

Cada nmero inteiro pode ser decomposto em fatores primos. Por exemplo:
150 75 25 5 1 | | | | | 2 3 5 5

E assim, 150 = 2*3*5*5 O mximo divisor comum pode ser obtido atravs da multiplicao dos fatores primos que os dois valores considerados tm em comum. Por exemplo:
a 195 195 65 13 13 1 b 150 75 25 5 1 1 | 2 | 3 | 5 | 5 | 13 |

Logo, mdc(195,150) =

3*5 = 15.

Uma possvel idia para obter o mximo divisor comum baseada em decomposio em fatores primos at que um dos dois valores considerados seja reduzido ao valor um a seguinte: Dado um nmero primo (o primeiro a ser considerado o valor 2) verificamos se a e/ou b so divisveis por tal nmero primo. Em caso afirmativo, o valor que divisvel substitudo pelo resultado da diviso e uma nova tentativa de reduo feita com o mesmo nmero primo. Se nenhum dos dois valores considerados divisvel pelo nmero primo em questo, ento calculamos o prximo nmero primo e repetimos o ciclo. Deixamos de repetir o ciclo quando um dos dois valores considerados reduzido a 1, pois a partir deste ponto no existe mais nenhum nmero primo que possa dividir os dois. O processo descrito reduz os nmeros atravs de divises sucessivas, mas no ainda calculado o mximo divisor comum. Para fazer isto conjuntamente com o processo de reduo, definimos como primeiro valor divisor divisor comum como sendo 1 e, ao longo do processo, toda vez que tivermos um nmero primo que divida os dois, multiplicamos o valor j computado como divisor comum por tal nmero primo. Ao final do processo temos, ento, como divisor comum o produto de todos os nmeros primos que dividem ambos os valores originalmente considerados que assim passa a representar o mximo divisor comum j que representa a produtria de todos os fatores primos divisores em comum. No nosso exemplo em particular isto pode ser representado assim:
a 195 195 b 150 75 | | primo res 2 1 3 3

11

65 13 13

25 5 1

| | |

5
5

15 15 ^ resultado

Se usarmos o smbolo para indicar a diviso inteira e o smbolo <> para indicar o operador de comparao que verifica se os valores de seus dois operandos so diferentes entre si, ento o algoritmo pode ser descrito como sendo: 1. primo < 2 2. res < 1 3. leia a e b 4. enquanto a<>1 OU b<>1 faa 1. se a divisvel por primo ento 1. a < aprimo 2. se b divisvel por primo ento 1. b < bprimo 2. res < res*primo seno 1. se b divisvel por primo ento 1. b < bprimo seno 1. calcule o prximo primo 5. apresente res Resta ainda um problema a resolver: como vamos calcular o prximo nmero primo? Uma maneira rasteira de fazer isto aumentar o valor do candidato a primo em uma unidade a cada ciclo e testar se existe algum valor entre 2 e o valor de candidato a primo menos 1 que divida o candidato a primo. Se existir repetimos o ciclo. Em caso contrrio, acabamos de achar o nosso prximo primo. Esta idia inicial pode ser melhorada. Sabemos que alm do 2 no temos outro primo par. Assim basta a cada ciclo aumentar o candidato a primo de 2 em 2 a partir de nmeros primos maiores que 2. Outra melhoria do processo reduzir o nmero de potenciais divisores a serem testados. Se no encontrarmos um divisor at, inclusive, o valor correspondente ao resultado da diviso inteira do candidato a primo por dois, ns no vamos mais encontrar nenhum dali em diante. Tambm precisamos considerar como potenciais divisores apenas os mpares no intervalo de 3 at o limite mximo em que tivermos chances de encontrar um divisor, j que s so considerados candidatos a primos valores mpares. O algoritmo melhorado at este ponto para o clculo do prximo primo pode, ento, ser descrito da seguinte forma: 1. se primo=2 ento primo < 3 seno 1. repita 1. primo < primo+2 2. p < 1 3. limite < primo2 4. repita 1. p < p+2 at que primo seja divisvel por p OU p>=limite at que no tiver sido encontrado divisor entre os potenciais divisores Nota: O universo de busca de divisores de um candidato a primo poderia ser ainda mais reduzido. Basta considerar como potenciais divisores apenas os primos entre 3 e o limite (isto , o resultado da diviso inteira do candidato a primo por dois). A implementao dessa estratgia, contudo, j mais complexa.

12

Aplicao

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036

public class Mdc3 { public static void main(String argv[]) { int a, b, p, limite, primo = 2, mdc = 1; a = IO.readInt(); b = IO.readInt(); while (a != 1 || b != 1) { if (a % primo == 0) { a = a / primo; if (b % primo == 0) { b = b / primo; mdc = mdc * primo; } } else if (b % primo == 0) b = b / primo; else // achar proximo primo if (primo == 2) primo = 3; else do { primo = primo + 2; p = 1; limite = primo / 2; do { p = p + 2; } while (primo % p != 0 && p < limite); } while (primo % p == 0); } IO.writeln(mdc); } }

13