quinta-feira, 14 de dezembro de 2017

Pensar primeiro…

Programamos para resolver problemas, pelo que o centro da nossa atenção deve ser o problema. Por isso a primeira coisa que devemos fazer é pensar. Pensar para perceber o enunciado: qual é a entrada, qual é a saída. Pensar para definir uma estratégia: posso decompor o problema em sub-problemas mais simples? Já resolvi algum problema semelhante?

Este intróito vem a propósito da dificuldade que alguns encontraram em resolver o problema seguinte: Suponha que tem um dicionário que relaciona receitas com ingredientes. Faça um programa que retorne outro dicionário contendo os ingredientes usados no maior número de receitas, bem como as receitas em que cada um é usado. Por exemplo:

>>> receitas={’sonhos’:[’agua’,’farinha’,’manteiga’, ’ovos’,’acucar’],’rabanadas’:[’pao’,’leite’,’ovos ’,’manteiga’,’acucar’],’leite creme’:[’acucar’,’ farinha’,’ovos’,’leite’]} 
>>> ingredientes_mais_usados(receitas)
{’ovos’: [’sonhos’, ’rabanadas’, ’leite creme’], ’ 
acucar’: [’sonhos’, ’rabanadas’, ’leite creme’]} 
Se pensarmos um pouco verificamos que temos que passar de um dicionário em que as chaves são nomes de receitas e os valores são listas de produtos, para um dicionário em que as chaves são produtos e os valores listas de receitas onde esses produtos aparecem. Ou seja: temos que inverter o dicionário. Este é um problema que já resolvemos no passado:
def meu_inverte_dicio(dicio):
    novo_dicio = dict()
    for ch, val in dicio.items():
        for  ingrediente in val:
            novo_dicio[ingrediente] = novo_dicio.setdefault(ingrediente,[]) + [ch]
    return novo_dicio
Esta solução mostra a grande vantagem em usar o método setdefault!!

A partir do momento que temos o dicionário invertido temos que o percorrer à procura dos elementos cujo valor tem tamanho máximo. Já sabemos como resolver o problema semelhante de encontrar o elemento que é o “maior” de acordo com um dado critério, quando os elementos estão guardados numa lista. Neste caso, o problema é mais complexo por os elementos estarem num dicionário e por querermos não um mas todos os que têm dimensão máxima. Uma solução, possível passa por construir primeiro uma lista e só depois passar a lista a dicionário.

def meu_ingredientes_mais_usados(dicio):
    dicio_ingredientes = meu_inverte_dicio(dicio)
    lista_resultado = []
    tamanho = 0
    for ch, val in dicio_ingredientes.items():
        if len(val) > tamanho:
            lista_resultado = [(ch,val)]
            tamanho = len(val)
        elif len(val) == tamanho:
            lista_resultado.append((ch,val))
    dicio_resultado = dict(lista_resultado)
    return dicio_resultado
Mas nada impede que se construa o dicionário final directamente:
def meu_ingredientes_mais_usados_b(dicio):
    dicio_ingredientes = meu_inverte_dicio(dicio)
    dicio_resultado = dict()
    tamanho = 0
    for ch, val in dicio_ingredientes.items():
        if len(val) > tamanho:
            dicio_resultado.clear()
            dicio_resultado[ch] = val
            tamanho = len(val)
        elif len(val) == tamanho:
            dicio_resultado[ch] = val
        print(dicio_resultado)
    return dicio_resultado
Como se pode ver, cada vez que encontramos uma solução melhor, limpamos o dicionário (método clear) e actualizamos de seguida.

quarta-feira, 22 de novembro de 2017

Teste # 2 - TP2

P2

O problema: Duas palavra dizem-se deslocadas de n posições se se puder obter os caracteres de uma deslocando cada caracter da outra n posições no alfabeto. Por exemplo, HAL e IBM são palavras deslocadas com uma distância de 1. Escreva um programa que determina se duas palavras são deslocadas ou não de n.

A solução. Definimos o alfabeto e depois procuramos as posições no alfabeto dos caracteres presentes nas duas palavras. É preciso que todas as diferenças tenham o mesmo valor, igual a n.

