quarta-feira, 18 de novembro de 2015

Teste #2 - Turma TP2

Pergunta 1
Diga o que são objectos mutáveis e exemplifique.
Resposta: São objectos que podem ter o seu valor alterado sem alterar a sua identidade. As listas são um exemplo.

b) Analise a listagem seguinte e diga, justificando, o que vai aparecer no lugar dos pontos de interrogação.
>>> x_2 = ([1,2,3],[4,5,6])
>>> x_2[0][1] = 'b'
>>> x_2
???             --> ([1,'b',3],[4,5,6])
>>> x_2[0] = (7,8,9)
???            --> Dá erro de atribuição. Não  se pode alterar, por atribuição,  o valor de um tuplo
Pergunta 2
Pretendia-se obter o valor aproximado da função geo(r) igual ao somatório com i a variar de 0 até infinito de 1/(r^i). Valorizava-se a consideração da precisão.
Versão sem precisão (soma de n termos):
def geo(r,n):
    res = 0
    for i in range(n):
        res += 1/pow(r,i)
    return res
Agora uma versão com a precisão a ser definida.
def geo_b(r,prec):
    erro = 1
    res = 0
    n = 0
    while erro > prec:
        aux = res
        res += 1/pow(r,n)
        erro = abs(aux - res)
        n += 1
    return res
Esta solução funciona com base na consideração da diferença de dois valores consecutivos. Para este tipo de séries infinitas isso não causa problema. Podemos mesmo usar outra solução:
def geo_c(r,prec):
    i = 1
    termo = res = 1
    while termo > prec:
        termo = 1/pow(r,i)
        res += termo
        i += 1
    return res
Pergunta 3
Pedia-se para transformar uma imagem a preto e branco, representada por uma lista de listas de uns (preto) e zeros (branco), na sua imagem no espelho, sem destruir a imagem original. Uma pequena reflexão diz-nos que é suficiente inverter a imagem, linha a linha.
def espelho(imagem):
    nova_imagem = []
    for linha in imagem:
        nova_linha = linha[:]
        nova_linha.reverse()
        nova_imagem.append(nova_linha)
    return nova_imagem

Teste #2 - Turma TP1

Pergunta 1

a) Diga o que entende por um objecto ser homogéneo. Indique um tipo de objectos que tem esta característica.
Resposta: quando as componentes do objecto têm quer ser todas do mesmo tipo. Exemplo: cadeias de caracteres.

b) Considere a seguinte listagem. Diga, justificando, o que vai aparecer no lugar dos pontos de interrogação.
>>> x_1 = [(1,2,3),(4,5,6)]
>>> x_1[0] = (7,8,9)
>>> x_1
??? --> [(7,8,9),(4,5,6)]
>>> x_1[0][1] = 'b'
??? --> Dá erro de atribuição. Não  se pode alterar, por atribuição,  o valor de um tuplo
Pergunta 2
Implemente um programa que calcula o valor aproximado da função zeta(s) igual ao somatório quando n varia de 1 a infinito de 1/(n^s). Valorizava-se uma solução que controlasse o erro máximo da solução.

Sem considerar a precisão, a solução baseia-se em somar um certo número de termos:
def zeta(s,k):
    res = 0
    for n in range(1,k+1):
        res += 1/pow(n,s)
    return res
Considerando agora a precisão:
def zeta(s,prec):
    erro = 1
    res = 0
    n = 1
    while erro > prec:
        aux = res
        res += 1/pow(n,s)
        erro = abs(aux - res)
        n += 1
    return res
Pergunta 3
Dadas duas imagens a preto e branco, representadas por uma lista de listas de uns (preto) e zeros (branco), construa uma nova imagem, sem destruir as anteriores, que é preta (um) numa dada posição apenas quando as duas imagens de entrada são ambas pretas nessa posição. A solução mais básica:
def intersecta(img_1, img_2):
    nova_img = []
    for i in range(len(img_1)):
        nova_linha = []
        for j in range(len(img_1[0])):
            if img_1[i][j] == 1 and img_2[i][j] == 1:
                nova_linha.append(1)
            else:
                nova_linha.append(0)
        nova_img.append(nova_linha)
    return nova_img
