domingo, 19 de fevereiro de 2017

EXAME RECURSO - P1

Dado o programa
 
def xpto(lista):
    # ordena lista de modo decrescente
    for i in range(len(lista)):
        m = max(lista[i:])
        ind = lista.index(m)
        lista[i],lista[ind] = lista[ind], lista[i]
    return lista
pretende-se saber o que faz, como o faz e se, eventualmente, tem algum erro e como pode ser corrigido.

O programa recebe como entrada uma lista de números e devolve essa mesma lista ordenada de modo decrescente. Funciona percorrendo a lista da esquerda para a direita e na etapa i determinar a posição do maior elemento da sub-lista desde a posição i até ao final. De seguida esse elemento troca a sua posição com o elemento na posição i. Existe uma situação em que este algoritmo não funciona, quando existem elementos repetidos. Para corrigir, basta usar a instrução
 
ind = lista.index(m,i)
no lugar de
 
ind = lista.index(m)

domingo, 8 de janeiro de 2017

Teste 3 - TP2

Problema 2

Saber se temos prémio no euromilhões e o seu valor não é tarefa difícil. Na solução abaixo admitimos que vamos ter a chave correcta e a nossa chave representadas por uma lista de dois elementos. O primeiro, é uma lista de cinco números e, o segundo, uma lista de dois números (as estrelas). Por outro lado o valor dos prémios está armazenado num dicionário em que a chave é um tuplo (n1, n2) que traduz quantos números e estrelas acertámos e o valor é o prémio.
def premio_euro(dicio,chave_certa, minha_chave):
    # as chaves estão na forma [[5 números],[2 estrelas]]
    # verifica números
    num_certos = chave_certa[0]
    num_meus = minha_chave[0]
    conta_n = 0
    for num in num_meus:
        if num in num_certos:
            conta_n += 1
    # verifica estrelas
    est_certas = chave_certa[1]
    est_minhas = minha_chave[1] 
    conta_e = 0
    for est in est_minhas:
        if est in est_certas:
            conta_e += 1
    # calcula prémio
    return dicio.get((conta_n,conta_e),0)
Problema 3

Pretende-se normalizar os números guardados num ficheiro. Cada linha do ficheiro contém um nome e os números. A normalização é feita por linha, subtraindo a cada número a média dos valores dos números da linha e dividindo o resultado pelo respectivo desvio padrão.
import statistics

def normaliza_fich(fich_entrada, fich_saida):
    # abre ficheiros
    f_in = open(fich_entrada,'r',encoding='utf8')
    f_out = open(fich_saida,'w',encoding='utf8')
    # lê e normaliza por linha
    for linha in f_in:
        # recolhe nome e números
        linha = linha.strip().split()
        nome = linha[0]
        numeros = [int(num) for num in linha[1:]]
        # normaliza números
        num_normais = normaliza(numeros)
        # escreve resultado
        nova_linha = nome + ' '.join([str(num) for num in num_normais]) + ‘\n'
        f_out.write(nova_linha)
    # fecha ficheiros
    f_in.close()
    f_out.close()
    
def normaliza(numeros):
    media = statistics.mean(numeros)
    desvio_pad = statistics.stdev(numeros)
    return [(num - media)/desvio_pad for num in numeros]

A função auxiliar normaliza usa o módulo statistics para normalizar os números dados numa lista.

sábado, 7 de janeiro de 2017

Teste 3 - TP1

Problema 2

Num dicionários a diferentes chaves podemos ter associado o mesmo valor. O problema de calcular qual o valor mais frequente pode ser resolvido de forma simples do seguinte modo:
def moda(dicio):
    # inverte dicio
    novo_dicio = {}
    for c,v in dicio.items():
        novo_dicio[v] = novo_dicio.get(v,[]) + [c]
    # passa a lista ordenada
    lista_items = list(novo_dicio.items())
    mais_freq = (0,[])
    for val,lst in lista_items:
        if len(lst) > len(mais_freq[1]):
            mais_freq = (val,lst)
    # devolve o mais valor mais frequente
    return mais_freq    
Como os comentários indicam, a estratégia de solução passa por inverter o dicionário, converter para uma lista e depois calcular o elemento, i.e. o par (valor, lista das chaves com esse valor), mais frequente. Para os pythónicos apresentamos outra solução que recorre a funções anónimas:
def moda_b(dicio):
    # inverte dicio
    novo_dicio = {}
    for c,v in dicio.items():
        novo_dicio[v] = novo_dicio.get(v,[]) + [c]
    # passa a lista ordenada
    lista_items = list(novo_dicio.items())
    lista_items.sort(key=lambda x: len(x[1]),reverse=True)
    # devolve o mais valor mais frequente
    return lista_items[0]
A função anónima (lambda) é usada para o ordenamento ser feito de acordo com o tamanho do elemento na posição 1.

Problema 3

O problema envolvia um ficheiro em que cada linha é formado por um nome e números (>= 3). Pretende-se criar um novo ficheiro com o nome e a média dos números depois de retirar o menor e o maior.

Uma solução mágica:
def fich_media(fich_entrada,fich_saida):
    # modificar linha a linha
    # abre ficheiros
    f_in = open(fich_entrada,'r',encoding='utf8')
    f_out = open(fich_saida,'w',encoding='utf8')
    
    # trata por linha
    for linha in f_in:
        # escolhe numeros
        linha = linha.strip().split()
        nome = linha[0]
        numeros = [ int(num) for num in linha[1:]]
        numeros.sort()
        
        # calcula média
        media = sum(numeros[1:-1])/(len(numeros)-2)
        # escreve nova linha
        f_out.write(nome + str(media) + '\n')
    # fecha ficheiros
    f_in.close()
    f_out.close()
Dada a natureza do enunciado resolvemos natural tratar o problema linha a linha. Cada linha é partida e os seus números ordenados. Calculamos de seguida a média retirando o primeiro (o mais pequeno) e o último (o maior).