def deslocadas(pal_1, pal_2,n):
    """ 
    pal_1 é pal_2 quand se deslocam todos os caracteres de n posições?
    Exemplo: IBM,HAL e n = 1.
    """
    alfabeto ='abcdefghijklmnopqrstuvwxyz'
    for i in range(len(pal_1)):
        indice_1 = alfabeto.find(pal_1[i])
        indice_2 = alfabeto.find(pal_2[i])
        if (indice_1 - indice_2) != n:
            return False
    return True
Notar que a ordem porque efectuamos a comparação dos índices é relevante. Se n for positivo a primeira palavra deve estar à esquerda da segunda, se for negativo é o contrário. Se quisermos não nos preocupar com a ordem e usar sempre valores positivos de n só temos que acrescentar:
def desloc(p_1,p_2,n):
    return (deslocadas(p_1, p_2,n) or deslocadas(p_2, p_1,n)
P3

O problema: Suponha que tem duas imagens a preto e branco e pretende saber se uma delas, a mais pequena,ocorre na imagem maior. Escreva um programa que resolve esta questão.

A solução. O problema desdobra-se em dois sub-problemas: saber se a operação é possível e, no caso afirmativo, calcular a ocorrência do canto superior esquerdo. Para simplificar a questão criámos um pequeno programa auxiliar que determina se duas imagens são iguais ou não.

def ocorre(img_1, img_2):
    # possivel
    n_linhas_1 = len(img_1)
    n_colunas_1 = len(img_1[0])
    n_linhas_2 = len(img_2)
    n_colunas_2 = len(img_2[0]) 
    possivel = (n_linhas_1 <= n_linhas_2) and (n_colunas_1 <= n_colunas_2)
    # verifica
    for l in range(n_linhas_2 - n_linhas_1 + 1):
        for c in range(n_colunas_2 - n_colunas_1 + 1):
            # verifica se imagem 1 ocorre a partir da posição (l,c)
            if igual(img_1,img_2,l,c):
                return (l,c)
    return (-1,-1)

def igual(img_1,img_2,l,c):
    for i in range(len(img_1)):
        for j in range(len(img_1[0])):
            if img_1[i][j] != img_2[l+i][c+j]:
                return False
    return True

Teste # 2 - TP1

P2

O problema: Um palavra diz-se embebida noutra palavra se os seus caracteres ocorrerem nesta pela mesma ordem, embora não necessariamente de modo consecutivo. Por exemplo, ADIA está embebida em ACADEMIA. Escreva um programa que dadas duas palavras, determina se uma delas está embebida na outra.

A solução. Não há grande mistério. Analisamos os caracteres da palavra mais pequena e verificamos se está presente na palavra maior. Para garantir o problema da ordem cada vez que fazemos a pesquisa limitarmos o campo de procura de modo que a posição inicial seja a posição imediatamente à frente da posição onde se encontrou o último caractere. notar que mal um caractere não esteja presente o programa termina com False.
def embebida(pal_1, pal_2):
    """ verifica se pal_1 está embebida em pal_2."""
    pos = 0
    for car in pal_1:
        indice = pal_2.find(car,pos)
        if indice == -1:
            return False
        pos = indice + 1
    return True
P3

O problema: Suponha que tem duas imagens a preto e branco e pretende que uma delas, a mais pequena, substitua uma parte da imagem maior. Escreva um programa que dadas as duas imagens e o ponto de inserção efectue a modificação.

A solução. A solução apresentada tem duas partes: uma em que se verifica se a operação é possível, e a segunda em que se processa a alteração. como se pode ver a estratégia consistiu em percorrer a imagem mais pequena e colocar os seus valores no sítio certo da imagem maior.

def altera_img(img_1, img_2,pos_l, pos_c):
    """ altera a imagem 2 com a imagem 1 a partir das posição (pos_l,pos_c)"""
    # é possível?
    n_linhas_1 = len(img_1)
    n_colunas_1 = len(img_1[0])
    n_linhas_2 = len(img_2)
    n_colunas_2 = len(img_2[0]) 
    possivel = ((pos_l + n_linhas_1) <= n_linhas_2) and ((pos_c + n_colunas_1) <= n_colunas_2)
    if possivel:
        # modifica
        for l in range(n_linhas_1):
            for c in range(n_colunas_1):
                img_2[pos_l + l][pos_c + c] = img_1[l][c]
    return img_2

sábado, 18 de novembro de 2017

Variações em torno de vogais

Nas aulas discutimos o problema de escrever um programa que leia um texto e indique para cada vogal a lista das suas ocorrências no texto. Trata-se de um problema em que, naturalmente, se opta por um dicionário para representar o resultado. Assim, para:
txt = 'ernesto ui ui cuidado com os alunos’
o resultado deve ser:
{'e': [0, 3], 'o': [6, 20, 23, 26, 33], 'u': [8, 11, 15, 31], 'i': [9, 12, 16], 'a': [18, 29]}
A solução evidente segue o padrão ciclo-acumulador. Aqui o acumulador é o dicionário que vai sendo actualizado à medida que percorremos o texto caractere a caractere.
def vogais_x(texto):
    dicio = dict()
    for i in range(len(texto)):
        if texto[i] == 'a':
            dicio['a'] = dicio.get('a',[]) + [i]
        elif texto[i] == 'e':
            dicio['e'] = dicio.get('e',[]) + [i]
        elif texto[i] == 'i':
            dicio['i'] = dicio.get('i',[]) + [i]
        elif texto[i] == 'o':
            dicio['o'] = dicio.get('o',[]) + [i]
        elif texto[i] == 'u':
            dicio['u'] = dicio.get('u',[]) + [i]  
        else:
            continue
    return dicio
Todos concordaremos que é uma solução feia. Todos aqueles ifs podem ser facilmente removidos. Por outro lado, sabemos que vamos precisar não apenas das posições mas também dos caracteres, pelo que nos interessa percorrer o texto obtendo estes dois elementos. Daí uma nova versão:
def vogais_y(texto):
    vogais ='aeiou'
    dicio = dict()
    for i,car in enumerate(texto):
        if texto[i] in vogais:
            dicio[car] = dicio.get(car,[]) + [i]
    return dicio 
Estas soluções constroem o dicionário de modo incremental. No entanto, nós sabemos quais são as chaves do dicionário, e sabemos ainda que nunca mudam. Daí , a possibilidade de criar inicialmente o dicionário com as posições todas iguais a listas vazias, recorrendo ao método fromkeys. Como consequência deixamos de necessitar do uso do método get.
def vogais(texto):
    vogais ='aeiou'
    dicio = dict.fromkeys(vogais, [])
    for i,car in enumerate(texto):
        if car in vogais:
            dicio[car] = dicio[car] + [i]
    return dicio  
O leitor atento dirá que ainda se pode ter uma solução mais elegante usando uma atribuição aumentada: +=.
def vogais_z(texto):
    vogais ='aeiou'
    dicio = dict.fromkeys(vogais, [])
    for i,car in enumerate(texto):
        if car in vogais:
            dicio[car] += [i] # <——— 
    return dicio 
No entanto, se correr este código verificará que não funciona. Ou melhor, corre mas apresenta o resultado errado:
{'a': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33], 'e': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33], 'i': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33], 'o': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33], 'u': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33]} 
A explicação para o resultado (todas as vogais ocorrem nas mesmas posições, igual à união das posições de cada uma…) é simples: a construção do dicionário usando fromkeys faz com que os valores iniciais sejam o mesmo objecto (partilha de memória) e a instrução += não constrói objectos novos. Uma solução consiste em alterar a construção do dicionário inicial, usando dicionários por compreensão:
def vogais_e(texto):
    vogais ='aeiou'
    dicio = {vog:[] for vog in 'aeiou'}
    for i,car in enumerate(texto):
        if car in vogais:
            dicio[car] += [i]
    return dicio 
