Declaratieve Talen/Oplossing winkelen

Ga naar: navigatie, zoeken

Oplossing 1 (basisopgave)

prijs(delhaize, zeep, 8).
prijs(delhaize, prei, 10).
prijs(delhaize, zout, 6).
prijs(carrefour, prei, 9).
prijs(carrefour, soep, 19).
prijs(champion, zeep, 7).
prijs(champion, prei, 11).
prijs(champion, pinda, 6).

leveranciers([delhaize,carrefour,champion]).

bestorder(Bestelling, Plaatsing) :-
	findall(P, bestelling(Bestelling, P), Ps),
	list_to_set(Ps,Pset),
	filter_2(Pset,Max2Ps),
	minimum_kost(Max2Ps, Bestelling, Plaatsing).
	
%zoek de plaatsing met de laagste kost uit een lijst van plaatsingen
minimum_kost([P],_, MinP) :- 
	MinP = P.
	
minimum_kost([P|Ps],Bestelling, MinP) :-
	minimum_kost(Ps, Bestelling, MinPSub),
	kost(Bestelling, P, KostP),
	kost(Bestelling, MinPSub, SubKost),
	(KostP < SubKost ->
		MinP = P
		;
		MinP = MinPSub
	).
	

%filter plaatsingen met meer dan 2 winkels
filter_2([P],Ps) :-
	(max_2_winkels(P) ->
		Ps = [P]
		;
		Ps = []
	).
filter_2([P|Prest],Ps) :-
	filter_2(Prest,Precur),
	(max_2_winkels(P) ->
		Ps = [P|Precur]
		;
		Ps = [Precur]
	).
	
%bestaat de plaatsing uit maximaal 2 winkels?
max_2_winkels([_/_]).
max_2_winkels([_/Winkel|Rest]) :-
	max_2_winkels(Rest, [Winkel]).

max_2_winkels([_/Winkel],Winkels) :-
	list_to_set([Winkel|Winkels],X),
	length(X,Aantal),
	Aantal =< 2.
max_2_winkels([_/Winkel|Rest],Winkels) :-
	list_to_set([Winkel|Winkels],VerschillendeWinkels),
	length(VerschillendeWinkels,Aantal),
	Aantal =< 2,
	max_2_winkels(Rest,[Winkel|Winkels]).

%kost van een bestelling/plaatsing
kost([Product/Aantal], [Product/Winkel],TotaalKost) :-
	prijs(Winkel, Product, StukKost),
	TotaalKost is StukKost * Aantal.
kost([Product/Aantal|RestBest],[Product/Winkel|RestPlaatsing], TotaalKost) :-
	kost(RestBest,RestPlaatsing, Subkost),
	prijs(Winkel, Product, StukKost),
	Kost is StukKost * Aantal,
	TotaalKost is Kost + Subkost.

%een mogelijke bestelling
bestelling([Product/_],Plaatsing) :-
	leveranciers(Product, Leveranciers),
	select(Leveranciers, Leverancier),
	Plaatsing = [Product/Leverancier].
bestelling([X|Xs],Plaatsing) :-
	bestelling(Xs,P1),
	bestelling([X],[P2]),	
	Plaatsing = [P2|P1].
	
%alle leveranciers van een product
leveranciers(Product, Leveranciers) :- findall(X,prijs(X,Product,_),Leveranciers).

%selecteer een element uit een lijst
select([L],X) :- X = L.
select([L|Lijst],X) :-
	X = L;
	select(Lijst,X).

Oplossing 2 (volledige opgave)

Dit werkt voor zover ik getest heb. Basisopgave (bestorder) + uitbreiding (optimalorder).

optimalorder(O,Plaatsing) :-
	findall(P,bestorder(O,P),Pn),
	findall(P-Prijs,(member(P,Pn),totale_korting(O,P,Kort),kost(O,P,Kost),Prijs is Kost-Kort),PKn),
	findall(K,member(_-K,PKn),Kn),
	min_list(Kn,Min),
	member(Plaatsing-Min,PKn),
	!.
	
totale_korting(O,P,K) :- 
	zoek_winkels(P,Wn),
	korting(O,P,Wn,0,K).

zoek_winkels(P,Wn) :-
	findall(W,member(_/W,P),WDub),
	list_to_set(WDub,Wn).
	
korting(_,_,[],K,K).	
korting(O,P,[W|Wn],K,TotK) :-
	findall(Am,(member(Pr/W,P),member(Pr/Am,O)),Ams),
	sumlist(Ams,Sum),
	kortingspercentage(W,Sum,Korting),
	Knew is Korting + K,
	korting(O,P,Wn,Knew,TotK).
	