Uma alternativa que evita o teste dentro dos ciclos:
def intersecta(img_1, img_2):
    nova_img = []
    for i in range(len(img_1)):
        nova_linha = []
        for j in range(len(img_1[0])):
                nova_linha.append(img_1[i][j] * img_2[i][j])
        nova_img.append(nova_linha)
    return nova_img
Em vez do produto também se podia usar um AND lógico. Ainda outra alternativa seria construir uma imagem só com zeros e colocar a um sempre que o produto das outras for igual a um,, ou a soma for igual a 2. Fica para exercício.

terça-feira, 17 de novembro de 2015

Teste # 1 - Especial

Pergunta 2

Uma slot-machine tem três posições, podendo aparecer um de 5 objectos diferentes em cada posição. Pretendia-se uma programa que simulasse uma jogada indicando se se ganhou (todas as posições iguais) ou se se perdeu. Esta pergunta era muito simples, tanto mais que se dava a informação relativamente ao método choice do módulo random. Assim uma solução possível seria:
import random

def slot_machine():
    valores = 'ABCDE'
    res_1 = random.choice(valores)
    res_2 = random.choice(valores)
    res_3 = random.choice(valores)
    if res_1 == res_2 and res_2 == res_3:
        return True
    return False
Notar que os valores possíveis são fixos e o número de posições também (igual a 3). Podemos generalizar para qualquer conjunto de objectos e qualquer número de posições. Vejamos como.
def slot_machine_b(valores,k):
    res = ''
    for i in range(k):
        res += random.choice(valores)
    return all_equal(res)

def all_equal(cadeia):
    conta = cadeia.count(cadeia[0])
    return conta == len(cadeia)
Como se observa passamos a usar um ciclo for com uma variável a fazer de acumulador. Uma variante para este caso:
def slot_machine_c(valores,k):
    ganhar = [''.join(k * [str(elem)]) for elem in valores]
    res = ''
    for i in range(k):
        res += random.choice(valores)
    return res in ganhar
Aqui usamos listas por compreensão para gerar todas as sequências de vitória, o que facilita o teste de tudo igual.

Pergunta 3