E pronto. Espero que tenha entendido. Ah, já agora, ainda outra solução….
def vogais_d(texto):
    vogais ='aeiou'
    dicio = {vog:[] for vog in 'aeiou'}
    for i,car in enumerate(texto):
        if car in vogais:
            dicio[car].append(i)
    return dicio

segunda-feira, 23 de outubro de 2017

Teste #1 - TP4

P1

O seguinte programa possui um ou mais erros/omissões. Identifique o(s) erro(s)/omissão(ões)relacionando-o(s) com o conceito de espaço de nomes do python.
def  polar_to_cart_x(r, ang):
    x = r * math.cos(ang)
    return x
if  __name__  == “__main__":
    print(polar_to_cart_x (10, pi/4))
Não é feita a importação do módulo math e devemos usar math.pi e não pi.

P2

Escreva um programa para contar o número de pontos que estão dentro de uma circunferência de raio r centrada em (0, 0). O programa deverá receber o número de pontos atestar (num) e o raio (r) como parâmetros. As coordenadas de cada ponto deverão ser pedidas interativamente ao utilizador. No final, o programa deverá indicar o número de pontos que estão dentro da circunferência.
import math

def dentro(raio, x, y):
    len = math.sqrt(x ** 2 + y ** 2)
    if len <= raio:
        return True
    else:
        return False
    