bestorder(O,Plaatsing) :-
	findall(P-K,(plaatsing(O,P),kost(O,P,K)),PKnDub),
	list_to_set(PKnDub,PKn),
	findall(K,member(_-K,PKn),Kn),
	min_list(Kn,Min),
	member(Plaatsing-Min,PKn).
	
kost(O,Pl,Kost) :- kost(O,Pl,0,Kost).
	
kost(_,[],K,K) :- !.
kost(Order,[Pr/W|Pn],Kost,TotKost) :-
	prijs(W,Pr,P),
	member(Pr/Am,Order),
	K is Kost + P * Am,
	kost(Order,Pn,K,TotKost),
	!.
	
plaatsing(O,Res) :-
	findall(Wi,prijs(Wi,_,_),WnDub),
	list_to_set(WnDub,Wn),
	( plaatsing_een(O,Res,Wn)
	; plaatsing_twee(O,Res,Wn)
	).

plaatsing_twee(Order,Plaatsing,Wn) :-
	select(W1,Wn,Wrest),
	member(W2,Wrest),
	plaatsing_twee(Order,W1,W2,[],Plaatsing).
	
plaatsing_twee([],_,_,P,P).
plaatsing_twee([Pr/_|On],W1,W2,Pl,PlRes) :-
	(	prijs(W1,Pr,_),
		W = W1
	;	prijs(W2,Pr,_),
		W = W2
	),
	plaatsing_twee(On,W1,W2,[Pr/W|Pl],PlRes).
	
plaatsing_een(Order,Plaatsing,Wn) :- 
	member(W,Wn),
	plaatsing_een(Order,W,[],Plaatsing).

plaatsing_een([],_,P,P).
plaatsing_een([Pr/_|On],Wi,Pl,PlRes) :-
	prijs(Wi,Pr,_),
	plaatsing_een(On,Wi,[Pr/Wi|Pl],PlRes).

Oplossing 3 (basisopgave)

% Overzicht van alle tarieven.
prijs(delhaize, zeep, 8).
prijs(delhaize, prei, 10).
prijs(delhaize, zout, 6).
prijs(carrefour, prei, 9).
prijs(carrefour, soep, 19).
prijs(champion, zeep, 7).
prijs(champion, prei, 11).
prijs(champion, pinda, 6).  

% Lijst van leveranciers.
leveranciers([delhaize,carrefour,champion]).

% Berekent alle plaatsingen die voldoen aan de minimale totaalprijs.
bestorder(Lijst, Resultaat) :- 
	findall(Totaal-Plaatsing, (plaatsingen(Lijst, Plaatsing), maxtwee(Plaatsing), totaal(Plaatsing, Totaal)), MogelijkePlaatsingen),
	geef_minimum(MogelijkePlaatsingen, MinimalePlaatsing),
	geef_plaatsing(MinimalePlaatsing, MogelijkePlaatsingen, Resultaten),
	member(Resultaat, Resultaten).

% Geeft de minimale totaalprijs van een lijst met mogelijke plaatsingen.
geef_minimum([Totaal-_Plaatsing], Totaal) :- !.
geef_minimum([Totaal-_Plaatsing|Rest], Minimum) :-
	geef_minimum(Rest, TussenTotaal),
	Minimum is min(Totaal, TussenTotaal).

% Geeft alle plaatsingen die voldoen aan de minimale totaalprijs.
geef_plaatsing(Minimum, Lijst, Resultaat) :-
	findall(Plaatsing, member(Minimum-Plaatsing, Lijst), Resultaat).

% Berekent de totale prijs van een order.
totaal([], 0) :- !.
totaal([Product/Leverancier|Rest], Totaal) :-
	prijs(Leverancier, Product, Prijs),
	totaal(Rest, TussenTotaal),
	Totaal is Prijs + TussenTotaal.	

% Gaat na welke leverancier een bepaald deel van een plaatsing voor zijn rekening kan nemen.
plaatsingen([], []) :- !.
plaatsingen(Lijst, [Product/Leverancier|Rest]) :-
	select(Product/_Aantal, Lijst, NieuweLijst),
	prijs(Leverancier, Product, _Prijs),
	plaatsingen(NieuweLijst, Rest).

% Stelt een lijst op van alle leveranciers die een deel van een plaatsing uitvoeren.
geef_leveranciers([], []) :- !.
geef_leveranciers([_Product/Leverancier|Rest], [Leverancier|Resultaat]) :-
	geef_leveranciers(Rest, Resultaat).

% Gaat na of de plaatsing door maximaal twee verschillende leveranciers uitgevoerd wordt.
maxtwee(Plaatsing) :-
	geef_leveranciers(Plaatsing, Lijst),
	list_to_set(Lijst, Verzameling),
	length(Verzameling, Aantal),
	Aantal =< 2.