62 anos da morte de Alan Turing

Hoje completa 62 anos a morte de Alan Turing, personagem fundamental da história da computação e um dos maiores gênios do século XX.

Turing formalizou os conceitos de “algoritmo” e “computação” ao criar a máquina universal abstrata que serve de modelo para nossos computadores digitais.

Além disso, inaugurou a inteligência artificial através de um famoso artigo, “As máquinas podem pensar?”, que propõe um teste que tentamos vencer até hoje.

Não bastasse as contribuições mais teóricas, historiadores estimam que a Segunda Guerra Mundial foi encurtada em dois anos devido aos aliados conseguirem decodificar mensagens criptografadas pela Enigma, operação na qual Turing teve papel determinante.

Porém, apesar da vitória contra os nazistas, da criação do computador e da inteligência artificial, Turing foi submetido à castração química porque a sociedade não aceitava sua orientação sexual. Se envenenou aos 42.

As lutas da população LGBT são necessárias. Todo apoio!

Publicado originalmente no Facebook.

Qual a utilidade do algoritmo?

No primeiro artigo desta série, o Hélio comentou: ‘Algoritmos’ é um tema pouco valorizado por muitos programadores iniciantes, que querem logo botar a mão na massa.

É a mais pura verdade. Quando eu tive a minha primeira noção do que são algoritmos (no dia 12 de julho de 2004, no Curso de Programação da Olimpíada Brasileira de Informática na UNICAMP), confesso que fiquei decepcionado. Qual a utilidade de algo tão formal pra algo que já sabemos fazer? Eu já programava em C e achei um saco o professor escrevendo no quadro aqueles pseudocódigos de problemas super simples, perdendo tempo com isso ao invés de programar. Porém, hoje percebo que algoritmos são muito mais legais (e importantes) do que eu pensava. Claro que para somar dois inteiros não há necessidade de escrever um pseudocódigo, mas algoritmos são representações essenciais para problemas mais complexos e grandes aplicações.

Acredito que você que já leu os dois primeiros artigos já deve saber, mas não custa lembrar: algoritmo é a relação entre entrada e saída do programa, é o rascunho do programa, o projeto. E um projeto antes de colocar a mão na massa é indispensável. Enquanto a implementação é a função dos pedreiros, o algoritmo é a função dos engenheiros. Se os engenheiros não existissem, acho que os pedreiros não iam conseguir fazer as casas e os prédios que eles constroem hoje em dia!

Não querendo desmerecer alunos de sistemas de informação, mas a maioria deles não passa de pedreiros.

O algoritmo sempre existe, mesmo que apenas no seu pensamento. A primeira coisa que você pensa quando quer fazer uma aplicação (a não ser que você seja louco) é: o que é a aplicação? O que ela vai fazer? E se você sair implementando uma coisa complexa, você vai se decepcionar depois demorando mais do que o tempo de fazer a implementação só para limpar o código! Por isso, representar algoritmos complexos é essencial.

E mais… Mesmo se você tivesse tempo infinito, memória infinita, etc. e tal (não tivesse necessidade de limpar o código), você vai precisar de um algoritmo pra provar que o seu programa funciona, para se organizar e para estudar a sua lógica. Se você não planejar um algoritmo para o caso acima, qualquer funcionalidade que você queira adicionar no meio, por falta de projeto e organização, vai demorar bem mais tempo. Por isso, algoritmo não é uma perda de tempo antes da programação, mas a programação é que se torna uma perda de tempo quando não teve um algoritmo (ou, um projeto). O livro Algoritmos: Teoria e Prática reforça essa interessante idéia na página 7:

Suponha que os computadores fossem infinitamente rápidos e que a memória do computador fosse livre. Você teria alguma razão para estudar algoritmos? A resposta é sim, se não por outra razão, pelo menos porque você ainda gostaria de demonstrar que o método da sua solução termina, e o faz com a resposta certa.