def conta_dentro_circulo(num, raio):
    num_dentro = 0
    for i in range(num):
        x = eval(input("x: "))
        y = eval(input("y: "))
        
        if dentro(raio, x, y):
            num_dentro = num_dentro + 1
    
    return num_dentro
            
    
if __name__ == "__main__":
    print(conta_dentro_circulo(3, 20))
P3

Usando o módulo turtle, escreva um programa que lhe permita desenhar formas do tipo da ilustrada na Figura. Cada forma é composta por um rectângulo com uma circunferência interna, centrados no mesmo ponto.O raio da circunferência dependerá das dimensões do rectângulo, de acordo com o ilustrado na figura. O programa deverá ser parametrizável de maneira a permitir escolher o número de formas, as dimensões mínima e máxima dos lados dos rectângulos, bem como as posições (x e y) dos mesmos. As coordenadas e dimensões do rectângulo envolvente de cada forma deverão ser geradas aleatoriamente.Soluções modulares serão valorizadas.
import turtle
import random

def rectangulo(xr, yr, lado1, lado2):
    #preparar
    turtle.penup()
    turtle.goto(xr,yr)
    turtle.pendown()
    
    #desenhar
    for i in range(4):
        if i%2 == 0:
            turtle.forward(lado1)
        else:
            turtle.forward(lado2)
        turtle.right(90)
        
def circulo(xc, yc, raio):
    #preparar
    turtle.penup()
    turtle.goto(xc, yc)
    turtle.pendown()
    
    #desenhar
    turtle.circle(raio)
    

def forma_rect_circulo(xf, yf, lado1, lado2):
    #rectangulo
    rectangulo(xf, yf, lado1, lado2)

    #circulo no interior
    raio = min(lado1, lado2) / 2
    xc = xf + lado1/2
    yc = yf - lado2/2 - raio
    circulo(xc, yc, raio)
    
    
def desenha_formas(num, xmin, xmax, ymin, ymax, ladomin, ladomax):
    
    #sortear e desenhar
    for i in range(num):
        xf = random.randint(xmin, xmax)
        yf = random.randint(ymin, ymax)
        lado1 = random.randint(ladomin, ladomax)
        lado2 = random.randint(ladomin, ladomax)
        forma_rect_circulo(xf, yf, lado1, lado2)
    
    
if __name__ == "__main__":
    turtle.hideturtle()   
    desenha_formas(5, -300, 300, -300, 300, 50, 150)
    turtle.exitonclick()

domingo, 22 de outubro de 2017

Exercícios de Programação Descendente (II)

Nas aulas foi colocado o problema de visualizar na forma de um histograma o resultado de uma experiência de lançamento de um dado. O histograma permitia saber quantas vezes saiu cada número. Pretende-se algo como a figura ilustra.

Vamos novamente tentar perceber se podemos dividir o problema em sub-problemas de modo a tornar a nossa missão mais fácil. Perante o anunciado é evidente que temos dois sub-problemas: (1) efectuar a experiência contando o número de vezes que saiu cada número e, (2) usar essa informação para construir o histograma. As condições do enunciado forçam a usar o modulo turtle para a visualização! É clara a existência de uma dependência entre os dois sub-problemas, pelo que antes de começarmos a resolver em separado cada um deles precisamos definir o seu interface. Uma opção que se impõe é que o sub-problema (1) depende do número de lançamentos n para construir um tuplo (n1,n2,n3,n4,n5,n6), com ni igual ao número de vezes que saiu o número i, e n1+n2+n3+n4+n5+n6 = n. A escolha de um tuplo é natural pois precisamos de um contentor, com a função de memória. Tomadas estas decisões, podemos passar a concretização do programa.
import turtle

