Faculdade de Engenharia da Universidade do Porto
Licenciatura em Engenharia Informática e Computação
Programação em Lógica
Época Normal – Com Consulta / Duração: 2h30m
2003/2004
3º Ano
LEIC
Nome:
Data:
19/01/2004
Tópicos de Resolução do Exame de PL de 19 de Janeiro de 2004.
Diversas sugestões de resoluções são apresentadas, contendo algumas, propositadamente, pequenos “Bugs” e sugestões de trabalho. Sugere-se a análise de todas as resoluções realizando rastreios apropriados de forma a compreende-las bem.
GRUPO I – Programação em Prolog (7 val.)
:-use_module(library(lists)).
país(alemanha, europa, 82, [frança, belgica, holanda, suica]). país(australia, oceania, 19, []).
país(belgica, europa, 10, [frança, holanda, alemanha]). país(espanha, europa, 40, [portugal, frança]).
país(frança, europa, 59, [espanha, suica, belgica, alemanha, italia]). país(holanda, europa, 15, [belgica, alemanha]).
país(indonesia, oceania, 210, []).
país(italia, europa, 57, [frança, suica]). país(madagascar, africa, 17, []).
país(portugal, europa, 10, [espanha]).
país(suica, europa, 7, [frança, alemanha, italia]). %1.1 a) pop_elevada(+Continente, -Lista).
pop_elevada(Continente, Lista):-
findall(Pop-Pais, (país(Pais, Continente, Pop, _), Pop>15), ListaIni), keysort(ListaIni, Lista).
% 1.1 b) isolados_grandes(Lista).
isolados_grandes(Lista):- findall(Cont,
(isolado_grande(Pais1, Cont), isolado_grande(Pais2, Cont) , Pais1\=Pais2), Lista1),
sort(Lista1, Lista). isolado_grande(Pais, Cont):-
país(Pais, Cont, Pop, Front), Pop>15, length(Front, L), L<3.
% Outra versão
isolados_grandes2(Lista):- findall(Cont,
(país(_,Cont,_,_),findall(Pais1, isolado_grande2(Pais1, Cont), L), length(L,N), N>=2),
Lista1), sort(Lista1, Lista). isolado_grande2(Pais, Cont):-
país(Pais, Cont, Pop, Front), Pop>15, length(Front, L), L<3. % 1.2 Função dos Cuts
% O Cut em Imaturo funciona como no predicado Not. Se X for adulto então (!, fail) não é % imaturo. Senão é imaturo. É’ um Cut Vermelho pois é essencial para o funcionamento do % programa.
% O Cut em adulto evita explorar espaço de pesquisa em que é impossível estar a solução. % Se X for uma pessoa, então só será adulto se tiver uma idade maior ou igual a 18. Só % se X não for uma pessoa é que se vai verificar se X é uma Tartaruga, ... É um Cut verde % pois não altera as soluções obtidas mas sim a eficiência do programa.
% 1.3 a) caminho(+NoInicio, +NoFim, -Lista), ligacao(1, 2). ligacao(1, 3). ligacao(2, 4). ligacao(3, 4). ligacao(3, 6). ligacao(4, 6). ligacao(5, 6). ligacao2(X,Y):- ligacao(X,Y). ligacao2(X,Y):- ligacao(Y,X).
caminho(NoInicio, NoFim, Lista):- caminho(NoInicio, NoFim, [NoInicio], Lista, 5). caminho(NoInicio, NoFim, Lista, ListaFim,_):-
ligacao2(NoInicio, NoFim),
append(Lista, [NoFim], ListaFim).
caminho(NoInicio, NoFim, Lista, ListaFim, N):- N>0,
ligacao2(NoInicio, NoInterm), NoInterm \= NoFim,
\+(member(NoInterm, Lista)), append(Lista, [NoInterm], Lista2), N2 is N-1,
caminho(NoInterm, NoFim, Lista2, ListaFim, N2).
% Outra versão. Sera' que funciona? Porque?
caminho2(NoIni, NoFim, Lista):- caminho2(NoIni, NoFim, Lista, 5). caminho2(_,_,_,0):- !,fail.
caminho2(NoIni, NoFim, [NoIni,NoFim], _):- ligacao2(NoIni,NoFim). caminho2(NoIni, NoFim, [NoIni|Rest], N):-
N2 is N-1,
ligacao2(NoIni, NoInt),
caminho2(NoInt, NoFim, Rest, N2). % Ainda outra Versão!
caminho3(NoIni, NoFim, Lista):- caminho3(NoIni, NoFim, Lista, 0). caminho3(NoIni, NoFim, [H|Rest], N):-
N < 4,
ligacao2(NoIni, NoInt),
(NoInt = NoFim, [H|Rest] = [NoIni, NoFim] ;
H = NoIni, N2 is N+1, caminho3(NoInt, NoFim, Rest, N2)).
% 1.3 b) ciclos(+No, +Comp, -Lista),
ciclos(No, Comp, Lista):-
findall(Ciclo, caminho(No, No, [], Ciclo, Comp), Lista).
% 2.1) divideN(+Lista, +N, -ListaN, -ListaR)
divideN(L, 0, [], L).
divideN([H|T], N, [H|T2], ListaR):-
N2 is N-1, divideN(T, N2, T2, ListaR).
% 2.2) opera_panquecas(+N, +Lista , -Lista_Res)
opera_panquecas(N, Lista, Lista_Res):-
divideN(Lista, N, ListaN, ListaResto), reverse(ListaN, ListaNInv),
append(ListaNInv, ListaResto, Lista_Res).
% 2.3) lista_ordenada(+N, -Lista)
lista_ordenada(N, Lista):- lista_ordenada(1, N, Lista). lista_ordenada(N, N, [N]) .
lista_ordenada(X, N, [X|T]) :- X1 is X+1,
% Outra Versão
lista_ordenada2(N, Lista):- lista_ordenada2(N, [], Lista). lista_ordenada2(0, Lista, Lista) .
lista_ordenada2(N, Resto, Lista) :- N2 is N-1,
lista_ordenada2(N2, [N|Resto], Lista). % Ainda outra Versão
lista_ordenada3(1, [1]) . lista_ordenada3(N, Lista) :-
N2 is N-1,
append(Lista2, [N], Lista), lista_ordenada3(N2, Lista2).
% 2.4) resolve_panquecas(+Lista, -Solucao, -Estados)
resolve_panquecas(Lista, Solucao, [Lista|Estados]):- length(Lista, N),
lista_ordenada(N, Final),
resolve_panquecas(Lista, Final, Solucao, Estados, []). resolve_panquecas(Final, Final, [], [], _).
resolve_panquecas(Lista, Final, [N|RestoOp], [Est|RestoEst], Est_Visit):- member(N, Lista),
opera_panquecas(N, Lista, Est), \+(member(Est, Est_Visit)),
resolve_panquecas(Est, Final, RestoOp, RestoEst, [Est|Est_Visit]).
% Versão 2
resolve_panquecas2(Lista, Solucao, Estados):- length(Lista, N),
lista_ordenada(N, Final),
resolve_panquecas2(Lista, Final, [], Solucao, [Lista], Estados). resolve_panquecas2(Final, Final, SolFinal, SolFinal, EstFinal, EstFinal). resolve_panquecas2(Lista, Final, Solucao, SolFinal, Estados, EstFinal):-
member(N, Lista),
opera_panquecas(N, Lista, Lista_Res), \+(member(Lista_Res, Estados)), append(Solucao, [N], NovaSol),
append(Estados, [Lista_Res], NovosEst),
resolve_panquecas2(Lista_Res, Final, NovaSol, SolFinal, NovosEst, EstFinal). % Versão 3 - Será que funciona? Porque?
resolve_panquecas3(Final, [], [Final]):- lista_ordenada(_, Final). resolve_panquecas3(Lista, [N|RestoOp], [Lista|RestoEst]):-
member(N, Lista),
opera_panquecas(N, Lista, Est),
resolve_panquecas3(Est, RestoOp, RestoEst), \+(member(Lista, RestoEst)).
% Versão 4 - Sem Backtracking (usando heuristica para resolver o problema) resolve_panquecas4(Final, [], [Final]):- lista_ordenada(_, Final).
resolve_panquecas4(Lista, [Op|RestoOp], [Lista|RestoEst]):- selectN(Lista, Op), !,
opera_panquecas(Op, Lista, Est), !,
resolve_panquecas4(Est, RestoOp, RestoEst). selectN(Lista, Pos):-
length(Lista, Size), reverse(Lista, Lista2),
get_pos(Lista2, Size, _, Pos).
get_pos([H|Resto], H, Elem, Pos):- H2 is H-1, get_pos(Resto, H2, Elem, Pos). get_pos(Lista, Elem, Elem, Pos):- search(Lista, Elem, Elem, Pos).
search([Elem], Elem, _, Elem). % Element is on top, put it on bottom search([Elem|_], Elem, PosF, PosF). % Element is not on top, get it to top
/*
% Predicados Auxiliares (library lists) append([], List, List).
append([H|Tail], List, [H|Rest]) :- append(Tail, List, Rest). member(Element, [Element|_]).
member(Element, [_|Rest]) :- member(Element, Rest). reverse(List, Reversed) :- reverse(List, [], Reversed). reverse([], Reversed, Reversed).
reverse([H|Tail], SoFar, Reversed) :- reverse(Tail, [H|SoFar], Reversed). */ % 3.1 :-use_module(library(clpfd)). puzzle([A,B,C]):- domain([A,B,C],1,50), domain([A1,B1],0,5), domain([A2,B2],0,9), A #= A1*10+A2, B #= B1*10+B2, B #= 2*A, C #= 10+B, A+B #> 10, A1 mod 2 #\= A2 mod 2, B1 mod 2 #= B2 mod 2, labeling([ff],[A,B,C]). % 3.2 a)
% NMP is NP*NM % Nove variáveis para o exemplo, representando a
% length(VarMaquina, NMP), % Quantidade produzida por cada máquina de cada produto
% domain(VarMaquina, 0, 50), % Dominio entre 0 e 50 (embora se pudesse reduzir…)
% length(VarProd, NP), % Podia-se adicionar a Quantidade produzida de cada produto
% domain(VarProd, 0, 150), % com Dominio entre 0 e 150 (podia-se reduzir…)
% Explicar isto mais detalhadamente % 3.2 b)
% Hard Constraints – Restrições Rígidas
% HC1: Não Produzir mais do que máximo de cada produto (explicar…)
% HC2: Horas de utilização das máquinas não ultrapassam o máximo (explicar)
% HC3: Quantidade Total de cada Produto igual à soma do produzido em cada maquina
% Esta restrição não é precisa (depende das variáveis utilizadas…)
% Soft Constraints – Restrições Flexíveis
% FC1: Lucro é igual à soma dos lucros das unidades de produtos produzidas. % Esta é a Função de avaliação a maximizar.
% Explicar isto mais detalhadamente % 3.2 c) :-use_module(library(clpfd)). produtos(3, [60,30,20]). maquinas(3, [500, 350, 150]). producao([[9,3,5], [5,4,20], [3,20,2]]). lucros([50, 20, 25]).
%Versão 1: Não totalmente genérica (80% Cotação…)
solve(VarProd, VarMaquina, Lucro):-
produtos(NP, MaxProd), % Leitura de Dados
maquinas(NM, Horas), producao(Producao), lucros(LucroProd), NMP is NM*NP,
length(VarProd, NP), % Variáveis e Domínios
VarProd = [P1, P2, P3], domain(VarProd, 0, 150), length(VarMaquina, NMP),
domain(VarMaquina, 0, 50),
maximo_produtos(VarProd, MaxProd), % HC1: Não Produzir mais do que
% máximo de cada produto
Horas = [HA, HB, HC],
Producao = [[PA1, PA2, PA3], [PB1, PB2, PB3], [PC1, PC2, PC3]],
A1*PA1+A2*PA2+A3*PA3 #=< HA, % HC2: Horas de utilização das máquinas
B1*PB1+B2*PB2+B3*PB3 #=< HB, % não ultrapassam o máximo
C1*PC1+C2*PC2+C3*PC3 #=< HC,
A1+B1+C1 #= P1, % HC3: Quantidade Total de cada Produto igual
A2+B2+C2 #= P2, % à soma do produzido em cada maquina
A3+B3+C3 #= P3,
scalar_product(LucroProd, VarProd, #=, Lucro),
% FC1: Lucro é igual 'a soma dos lucros dos produtos
maximize(labeling([ff], VarMaquina), Lucro). % Labeling. Pesquisa da solução. maximo_produtos([], []).
maximo_produtos([V|RProd], [Max|RMax]):- V #=< Max, maximo_produtos(RProd, RMax).