Documente Academic
Documente Profesional
Documente Cultură
Dados
Input: salinas (Pret, Max1, Max2). %3 inteiros
Pret - Número de gramas pretendido.
Max1 - Capacidade máxima da caixa 1 em gramas.
Max2 - Capacidade máxima da caixa 2 em gramas.
Resultado
Output: uma lista de acções -
e(c1) - enche caixa 1
e(c2) - enche caixa 2
d(c1) - despeja caixa 1
d(c2) - despeja caixa 2
d(c1,c2)- despeja a caixa 1 sobre a 2 até encher a 2 ou vazar a 1
d(c2,c1)- despeja a caixa 2 sobre a 1 até encher a 1 ou vazar a 2
d_saco(c1) - despeja a caixa 1 no saco do cliente.
d_saco(c2) - despeja a caixa 2 no saco do cliente.
Exemplos
input: salinas(100,500,200).
output: [e(c1),d(c1,c2),d(c2),d(c1,c2),d_saco(c1)]
-----------
input: (50,500,200).
output: no
-----------
input:(200,250,550).
output: [e(c1),d(c1,c2),e(c1),d(c1,c2),e(c1),d(c1,c2),d_saco(c1)]
-----------
input:(100,250,550).
output:
[e(c2),d(c2,c1),d(c1),d(c2,c1),d(c1),d(c2,c1),e(c2),d(c2,c1),d(c1),d(c2,c1),d_sa
co(c2)]
ou qq uma das seguintes (que têm o mesmo número de movimentos), só precisa de devolver uma solução.
[e(c2),d(c2,c1),d(c1),d(c2,c1),d(c1),d_saco(c2),e(c2),d(c2,c1),d(c1),d(c2,c1),d_saco(c2)]
ou
[e(c2),d(c2,c1),d(c1),d(c2,c1),d_saco(c2),e(c2),d(c1),d(c2,c1),d(c1),d(c2,c1),d_saco(c2)]
ou
[e(c2),d(c2,c1),d(c1),d(c2,c1),d_saco(c2),d(c1),e(c2),d(c2,c1),d(c1),d(c2,c1),d_saco(c2)]
ou
[e(c2),d(c2,c1),d(c1),d(c2,c1),d_saco(c2),d(c1,c2),e(c2),d(c2,c1),d(c1),d(c2,c1),d_saco(c
2)]
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2003/A – Concurso Nacional de Programação em Lógica 2003
?- ca([0,0,0,1,0,0,0],p+(1-2*p)*(q+r-q*r),13).
0 0 0 1 0 0 0
0 0 1 1 1 0 0
0 1 1 0 0 1 0
1 1 0 1 1 1 1
1 0 0 1 0 0 0
1 1 1 1 1 0 0
1 0 0 0 0 1 0
1 1 0 0 1 1 1
1 0 1 1 1 0 0
1 0 1 0 0 1 0
1 0 1 1 1 1 1
1 0 1 0 0 0 0
1 0 1 1 0 0 0
1 0 1 0 1 0 0
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício – CNPL2003/B – Concurso Nacional de Programação em Lógica 2003
Introdução
Um dicionário contem definições (ou entradas) para expressões de uma língua, podendo essas definições
conter referências a outras expressões. Se uma referência numa das definições estiver errada é necessário
corrigir o dicionário de uma forma eficiente.
O autor do dicionário de que aqui se trata é muito propenso a erros do tipo: dar como referência a expressão E
se o que deveria referir é uma expressão E' que aparece referida numa das definições de E. Por exemplo,
referir gato em vez de felino, que aparece referido numa definição de gato --- é claro que na definição de gato
como marosca (aqui há gato!), felino não consta.
Supõe-se que o autor do dicionário não é tão incompetente que crie definições contendo referências para a
própria expressão a definir.
Tarefa
Escreva um programa que resolva problemas do tipo anterior, em que um dicionário é representado por uma
lista de entradas da forma e(X,Defs) com X um termo único que descreve a expressão, e Defs uma lista
não vazia de definições.
Cada definição é representada por um termo d(N,Descr) com N o número da definição e Descr uma lista
de termos de duas formas: p(P) para palavras e ref(R) para referências.
Dados
O programa a escrever tem como dados o dicionário errado D e uma expressão E, passados como argumentos
ao predicado de topo.
Resultados
Para um dicionário errado D e uma expressão E pretende-se obter um dicionário corrigido C substituíndo
todas as referências a E por referências a Ec, que é (segundo o autor do dicionário!) a única expressão
referida nas definições de E. Para evitar problemas futuros com o referido autor, o programa deve falhar se
houver mais que uma referência nas definições de E, bem como em situações de: mais de uma entrada para a
expressão dada, ou não existência de referência que se possa usar.
Por questões de eficiência, o dicionário errado só pode ser visitado (explicita ou implicitamente a nível do
programa Prolog) uma única vez.
O predicado de topo do programa deve ser refsx(D,E,C).
Exemplos
Exemplos usando o seguinte dicionário:
[e(autor,[d(1,[p(criador)]),
d(2,[p(escritor)]),
d(3,[p(compositor)])]),
e(ave,[d(1,[p(comida),p(de),ref(gato)])]),
e(gato,[d(1,[ref(felino),p(pequeno)]),
d(2,[p(marosca)])]),
e(leao,[d(1,[p(especie),p(de),ref(gato)])]),
e(marosca,[d(1,[p(tramoia)])]),
e(rato,[d(1,[p(comida),p(de),ref(gato)]),
d(2,[p(esperto)]),
d(3,[p(alvo),p(para),ref(gato)])]),
e(terminal,[d(1,[p(fim)])])]
?- teste_dic(D), refsx(D,gato,C).
D = ...
C = [e(autor,[d(1,[p(criador)]),
d(2,[p(escritor)]),
d(3,[p(compositor)])]),
e(ave,[d(1,[p(comida),p(de),ref(felino)])]),
e(gato,[d(1,[ref(felino),p(pequeno)]),
d(2,[p(marosca)])]),
e(leao,[d(1,[p(especie),p(de),ref(felino)])]),
e(marosca,[d(1,[p(tramoia)])]),
e(rato,[d(1,[p(comida),p(de),ref(felino)]),
d(2,[p(esperto)]),
d(3,[p(alvo),p(para),ref(felino)])]),
e(terminal,[d(1,[p(fim)])])] ? ;
no
?- teste_dic(D),
refsx([e(leao,[d(1,[p(mau)])])|D],leao,C).
Mais de uma entrada para :leao
no
?- teste_dic(D), refsx(D,rato,C).
Mais de uma referencia na definicao de :rato
no
?- teste_dic(D), refsx(D,felino,C).
Nao existe referencia na definicao de :felino
no
?- teste_dic(D), refsx(D,lixo,C).
Nao existe referencia na definicao de :lixo
no
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2003/D – Concurso Nacional de Programação em Lógica 2003
Introdução
Como estão certamente fartos de saber, os números racionais são aqueles que se podem obter a partir da
divisão de dois números inteiros. Quando passado para a forma decimal, um número racional nem sempre
pode ser representado com um número finito de casas decimais. Por exemplo: 1/3 = 0.33333... Mas, para os
números racionais, essa sequência (mesmo que infinita) de casas decimais não pode ser qualquer: a partir de
certa altura há sempre uma sequência de algarismos que se repete indefinidamente. Por isso mesmo, até é
usual representar esses números na forma decimal com a dita sequência no fim e entre parêntesis.
Por exemplo: 1/3 = 0.(3) 8/15 = 0.5(3) 1/7 = 0.(142857) 10/7 = 1.(428571)
Tarefa
Escrever um predicado Prolog que, dados dois números inteiros positivos n e m, calcule a sequência de
algarismos que se repete indefinidamente na representação decimal do número racional n/m.
Resultados
Deverá implementar o predicado ciclo(N,M,C) que, quando chamado com N e M instanciados com números
inteiros positivos, devolve em C uma lista com a sequência de algarismos que se repete indefinidamente na
representação decimal de N/M. Se N/M se conseguir representar com um número finito de casas decimais,
então em C deve ser devolvido [0]. Note que, em rigor, essa é uma sequência que se repete indefinidamente:
por exemplo, 1/4 = 0.25(0). No caso de haverem várias sequências possíveis (por exemplo 1/11 = 0.(09) =
0.0(90)), o seu predicado deverá devolver apenas uma delas. E, já agora, que seja aquela que dá origem a uma
representação mais curta do número (o 0.(09) é maiscurto que o 0.0(90), pelo que deverá ser devolvido [0,9]
e não [9,0]).
Exemplo
| ?- ciclo(1,3,C).
C = [3]; | ?- ciclo(1,11,C).
no C = [0,9];
| ?- ciclo(8,15,C). no
C = [3]; | ?- ciclo(1,4,C).
no C = [0];
| ?- ciclo(1,7,C). no
C = [1,4,2,8,5,7]; | ?- ciclo(7,7,C).
no C = [0];
| ?- ciclo(10,7,C). no
C = [4,2,8,5,7,1];
no
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2003/E – Concurso Nacional de Programação em Lógica 2003
CNPL 2003/E. CNPL 2003 – Celebridades (nota: Este enunciado contém alguns erros)
Introdução
Numa festa, uma pessoa C é uma celebridade se C é uma pessoa que toda a gente conhece. Por exemplo, na
figura abaixo, está representada uma festa em que a celebridade é o Ivo (uma seta de A para B significa que A
conhece B).
Dado que festas sem celebridades não são interessantes, nem sequer são consideradas.
Tarefa
A tarefa é escrever um programa eficiente para encontrar a celebridade da festa. Por eficiente entende-se que
a pesquisa deve ser linear no número de pessoas da festa.
Dados
O programa recebe a festa através do ficheiro “ party.pl” . A festa é representada por factos people/1 (cujo
parâmetro é a lista de pessoas na festa) e knows/2 (que significa que o primeiro parâmetro conhece o
segundo).
Resultados
Durante a pesquisa, o programa deverá imprimir os testes que executa, ou seja, imprimir frases do tipo “ Será
que a Ana conhece o Zé?” .
Exemplo
people([ana,ze,to,rui,ivo]).
knows(ana,to).
knows(ana,ivo).
knows(ze,ivo).
...
A invocação do programa deverá ser executada através do predicado celebrity/1 cujo parâmetro deverá ficar
unificado com a celebridade da festa.
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2003/F – Concurso Nacional de Programação em Lógica 2003
Introdução
Pretende-se desenvolver um programa para descodificar mensagens cifradas de acordo com uma cifra de
substituição: cada letra da mensagem original foi substituida por outra letra (existindo uma bijecção entre as
letras do alfabeto original e as letras do alfabeto do código). Por exemplo, podemos substituir a letra 'e' pela
letra 'r', a letra 'h' pela letra 'x', a letra 'l' pela letra 'v' e a letra 'o' pela letra 'p' e a mensagem 'hello' será
codificada como 'xrvvp'.
O processo de descodificação de um código deste não pode ser cego (no sentido de se tentarem todas as
combinações pois existem 26! combinações possíveis). Uma solução passa por tirar partido do facto de nem
todas as letras aparecerem com a mesma frequência. De facto, as letras em Inglês surgem pela seguinte ordem
(da mais frequente para a menos frequente): E, T, A, O, I, N, S, H, R, D, L, C, U, M, W, F, G, Y, P, B, V, K,
J, X, Q, Z. Então, a primeira etapa passa por contar o número de vezes que cada letra surge na mensagem, e
ordená-las por ordem descrescente de ocorrência. Com esta informação, podemos tentar quebrar o código:
basta associar a letra mais frequente à letra 'E', a segunda frequente à letra 'T' e assim sucessivamente (para
não surgirem confusões entre as letras já foram substituidas e as que ainda não foram, assume-se que as letras
na mensagem original (codificada) são minúsculas e que as letras que já foram descodificadas são
maiúsculas). Este método mesmo assim não é muito eficiente. Imagine-se que se chegou a uma situação
ÄOEET wTrEd". Obviamente, este caminho não irá conduzir à solução pois AOEET, que já resulta da
substituição, não é uma palavra inglesa. Por isso, uma optimização passa por, sempre que se tiver uma
palavra completa já instanciada, testar se esta existe, recorrendo a um dicionário. Se existir, continuar a
descodificação, senão, voltar atrás e procurar outra substituição, modificando a última substituição feita. Por
exemplo, se a última substituição foi 'x' por 'O' então, em vez de se considerar 'O' deve considerar-se a letra
seguinte na lista, ou seja, 'I'. Assume-se que os espaços estão no sítio certo.
Tarefa
Escrever em Prolog um programa para descodificar mensagens codificadas de acordo com o método anterior.
Dados
O programa deverá receber como argumentos uma string correspondendo à mensagem codificada. Está
disponível um ficheiro dicionario.pl com um pequeno dicionário de palavras inglesas (através do predicado
word/1).
Resultados
O programa deverá ser chamado através do predicado decode/1 e mostrará no ecrã a mensagem
descodificada, tal como nos exemplos seguintes:
?- decode(’uaof of l uxfu uv uax cxmvcxg’).
A mensagem e: THIS IS A TEST TO THE DECODER
?- decode(’rxpp cvqx tve alzx hlqlnxc uv cxmvcx uax hxfflnx’).
...
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2003/G – Concurso Nacional de Programação em Lógica 2003
Introdução
Pretende-se pintar um disco com n coroas circulares e 2n sectores, usando para tal apenas duas cores
(preto e branco). Para que haja alguma diversidade na pintura do discos, mas que as diferenças
sejam esbatidas, a pintura deverá obedecer às seguintes regras:
Dos discos abaixo, ambos com n = 3, apenas o da esquerda está pintado de acordo com as regras.
Quanto ao da direita, não só o padrão "preto-branco-preto" se repete (sectores indicados com setas)
como existem sectores adjacentes que diferem na cor de mais do que uma coroa (o sector mais
acima difere de ambos os seus adjacentes na cor das coroas mais externa e mais interna.
Tarefa
Deverá implementar o predicado pintar(N,L) que, quando chamado com N instanciado com um
número inteiro positivo, devolve em L uma forma de pintar um disco com N coroas e 2N sectores.
Resultados
A forma de pintar um disco é devolvida no segundo argumento do predicado pintar/2 como uma
lista L. Cada elemento de L corresponde à forma de pintar cada um dos sectores do disco, sendo que
elementos consecutivos correspondem a sectores adjacentes. Note que o primeiro e último
elementos de L também correspondem a sectores adjacentes. Cada elemento da lista L é ele próprio
uma lista de valores preto e branco, correspondendo à pintura de cada uma das coras desse sector,
do seu interior para o exterior.
Por exemplo, o resultado abaixo corresponde à forma de pintar o disco da esquerda.
| ?- pintar(3,L).
L = [ [branco,branco,branco],
[branco,branco,preto],
[branco,preto,preto],
[branco,preto,branco],
[preto,preto,branco],
[preto,preto,preto],
[preto,branco,preto],
[preto,branco,branco] ];
no
| ?-
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2003/H – Concurso Nacional de Programação em Lógica 2003
Introdução
A maioria dos telemóveis tem uma stack que guarda os últimos números chamados (até a um máximo de M).
Teoricamente a sua gestão consistira apenas em inserir no topo da stack, o último número chamado. Contudo
é preciso atender a duas restrições:
1. a stack só pode conter no máximo M números, o que significa que, o inserir mais um número, o mais
antigo deve ser eliminado, se a stack estiver cheia.
2. se o número a inserir já estiver guardado em alguma posição da stack, então passa para o topo, e não
se acrescenta nenhum número novo, isto é, a stack não pode conter números repetidos.
Além de tudo isso, em vez do número, deve ser inserida na stack a sigla correspondente, caso exista numa
lista telefónica (pessoal) presente.
Tarefa
Escreva um programa Prolog que, sendo dado um novo número telefónico e a stack dos últimos números
chamados, e tendo em consideração a lista telefónica existente na base de conhecimento, devolva a stack
actualizada.
Dados
Os únicos dados do problema são: o novo número a inserir na stack; e a stack actual---ambos argumentos do
predicado (a desenvolver) gere/3.
A lista telefónica a usar, para procurar a sigla associada a cada número, terá de ser acrescentada à base de
conhecimento na forma de um conjunto de factos correspondentes ao predicado lista/2 cujo 1º argumento
é um número de telefone e o 2º argumento é a sigla associada.
Resultado
O resultado esperado, nova stack com o número dado à cabeça, será fornecido como 3º argumento do
predicado gere/3.
Exemplos
Supondo que a BC contém a lista telefónica:
lista( 12345, aaa ).
lista( 23456, bbb ).
lista( 34567, ccc ).
lista( 45678, ddd ).
lista( 56789, eee ).
lista( 67890, fff ).
Seguem-se 4 exemplos de uso do gestor, gere, da stack dos números chamados (em que se supõe que o
comprimento máximo é M=4):
?- gere( 12345, [bbb], Snova ).
Snova = [ aaa, bbb ]
yes
Exemplo
Considere as regras:
rule(f1, [], []).
rule(f2, [], []).
rule(g, [f1, f2], [preprocess([f1,f2]), compile(g)]).
rule(h, [g], [bork(h)]).
rule(h, [f2], [shortcut(h)]).
rule(l, [m], [foo]).
rule(m, [l], [bar]).
Poderemos obter a seguinte sessão:
| ?- load(rules).
| ?- make(g, A, S).
A = [preprocess([f1,f2]), compile(g)],
S = [f2, f1]
| ?- make(h, A, S).
A = [shortcut(h)],
S = [f2]
| ?- make(l, A, S).
ERROR: Unhandled exception: loop([l, m])
| ?- make(x, A, S).
no
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2002/A – Concurso Nacional de Programação em Lógica 2002
CAÇA ÀS HIPÓTESES
Introdução
Num sistema de conhecimento baseado em modelos, o conhecimento causal é apresentado por regras do tipo
rN :: C <- A1 & A2 & ...& An
com o significado usual de que os antecedentes A1, A2, ... An, causam o consequente C (rN é apenas um
identificador para a regra). Para simplificar, assumiremos que os antecedentes e os consequentes são
proposições atómicas, representadas por átomos. Alguns desses átomos podem ser conhecidos, o que é
especificado através de factos do tipo
fN ! F.
em que F é o facto observado e fN um identificador único. Num sistema deste tipo, a tarefa de diagnóstico
consiste em determinar hipóteses que juntamente com os factos e as regras conhecidas “ justifiquem”
causalmente um determinado conjunto de observações. Nem todas as hipóteses são interessantes, pelo que o
sistema apenas considera hipóteses especificadas através de declarações
hN ? H.
em que H é uma hipótese e hN um identificador único. De notar que o mesmo átomo pode ser declarado
como hipótese e como facto (por exemplo, após ter sido validado).
Exemplo
Um exemplo de uma base de conhecimento causal pode ser
r1 :: febre <- gripe.
r2 :: febre_alta <- febre & fraqueza.
r3 :: dores <- gripe & febre_alta.
r4 :: febre <- anginas.
r5 :: dor_de_garganta <- anginas.
r6 :: dor_de_garganta <- constipação.
f1 ! fraqueza.
h1 ? gripe.
h2 ? fraqueza.
h3 ? anginas.
h4 ? constipação.
Neste exemplo, a observação de febre, pode ser justificada pelas hipóteses de gripe (pela regra r1) ou
anginas (pela regra r4). Já a observação de dores é justificada pela hipótese de gripe (em conjunto
com o facto f1 e as regras r1 e r2). Finalmente, a justificação das observações febre_alta e
dor_de_garganta é justificada ou pela hipótese simples anginas (com o facto f1 e as regras r2 e r4),
quer pela hipótese dupla constipação e gripe (com o facto f1 e as regras r1, r2 e r6).
Tarefa
O objectivo deste problema é implementar um predicado diag(+O, -K, -H), com o seguinte significado
1. O é um conjunto de observações que se pretende justificar, na forma O1 & O2 & ...& On (n
>= 1).
2. K retorna uma lista de identificadores de factos e regras que justificam em conjunto com H (ver
abaixo) as observações O;
3. H retorna uma lista com um conjunto de hipóteses, com cardinalidade mínima, que em conjunto
com os factos e regras de K justificam as observações O.
De notar, que o predicado só deve retornar hipóteses de cardinalidade mínima. Como a observação de
febre_alta e dor_de_garganta pode ser justificada pela hipótese simples anginas, o predicado
diag/3 não deve retornar a hipótese dupla gripe e constipação. Por retrocesso, o predicado deve
retornar todas as hipóteses de cardinalidade mínima, podendo repetir hipóteses se as regras e factos utilizados
forem diferentes.
Para a sua implementação do predicado diag/3 pode tomar em consideração que
a) todas as observações devem ter uma justificação com um máximo de 5 hipóteses
b) as regras da sua base de conhecimento não contêm dependências circulares.
Por exemplo, as regras abaixo não podem co-existir, pois febre_alta depende de febre e febre
depende de febre_alta.
rx :: febre <- febre_alta.
r2 :: febre_alta <- febre, fraqueza.
Note bem, que o programa deverá permitir ler ou dum ficheiro ou dum terminal o conjunto de regras e factos
que constituem a sua base de conhecimento, na sintaxe apresentada (poderá usar outra sintaxe, mas sofrerá de
uma penalização de 20% na avaliação).
Resultados
Assumindo que o ficheiro “ dados.pl” contem a base de conhecimento acima (regras r1 a r6, o facto f1 e as
hipóteses h1 a h4), o predicado diag/3 deve produzir os diagnósticos seguintes:
?- diag(febre, K, H).
K = [r1] , H = [gripe] ;
K = [r4] , H = [anginas] ;
no.
?- diag(febre_alta & dor_de_garganta, K, H).
K = [r5,f1,r4,r2], [H] = anginas ;
no.
?- diag(dores & dor_de_garganta, K, H).
K = [r5,f1,r1,r2,r3] , H = [gripe,anginas] ;
K = [r6,f1,r1,r2,r3] , H = [gripe,constipação] ;
K = [r5,f1,r4,r2,r3] , H = [gripe,anginas] ;
no.
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2002/B – Concurso Nacional de Programação em Lógica 2002
Palavras Cruzadas
Introdução
Todos sabem, de certeza, o que é um jogo de palavras cruzadas. Nem vale a pena estar com grandes
explicações. Aqui vamos jogar um jogo, também solitário, parecido com o das palavras cruzadas. Tal como
nas palavras cruzadas clássicas, há um quadro que inicialmente tem uma série de quadrados em branco e
outros preenchidos a negro. Só que, ao contrário do jogo clássico, aqui sabemos à partida quais as palavras
que temos de colocar no quadro (nas palavras cruzadas só temos dicas sobre que palavras poderão ser). Não
sabemos é em que posição colocar essas palavras (no jogo clássico, neste aspecto a coisa é mais facilitada,
pois sabe-se sempre em que linha ou coluna deve ser colocada cada palavra). O objectivo do jogo é colocar
no quadro todas as palavras dadas inicialmente , seguindo as regras usuais das palavras cruzadas (um caracter
apenas por quadrado; não podem haver duas palavras consecutivas, na mesma linha ou coluna, sem que pelo
meio haja pelo menos um quadrado a preto, etc).
Por exemplo, se forem dadas as palavras CENPL, PROLOG, UPA, PAI, POOP, GATO, CAO, PATITO,
ANIZ, PIGET, ZIG, TPTI e o quadro da figura 1, uma solução possivel é a que se apresenta na figura 2.
U P O O P
P A I R
A G A T O
C E N P L
P A T I T O
O Z I G
Figura 1 Figura 2
O que se pretende é que faça um programa Prolog que, dados um quadro não preenchido de palavras cruzadas
e uma lista de palavras a colocar, preencha o quadro com essas palavras.
Dados
O programa deverá ser chamado através do predicado cruzadas/0 e afixar os resultados no terminal
conforme explicado abaixo.
A descrição do quadro inicial e da lista de palavras a colocar é feita, respectivamente, através dos factos
quadro/1 e palavras/1, que serão adicionados ao seu programa.
O argumento do facto palavras/1 será uma lista de palavras, sendo que cada palavra (para lhe simplificar a
tarefa) é dada como uma lista de caracteres. Por exemplo, se se pretender colocar a lista de palavras do
exemplo acima, adiciona-se o facto:
palavras([[c,e,n,p,l],[p,r,o,l,o,g],[u,p,a],[p,a,i],[p,o,o,p],[g,a,t,o],
[c,a,o],[p,a,t,i,t,o],[a,n,i,z],[p,i,g,e,t],[z,i,g],[t,p,t,i]]).
O argumento do facto quadro/1 é uma matriz, representada como uma lista de listas (onde cada lista
elemento corresponde a uma linha). Nessa matriz as posições correspondentes a quadrados em branco têm
variáveis livres, e as correspondentes a quadrados a preto têm o símbolo `+'
. Por exemplo, o quadro da figura
1 é representado por:
quadro([[_,+,_,_,_,_],
[_,_,_,+,+,_],
[_,+,_,_,_,_],
[+,_,_,_,_,_],
[_,_,_,_,_,_],
[+,_,+,_,_,_]]).
Resultados
Caso não seja possivel colocar todas as palavras no quadro, o predicado cruzadas/0 deve falhar. Se for
possivel, deve escrever no ecrã o quadro preenchido com as palavras (uma linha do ecrã por linha do quadro;
os quadrados preenchidos a preto devem ter o símbolo `+' ; se houverem casas por preencher após a colocação
de todas as palavras, no seu lugar deverá estar o símbolo `_').
Por exemplo, se chamado com os dois factos acima, o resultado deverá ser o que se apresenta à esquerda. Se,
para o mesmo quadro, a lista de palavras for a acima, mas sem as duas últimas palavras (tpti e zig) o resultado
será o que apresenta à direita (note o `_'na penúltima posição da última linha):
| ?- cruzadas. | ?- cruzadas.
u+poop u+poop
pai++r pai++r
a+gato a+gato
+cenpl +cenpl
patito patito
+o+zig +o+z_g
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2002/C – Concurso Nacional de Programação em Lógica 2002
Exemplo
Note que
• é irrelevante a ordem pela qual os cubos são empilhados (e portanto pode ser considerada fixa);
• a escolha da face frontal de um cubo apenas determina a face de trás (ou vice-versa);
• a escolha da face lateral direita determina a da face lateral esquerda (ou vice-versa).
O problema pode portanto ser resolvido escolhendo primeiro as faces frente/trás e depois, face a esta escolha,
escolhendo as faces laterais.
Tarefa
A tarefa é escrever um programa que solucione o problema. Para isso considere que os dados do problema (os
quatro cubos) são representados pelo seguinte grafo não orientado e etiquetado:
• os nós do grafo são as quatro cores — a,b,c,d;
• para i=1,2,3,4 e para todo o par de cores (cor,cor’), existe uma aresta entre cor e cor’ etiquetada
com o número i sse esse par de cores se encontra em faces opostas no cubo i.
Esta forma de representar os dados reflecte o facto da escolha de uma face de cada cubo determinar a face
oposta e permite portanto restringir o espaço de pesquisa da solução.
Dados
O programa será invocado através do predicado empilha_cubos/1 com o argumento instanciado com uma
lista de triplos
Frt/Tras Esq/Dir
Cubo 1 : d / a c / b
Cubo 2 : c / c b / a
Cubo 3 : b / d a / c
Cubo 4 : a / b d / d
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2002/D – Concurso Nacional de Programação em Lógica 2002
DETECTIVES
Introdução
O local, uma estância de verão, tinha um aspecto tão agradável que seria impossível imaginá-la como palco
de um crime de odor tão sórdido. A estância era composta por sete bungalows, quatro junto à lagoa (A, B, C,
D), dois junto ao oceano (F e G) e um no meio (E), ligados por caminhos de acordo com a figura:
Um empregado viu um homem de aspecto sinistro, carregando um balde grande, aproximar-se da estância
vindo da lagoa e esconder-se dentro de um dos bungalows próximos desta. O homem percorreu os caminhos
da estância deixando rastos de peixe podre por todo o lado.
A polícia, chamada ao local, detectou pelas pegadas lamacentas deixadas pelo criminoso, que este tinha
percorrido todos os caminhos da estância exactamente uma vez. Os detectives repararam também que não
havia pegadas que conduzissem para fora da estância, concluindo por isso que o infractor se devia ainda
encontrar escondido dentro de um dos bungalows. Infelizmente, as pegadas eram tão pouco nítidas que os
detectives foram incapazes de determinar a direcção destas. Para piorar ainda mais as coisas, o empregado da
estância que tinha avistado o infractor não se lembrava do bungalow em que este tinha entrado. A polícia
ficou impossibilitada de reproduzir o caminho do infractor. O máximo que conseguiu apurar foi que ele
nunca percorreu o mesmo caminho duas vezes.
Tarefa
Escrever em Prolog um programa para auxiliar a polícia a descobrir em que bungalow o infractor se encontra
escondido. O programa deverá poder ser aplicado em estâncias com topologias diferentes da do exemplo, mas
mantendo sempre o requisito de cada caminho ser percorrido exactamente uma vez.
Dados
O programa deverá receber como argumentos a lista dos possíveis pontos de partida (no caso do exemplo, a
lista [a, b, c, d]) e a topologia da estância, representada por uma lista de pares correspondentes aos vários
caminhos (no caso do exemplo, [(a,b), (a,c), (b,c), (b,e), (b,g), (c,e), (c,d), (d,e), (d,f), (e,g), (f,g)]).
Resultados
O programa deverá ser chamado através do predicado detective/2 que mostrará no ecrã o par (ou pares,
caso exista mais do que uma solução) de bungalows em que o primeiro elemento do par é o bungalow de
onde o infractor partiu e o segundo elemento do par é o bungalow onde o infractor está escondido.
Exemplos:
?- detective([a,b,c,d],
[(a,b),(a,c),(b,c),(b,e),(b,g), (c,e),(c,d),(d,e),(d,f),(e,g),(f,g)]).
[(d, g)]
?- detective([a,b],[(a,b),(a,c),(b,c)]).
[(a, a), (b, b)]
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2002/E – Concurso Nacional de Programação em Lógica 2002
Alocador de Registos1
Introdução
Na construção de compiladores, um sub-problema comum e bem identificado é o da geração de código para
expressões aritméticas. As arquitecturas reais terem um número limitado de registos, facto que condiciona o
código que pode ser gerado. Um compilador deverá saber qual o número de registos de que dispõe para
calcular as expressões intermédias.
Tarefa
Considere que estamos a gerar código para um PDP-11 glorificado (ie. com número arbitrário de registos),
que dispõe das seguintes instruções:
Instrução Acção Observações
mov SRC, DST DST ← SRC Copia SRC para DST.
add SRC, DST DST ← DST + SRC Efectua uma operação aritmética.
Os operandos poderão ser registos, da forma rN (p/ex. r0, r7), constantes da forma #NNN (p/ex. #12) ou
nomes, que obedecem à sintaxe habitual para os identificadores.
Pretende-se determinar o número mínimo de registos com os quais se consegue avaliar uma determinada
1
expressão sem ``spilling,'
' produzindo neste processo uma sequência de instruções que avalie a expressão,
colocando o resultado no registo r0.
O algoritmo a aplicar pode ser resumido da seguinte forma: considere que se pretende gerar código para uma
expressão E=EL @ ER, em que ER e EL são expressões e @ é uma operação aritmética. Seja NL e NR o número
de registos necessários à avaliação de EL e ER respectivamente, o número de registos para avaliar E será dado
por N' =max(NL,NR+1) se avaliarmos EL primeiro e guardarmos o seu valor num registo enquanto avaliarmos
ER. Se optarmos por avaliar ER primeiro, precisaremos de N' '=max(NL+1,NR) registos. O objectivo será
escolher a ordem de avaliação que resulte no menor valor de N (de entre N'e N' '
), preservando a correcção do
resultado (a operação deverá ser comutativa).
Notas:
• Para avaliar uma expressão trivial (constante ou nome) precisamos de 0 registos pois estas podem ser
utilizadas directamente em operações.
• Depois de avaliar uma subexpressão, todos os registos usados nesse processo (excepto aquele em que
ficou o resultado) estão disponíveis para avaliar outra subexpressão.
• Atenção: aplicam-se as regras de comutatividade habituais.
Dados
Pretende-se obter N, tal que sejam necessários N registos distintos (de r0 a rN-1) para avaliar uma expressão
aritmética E. E será representada por um termo Prolog ``ground' '
, encarado como uma árvore cujas folhas
poderão ser átomos (designam os nomes) ou números (designam as constantes) e cujos nós interiores
corresponderão a uma operação aritmética (+/2, -/2, */2 e ’/’/2). Deverá ser implementado o predicado
sethi(+EXPR, ?NREGS) que produz como output uma sequência de instruções do PDP-11 correspondente
à avaliação de EXPR com NREGS registos, colocando o resultado final no registo r0. NREGS será instanciado,
caso esteja livre à entrada
Resultados
Exemplos de execução:
| ?- sethi(a+7*b, NR).~
mov #7,r0
mul b,r0
add a,r0
NR = 1
Yes
| ?- sethi((a+3*b-4/c)*(7-8*d), NR).
mov #3,r0
mul b,r0
add a,r0
mov #4,r1
div c,r1
sub r1,r0
mov #7,r1
mov #8,r2
mul d,r2
sub r2,r1
mul r1,r0
NR = 3
yes
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2002/F – Concurso Nacional de Programação em Lógica 2002
CAVALEIRO DE EULER
Introdução
O problema que enunciamos de seguida foi colocado pelo matemático Leonhard Euler há mais de 200 anos.
Diz respeito à procura de um caminho Hamiltoniano num tabuleiro de xadrez (caminho que passa uma e uma
só vez por cada uma das casas do tabuleiro).
Começamos por lembrar que o cavalo de xadrez, numa dada casa do tabuleiro, pode ocupar, depois do seu
salto, um máximo de oito casas diferentes. Ele pode movimentar-se duas casas para cima, seguido de uma
deslocação para a esquerda ou para a direita; movimentar-se de duas casas para baixo, seguido de uma
deslocação para a esquerda ou para a direita; movimentar-se de duas casas para a direita, seguido de uma
deslocação para cima ou para baixo; movimentar-se de duas casas para a esquerda, seguido de uma
deslocação para cima ou para baixo. A questão colocada por Euler foi a de saber se dado um tabuleiro N ×N é
possível, ou não, a um cavalo, partindo de uma casa 1, passar uma só vez por cada uma das restantes N2 - 1
casas. Esta tem sido uma questão bem estudada, com algumas publicações científicas recentes, e algumas
variações do enunciado. Sabe-se que para tabuleiros de dimensão N ≥ 5 existem sempre muitas soluções.
Segue-se uma resposta possível para o tabuleiro de xadrez convencional (8 ×8):
1 42 37 44 25 4 15 18
38 55 40 3 14 17 24 5
41 2 43 36 45 26 19 16
56 39 54 13 48 35 6 23
63 12 57 46 61 22 27 20
58 53 62 49 34 47 30 7
11 64 51 60 9 32 21 28
52 59 10 33 50 29 8 31
O Algoritmo de Warnsdorff
É fácil engendrar um método de pesquisa para encontrar um caminho, se um existir: backtracking.
Deslocamos o cavalo tão longe quanto possível e, se chegarmos a um beco sem saída, voltamos atrás de
maneira a seguirmos outra direcção. O problema é que para tabuleiros grandes (nem é preciso que N seja tão
grande quanto isso...) o backtracking torna-se muito lento.
Em 1823 Warnsdorff propôs um algoritmo com a pretensão de encontrar sempre um caminho (quando ele
existe) sem nunca retroceder. Em cada posição do cavalo faz-se uma classificação das posições sucessoras,
deslocando-se o cavalo para o sucessor com a maior classificação. As posições sucessoras são as casas do
tabuleiro que ainda não foram visitadas e podem ser alcançadas por um único salto do cavalo a partir da
posição corrente. A classificação é maior para o sucessor com menor número de sucessores. Desta maneira,
as casas que tendem a estar isoladas são visitadas primeiro, evitando-se que fiquem de facto isoladas.
O tempo necessário para a execução deste algoritmo cresce linearmente com o número de casas do tabuleiro,
o que é bom, mas infelizmente sabemos hoje, com a ajuda dos computadores (que não estavam disponíveis
no tempo de Warnsdorff) que para tabuleiros de lado N > 76 podem ocorrer becos sem saída. Existem
algoritmos recentes de complexidade linear para todo o N, mas são extremamente complicados, lidando com
muitos casos particulares cujas soluções são já conhecidas à priori. A questão de saber se existe um
algoritmo, comparável ao de Warnsdorff em simplicidade, que resolva o problema em tempo linear para todo
o N SHUPDQHFHHPDEHUWR
Tarefa
Na verdade, o algoritmo de Warnsdorff não elimina completamente a necessidade de backtracking. Surgem
situações em que existe mais do que um sucessor com menor número de sucessores, alguns dos quais
conduzem a becos sem saída. Aqui vamos pedir que implementem um melhoramento do esquema de
classificação de Warnsdorff. Acrescentamos a regra que, em caso de empate, escolhe um dos sucessores
(X,Y) com maior distância ao centro do tabuleiro: distancia = (X - C)2 + (Y - C)2, com C = N/2, onde N é a
dimensão do lado do tabuleiro. Note-se que, mesmo com esta regra adicional, podem existir vários candidatos
à posição seguinte do cavalo (todas elas devem ser usadas, se necessário, por backtracking). Para garantir a
unicidade de solução, deve adoptar as seguintes convenções:
• Prioridade dos saltos por ordem decrescente (olhe para o tabuleiro como uma matriz - o primeiro
elemento do par denota o incremento na linha, o segundo o incremento na coluna): (-1,2), (-2,1),
(-2,-1), (-1,-2), (1,-2), (2,-1), (2,1), (1,2).
• Considere como ponto de partida do cavalo o canto superior esquerdo: (1,1). No caso de não
existir solução (com as regras adoptadas) deve procurar uma usando um outro ponto de partida.
Partindo de (1,1) deve incrementar a coluna e só depois a linha: (1,1), (1,2), (1,3), …, (N,N).
A sua tarefa consiste então em escrever um programa Prolog que dada a dimensão N do tabuleiro (N
devolva o tabuleiro T, descrito por uma lista de linhas, onde cada linha é uma lista de N números da sucessão
1,…,N2, que descreve o caminho descrito pelo ‘‘Cavaleiro de Euler’’. Implemente para isso o predicado
wm(N,T) (iniciais de Warnsdorff Melhorado).
Dados
O seu programa será testado através do seguinte predicado ce(N) (iniciais de Cavaleiro de Euler):
ce(N) :- wm(N,L), mostra(L).
mostra([]).
mostra([L|R]) :- mostraLinha(L), nl, mostra(R).
mostraLinha([]).
mostraLinha([X|R]) :-
X < 10, write(’ ’), write(X), write(’ ’), mostraLinha(R).
mostraLinha([X|R]) :-
X < 100, write(’ ’), write(X), write(’ ’), mostraLinha(R).
mostraLinha([X|R]) :-
write(X), write(’ ’), mostraLinha(R).
Resultados
Mostramos de seguida alguns exemplos que ilustram os resultados esperados.
?- ce(2).
No
?- ce(5).
1 10 15 20 3
16 21 2 9 14
11 8 23 4 19
22 17 6 13 24
7 12 25 18 5
Yes
?- ce(8).
1 42 37 44 25 4 15 18
38 55 40 3 14 17 24 5
41 2 43 36 45 26 19 16
56 39 54 13 48 35 6 23
63 12 57 46 61 22 27 20
58 53 62 49 34 47 30 7
11 64 51 60 9 32 21 28
52 59 10 33 50 29 8 31
Yes
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2002/F – Concurso Nacional de Programação em Lógica 2002
A empresa Mind-theGap, Lda. que explora o Metro, encomendou-lhe um programa para aconselhar os
utentes sobre quais os melhores percursos a seguir nos seus trajectos. Para já, pretendem que o programa
considere exclusivamente a zona representada no mapa.
Este Metropolitano tem duas peculiaridades: as composições são novíssimas e deslocam-se a velocidades
muito elevadas, pelo que os tempos de viagem são curtos quando realizados numa única linha; contudo, as
estações são antigas e pouco funcionais, pelo que o tempo necessário para mudar de linha é muito elevado. Se
considerarmos que o custo de uma viagem na zona em causa é fixo, ou seja, independente da distância
percorrida e do número de linhas utilizado, compreenderemos o pedido feito pela Mind-the-Gap: “ Queremos
que o programa forneça ao utente o percurso que lhe permita chegar ao destino com o número mínimo de
mudanças de linha. Nem que o utente tenha que dar a volta completa à cidade! É melhor do que mudar de
linha para encurtar caminho” .
Tarefa
Escrever um programa Prolog planner/3 que produza um percurso de uma estação de origem X a outra
estação de destino Y. Um percurso entre duas estações X e Y pode envolver mais do que uma linha. Quando
tal acontece, é necessário realizar pelo menos uma mudança de linha numa estação de ligação. Por exemplo,
um percurso possível entre as estações 14 e 10 consiste em efectuar uma viagem na linha B até à estação 16,
seguida de outra na linha D até à estação 10.
Dados
O programa será invocado através do predicado
planner( X, Y, Percurso)
que deverá ter os dois primeiros argumentos instanciados com Estações do mapa da Figura 1: X com a
estação de partida, Y com a estação de chegada.
A descrição da topologia do Metropolitano é fornecida no ficheiro metro.pl. Neste ficheiro, cada linha é
descrita por uma estrutura com a forma linha(Nome, Estacoes), onde Nome identifica a linha e
Estacoes é uma lista com as estações da linha.
Por exemplo:
linha('
B', [13,14,15,16,17,7])
Resultados
O programa deverá instanciar Percurso, o terceiro argumento, com uma lista cujos elementos terão a forma
Linha-Muda, significando que o percurso utiliza Linha até à estação Muda. Esta lista deverá conter um
percurso entre X e Y que minimize as mudanças de linha. Apenas se exige uma solução.
Exemplos:
?- planner(3, 8, Percurso).
Percurso = [’A’ - 8]
yes
?- planner(6, 10, Percurso).
Percurso = [’A’ - 2,’D’ - 10]
yes
?- planner(23, 17, Percurso).
Percurso = [’E’ - 11,’A’ - 13,’B’ - 17]
yes
QUASE
Introdução
Nós, os da informática, somos bons em potências de 2. Todos sabemos que 210 é 1024 e que com quatro bytes
podemos representar números inteiros entre –231 e 231-1, ou seja, entre cerca de menos dois mil milhões e
cerca de mais dois mil milhões.
Também sabemos que o único factor primo de uma potência de 2 é 2. Logo, todos os divisores de uma
potência de 2 são potências de 2. Claro, dirá você. Mas então vejamos isto sob uma perspectiva diferente.
Dado um número inteiro positivo x, será que existe uma potência de 2 da qual x é “ quase” um divisor? Por
definição, diremos que um número inteiro x maior que 1 é quase divisor de y se x for divisor de y ou x for
divisor de y + 1 ou x for divisor de y – 1. (Repare que o problema só tem interesse se desconsiderarmos a
potência de expoente zero, 20, pois todos os números inteiros positivos seriam quase divisores de 1.)
Pois bem, um momento de reflexão mostra que se x for ímpar, então sim, existe uma potência de 2 da qual x é
quase divisor. De facto, existindo uma, existe uma infinidade, mas estamos interessados apenas na menor de
todas. Qual será ela, para um dado x?
Tarefa
Escreva um programa Prolog quase(+X, -N) que, dado um número inteiro X ímpar, instancie N com o
expoente da menor potência de 2 tal que X é quase divisor de 2N. O número representado por X pertence ao
intervalo 21+1..109-1.
Dados
O programa será invocado através do predicado quase(X, N) que deverá ter o primeiro argumento
instanciado com um inteiro pertencente ao intervalo 21+1..109-1.
Resultados
O programa deverá instanciar N, o segundo argumento, com o expoente da menor potência de 2 tal que X é
quase divisor de 2N.
Exemplos:
?- quase(5, N). Correcto: 210 = 1024 e 25 é um divisor de 1024+1.
N = 2 ?- quase(2001, N).
Yes N = 308
2
Correcto: 2 = 4 e 5 é um divisor de 4 + 1. yes
?- quase(25, N). ?- quase(999999999, N).
N = 10 N = 667332
Yes yes
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2001/A – Concurso Nacional de Programação em Lógica 2001
O Robot Atarefado
Introdução
Um Robot desloca-se numa área quadrangular como a representada na
Figura 1, onde se encontram mesas (pelo menos uma) e um armário. A
área tem NxN posições discretas, e cada mesa ocupa apenas uma M
posição, o mesmo sucedendo com o Robot e o armário. As mesas e o
armário não se tocam (ou seja, não ocupam posições contíguas). R
A
O Robot tem por função transportar livros que estão em cima das
mesas para o armário. Todas as mesas têm um (e um só) livro sobre M
Y
elas. M
Considere-se ainda o seguinte: M
• o Robot pode deslocar-se na horizontal e na vertical, mas não na
diagonal;
• em cada passo do seu movimento, o Robot desloca-se uma casa;
X
• o Robot só pode transportar um livro de cada vez;
• para pegar num livro existente numa mesa, o Robot tem que passar pela casa ocupada por essa mesa;
• sempre que o Robot passe por uma casa onde exista uma mesa com livro, pega obrigatoriamente nesse
livro;
• para pousar no armário um livro que esteja a transportar, o Robot tem que passar pela casa ocupada pelo
armário; quando tal acontece, o Robot pousa obrigatoriamente o livro no armário;
• o Robot não pode passar por uma casa ocupada por uma mesa excepto para pegar no livro nela existente;
o Robot não pode passar pela casa ocupada pelo armário excepto para pousar um livro nele.
Quando inicia a sua tarefa, o Robot começa por se deslocar para uma
mesa, onde pega no livro nela existente. Depois, transporta-o para o
armário. Visita depois cada uma das restantes mesas, transportando de M
cada uma delas um livro para o armário. R
A Figura seguinte 2 ilustra um dos caminhos possíveis para o exemplo. A
M
Tarefa Y
M
Escrever um programa Prolog atarefado/5 que produza um M
caminho completo do Robot, desde a sua posição inicial até ao
momento em que conclui a sua tarefa. Um caminho entre duas
posições A e B é uma sequência de posições contíguas, representadas X
por pares X/Y, que começa em A e termina em B.
Dados
O programa será invocado através do predicado
atarefado(R, LMesas, A, N, Caminho)
que deverá ter os quatro primeiros argumentos instanciados: R com a posição inicial do Robot; LMesas com a
lista das posições ocupadas pelas mesas; A com a posição ocupada pelo armário; e N com a dimensão da
área.
Resultados
O programa deverá instanciar Caminho, o quinto argumento, com a lista de posições percorridas pelo Robot
na resolução do problema. Apenas se exige uma solução.
Exemplos:
?- atarefado(1/1, [3/3], 1/3, 3, Caminho).
Caminho = [1 / 1,2 / 1,3 / 1,3 / 2,3 / 3,2 / 3,1 / 3]
yes
Diagnóstico de Circuitos
Introdução
Algumas das avarias mais frequentes em portas (“ gates” ) de circuitos digitais, conhecidas por “ stuck-at-0” e
“ stuck-at-1” , fazem com que as saídas dessas fiquem fixas num valor lógico constante, (0 ou 1,
respectivamente) independentemente do valor das entradas.
Pretende-se que defina um predicado, circuito/2, que para um circuito constituído exclusivamente por portas
NAND, permita não só avaliar o seu funcionamento correcto mas também diagnosticar a existência de uma
ou mais portas avariadas. Esse predicado, tem os seguintes argumentos:
• Portas lista de portas NAND. Cada porta é representada pelo functor nand/3 com argumentos:
− Um identificador único da porta (um inteiro)
− Uma lista de entradas da porta
− A saída da porta
• Avarias lista de avarias. Cada avaria é representada por um par N/S em que N é o identificador da gate
avariada e S o tipo da avaria (s0 ou s1).
As entradas e saídas das portas podem ser constantes booleanas (0 ou 1) ou variáveis, sendo uma ligação
entre uma saída e uma variável representada pela mesma variável
Exemplo: O circuito seguinte que, sem avarias, representa as condições D { B t A e E { A t B
2 D
A
1 C
3 E
B
pode ser testado chamando-se o predicado
circuit o( [ nand( 1,[ A,B] ,C) , nand( 2,[ A,C] ,D) , nand( 3,[ B,C] ,E) ] ,L)
em que as variáveis A,B, C, D e E podem ser instanciadas a valores booleanos (0 ou 1) e a lista de avarias L
pode ser igualmente instanciada, total ou parcialmente.
Exemplos:
?- circuit o( [ nand( 1,[ A,B] ,C) , nand( 2,[ A,C] ,D) , nand( 3,[ B,C] ,E) ] , [ ] ) , A= 1, B= 0.
C = 1,
D = 0, E = 1;
no % sem gat es avariadas é falso que B t A ( D = 0 ) é verdadeiro que A t B ( E= 1)
?- circuit o( [ nand( 1,[ A,B] ,C) , nand( 2,[ A,C] ,D) , nand( 3,[ B,C] ,E) ] , [ ] ) , D= 0, E= 0.
no % sem gat es avariadas é im possível ser falso que B t A e A t B
?- circuit o( [ nand( 1,[ A,B] ,C) , nand( 2,[ A,C] ,D) , nand( 3,[ B,C] ,E) ] , [ ] ) , A= B.
A = 1, B = 1, C = 0,
D = 1, E = 1;
A = 0, B = 0, C = 1,
D = 1, E = 1;
no % sem gat es avariadas, se A= B ent ão é B t A e A t B
?- circuit o( [ nand( 1,[ A,B] ,C) , nand( 2,[ A,C] ,D) , nand( 3,[ B,C] ,E) ] , [ ] ) , D= E.
A = 1, B = 1, C = 0,
D = 1, E = 1;
A = 0 , B = 0, C = 1,
D = 1 , E = 1;
no % sem gat es avariadas, é im possível B t A e A t B serem am bos falsos ( D= E= 0 )
% sem gat es avariadas, se B t A e A t B ( D = E= 1 ) ent ão deve ser A = B
?- circuit o( [ nand( 1,[ A,B] ,C) , nand( 2,[ A,C] ,D) , nand( 3,[ B,C] ,E) ] , [ X] ) , A= 1, B= 1, D= 0, E= 1.
A = 1, B = 1, C = 0, D = 0, E = 1 ,
X = 2 / s0 ;
no % se A= B= 1 , não é possível ser falso B t A. O circuit o t em um a avaria. Com o E= 1, a
% única avaria que j ust ifica o erro, envolvendo um a só port a é a port a 2 est ar st uck- at - 0.
?- circuit o( [ nand( 1,[ A,B] ,C) , nand( 2,[ A,C] ,D) , nand( 3,[ B,C] ,E) ] , [ X] ) , A= 1, B= 1, D= 0.
A = 1, B = 1, C = 0, D = 0, E = 1 ,
X = 2 / s0 ;
A = 1, B = 1, C = 1, D = 0, E = 0,
X = 1 / s1 ;
No % se A= B= 1 , não é possível ser falso B t A. O circuit o t em pois um a avaria. Com E= 1, a
% única avaria que j ust ifica o erro, envolvendo um a só port a é a port a 2 est ar st uck- at - 0.
% Com E= 0, a única avaria envolvendo um a só port a, é a port a 1 est ar st uck- at - 1.
?- circuit o( [ nand( 1,[ A,B] ,C) , nand( 2,[ A,C] ,D) , nand( 3,[ B,C] ,E) ] , L) , A= 0, B= 0, D= 0, E= 0.
A = 0, B = 0, C = 1 , D = 0, E = 0 ,
L = [ 2 / s0 , 3 / s0 ] ;
A = 0, B = 0, C = 0, D = 0, E = 0,
L = [ 1 / s0 , 2 / s0 , 3 / s0 ] ;
A = 0, B = 0, C = 1 ,D = 0, E = 0 ,
L = [ 1 / s1 , 2 / s0 , 3 / s0 ] ;
no. % se A= B= 0 , não é possível ser falso B t A nem é possível ser falso B t A. O circuit o
% t em pois avarias. Os dois erros nas saídas só são explicadas com , pelo m enos, duas
% port as avariadas.
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/A – Concurso Nacional de Programação em Lógica 2000
C1 C2 C3
C4 C5 C6
C7 C8 C9
Tarefa
Atendendo a que, para ganhar a aposta, tem de:
escreva um programa Prolog que lhe permita vencer a aposta e, já agora, calcular quanto vai ganhar.
Resultados
O programa deve ser invocado através do predicado aposta/0 que mostrará no ecrã uma lista com os
valores das notas existentes nas caixas C1 a C9 (por esta ordem).
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/B – Concurso Nacional de Programação em Lógica 2000
Carreiras de Autocarro
Introdução
O problema seguinte foi inspirado num outro proposto nas Olimpíadas Internacionais de Informática, IOI’94:
Um homem chega a uma paragem de autocarro às 9H00 de um dia e ai permanece 60 minutos (até
às 9H59), anotando o momento de chegada de cada autocarro que ai pára (apenas regista os
minutos). Note que esse Observador poderia ter escolhido outra qualquer hora do dia, pois o
padrão de passagem dos autocarros repete-se de hora a hora.
Os autocarros podem pertencer a carreiras diferentes e podem chegar no mesmo momento, mas os
autocarros da mesma carreira chegam sempre a intervalos regulares (tendo cada carreira
frequências de passagem diferentes).
O Observador em causa constatou, ainda, que todas as carreiras ai pararam pelo menos duas
vezes.
Tarefa
Escreva um programa Prolog que, dada a lista ordenada com os minutos de paragem de cada autocarro
observado, indique um conjunto de carreiras em que eles podem ser agrupados, identificando a respectiva
frequência.
Dados
É dada a lista de minutos correspondente às paragens registadas, na forma do primeiro argumento do
predicado agrupa/2 pelo qual o seu programa deve ser invocado.
Resultados
O resultado esperado, conjunto de carreiras em que os autocarros observados podem ser agrupados de modo a
satisfazer as restrições acima indicadas, deve ser apresentado na forma de uma lista de pares em que o
primeiro elemento do par é o minuto correspondente à primeira paragem dessa carreira e o segundo elemento
do par é a frequência de paragem respectiva.
Essa lista unificará com a variável passada como segundo argumento do referido predicado
agrupa/2.
Exemplo:
?- agrupa([0,4,5,7,10,20,20,24,30,35,37,40,44,50,50], Carreiras).
Carreiras = [(0,10),(4,20),(5,15),(7,30)]
yes
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/C – Concurso Nacional de Programação em Lógica 2000
B R A GA B RA G A
B R A GA B RA G A
Tarefa
Escreva um programa Prolog que proceda à etiquetação do dito mapa, sendo conhecidas as suas dimensões, a
posição das cidades e o respectivo nome.
Dados
As dimensões (C*L) do mapa são definidas pelo predicado binário dimensoes/2 (que deve supor existente
na sua BC).
As cidades serão definidas através de uma lista de triplos, que constitui o argumento único do predicado
cidades/1 (suponha-o também existente na sua BC), em que o primeiro e segundo elementos são as
coordenadas (X,Y) da quadrícula que representa a cidade, e o terceiro elemento é o nome da cidade.
Note que as colunas são numeradas a partir de 0 (zero) da esquerda para a direita e as linhas, também, a partir
de 0 (zero) de baixo para cima; a quadrícula (0,0) corresponderá ao canto inferior esquerdo do mapa.
Resultados
O resultado esperado, colocação das etiquetas junto de cada cidade, será produzido ao invocar o predicado
sem argumentos etiqueta/0, devendo ser apresentado na forma de uma lista de triplos (coordenadas da
quadrícula onde começará cada etiqueta, e respectivo nome) que constituirá o argumento único do predicado
nomes/1 a ser acrescentado à BC.
Exemplo
Gruas em Comboio
Introdução
O problema seguinte foi inspirado num outro descrito em Jogos da Mente, Puzzlegramas, Temas e
Debates, 1994:
Uma fila compacta de camiões-grua que se dirige de Norte para Sul encontra-se com uma fila semelhante
que se dirige de Sul para Norte. A estrada não permite o cruzamento de dois camiões e os camiões não
podem recuar. No entanto, um camião-grua pode levantar um outro camião-grua que lhe esteja à frente e
pô-lo atrás, desde que para tal exista um espaço livre de 15 metros; notar que os camiões da fila que vai
para Norte têm a frente para Norte e vice-versa. Supondo que o espaço entre os camiões à frente de cada fila
é precisamente de 15 metros e que as filas têm cada uma n camiões, pretende-se saber se as filas poderão
continuar o seu caminho e como.
Tarefa
Escreva um programa que resolva problemas do tipo anterior, descrevendo as várias operações
necessárias para que as filas continuem se tal for possível.
Dados
É dado o comprimento das filas (que são iguais).
Resultados
O programa deve ser posto em execução chamando o predicado gruas/1 cujo argumento é o dado.
O programa deve imprimir a sequência de operações a realizar como no exemplo abaixo, ou indicar
que não existe solução.
Exemplo:
?- gruas(2).
a-ultimo a-1 :___: b-1 b-ultimo
a-ultimo :___: a-1 b-1 b-ultimo
a-ultimo b-1 a-1 :___: b-ultimo
a-ultimo b-1 a-1 b-ultimo :___:
a-ultimo b-1 :___: b-ultimo a-1
:___: b-1 a-ultimo b-ultimo a-1
b-1 :___: a-ultimo b-ultimo a-1
b-1 b-ultimo a-ultimo :___: a-1
yes
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/E – Concurso Nacional de Programação em Lógica 2000
O Problema de Langford
Introdução
Este problema é adaptado de Mathematical Magic Show, de Martin Gardner. O matemático escocês
C. Dudley Langford, ao ver o seu filho brincar com 3 pares de cubos de cores diferentes, verificou
que existia 1 cubo entre os cubos vermelhos, 2 cubos entre os amarelos e 3 cubos entre os azuis.
Substituindo as cores por números, a sequência pode ser representada por
312132
Figura 1
A colocação das peças no tabuleiro obedece às regras da Batalha Naval: as peças não se podem tocar (nem
nos cantos), e podem ser rodadas, pelo que podem assumir as 4 formas diferentes que se representam na
Figura 2.
Figura 2
Na Figura 3 está representada uma possível solução para a colocação de 3 peças num tabuleiro de dimensão
5.
Figura 3
Tarefa
Escrever um programa Prolog que produza uma solução para a colocação de M peças num tabuleiro quadrado
de dimensão N.
Dados
O programa deverá ser invocado através do predicado minibn/2 que deverá ter os dois argumentos
instanciados: o primeiro com a dimensão do tabuleiro, o segundo com o número de peças a colocar.
Resultados
O programa deverá encontrar uma solução para o problema e desenhar o tabuleiro resultante no ecrã. As
casas ocupadas por peças deverão ser representadas no ecrã pela letra '
p', enquanto que as casas não ocupadas
deverão ser representadas pelo símbolo '
.'(ponto final). Caso não o programa não encontre nenhuma solução,
deverá falhar.
Exemplos:
?- minibn(4,2).
no
?- minibn(5,3).
Solucao:
Linha 1: ppp..
Linha 2: .p..p
Linha 3: ...pp
Linha 4: .p..p
Linha 5: ppp..
yes
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/G – Concurso Nacional de Programação em Lógica 2000
1 1
4 2 3 1 3 2 3
5 6 2 4 5 6 4 5 6
Note, por exemplo, que os movimentos das figuras 3 e 4 não são válidos. No primeiro caso, a moeda 2, ao
mover-se afecta a posição das moedas 1 e 4 (têm que ser afastadas para ela passar). No segundo caso, a
moeda 2 (que se move) fica apenas a tocar numa moeda.
Mas o movimento da figura 5 já é válido. Apesar de após o movimento a moeda 4 só ficar a tocar numa outra
moeda, a moeda que se move (neste caso a 1) fica a tocar em duas. E é só sobre a moeda que se move que se
impõe esta restrição.
Tarefa
Escrever um programa Prolog que resolva este problema, ou seja, que devolva uma lista com 3 movimentos
de moeda que, a partir do paralelograma inicial, coloque as moedas em círculo.
Resultados
O programa deverá ser posto em execução chamando o predicado moedas/1 cujo argumento de saída deverá
ser instanciado com uma sequência (i.e. lista) de 3 movimentos que resolva o problema. Para identificação, as
moedas são numeradas de 1 a 6, sendo a situação inicial a da figura 1.
Cada movimento deverá ser um termo da forma move(Moeda,ListaAdjacentes) que representa o movimento
da moeda Moeda para junto das moedas ListaAdjacentes. Por exemplo, o movimento da figura 5 seria
representado por move(1,[2,3]).
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/H – Concurso Nacional de Programação em Lógica 2000
Jogo do Nim
Introdução
O Nim joga-se com pilhas de feijões e foi primeiro estudado por Charles Bouton em 1901.1 Neste
jogo, cada jogador, quando lhe toca jogar, pode retirar feijões de uma só pilha, mas quantos quizer,
de um mínimo de um a um máximo de toda a pilha. Ganha o jogador que retirar o último punhado
de feijões.
Tarefa
A sua tarefa consiste em escrever um programa Prolog que permita jogar o Nim sem nunca perder!
Nímeros e o jogo do Nim
``Nímero' 'é a palavra que resulta de se combinar ``Nim'
'com ``número''.2 Os nímeros têm uma nova
aritmética, para a qual usaremos aqui os símbolos ⊕ e ≡ . Quais são as posições boas que convém
atingir num jogo Nim? Se se estiver a jogar Nim apenas com um monte, a resposta é fácil. É bom
passar para 0 e é mau passar para qualquer outro nímero 1, 2, 3, 4, 5, 6, ... visto que então o
oponente poderá contra-atacar, passando para 0. Eis o que é necessário saber sobre a adição de
nímeros:
• A soma de dois nímeros iguais é sempre zero.
• Se o maior de dois nímeros for uma potência de 2: 1 ou 2 ou 4 ou 8 ou 16 ou ..., eles somam-se como
os correspondentes números ordinários.
Com estas regras e as leis da álgebra, pode fazer-se aritmética Nim. Exemplo:
5 ⊕6 ≡ (4 ⊕1) ⊕(4 ⊕2) ≡ (4 ⊕4) ⊕(1 ⊕2) ≡ 3.
Qualquer conjunto de montes Nim, a, b, c, ... pode ser substituído por um monte único de tamanho a
⊕b ⊕c⊕…. Assim, a resposta à questão colocada será: deverá tentar--se jogar para uma situação
a, b, c, ... para a qual se tenha a ⊕b ⊕c ⊕… ≡ 0. Vejamos um exemplo. Se o utilizador passou para
3, 5, 7 (ou tiver escolhido esta como configuração inicial) então, como 3 ⊕5 ⊕7 ≡ (2 ⊕1) ⊕(4 ⊕1)
⊕7 ≡ 6 ⊕7 ≡ … ≡ 1 o seu programa deverá jogar para uma das possibilidades 2, 5, 7 ou 3, 4, 7 ou
3, 5, 6 que somam zero: 2 ⊕5 ⊕7 ≡ 3 ⊕4 ⊕7 ≡ 3 ⊕5 ⊕6 ≡ 0. Prova-se que é sempre possível passar
de uma situação perdente para uma ganhante. Para transformar uma situação perdente numa
ganhante, somamos o valor do tamanho da situação com o número de feijões de uma determinada
pilha. Se o resultado for inferior ou igual ao número de feijões da pilha, devemos tirar elementos
dessa pilha de modo a deixar lá esse resultado. Se o resultado for superior, devemos tirar feijões de
uma outra pilha. Por exemplo, da situação perdente 1, 8, 4, 7 (que tem tamanho 1 ⊕8 ⊕4 ⊕7 ≡ 10)
podemos passar para a situação ganhante 1, 2, 4, 7: 10 ⊕8 ≡ 2 < 8 e 1 ⊕2 ⊕4 ⊕7 ≡ 0.
Dados
Os dados a considerar neste problema são o número de pilhas e o número de feijões em cada pilha.
Estes dados serão especificados pelo utilizador por uma lista Pilhas do tipo [NumFeijPilha1,
..., NumFeijPilhaN] aquando da chamada ao predicado nim(Pilhas). Antes do início do jogo
propriamente dito, o seu programa deve usar o predicado soma(Pilhas,T), que devolve em T a
soma dos nímeros em Pilhas, para decidir se começa ele ou se, pelo contrário, opta por deixar o
utilizador fazer a primeira jogada. Todas as jogadas do computador devem ser realizadas à custa do
predicado proximaSituacao(SP,SG) que, dada uma situacao perdente SP, devolve uma situação
ganhante SG (outras situações ganhantes deverão poder ser obtidas por backtracking).
Resultados
Seguem-se alguns exemplos:
?- soma([1,8,4,7],S).
S = 10
?- proximaSituacao([1,8,4,7],PS).
PS = [1,2,4,7];
no
?- nim([1,1,2,3,1]). % Computador cede a 1a jogada ao utilizador
--> Utilizador joga para: [1,1,2,0,1]. % por exemplo
--> Computador joga para: [1,1,1,0,1]
--> Utilizador joga para: ........... % etc.
1
Mais informação sobre este e outros jogos do mesmo tipo, pode ser encontrada no Boletim no 40 da Sociedade Portuguesa de
Matemática no artigo Jogórios de Jorge Nuno Silva.
2
J. H. Conway e R. K. Guy, O Livro dos Números, colecção Gradiva Universidade de Aveiro, 1999.
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/H – Concurso Nacional de Programação em Lógica 2000
Note que poderá haver outras soluções obtidas a partir destas 14 acrescentando espelhos que não são usados
para reflectir raios. É claro que estas não têm qualquer interesse.
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/I – Concurso Nacional de Programação em Lógica 2000
Sangradura
Introdução
Os cincos duques de Sangradura (Bóris, o Cruel; Bóris, o Impiedoso; Ferdinando, o Selvagem; Ivan, o
Flagelador; e Klaus, o Inqualificável) decidiram declarar guerra a países vizinhos, usando pretextos fúteis.
Partindo da seguinte informação:
• O governador de Pacífica provocou a ira de um duque de Sangradura, que não era Klaus, o
Inqualificável, aprovando uma lei anti-violência que o duque considerou contrária à ordem natural da
criação e, portanto, herética;
• Bóris, o Cruel, levou dez dias para triunfar sobre o seu inimigo;
• A guerra mais longa não foi travada contra Betífica;
• Foi o duque Ferdinando, o Selvagem, quem declarou guerra quando o monarca de um país vizinho se
esqueceu de o cumprimentar pelo seu aniversário com um presente apropriado;
• A vítima de Ferdinando, o Selvagem, não foi Misericórdia;
• A guerra mais curta foi a declarada quando alguns pombos de um país vizinho invadiram o espaço
aéreo do ducado de Sangradura;
• A guerra de Ivan, o Flagelador, foi contra Inocência, que não era o país onde hastearam a bandeira de
Sangradura de cabeça para baixo, ofendendo o duque que estava de visita oficial à capital do país
inimigo;
• A guerra com Miserircórdia durou sete dias; Felícia não conseguiu resistir tanto;
• Chamava-se Bóris o duque que reinava quando um cidadão inocente de Sangradura foi injustamente
feito prisioneiro por um estado vizinho, por ter assassinado acidentalmente um habitante local a quem
devia dinheiro;
determine, para cada duque, o país atacado, o motivo da guerra e quantos dias se passaram em cada caso (2,
4, 7, 10 ou 13 dias), até ao triunfo inevitável das forças de Sangradura.
Tarefa
A sua tarefa consiste em escrever um programa Prolog que solucione o desafio acima colocado.
Resultados
O programa deve ser invocado através do predicado sangradura/0 que mostrará no ecrã os cinco quádruplos
[Duque,País,Pretexto,DuraçãoDaGuerra] que são consequência lógica da informação dada.
1
A fonte de inspiração foi o Jornal ‘‘O Integral’’ no 5, Ano III, do Clube de Matemática - Univ. Aveiro
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL2000/J – Concurso Nacional de Programação em Lógica 2000
?- vira(n(1,[n(2,[n(4,[]),n(5,[]),n(6,[])]),
n(3,[n(7,[]),n(8,[n(9,[]),n(10,[])])])]),
3,V).
V = n(3,[n(7,[]),
n(8,[n(9,[]),n(10,[])]),
n(1,[n(2,[n(4,[]),n(5,[]),n(6,[])])])]) ? ;
no
?- vira(n(11,[n(12,[]),
n(1,[n(2,[n(4,[]),n(5,[]),n(6,[])]),
n(3,[n(7,[]),
n(8,[n(9,[]),n(10,[])])])])]),
3,V).
V = n(3,[n(7,[]),
n(8,[n(9,[]),n(10,[])]),
n(1,[n(11,[n(12,[])]),
n(2,[n(4,[]),n(5,[]),n(6,[])])])]) ? ;
no
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL99/A – Concurso Nacional de Programação em Lógica 1999
Labirinto da Cobra
Introdução
Uma cobra move-se num labirinto onde existem vários obstáculos e vários ovos. O objectivo da cobra é,
partindo de uma posição inicial pré-definida, comer todos os ovos existentes no labirinto (i.e. passar por todos
os pontos em que existem ovos) sem chocar contra os obstáculos nem contra si própria.
Inicialmente a cobra é pequena mas de cada vez que come um ovo o seu tamanho aumenta de uma unidade.
Durante o jogo a cobra pode mover-se para a direita, esquerda, para cima e para baixo (sem nunca sair do
labirinto, claro).
Tarefa
Escrever um programa em Prolog que, dado um labirinto conforme descrito abaixo, devolva os movimentos
que a cobra deve fazer para comer todos os ovos. Os movimentos possiveis são e (mover para a esquerda), d
(mover para a direita), b (mover para baixo) e c (mover para cima).
Dados
O labirinto consiste numa matriz quadrada cuja dimensão é dada através de um facto dimensao/1, onde os
obstáculos estão assinalados por uma série de factos obstaculo(X,Y) (indicando que existe um obstáculo
na linha X coluna Y da matriz). A localização dos vários ovos é dada por uma série de factos ovo(X,Y)
(indicando que existe um ovo na linha X coluna Y da matriz).
Inicialmente a cobra tem dimensão 1 (i.e. ocupa apenas uma posição da matriz) e a está na linha 1 coluna 1 da
matriz (posição essa que garantidamente não tem nenhum obstáculo nem ovo).
De cada vez que a cobra come um ovo (i.e. passa pela primeira vez numa posição que tenha um ovo) o seu
tamanho aumenta de uma unidade.
Resultados
O programa deve ser posto em execução chamando o predicado movimentos/1 cujo argumento de saida
deverá ser instanciado com uma lista de movimentos que resolva o problema da cobra. A descrição do
labirinto está num outro ficheiro que, para testar o seu programa, será consultado previamente.
Exemplo
Com a seguinte descrição do labirinto:
dimensao(4).
uma solução seria:
obstaculo(2,2).
obstaculo(3,2). ?- movimentos(M).
obstaculo(4,2). M = [d,d,d,b,e,c,e,e,b,b,b]
ovo(1,4). yes
ovo(4,1).
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL99/B – Concurso Nacional de Programação em Lógica 1999
Pares Espelhados
Introdução
Pretende-se
1. Dada uma lista L de elementos não repetidos, formar uma lista LP de pares em espelho (X,Y) onde
X está em L e Y é o elemento da inversa de L com a mesma posição de X. A lista L só pode ser
visitada uma vez e a posição de (X,Y) na lista resultante é a posição de X em L.
2. Dado um elemento A de L e a lista LP, como na questão anterior, obter uma lista LD apagando A de
LP. Isto é, LD é a lista de pares obtida como na alínea anterior mas para a lista resultante de apagar A
em L. A lista LP só pode ser visitada uma vez.
Tarefa
Escreva um programa Prolog que resolva as duas questões anteriores, seguindo a condição de visitar uma
única vez a lista dada.
Dados
Os dados são os indicados nas questões acima. No primeiro caso, uma lista fechada com elementos quaisquer
não unificáveis. No segundo, uma lista de pares em espelho e um elemento de um par.
Resultados
O programa deve ser chamado através de um dos seguintes predicados:
forma_pares(L,LP)
apaga_elemento(LP,X,NLP)
em que se supõe instanciados: L, no primeiro, e LP e X no segundo; os resultados virão em LP e NLP,
respectivamente.
Exemplos
?- forma_pares([a,b,c,d],LP).
LP = [(a,d),(b,c),(c,b),(d,a)] ;
no
?- apaga_elemento([(a,d),(b,c),(c,b),(d,a)],b,NLP).
NLP = [(a,d),(c,c),(d,a)] ;
no
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL99/E – Concurso Nacional de Programação em Lógica 1999
Indícios e Enganos
Introdução
O problema seguinte foi adaptado de Jogos da Mente, Puzzlegramas, Temas e Debates, 1994:
Um extra-terrestre foi visto por quatro pessoas que o descreveram de maneiras diferentes. A primeira disse
que tinha pele verde, era baixo, tinha uma máscara e luvas amarelas. A segunda confirmou a máscara e as
luvas, mas disse que a pele era amarela e a estatura era média. A terceira referiu que era baixo, que a pele
não estava visível, que tinha luvas amarelas e não usava máscara. A última disse que era alto e de pele
castanha, tinha máscara e luvas verdes. Supondo que cada pessoa só acertou num dos detalhes que deu, e
que cada detalhe do extra-terrestre só foi descrito correctamente por uma pessoa, como era o extra-
terrestre?
Tarefa
Escreva um programa que resolva problemas genéricos do tipo anterior, em que há n pessoas e n tipos de
indícios, cada pessoa só acertou num indício e cada indício só foi descrito correctamente por uma pessoa.
Dados
É dada uma lista com n elementos da forma P:L, sendo P a identificação da pessoa, e L uma lista de n pares
da forma I-Obs, com I o nome de um indício e Obs o ``valor' 'observado pela pessoa P para o indício I.
Pressupõe-se que na lista dada as identificações das pessoas são únicas, que nas listas de indícios observados
há apenas n indícios diferentes e em cada lista os indícios aparecem uma e uma só vez.
Resultados
O programa deve ser posto em execução chamando o predicado indicios/1 cujo argumento é a lista com
os dados.
O programa deve imprimir todas as hipóteses compatíveis com os dados e as condições impostas no
enunciado acima.
Exemplos:
?- indicios([
p1:[pele-verde, altura-baixo, mascara-sim, luvas-amarelas],
p2:[pele-amarela, altura-media, mascara-sim, luvas-amarelas],
p3:[pele-indefinida, altura-baixo, mascara-nao, luvas-amarelas],
p4:[pele-castanha, altura-alto, mascara-sim, luvas-verdes]]).
Hipotese:
pele-verde referido por:p1
altura-media referido por:p2
mascara-nao referido por:p3
luvas-verdes referido por:p4
Nao existem (mais) hipoteses
yes
?- indicios([
p1:[pele-verde, altura-baixo, mascara-sim, luvas-amarelas,
olhos-azuis],
p2:[pele-amarela, altura-media, mascara-sim, luvas-amarelas,
olhos-pretos],
p3:[pele-indefinida, altura-baixo, mascara-nao, luvas-amarelas,
olhos-roxos],
p4:[pele-castanha, altura-alto, mascara-sim, luvas-verdes,
olhos-castanhos],
p5:[pele-roxa, altura-media, mascara-sim, luvas-azuis,
olhos-amarelos]]).
Hipotese:
pele-verde referido por:p1
olhos-pretos referido por:p2
mascara-nao referido por:p3
altura-alto referido por:p4
luvas-azuis referido por:p5
Hipotese:
olhos-azuis referido por:p1
pele-amarela referido por:p2
mascara-nao referido por:p3
altura-alto referido por:p4
luvas-azuis referido por:p5
Nao existem (mais) hipoteses
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL99/F – Concurso Nacional de Programação em Lógica 1999
Animais e Caça
Introdução
Atente ao seguinte enunciado:
Cinco amigos de nomes predestinados, Coelho, Pato, Rola, Pombo e Lebre, regressam de uma caçada.
Trazem consigo animais correspondentes aos respectivos nomes. Cada um matou um único animal, que não
corresponde, porém, ao seu nome. Cada um falhou um animal diferente do que matou e que não corresponde
também ao próprio nome. O coelho foi morto pelo caçador cujo nome é o do animal falhado por Rola. O
pato foi morto pelo caçador cujo nome é o do animal falhado por Lebre. Pato, que falhou uma lebre, ficou
muito aborrecido por ter morto apenas uma rola. Quais os bichos mortos e falhados por cada um dos
caçadores? in "Pierre Berloquin, 100 Jogos Lógicos, O Prazer da Matemática, no 4, Gradiva"
Tarefa
Escreva um programa em Prolog que resolva este jogo, i.é, determine a resposta à questão acima colocada
Resultados
O seu programa deve ser activado através do predicado
jogo/0
que mostra no ecrã os 5 triplos
Cacador/Animal_Morto/Animal_Falhado
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL98/B – Concurso Nacional de Programação em Lógica 1998
Cobre e Coberturas
Introdução
Considere que tem um tabuleiro de N x M casas e um número ilimitado de pedras de dominó (todas iguais e
sem pintas). As dimensões das casas do tabuleiro e das pedras de dominó são tais que uma pedra de dominó
cobre precisamente duas casas do tabuleiro. Se tivermos um tabuleiro 2x2 bastam duas pedras de dominó
para cobrir o tabuleiro e todas as pedras do dominó estão a cobrir alguma casa do tabuleiro, havendo assim
uma cobertura perfeita. No entanto, se retirarmos um canto do tabuleiro já não será possível cobrir o tabuleiro
de um modo perfeito. Se tivermos um tabuleiro 4x4 é fácil de verificar que são necessárias exactamente 8
pedras para cobrir perfeitamente o tabuleiro. O que acontecerá se fizermos dois buracos no tabuleiro, isto é,
se retirarmos duas casas quaisquer do tabuleiro? Será ainda possível cobrir perfeitamente o tabuleiro?
Tarefa
Implemente um programa Prolog para determinar se um tabuleiro (eventualmente com buracos) pode ser
coberto perfeitamente por peças de dominó.
Dados
Toda a informação que é necessária para executar o programa que lhe é solicitado,
será fornecida através dos argumentos do predicado usado para o activar, como se detalha a seguir. O
programa deve ser invocado com o predicado
cobertura(NLinhas,NColunas,Lista_Buracos)
em que NLinhas indica o número de linhas do tabuleiro, NColunas o número de colunas do tabuleiro e
Lista_Buracos é uma lista com as coordenadas b(L,C) dos buracos do tabuleiro.
Resultados
O predicado cobertura/3 deve ser executado com sucesso se for possível cobrir perfeitamente o tabuleiro
com pedras de dominó e deve falhar se tal não for possível.
Exemplos:
Dado que é possível cobrir perfeitamente um tabuleiro de 2x2 que tem 0 buracos ([]),
cobertura(2,2,[]) deve ser executado com sucesso:
?-cobertura(2,2,[]).
yes
uma vez que um tabuleiro de 2x2 com um buraco em 1x1, ou seja, ao qual foi retirado um canto, não pode
ser coberto perfeitamente, cobertura(2,2,[b(1,1)]) deve falhar:
?-cobertura(2,2,[b(1,1)]).
no
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL98/D – Concurso Nacional de Programação em Lógica 1998
Codificador Primo
Introdução
Consideremos os números naturais e a ideia muito simples de número primo: um número que não se pode
obter pela multiplicação de dois números mais pequenos onde não se inclui a unidade. Sabia-se desde tempos
remotos, como um facto empírico, que todos os números se exprimem, de forma única, como um produto de
primos; e os Gregos conseguiram prová-lo.
Algumas perguntas sobre primos surgem naturalmente. Por exemplo: Quantos primos existem e como estão
os primos distribuídos entre os outros naturais? Como se determinam primos? Como é que se mostra que um
dado natural é, ou não, um primo? Como se factoriza, num produto de primos, um natural arbitrário?
Mesmo com tão simples matéria-prima, a matemática fez uma obra espantosa e, se questões como as acima
colocadas, podem ser olhadas numa perspectiva abstracta e como um ramo da matemática inaplicável, a
verdade é que estas questões têm um significado crucial para a criptografia; tanto mais que têm havido sérias
tentativas de classificar alguns dos resultados desta área como segredos militares.
Tudo indica que são os métodos de codificação baseados em números primos que constituirão a parte central
do sistema de segurança da futura auto-estrada da informação.
Tarefa
Implemente em Prolog um codificador de mensagens e o respectivo descodificador. O codificador deve
substituir o código ASCII de cada caracter da mensagem por um outro de acordo com o algoritmo de
encriptação descrito pelos seguintes passos:
• Converter a mensagem dada numa lista L com os códigos ASCII de cada caracter.1
• Gerar a lista P de todos os primos até ao código ASCII do primeiro caracter da frase a codificar (1o
elemento de L), usando um predicado primos/2 que terá de desenvolver segundo o método a
indicar mais adiante.
• O primeiro caracter da frase não é alterado.
• Ao segundo caracter somamos o primeiro número primo em P; ao terceiro caracter somamos o
segundo elemento de P, etc, considerando a lista P de um modo circular --depois de somar o último
primo em P, voltar ao início da lista.
Lembramos que o código ASCII é um número entre 0 e 255 e que é preciso ter o cuidado de garantir que os
códigos ASCII codificados também estão nesta gama! Mais uma vez, deve usar a noção de circularidade: por
exemplo, se estamos a somar o primo 3, o código ASCII 33 será transformado no 36 enquanto o 254 será
transformado no 1. O algoritmo de descodificação deverá proceder à operaç ão inversa do de codificação.
Subtarefa
Implemente um predicado em Prolog para gerar números primos segundo o algoritmo designado por Crivo de
Eratóstenes. Esse método permite-nos construir uma lista de todos os primos até um limite dado, de acordo
com a seguinte descrição3:
‘‘Escrevemos os naturais desde 2 até ao limite desejado; por exemplo 200:
Começando no princípio da lista, o primeiro número que encontramos é 2, um primo. Deixamos 2 de lado, e
passamos à frente marcando os números de 2 em 2, isto é, marcamos 4, 6, 8, 10, . Depois de ter
marcado todos os números pares até 200, voltamos ao princípio da lista para encontrar o primeiro número
depois de 2 que não foi marcado: é 3. Deixamos 3 de lado (é primo), e passamos à frente marcando os
números de 3 em 3: marcamos 6, 9, 12, 15, . Prosseguimos assim. Depois de fazermos este jogo
repetidamente para 2, 3, , N, onde N é o maior natural não marcado tal que , então os
números da lista que ainda não foram marcados são os primos até 2002.'
'
Dados
O único dado necessário para a resolução da tarefa principal deste problema é a mensagem a codificar. Essa
mensagem será um parâmetro do predicado encode/2 que tem de desenvolver, como se explica na secção
seguinte. Para a subtarefa de geração de números primos também precisa de conhecer à partida apenas um
dado: o valor do limite superior da lista a gerar.
Resultados
O seu programa deve ser invocado através dos predicados:
a) encode/2 que recebe a mensagem e retorna no segundo argumento a lista dos códigos ASCII da mensagem
codificada
b) decode/2 que recebe a lista dos códigos ASCII da mensagem codificada e devolve no segundo argumento
a mensagem original.
c) primos/2 que recebe como primeiro argumento um número natural N e devolve no segundo argumento a
lista de todos os primos até N.
Exemplos
?- primos(100,L).
L = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,
53,59,61,67,71,73,79,83,89,97]
?- encode(’Ola malta’,C), decode(C,D).
C = [79,110,100,37,116,108,121,133,116]
D = ’Ola malta’
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL98/E – Concurso Nacional de Programação em Lógica 1998
Ginástica Rítmica
Introdução
Para organizar um espectáculo de ginástica rítmica, o responsável pelas actividades recreativas da Associação
Académica necessita de estabelecer um plano e distribuir trabalho pelos seus ginastas. O plano identifica a
sequência dos exercícios que vão ser apresentados e a distribuição de trabalho indica quais os ginastas que
vão participar em cada exercício. Para fazer a distribuição, o responsável pelo espectáculo necessita de
conhecer: os requisitos específicos de cada exercício; as características pertinentes de cada ginasta e as suas
exigências; e os ginastas que podem participar no dia do espectáculo em organização.
Tarefa
A sua tarefa consiste em desenvolver um programa em Prolog que realize automaticamente a referida
distribuição de trabalho --afectação de ginastas a cada exercício do plano - a partir duma base de
conhecimento que descreve precisamente: o plano; os exercícios; os ginastas; e os disponíveis (ginastas que
participarão no espectáculo).
Restrições:
O seu programa deve fazer a afectação tendo em consideração as seguintes regras:
• em cada exercício participarão pelo menos o número de ginastas especificado como mínimo para esse
exercício;
• em cada exercício não participarão mais ginastas do que o número especificado como máximo para
esse exercício;
• não serão deixados ginastas de fora, se tal não contrariar nenhuma exigência do exercício, como, por
exemplo, o total de participantes ter de ser par ou ímpar;
• se num exercício, forem deixados de fora alguns ginastas, esses serão os primeiros candidatos a
participar no exercício seguinte;
• os ginastas actuarão todos o mesmo número de vezes ou um número de vezes o mais próximo
possível;
• se o exercício especificar que é para ser executado aos pares, então cada par terá de ser formado por
ginastas de sexos diferentes, devendo, nesse caso, verificar-se que os elementos do par tenham a
mesma altura e o mesmo peso a menos de um delta pré-definido.
Dados
A Base de conhecimento necessária para testar o seu programa reside num ficheiro de nome
"ginastas.dat", o qual deve ser consultado no início. Esse ficheiro contém:
• os factos:
deltaAltura/1
deltaPeso/1
que definem, respectivamente, a máxima diferença entre alturas e entre pesos, permitida entre os
elementos de cada par;
• o facto plano/1 que especifica a lista de identificadores dos exercícios que se quer incluir no
espectáculo a organizar, isto é, descreve o plano do espectáculo;
• uma colecção de factos referente ao predicado: participa/1 sabendo-se que participa(num)
identifica cada ginasta que estará presente e apto no espectáculo em causa;
• uma colecção de factos referentes ao predicado: exercicio/6 cujos argumentos têm o seguinte
significado:
exercicio( id,descricao,minginastas,maxginastas,parimpar,tipo) em que parimpar indica se o
número de participantes tem de ser impar, par, ou se é indiferente; e tipo indica se esse
exercício é para ser executado aos pares, ou se é singular. Note-se que no caso do exercício
dever ser executado aos pares, os argumentos minginastas e maxginastas continuam a ser
referentes ao número de participantes que, nesse caso, será o dobro do número mínimo, ou máximo,
de pares pretendidos;
• uma colecção de factos relativos ao predicado: ginasta/6 cujos argumentos são ginasta(
num,nome,sexo,idade,peso,altura )
Resultados
O seu programa deve ser activado através do predicado de aridade zero distribui/0
Como resultado, deve acrescentar à base de conhecimento uma colecção de novos factos afectacao/2 em que
o primeiro argumento identifica um exercício e o segundo argumento é a lista de ginastas que o irão executar
ou a palavra retirar, caso não seja possível arranjar uma lista de ginastas que satisfaça todos os seus
requisitos.
Faculdade de Engenharia da Universidade do Porto 2003/2004
LEIC
Licenciatura em Engenharia Informática e Computação
(3º Ano)
Programação em Lógica 1º Sem
Docentes: Luís Paulo Reis e Eugénio da Costa Oliveira
Exercício CNPL98/F – Concurso Nacional de Programação em Lógica 1998
Lavandaria
Introdução
Uma dona de casa chegou de férias tendo trazido vários sacos de diferentes tipos de roupas (de corpo e de
casa) para lavar. Embora a sua máquina permita seleccionar várias temperaturas e escolher se se quer
centrifugar ou não, só tem capacidade para lavar 5kg de roupa de cada vez. Como fez grandes gastos nas
férias, pretende minimizar o número de lavagens, para poupar em água, detergente e energia eléctrica. Porém,
sente-se atrapalhada pois não sabe a melhor forma de combinar as roupas de modo a não ultrapassar a
capacidade da máquina e a garantir que fique bem lavada sem correr o risco de estragar alguma peça (o que
acontece se não respeitar as condições de lavagem próprias de cada artigo).
Tarefa
A sua tarefa consiste em desenvolver um programa em Prolog que determine uma solução possível para o
problema da dona de casa, ou seja, que proponha um agrupamento das peças de modo a respeitar as
restrições (da máquina e da roupa) anteriormente referidas.
Restrições:
O seu programa deve fazer o agrupamento tendo em consideração as seguintes regras:
• roupa branca não pode ser misturada com roupa de cor;
• cada lote não pode ultrapassar os 5kg;
• um tipo de roupa não pode ser lavado a uma temperatura mais alta que a indicada, nem pode ser
centrifugado se tal estiver interdito nas suas características;
• de preferência e sempre que tal não seja contra-indicado, as peças devem ser centrifugadas;
• quando a temperatura recomendada não é classificada como máxima, esse artigo terá de ser lavado
exactamente a essa temperatura.
Os Dados
A base de conhecimentos usada pelo programa reside num ficheiro de nome "lavanda.dat", o qual deve
ser consultado no início. Esse ficheiro contém uma colecção de factos referentes ao predicado:
roupaSuja/7
cujos argumentos são os descritos a seguir
roupaSuja(nomePecas,quantidade,pesoUnit,temperatura,tipo,centrifuga,cor)
em que tipo especifica se a temperatura indicada é a maxima ou a recomendada e centrifuga diz se pode (sim)
ou nao ser centrifugada. O argumento cor, toma o valor sim ou nao, conforme se trata de roupa de cor que
pode tingir ou de roupa branca.
Os Resultados
O seu programa deve ser activado através do predicado de aridade zero lava/0
Como resultado, deve acrescentar à base de conhecimento uma colecção de novos factos loteLavagem/3 em
que os argumentos terão o seguinte significado loteLavagem( temperatura,centrifuga,carga ) onde carga é
uma lista de pares que descreve o nome das peças e a quantidade que irão constituir o lote em causa.