def dados_histo(n):
    # experiência de lançamentos
    res = lanca_dado(n)
    # visualizaçao
    histograma(res)
    
    
def lanca_dado(n):
    pass


def histograma(res):
    pass


if __name__ == '__main__':
    n = 100
    dados_histo(n)
Vamos começar por resolver o segundo sub-problema. Para tal, vamos de novo decompor o sub-problema em sub-problemas. Aqui temos, pelo menos, duas opções: (a) numa leitura “vertical”, temos quatro sub-problemas: escreve os números de 1 a 6, desenha um traço, desenha os rectângulos e escreve os números correspondentes aos números de vezes que saiu cada número; (b) numa leitura “horizontal, temos seis sub-problemas idênticos: escrever um número, desenhar um traço, desenhar uma coluna e, novamente, escrever um número. A nossa escolha vai ser a segunda, pois é aquela que nos permite ter mais graus de liberdade.
import turtle

def dados_histo(n):
    # experiência de lançamentos
    res = lanca_dado(n)
    # visualizaçao
    histograma(res)
    
    
def lanca_dado(n):
    pass

def histograma(res):
    posx = -100
    posy = 0
    comp_linha = 80
    for i,alt in enumerate(res):
        # desenha caso i
        desenha(i+1,alt,posx,posy,comp_linha)
        # define parâmetros
        posx = posx + comp_linha

def desenha(i,alt, posx,posy,comp_linha):
    pass

if __name__ == '__main__':
    n = 100
    teste = (46,39,105,0,44,5)
    histograma(teste)
    #dados_histo(n)
No esboço de solução apresentado podemos verificar que o programa histograma se limita a desenhar cada caso em sequência. Note como conseguimos os valores do número e do número de vezes que saiu graça ao uso de enumerate. Note ainda que a opção tomada obriga a que cada caso singular tenha que saber as quatro componentes relevantes: posição, tamanho do traço, numero do dado e numero de vezes que saiu. A posição para desenhar a coluna vai ser o centro pelo que pode ser calculada a partir do conhecimento do tamanho do traço. Tal como está, podemos testar o programa … mesmo que este não faça nada! Esta é uma das vantagens da programação descendente: podemos testar primeiro as soluções para os problemas e depois de as integrarmos no programa principal, testar o programa principal. A eliminação de eventuais erros é deste modo mais fácil de fazer. Passemos ao caso mais básico. São quatro so sub-problemas básicos que o compõem: número, linha, coluna, número. No entanto os dois problema de escrita de um número são na realidade o mesmo.
def desenha(i,alt, posx,posy,comp_linha):
    # escreve número i   
    # desenha linha
    # desenha coluna
    # escreve número de vezes (alt) que saiu o número i  
    pass