Outra utilidade do algoritmo é compartilhar idéias de problemas complexos. Todo programador entende um pseudocódigo e por isso convém muitas vezes apresentar sua idéia de um programa em forma de um algoritmo, num pseudocódigo, ao invés de passar um programa implementado em C para uma dúzia de programadores de VBScript (e sem dúvidas é muito melhor do que ter que aprender uma linguagem da Microsoft!!!).

Existe uma série de algoritmos comuns que todo programador deve conhecer (projetos prontos para muitas coisas complicadas que precisamos implementar no dia-a-dia) e isso é o que vamos começar a estudar no próximo artigo. :)

Como representar um algoritmo?

No primeiro artigo desta série, expliquei o que é um algoritmo e até citei exemplos do cotidiano, como acordar ou falar com outra pessoa. Talvez você nem tenha se dado conta, mas usando listas numeradas eu representei os algoritmos ali presentes, inclusive destacando a entrada e a saída de cada situação-problema. Porém, não é sempre assim que representamos algoritmos.

Não existe uma regra padrão para a representação de algoritmos. Cada pessoa escreve de forma diferente, mas o importante é ser legível e convencionado. Por exemplo, o livro Algoritmos: Teoria e Prática* traz nas páginas 14 e 15 convenções do pseudocódigo que utiliza no livro inteiro. Já eu, quando vou passar o mesmo algoritmo, utilizaria outro tipo de código, você pode utilizar outro, e por aí vai. Mas todos têm que ter o mesmo foco: legibilidade e fácil implementação para qualquer linguagem.

* A partir deste artigo, sempre que eu falar “Cormen”, “CLRS”, “Introduction to Algorithms” ou “Algoritmos: Teoria e Prática” estarei me referindo a um livro que é referência essencial nessa área. A versão que tenho (de onde tiro o número das páginas) é a tradução da segunda edição americana publicada pela Elsevier em 2002.

Os pseudocódigos costumam parecer um código em linguagem Pascal traduzido para a sua língua. :) Usam quase sempre estruturas que estamos acostumados a usar na programação, como se, enquanto, para, arrays, etc. Eles existem para que o algoritmo seja de fácil leitura para qualquer programador, que programe em qualquer linguagem “normal”. Veja o pseudocódigo do Insertion Sort, um algoritmo de ordenação de vetores bastante simples:

para j \leftarrow{} 2 até comprimento do vetor, faça
elemento \leftarrow{} vetor[j]
i \leftarrow{} j - 1
enquanto i > 0 e vetor[i] > elemento, faça
  vetor[i + 1] \leftarrow{} vetor[i]
  i \leftarrow{} i - 1
fim-enquanto
vetor[i + 1] \leftarrow{} elemento
fim-para

(Não se preocupe em entender o que ele faz, AINDA, pois veremos isso mais adiante)

Se você programa em qualquer linguagem, você não terá dificuldade em traduzir esse pseudocódigo para ela. O pseudocódigo é sempre uma maneira bem simples de escrever o código. Veja por exemplo, o mesmo código em C:

for (j=2; vetor[j]!=NULL; j++) {
    elemento = vetor[j];
    for (i = j-1; i > 0 && vetor[i] > elemento; i--) {
        vetor[i+1] = vetor[i];
    }
    vetor[i+1] = elemento;
}

Você deve ter percebido que ao invés de usar três linhas com uma declaração, um condicional e um incremento, eu juntei todos num só for. Mas por isso o algoritmo é bem simples e sempre parte do princípio de que a sua linguagem é simples. Veja só a implementação do código em Pascal e compare-a com a do pseudocódigo:

for j:=2 to comprimento, do begin
    elemento := vetor[j];
    i := j-1;
    while i>0 && vetor[i] > elemento, do begin
        vetor[i+1] := vetor[i];
        i := i-1;
    end;
    vetor[i] := elemento;
end;