Pediam-se dois programas usando o módulo turtle. Um primeiro, que permitia desenhar uma lâmina, parametrizando a sua posição central, o tamanho dos dois braços e a orientação. O segundo, devia usar o primeiro para desenhar diferentes tipos de ventoinhas. Comecemos pelo primeiro.
def lamina(cent_x,cent_y,lado_1,lado_2,angulo, orient):
    # inicialização
    turtle.showturtle()
    turtle.penup()
    turtle.goto(cent_x, cent_y)
    turtle.setheading(orient)
    turtle.pendown()
    turtle.dot(10)
    # lamina superior
    turtle.forward(lado_2//2)
    turtle.lt(angulo)
    turtle.forward(lado_1)
    # recentra
    turtle.penup()
    turtle.goto(cent_x, cent_y)
    turtle.setheading(orient+180)
    turtle.pendown() 
    # lamina inferior
    turtle.forward(lado_2//2)
    turtle.lt(angulo)
    turtle.forward(lado_1)  
    # finaliza
    turtle.hideturtle()
Limita-se a ser … comprido. Mas não é difícil. Os comentários dispensam mais explicações. Procure tornar o código mais sintético, por exemplo, usando um mesmo programa para desenhar as duas partes da lâmina.

Resolvida esta questão, o segundo programa é trivial.
def ventoinha(num, dist, cent_x,cent_y,lado_1,lado_2,angulo, orient):
    # inicialização
    turtle.showturtle()
    turtle.penup()
    turtle.goto(cent_x, cent_y)
    turtle.pendown()
    turtle.dot(10) 
    # desenha num lâminas
    for i in range(num):
        lamina_c(cent_x,cent_y,lado_1,lado_2,angulo, orient)
        orient += dist

segunda-feira, 16 de novembro de 2015

Imagens à roda

Todos nos habituámos a brincar com programas que manipulam imagens. Com frequência queremos um programa que roda uma imagem de 90 ou de 180 graus. Vamos ver como podemos resolver o primeiro caso. Depois o segundo é trivial.

Já discutimos como podemos representar uma imagem a preto e branco em Python: uma lista de listas. Para resolver este problema sabemos que temos que percorrer toda a imagem e para cada elemento da imagem saber qual será a sua nova posição. Mas no caso de uma rotação de 90 graus isso é fácil de resolver se olharmos globalmente para a matriz que representa a nossa imagem: a coluna i da matriz vai passar a ser a linha i da nova matriz… Daí a solução seguinte:
def roda_90(imagem):
    """Baseia-se na construção da transposta da imagem vista como uma matriz."""
    imagem_aux = list()
    # troca colunas por linhas = transpõe
    for coluna in range(len(imagem[0])):
        nova_linha = list()
        for linha in imagem:
            nova_linha.append(linha[coluna])
        imagem_aux.append(nova_linha)
    # inverte dentro das linhas
    for linha in range(len(imagem_aux)):
        imagem_aux[linha] = imagem_aux[linha][::-1]
    return imagem_aux
Esta solução numa ideia simples: percorrer por colunas e para cada coluna ir buscar os elementos das diferentes linhas. O modo como fazemos isto obriga-nos no final a inverter todas as linhas. Esta ultima parte não é simpática. Mas podemos mudar as coisas.
def roda_90_b(imagem):
    """Baseia-se na construção da transposta da imagem vista como uma matriz."""
    imagem_aux = list()
    # troca colunas e linhas = transpõe
    for coluna in range(len(imagem[0])):
        nova_linha = list()
        for linha in imagem:
            nova_linha = [linha[coluna]] + nova_linha
        imagem_aux += [nova_linha]
    return imagem_aux
Como se pode ver, em vez de usarmos append, que coloca sempre no final, usamos a operação de concatenação de listas (+), colocando o novo elemento sempre à cabeça, o que permite ter no final a nova linha já invertida.

Como temos vindo a explicar ao longo destes pequenos posts há sempre muitas alternativas. Algumas fazem uso de um conhecimento mais profundo de Python. É o caso da solução que apresentamos a seguir.
def roda_90_c(imagem):
    copia = copy.deepcopy(imagem)
    transposta = list(zip(*copia))
    final = [linha[::-1] for linha in transposta]
    return final
Neste exemplo, usamos um argumento na forma *copia. Isto significa que o objecto copia vai ser desmembrado nos seus elementos e é sobre esses elementos que vai ser aplicada a função zip. Deste modo obtemos a transposta. Depois é só inverter cada linha usando listas por compreensão. Refira-se finalmente que usámos uma cópia profunda da imagem inicial para não a destruir.

Cuidar da imagem...

Durante as aulas vimos como se podia representar uma imagem a preto e branco através de uma lista de listas de 0s e 1s. A questão concreta colocada na aula era a de produzir o negativo de uma imagem, sem destruir a imagem original. O exercício não era difícil. Temos que percorrer toda a imagem e efectuar a alteração dos 0s para 1s e os 1s para 0s. Como a estrutura é 2D vamos precisar de dois ciclos. Como não queremos a destruição da imagem original precisamos construir uma nova. Uma solução que respeite o enunciado é a seguinte:
def negativo(imagem):
    nova_imagem = []
    for linha in imagem:
        nova_linha = []
        for coluna in linha:
            if coluna == 0:
                nova_linha.append(1)
            else:
                nova_linha.append(0)
        nova_imagem.append(nova_linha)
    return nova_imagem
Como se pode ver a imagem é construída a partir de uma imagem vazia. Os dois ciclos são percorridos pelo conteúdo.

Podemos fazer de modo um pouco diferente. Criamos uma cópia da imagem inicial, e alteramos de acordo com o enunciado. Neste caso percorremos os ciclos for pela posição, pois estas são necessárias para a actualização da imagem. A cópia é feita usando o método deepcopy do módulo copy para assegurar que as duas imagens ficam totalmente separadas.
import copy

def negativo(imagem):
    copia = copy.deepcopy(imagem)
    for linha in range(len(imagem)):
        for coluna in range(len(imagem[0])):
            if copia[linha][coluna] == 0:
                copia[linha][coluna] = 1
            else:
                copia[linha][coluna] = 0
    return copia
Estas duas versões parecem esgotar as alternativas. Mas não é bem verdade isso. Podemos evitar o recurso ao teste no interior dos dois ciclos.
def negativo_b(imagem):
    copia = copy.deepcopy(imagem) 
    for linha in range(len(imagem)):
        for coluna in range(len(imagem[0])):
            copia[linha][coluna] = (copia[linha][coluna] + 1) %  2
    return copia
O que fizemos? Usemos o conhecimento de que o resto da divisão de um numero por dois ou é 0 ou é 1. No nosso caso somamos 1 ao conteúdo da matriz. Se for 0, fica 1. Se for 1 passa a 0! E pronto. Agora é que esgotámos as alternativas. Certo? Não, errado! Mais uma variante:
def negativo_c(imagem):
    copia = copy.deepcopy(imagem) 
    for linha in range(len(imagem)):
        for coluna in range(len(imagem[0])):
            copia[linha][coluna] ^= 1
    return copia
Esta solução é semelhante à anterior, são que usamos a operação binária ou exclusivo.

segunda-feira, 26 de outubro de 2015

Teste # 1 - TP2

Pergunta 2

Como encontrar os números perfeitos entree 1 e um dado n? Por número perfeito entende-se um número que é igual ao produto de dois inteiros iguais. Por exemplo, 25 = 5 * 5 é um número perfeito. Outra forma de definir seria dizer que é um número cuja raiz quadrada é um inteiro.

Uma solução muito simples será calcular os sucessivos produtos de um inteiro por si próprio filtrando os resultados menores ou iguais a n.
def perfeito(n):
 for i in range(1,n+1):
  prod = i * i
  if prod <= n:
   print(n)
Fácil, certo? Mas será que vale a pena repetir i ciclo n vezes?? Por exemplo, se n=100 é claro que basta testar os inteiros … até 10, ou seja,até à raiz quadrada de 100. Daí uma nova solução.
import math

def perfeito(n):
 sup = int(math.sqrt(n))
 for i in range(1,sup+1):
  prod = i * i
  if prod <= n:
   print(n)
E pronto. Notar apenas que temos que garantir que os argumento de range são números inteiros.

Pergunta 3

Esta pergunta é semelhante à colocada na TP1. Daí que apresentemos apenas a solução. O leitor deve referir-se ao texto anterior caso não entenda o que é apresentado. Dito isto, chamamos a atenção para o modo como conseguimos que a formação seja diferente, alterando apenas o modo de modificar a nova posição.
import turtle

def triangulo(lado,orientacao,pos_x,pos_y,cor):
    # inicializa tartaruga
    turtle.showturtle()
    turtle.penup()
    turtle.goto(pos_x,pos_y)
    turtle.setheading(orientacao)
    turtle.colormode(255)
    turtle.pencolor(cor)
    turtle.pendown()
    # desenha triangulo
    for i in range(3):
        turtle.forward(lado)
        turtle.left(120)
    turtle.hideturtle()
    
def boneco(lado,orientacao,pos_x,pos_y,cor,n):
    for i in range(n):
        triangulo(lado,orientacao,pos_x,pos_y,cor)
        pos_x = pos_x - lado//5
        pos_y = pos_y - lado//5
        orientacao = orientacao + 15
        lado = lado + 10

if __name__ == '__main__':
    #triangulo(40,45,25,50,(0,255,255))
    boneco(50,0,0,0,(0,0,255),10)
    turtle.exitonclick()

Teste #1 - TP1

Pergunta 2

Era pedido um programa que calculasse a percentagem de ocorrências de caras no lançamento de uma moeda ao ar. O número dos lançamentos era um parâmetro do programa.

Não era preciso pensar muito para perceber que: (a) vamos precisar de simular o lançamento da moeda um número fixo de vezes e, (b) contar o número de vezes em que ocorreu caras. Um modelo inicial de solução poderá ser:
def caras(n):
 # inicializa contagem
 for i in range(n):
  # lança moeda
  if #caras?:
   # actualiza contagem
 return #percentagem
Aprofundando um pouco mais a solução, torna-se claro que vamos precisar de um acumulador que conte o número de vezes em que saiu caras:
def caras(n):
 # inicializa contagem
 conta = 0
 for i in range(n):
  # lança moeda
  if #caras?:
   # actualiza contagem
   conta = conta + 1
 return conta/n * 100
E estamos perante o padrão ciclo - acumulador!
Simular o lançamento pode ser conseguido recorrendo ao módulo random e ao método randint.
import random 

def caras(n):
 # inicializa contagem
 conta = 0
 for i in range(n):
  # lança moeda
  resultado = random.randint(0,1)
  if resultado == 0:
   # actualiza contagem
   conta = conta + 1
 return conta/n * 100
Nesta solução admitimos que 0 significa caras. Também podíamos usar outra representação, usando agora o método choice.
import random 

def caras(n):
 # inicializa contagem
 conta = 0
 for i in range(n):
  # lança moeda
  resultado = random.choice(’caras’,’coroas’)
  if resultado == ‘caras’:
   # actualiza contagem
   conta = conta + 1
 return conta/n * 100
Pergunta 3

Este problema tinha duas partes. Primeiro, escrever um programa que permite desenhar um quadrado, tendo como parâmetros o tamanho do lado, a cor do traço, a orientação e a posição. Segundo, usar este programa como auxiliar para desenhar vários quadrados, alterando os parâmetros em cada um de modo a desenhar uma forma semelhante a um nautilus. Divididas as questões deste modo a primeira questão era trivial (e já resolvida nas aulas,…).
import turtle

def quadrado(lado,orientacao,pos_x,pos_y,cor):
    # inicializa tartaruga
    turtle.showturtle()
    turtle.penup()
    turtle.goto(pos_x,pos_y)
    turtle.setheading(orientacao)
    turtle.colormode(255)
    turtle.pencolor(cor)
    turtle.pendown()
    # desenha quadrado
    for i in range(4):
        turtle.forward(lado)
        turtle.right(90)
    turtle.hideturtle()
Como se pode ver, no início inicializamos a tartaruga de acordo com os parâmetros, para de seguida desenhar o quadrado. Não há muito mais que se possa dizer.

Quanto à segunda parte do problema só tínhamos que perceber que se trata de repetir o desenho do quadrado com diferente orientação, posição e tamanho. E foi para isso que escrevemos o primeiro programa…
def boneco(lado,orientacao,pos_x,pos_y,cor,n):
    for i in range(n):
        # Desenha Quadrado
        quadrado(lado,orientacao,pos_x,pos_y,cor)
        # Actualiza parâmetros
        pos_x = pos_x + lado//5
        pos_y = pos_y + lado//5
        orientacao = orientacao + 15
        lado = lado + 10
Muitos não entenderam o significado da expressão “utilize o primeiro programa como auxiliar”. Espero que com o exemplo concreto tenham agora percebido.