Podemos resolver cada um destes três sub-problemas e testá-los isoladamente.
def coluna(posx,posy, lado_1, lado_2,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # desenha
    turtle.color(cor)
    turtle.begin_fill()
    for i in range(2):
        turtle.forward(lado_1)
        turtle.left(90)
        turtle.forward(lado_2)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()
    
def linha(posx,posy,comp_linha,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.color(cor)
    turtle.pendown()
    # linha
    turtle.forward(comp_linha) 
    turtle.hideturtle()
    
def escreve_numero(posx,posy,fonte,valor,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.color(cor)  
    turtle.pendown()
    # escreve
    turtle.write(valor,font=fonte)
    turtle.hideturtle()
Agora precisamos de integrar esses sub-problemas no sub-problema de desenho de uma componente. Aqui vamos ter que perceber as relações entre cada um dos sub-componentes. Em primeiro lugar, decidimos que a largura da coluna será igual a metade do comprimento do traço. Em segundo lugar, fixamos a fonte no tamanho 12 e controlamos a posição da escrita.
def desenha(i,alt, posx,posy,comp_linha):
    # escreve número i
    escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')    
    # desenha linha
    linha(posx,posy,comp_linha,'red')
    # desenha coluna
    coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
    # escreve número de vezes (alt) que saiu o número i  
    escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
Podemos agora testar o problema de visualizar. E está na hora de ver tudo junto.
import turtle

def dados_histo(n):
    # experiência de lançamentos
    res = lanca_dado(n)
    # visualizaçao
    histograma(res)
    
    
def lanca_dado(n):
    pass


def histograma(res):
    posx = -100
    posy = 0
    comp_linha = 80
    for i,alt in enumerate(res):
        # desenha caso i
        desenha(i+1,alt,posx,posy,comp_linha)
        # define parâmetros
        posx = posx + comp_linha

def desenha(i,alt, posx,posy,comp_linha):
    # escreve número i
    escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')    
    # desenha linha
    linha(posx,posy,comp_linha,'red')
    # desenha coluna
    coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
    # escreve número de vezes (alt) que saiu o número i  
    escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')


def coluna(posx,posy, lado_1, lado_2,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # desenha
    turtle.color(cor)
    turtle.begin_fill()
    for i in range(2):
        turtle.forward(lado_1)
        turtle.left(90)
        turtle.forward(lado_2)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()
    
def linha(posx,posy,comp_linha,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.color(cor)
    turtle.pendown()
    # linha
    turtle.forward(comp_linha) 
    turtle.hideturtle()
    
def escreve_numero(posx,posy,fonte,valor,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.color(cor)  
    turtle.pendown()
    # escreve
    turtle.write(valor,font=fonte)
    turtle.hideturtle()
  
    
    
    
if __name__ == '__main__':
    n = 100
    teste = (46,39,105,0,44,5)
    fonte = ('Arial', 24, 'bold')
    posx = 0
    posy = 0 
    cor_1 = 'black'
    cor_2 = 'red'
    comp = 80
    lado_1 = comp/2
    lado_2 = 50
    #escreve_numero(posx,posy,fonte,n,cor)
    #linha(posx,posy,comp,cor_2)
    #coluna(posx,posy, lado_1, lado_2,cor_2)
    #desenha(4,100, posx,posy,comp)
    histograma(teste)
    #dados_histo(n)
    turtle.exitonclick()
Agora é a vez do primeiro sub-problema, simular o lançamento do dado. Já fizemos isso em problemas anteriores semelhantes. Aqui a novidade reside no facto de querermos memorizar os resultados. Dado o facto de estarmos a usar tuplos, que são objectos imutáveis, vamos decompor esta questão em duas: (1) guardar os valores saídos em cada lançamento, (2) contar quantas vezes saiu cada um. Solução óbvia:
def lanca_dado(n):
    # lançamento
    resultado = tuple()
    for i in range(n):
        numero = random.randint(1,6)
        resultado = resultado + (numero,)
    # contagem
    conta = tuple()
    for i in range(1,7):
        conta_i = resultado.count(i)
        conta = conta + (conta_i,)
    return conta
Podemos finalmente testar o programa completo.
import turtle
import random

def dados_histo(n):
    # experiência de lançamentos
    res = lanca_dado(n)
    # visualizaçao
    histograma(res)
    
    
def lanca_dado(n):
    # lançamento
    resultado = tuple()
    for i in range(n):
        numero = random.randint(1,6)
        resultado = resultado + (numero,)
    # contagem
    conta = tuple()
    for i in range(1,7):
        conta_i = resultado.count(i)
        conta = conta + (conta_i,)
    return conta


def histograma(res):
    posx = -100
    posy = 0
    comp_linha = 80
    for i,alt in enumerate(res):
        # desenha caso i
        desenha(i+1,alt,posx,posy,comp_linha)
        # define parâmetros
        posx = posx + comp_linha

def desenha(i,alt, posx,posy,comp_linha):
    # escreve número i
    escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')    
    # desenha linha
    linha(posx,posy,comp_linha,'red')
    # desenha coluna
    coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
    # escreve número de vezes (alt) que saiu o número i  
    escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')


def coluna(posx,posy, lado_1, lado_2,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # desenha
    turtle.color(cor)
    turtle.begin_fill()
    for i in range(2):
        turtle.forward(lado_1)
        turtle.left(90)
        turtle.forward(lado_2)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()
    
def linha(posx,posy,comp_linha,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.color(cor)
    turtle.pendown()
    # linha
    turtle.forward(comp_linha) 
    turtle.hideturtle()
    
def escreve_numero(posx,posy,fonte,valor,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.color(cor)  
    turtle.pendown()
    # escreve
    turtle.write(valor,font=fonte)
    turtle.hideturtle()
  
    
    
    
if __name__ == '__main__':
    n = 100
    teste = (46,39,105,0,44,5)
    fonte = ('Arial', 24, 'bold')
    posx = 0
    posy = 0 
    cor_1 = 'black'
    cor_2 = 'red'
    comp = 80
    lado_1 = comp/2
    lado_2 = 50
    #escreve_numero(posx,posy,fonte,n,cor)
    #linha(posx,posy,comp,cor_2)
    #coluna(posx,posy, lado_1, lado_2,cor_2)
    #desenha(4,100, posx,posy,comp)
    #histograma(teste)
    #print(lanca_dado(n))
    dados_histo(n)
    turtle.exitonclick()
    
    
Para concluir o exercício, experimente com diferentes valores de tentativas. Que conclusões pode tirar à medida que n aumenta?? Identifique os pontos em que a solução não é genérica. como pode alterar a situação??

Na sala, alguns disseram que nos histogramas as colunas não estão separadas. A adaptação do código feito para que a visualização seja essa é mínima: retirar a linha na definição desenha, separar a cor do traço (pencolor) da cor de preenchimento (fillcolor) em desenha para que as colunas fiquem claramente a ver-se e, na função histograma, alterar o posicionamento ao longo do eixo dos xx da cada coluna.
import turtle
import random

def dados_histo(n):
    # experiência de lançamentos
    res = lanca_dado(n)
    # visualizaçao
    histograma(res)
    
    
def lanca_dado(n):
    # lançamento
    resultado = tuple()
    for i in range(n):
        numero = random.randint(1,6)
        resultado = resultado + (numero,)
    # contagem
    conta = tuple()
    for i in range(1,7):
        conta_i = resultado.count(i)
        conta = conta + (conta_i,)
    return conta


def histograma(res):
    posx = -100
    posy = 0
    comp_linha = 80
    for i,alt in enumerate(res):
        # desenha caso i
        desenha(i+1,alt,posx,posy,comp_linha)
        # define parâmetros
        posx = posx + comp_linha/2

def desenha(i,alt, posx,posy,comp_linha):
    # escreve número i
    escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')    
    # desenha coluna
    coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
    # escreve número de vezes (alt) que saiu o número i  
    escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')


def coluna(posx,posy, lado_1, lado_2,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # desenha
    turtle.pencolor('black')
    turtle.fillcolor(cor)
    turtle.begin_fill()
    for i in range(2):
        turtle.forward(lado_1)
        turtle.left(90)
        turtle.forward(lado_2)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()
    
def linha(posx,posy,comp_linha,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.color(cor)
    turtle.pendown()
    # linha
    turtle.forward(comp_linha) 
    turtle.hideturtle()
    
def escreve_numero(posx,posy,fonte,valor,cor):
    # posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.color(cor)  
    turtle.pendown()
    # escreve
    turtle.write(valor,font=fonte)
    turtle.hideturtle()
  
    
    
    
if __name__ == '__main__':
    n = 1000
    teste = (46,39,105,0,44,5)
    fonte = ('Arial', 24, 'bold')
    posx = 0
    posy = 0 
    cor_1 = 'black'
    cor_2 = 'red'
    comp = 80
    lado_1 = comp/2
    lado_2 = 50
    #escreve_numero(posx,posy,fonte,n,cor)
    #linha(posx,posy,comp,cor_2)
    #coluna(posx,posy, lado_1, lado_2,cor_2)
    #desenha(4,100, posx,posy,comp)
    #histograma(teste)
    #print(lanca_dado(n))
    dados_histo(n)
    turtle.exitonclick()
    
Para o leitor: e se quisermos que o histograma possa ter uma orientação qualquer??

sábado, 21 de outubro de 2017

Verdades...

Pense nisto ...