Linha por linha ela é exatamente igual! A única diferença é que o pseudocódigo é traduzido… Geralmente os pseudocódigos são parecidos sempre com essa base e suas implementações não são muito diferentes. E vai ser sempre dessa maneira que eu vou representar os algoritmos (usando pseudocódigos e alguns traduzindo para C para mostrar implementações). No entanto, qualquer dúvida sobre essa representação, fique a vontade para perguntar através dos comentários.

O que é um algoritmo?

Um algoritmo é um procedimento computacional definido que recebe um ou mais valores (entrada) e produz um ou mais valores (saída). O algoritmo é aquela fórmula matemática, aquele pedaço de código, que fica ali no meio da entrada e da saída para transformar o primeiro no segundo.

Vamos supôr por exemplo que temos a função:

f(x)=x23f(x) = \frac{x^{2}}{3}

A sua entrada é o x e a sua saída é o y (ou f(x), o valor que a função retorna).

O algoritmo aqui seria o seginte:

  1. Entrada: Receber o valor X.
  2. Elevar X ao quadrado e guardar o número resultante como Z.
  3. Dividir Z por 3 e guardar o número resultante como Y.
  4. Saída: Imprimir o valor Y.

O algoritmo, portanto, é a lógica do nosso problema matemático, ou, informático. É a seqüência de passos que eu faço na minha cabeça (ou, quando é complexo, no papel) antes de escrever, em C, a função f:

int f(int x) {
   int z, y;
   z = pow(x, 2);
   y = z/3;
   return y;
}

Se formos pensar, veremos que tudo o que fazemos é um algoritmo, é um procedimento que recebe uma entrada e envia uma saída. Não só no computador, mas na vida. Quando eu falo com alguém, eu espero sua entrada (o que a pessoa fala pra mim), então penso e transformo essa entrada numa saída (a resposta que vou dar pra pessoa). E assim é com várias outras coisas. Podemos dizer também que acordar é um algoritmo, por exemplo:

  1. Entrada: Meu cérebro disse que eu estou acordado!
  2. Percebi que acordei, mas estou com sono. Espero um pouco.
  3. Saída: Abrir os olhos.
  4. Saída: Se espreguiçar.
  5. Saída: Tirar a coberta.
  6. Saída: Sentar na cama.
  7. Saída: Sair da cama.

Podem existir vários algoritmos diferentes para resolver o mesmo problema. No caso de Acordar, cada um acorda de forma diferente, por exemplo. Foi até um exemplo meio estranho esse aí, mas outro algoritmo poderia dar outra saída, como por exemplo simplesmente abrir os olhos e cair da cama. Ou no caso acima da função matemática, poderíamos ter um algoritmo que fizesse a mesma coisa de maneira diferente também.

O algoritmo que usamos depende principalmente do tempo que ele demora pra ser executado e a memória que ele gasta no computador. Chamamos isso de custo. Quando começarmos a ver os algoritmos de ordenação de vetores (arrays), veremos que cada algoritmo faz uma coisa diferente, mas todos servem para o mesmo propósito: ordenar o vetor. Para uma entrada pequena, um pode ser mais rápido… Para uma maior, outro. Portanto, o algoritmo que queremos usar (o tempo que ele vai demorar pra ser executado e a memória que ele vai gastar no computador) depende principalmente do tamanho da entrada (que chamamos de n e no exemplo da função seria lá em cima seria a variável x).

Na maioria dos casos (e vai ser sempre assim aqui nos meus artigos), a entrada será o teclado (por exemplo, o usuário digita o X para a função) e a saída será a tela (por exemplo, o programa imprime o resultado da função, o Y, para a tela). Essas são a entrada e saída padrão (standard input output do C), que é usada nas olimpíadas e na maioria dos problemas que resolvemos no computador.

Em resumo, portanto, um algoritmo é a lógica de um programa computacional. Nos próximos artigos, isso deverá ser mais esclarecido e começaremos a ver algoritmos “de verdade” ;)

Qualquer dúvida, sugestão ou notificação de erro; poste um comentário ou me envie um e-mail (não só nesse, mas também nos próximos artigos). Espero que gostem.

© 2005–2020 Tiago Madeira