sexta-feira, 1 de novembro de 2013

Devagar se vai ao longe (II)

Neste post vamos regressar ao tema da complexidade inerente a desenvolver uma solução informática e ao melhor modo de a dominar. A ideia central é simples: dividir o problema em sub-problemas.
Comecemos com um dos problemas do livro, o problema 5.10. Lançamos dois dados, numerados de 1 a 6, um certo número de vezes, e queremos saber a percentagem das situações em que a soma dos dois números que saíram é par.
Perante o enunciado temos que ser capazes de perceber que o programa envolve a repetição dos lançamentos e a análise do resultado. Assim, podemos escrever um primeiro esboço de solução.
def dados_par(n):
    # inicialização
    for i in range(n):
        # lança dados
        # calcula soma
        # testa resultado e actualiza contagem
    return # resultado
Este esboço mostra os sub-problemas que identificámos e que descrevemos na forma de comentários. Como já não somos novatos em programação temos claro que vamos precisar de um contador para saber quantas vezes o resultado saiu par:
def dados_par(n):
    # inicialização
    conta = 0
    for i in range(n):
        # lança dados
        # calcula soma
        # testa resultado e actualiza contagem
    return 100 * conta/n
Vamos resolver agora o problema do lançamento dos dados. Recorremos ao módulo random e ao método randint.
import random

def dados_par(n):
    # inicialização
    conta = 0
    for i in range(n):
        # lança dados
        dado_1 = random.randint(1,6)
        dado_2 = random.randint(1,6)
        # calcula soma
        # testa resultado e actualiza contagem
    return 100 * conta/n
Falta agora tratar dos dois últimos problemas. Não nos levarão a mal se fizermos tudo de uma só vez.
import random

def dados_par(n):
    # inicialização
    conta = 0
    for i in range(n):
        # lança dados
        dado_1 = random.randint(1,6)
        dado_2 = random.randint(1,6)
        # calcula soma
        soma = dado_1 + dado_2
        # testa resultado e actualiza contagem
        if soma % 2 == 0:
            conta += 1
    return 100 * conta/n
Passemos à questão de imprimir uma pirâmide de números com o aspecto seguinte:

Claro que este é apenas um exemplo para o caso de queremos uma pirâmide com 5 linhas. A nossa solução terá que ser genérica. De qualquer dos modos, é claro que a solução passa por imprimir, uma a uma, as diferentes linhas.
def piramide_numeros(n):
    """Desenha uma pirâmide de números."""
    for i in range(1,n+1):
        # mostra linha i
        # linha seguinte
        print()
Avançámos pouco, certo? Como tratar cada linha? Da observação do resultado pretendido resulta claro que temos várias questões a resolver: onde começar a imprimir uma linha, como imprimir uma sequência que primeiro decresce e depois cresce. Ou seja o nosso problema de imprimir uma linha decompõe-se em três sub-problemas.
def piramide_numeros(n):
    """Desenha uma pirâmide de números."""
    for i in range(1,n+1):
        # mostra linha i
        # -- (1) posicionar
        # -- (2) imprime sequência decrescente
        # -- (3) imprime sequência crescente
        # linha seguinte
        print()
Posicionar parece ser a questão mais difícil, pelo que nos vamos preocupar com os outros. A sequência decrescente para uma dada linha i começa em i e vai até 1. Sabemos como fazer isso recorrendo ao iterável range com três argumentos e um ciclo for. Notar como o print da cada número não é seguido de mudança de linha graças aos terminador ser o espaço em branco (end = ‘ ‘). Notar ainda que o segundo argumento do range é 0, precisamente para que 1 seja o último a ser impresso. Finalmente, optámos por criar pirâmides em que os números podem ter um máximo de 3 dígitos (‘%3d’).
def piramide_numeros(n):
    """Desenha uma pirâmide de números."""
    for i in range(1,n+1):
        # mostra linha i
        # -- (1) posicionar
        # -- (2) imprime sequência decrescente
        for j in range(i,0,-1):
            print('%3d'% j, end = ' ')
        # -- (3) imprime sequência crescente
        # linha seguinte
        print()
O caso da sequências ascendente é semelhante só que o começo deve ser agora o número 2.
def piramide_numeros(n):
    """Desenha uma pirâmide de números."""
    for i in range(1,n+1):
        # mostra linha i
        # -- (1) posicionar
        # -- (2) imprime sequência decrescente
        for j in range(i,0,-1):
            print('%3d' % j, end = ' ')
        # -- (3) imprime sequência crescente
        for j in range(2,i+1):
            print('%3d' % j, end=' ')
        # linha seguinte
        print()
Vamos então ao posicionamento. Olhando para a figura verificamos que a última linha não obriga a nenhum posicionamento especial. A penúltima, terá que ser avançada do espaço máximo que um número pode ocupar (neste caso 3), a que se soma o espaço entre números (neste caso, 1). Na antepenúltima temos que avançar o dobro do espaço anterior. De um modo geral na linha i temos que avançar 4*(n-i) espaços! Estamos em condições de completar o nosso programa.
def piramide_numeros(n):
    """Desenha uma pirâmide de números."""
    for i in range(1,n+1):
        # mostra linha i
        # -- (1) posicionar
        print(4*(n-i) * ' ', end='')
        # -- (2) imprime sequência decrescente
        for j in range(i,0,-1):
            print('%3d'%j, end = ' ')
        # -- (3) imprime sequência crescente
        for j in range(2,i+1):
            print('%3d'%j, end=' ')
        # linha seguinte
        print()

Et voilá!

Sem comentários:

Enviar um comentário