Related
I have a semi complex shift scheduling problem in prolog. From what I saw it can be solved with CLF but I am not that familiar and the resources online didn't really help me.
The problem states that the company has 50 employees and that each employee can either work in the morning shift(M), the evening shift(E), the night shift(N) or have a rest day(R). The problem has 2 constraints: That at least 15 employees must work at the morning shift(M), 10 in the evening one(E) and 8 in the night one(N) and that no employee can work the night shift(N) and have a morning shift(M) the next day.Also in a period of 7 days an employee must have at least 2 days off fro example from day 1 to 7 atleast two R and the same from 2 to 8.
It asks to produce a 30 day schedule by satisfying the above constraints and that multiple solutions exist.
What could be some way to approach the problem and how could I implement it using code in prolog?
Thank you very much!
Here a solution with out the last task
days_in_month(30).
employees_num(50).
go :-
days_in_month(Days),
length(M, Days),
days(M),
show_days(M).
days([D1, D2|T]) :-
two_days(D1, D2),
(T = [] ; days([D2|T])).
other_day_constraints(D) :-
day_constraint(10, e, D),
maplist(rest_if_not_work, D).
day_constraint(Min, Element, Lst) :-
employees_num(EmpsNum),
list_has_ge_elements_being(Min, Element, EmpsNum, Lst).
two_days(D1, D2) :-
% Set the full number of employees, otherwise prevent_double_shift can shorten the list
employees_num(EmpsNum),
length(D1, EmpsNum),
length(D2, EmpsNum),
% Pass the 2-day constraint first
day_constraint(8, n, D1),
prevent_double_shift(D1, D2),
day_constraint(15, m, D2),
% Remainder of the day constraints
day_constraint(15, m, D1),
day_constraint(8, n, D2),
other_day_constraints(D1),
other_day_constraints(D2).
prevent_double_shift([], []).
prevent_double_shift([H1|T1], [H2|T2]) :-
(H1 == n -> dif(H2, m) ; true),
prevent_double_shift(T1, T2).
rest_if_not_work(E) :-
(var(E) -> E = r ; true).
show_days([]).
show_days([D|T]) :-
show_day(D),
show_days(T).
show_day(D) :-
forall(member(E, D), (upcase_atom(E, U), write(U))),
nl.
list_has_ge_elements_being(Min, Elem, MaxLen, L) :-
list_has_ge_elements_being_(L, Min, Elem, MaxLen).
list_has_ge_elements_being_(L, Min, Elem, Min) :-
!,
length(L, Min),
maplist(=(Elem), L).
list_has_ge_elements_being_(_L, 0, _Elem, _MaxLen).
list_has_ge_elements_being_([H|T], Min, Elem, MaxLen) :-
Min #> 0,
MaxLen #> Min,
( H = Elem,
Min0 is Min - 1
; Min0 = Min
),
MaxLen0 is MaxLen - 1,
list_has_ge_elements_being_(T, Min0, Elem, MaxLen0).
Here's part of the puzzle - I'm sure you can figure out the rest:
split_list_length_excl_rem(SegLen, L, Segs) :-
length(L, Len),
between(1, Len, SegLen),
split_list_length_excl_rem_(SegLen, Len, L, Segs).
split_list_length_excl_rem_(SegLen, Len, L, Segs) :-
( Len #< SegLen
-> Segs = []
; length(Seg, SegLen),
split_list_length_excl_rem_seg_(Seg, L, R),
Segs = [Seg|SegsT],
Len0 is Len - SegLen,
split_list_length_excl_rem_(SegLen, Len0, R, SegsT)
).
split_list_length_excl_rem_seg_([], R, R).
split_list_length_excl_rem_seg_([H|T], [H|LT], R) :-
split_list_length_excl_rem_seg_(T, LT, R).
Results in swi-prolog:
?- numlist(1, 30, NL), split_list_length_excl_rem(7, NL, S).
NL = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
S = [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21], [22, 23, 24, 25, 26, 27, 28]].
Oops, misread the week bit, this is more appropriate:
sliding_segment(SegLen, L, Seg) :-
length(L, Len),
between(1, Len, SegLen),
length(Seg, SegLen),
sliding_segment_(L, Seg).
sliding_segment_([H|T], Seg) :-
fill_list_copy(Seg, [H|T]).
sliding_segment_([_|T], Seg) :-
sliding_segment_(T, Seg).
fill_list_copy([], _).
fill_list_copy([H|T], [H|R]) :-
fill_list_copy(T, R).
Results in swi-prolog:
?- numlist(1, 30, NL), sliding_segment(7, NL, Seg).
Seg = [1, 2, 3, 4, 5, 6, 7] ;
Seg = [2, 3, 4, 5, 6, 7, 8] ;
Seg = [3, 4, 5, 6, 7, 8, 9] ;
...
Seg = [23, 24, 25, 26, 27, 28, 29] ;
Seg = [24, 25, 26, 27, 28, 29, 30] ;
false.
The code below successfully finds a member in a binary tree. Except that now I want to find a member in a 2-3 tree. (Similar to the tree shown in https://www.youtube.com/watch?v=vSbBB4MRzp4.) The last line below is an attempt to do this. But I am unsure what to use for the items marked TBD (and other parts of this line could be wrong). Any help on how to do this? Thanks!
bTree(L,X,R).
member(X, bTree(L,Y,_)) :-
X #< Y,
member(X,L).
member(X, bTree(_,X,_)).
member(X, bTree(_,Y,R)) :-
X #> Y,
member(X,R).
member(X, bTree(L,Y,R)) :-
X #> TBD, X #< TBD
member(X,TBD).
In a 2–3-tree, each node can be a:
2-node, say t(Left,Key,Right), that has one key and two children, or
3-node, say t(Left,Key1,Middle,Key2,Right), that has two keys and three children.
Thus, the 2-3-tree:
can be represented as:
mytree(
t(t(t(nil,1,nil),
4,
t(nil,5,nil,7,nil),
13,
t(nil,15,nil)),
16,
t(t(nil,17,nil,19,nil),
20,
t(nil,22,nil),
24,
t(nil,29,nil)))
).
To search this tree, you can use the following predicate:
:- use_module(library(clpfd)).
% search 2-nodes: t(Left, Key, Right)
has(t(L,X,_), K) :- K #< X, has(L, K).
has(t(_,K,_), K).
has(t(_,X,R), K) :- K #> X, has(R, K).
% search 3-nodes: t(Left, Key1, Middle, Key2, Right)
has(t(L,X,_,_,_), K) :- K #< X, has(L, K).
has(t(_,K,_,_,_), K).
has(t(_,X,M,Y,_), K) :- K #> X, K #< Y, has(M, K).
has(t(_,_,_,K,_), K).
has(t(_,_,_,Y,R), K) :- K #> Y, has(R, K).
Examples:
?- mytree(T), has(T, 51).
false.
?- mytree(T), has(T, 16).
T = t(t(t(nil, 1, nil), 4, t(nil, 5, nil, 7, nil), 13, t(nil, 15, nil)), 16, t(t(nil, 17, nil, 19, nil), 20, t(nil, 22, nil), 24, t(nil, 29, nil))) .
?- mytree(T), forall(has(T,X), writeln(X)).
1
4
5
7
13
15
16
17
19
20
22
24
29
T = t(t(t(nil, 1, nil), 4, t(nil, 5, nil, 7, nil), 13, t(nil, 15, nil)), 16, t(t(nil, 17, nil, 19, nil), 20, t(nil, 22, nil), 24, t(nil, 29, nil))).
I want to multiply elements in a List with findall/3. Specifically I have two functions double(X,Y) which doubles X and square(X,Y) that returns the squared value of X. My problem is that it the operation works only for the first element of the list.
double(X,Y) :- Y is X*2.
square(X,Y) :- Y is X*X.
map_f(Operation,[H|List],[R|Results]) :-
Predicate=..[Operation,H,R],
call(Predicate),
findall(X,( member(X,List) ), Results).
For example, if I type map_f(double,[3,1,2,6,3,1,6],L). ,
I expect the output: L = [6,2,4,12,6,2,12],
but instead it shows:
?- map_f(double, [3, 1, 2, 6, 3, 1, 6], List).
List = [6, 1, 2, 6, 3, 1, 6]
Yes (0.00s cpu)
Any help will be very appreciated.
If you want to use findall/3, you'd have to write it like this:
?- Xs = [3,1,2,6,3,1,6], findall(Y, ( member(X, Xs), double(X, Y) ), Ys).
Xs = [3, 1, 2, 6, 3, 1, 6],
Ys = [6, 2, 4, 12, 6, 2, 12].
If you really want to pass the predicate as an argument and use =.., the logic is still the same, you'd just have to re-write your definition so that it does the right thing:
map_f(Pred_name, L1, L2) :-
Goal =.. [Pred_name, X, Y],
findall(Y, ( member(X, L1), Goal ), L2).
Then:
?- map_f(double, [3,1,2,6,3,1,6], R).
R = [6, 2, 4, 12, 6, 2, 12].
?- map_f(square, [3,1,2,6,3,1,6], R).
R = [9, 1, 4, 36, 9, 1, 36].
But, instead of:
Goal =.. [Pred_name, Arg1, Arg2], Goal
it is easier to use call/N+1:
call(Pred_name, Arg1, Arg2)
So your definition will become:
map_f(Pred_name, L1, L2) :-
findall(Y, ( member(X, L1), call(Pred_name, X, Y) ), L2).
But really, all of this is completely unnecessary if you only have lists. You can just use maplist/N+1, like that:
?- maplist(double, [3,1,2,6,3,1,6], R).
R = [6, 2, 4, 12, 6, 2, 12].
... which iterates over the lists instead of backtracking over them. You can see a maplist implementation for example here:
https://github.com/SWI-Prolog/swipl-devel/blob/2d20d4e8ac28adfcede7a9bd231ea0d9d12d0bbb/library/apply.pl#L195-L205
If your predicate is a real relation (so if it works both ways), you can also use maplist both ways. findall cannot do that! Here is one silly example:
?- maplist(succ, [1,2,3], R).
R = [2, 3, 4].
?- maplist(succ, R, [1,2,3]).
R = [0, 1, 2].
?- map_f(succ, [1,2,3], R).
R = [2, 3, 4].
?- map_f(succ, R, [1,2,3]).
ERROR: Arguments are not sufficiently instantiated
I need to write a progam which is getting arguments:
list_of_numbers,
result_number,
Result_list (for generating list of signs).
And is generating a list of operation sings + and - that in arithmetical meaning have a result of result_number. Also, it does concatenation of numbers to make new ones so the arithmetical meaning would be right.
So, for example, if we have a method arrange_signs(list_of_numbers, result_number, Result_List), here's how it would work:
?- arrange_signs([1, 2, 3, 4, 5, 6, 7, 8, 9], 4, Result_List).
12-3+45-67+8-9 = 4
?- arrange_signs([1, 2, 3, 4, 5, 6, 7, 8, 9], 15, Result_List).
1+2-34+56+7-8-9 = 15
How to write a program which is doing that?
I've written a program which is doing this:
?- arrange_signs([12, 3, 45, 67, 8, 9], 4, Result_List).
12-3+45-67+8-9 = 4
?- arrange_signs([1, 2, 34, 56, 7, 8, 9], 15, Result_List).
1+2-34+56+7-8-9 = 15
But I'm not sure how to write a program that is working with [1, 2, 3, 4, 5, 6, 7, 8, 9].
Here's my code:
arrange_signs([Number|Number_List],Result,Result_List) :-
generateOperationList(Number_List, [], OperationList),
find_result(Number,Number_List,OperationList,Result),
getResult([Number|Number_List], OperationList, Result_List),
show_result(Result_List, Result).
generateOperationList([_Head|Tail], Temp_List, [First_Operation|OperationList]) :-
getSign(First_Operation),
generateOperationList(Tail, Temp_List, OperationList).
generateOperationList([],Temp_List,Temp_List).
getSign('-').
getSign('+').
getOperation(Number1, '-', Number2, Answer) :-
Answer is Number1 - Number2.
getOperation(Number1, '+', Number2, Answer) :-
Answer is Number1 + Number2.
getResult([Number|Number_List], [Operation|OperationList], [Number,Operation|Result_List]) :-
getResult(Number_List, OperationList, Result_List).
getResult(Number_List, [], Number_List).
find_result(Temp_Answer,[Number|Number_List],[Operation|OperationList],Result) :-
getOperation(Temp_Answer, Operation, Number, New_Temp_Result),
find_result(New_Temp_Result,Number_List,OperationList,Result).
find_result(New_Temp_Result,[],[],New_Temp_Result).
print_result(Ready_Result) :-
write(Ready_Result).
show_result([First_Element|Result_List],Result) :-
generate_result([First_Element|Result_List],Result,'',Ready_Result),
print_result(Ready_Result).
generate_result([First_Element|Result_List],Result,Formatting_Result,Ready_Result) :-
atom_concat(Formatting_Result, First_Element, New_Formatting_Result),
generate_result(Result_List,Result,New_Formatting_Result,Ready_Result).
generate_result([],Result,Formatting_Result,Ready_Result) :-
atom_concat(Formatting_Result, '=', Temp_Variable),
atom_concat(Temp_Variable, Result, New_Formatting_Result),
generate_result(New_Formatting_Result,Ready_Result).
generate_result(New_Formatting_Result,New_Formatting_Result).
Sorry but I find difficult to understand your code.
I propose the following solution
atomL_concat([], '').
atomL_concat([A | T], C1) :-
atomL_concat(T, C0),
atom_concat(A, C0, C1).
arrangeS([], Target, Target, [' = ', ATarget]) :-
number_atom(Target, ATarget).
arrangeS([NH | NT], Target, Sum0, ['+', ANH | ST]) :-
Sum1 is Sum0 + NH,
arrangeS(NT, Target, Sum1, ST),
number_atom(NH, ANH).
arrangeS([NH | NT], Target, Sum0, ['-', ANH | ST]) :-
Sum1 is Sum0 - NH,
arrangeS(NT, Target, Sum1, ST),
number_atom(NH, ANH).
arrangeS([NH1, NH2 | NT], Target, Sum, ResList) :-
NH is NH1 * 10 + NH2,
arrangeS([NH | NT], Target, Sum, ResList).
arrange_signs(NumList, Target, ResList) :-
arrangeS(NumList, Target, 0, ['+'|ResList]),
atomL_concat(ResList, PrintList),
write(PrintList), nl.
If you want accept solutions starting with a negative number (by example: "-12-3-4+5-6+7+8+9 = 4") you can remove the first + removal and write arrange_signs/2 as
arrange_signs(NumList, Target, ResList) :-
arrangeS(NumList, Target, 0, ResList),
atomL_concat(ResList, PrintList),
write(PrintList), nl.
but, in this case, the solutions starting with a positive number are preceded by a + sign (so "+1+2-34+5+6+7+8+9 = 4" instead of "1+2-34+5+6+7+8+9 = 4").
I'm trying to generate a 4x4 magic square puzzle, and once a solution that is valid is found the list gets printed. I have the rules to print a given solution and generate a random board as well as solve it, but I don't know how I could have the generate fact be called until it returns true, and then print it. Here is my code:
check([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]) :-
A+B+C+D=:=34,
E+F+G+H=:=34,
I+J+K+L=:=34,
M+N+O+P=:=34,
A+E+I+M=:=34,
B+F+J+N=:=34,
C+G+K+O=:=34,
D+H+L+P=:=34,
A+F+K+P=:=34,
D+G+J+M=:=34.
solve([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]) :-
permutation(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]),
check([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]).
generate :-
random_permutation(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]),
solve([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]),
printlist([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]).
printlist([X|List]) :-
write(X),nl,
printlist(List).
I know there isn't looping in the traditional sense with Prolog, but I don't quite understand how I could run until a valid case is found(also I am aware the brute force method used here could take quite some time).
Any insight as to solving this would be greatly appreciated!
Your program is calling for another approach!
Just look at the goal:
permutation(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]),
This goals will produce 16! solutions. In other words: 20 922 789 888 000. So, in case you have a fast computer at your disposal, ...
To improve this situation, we have to reduce the number of solutions or answers. But, how? Prolog has something very nice: the logic variable. That is, we can subsume many solutions within a single answer using constraints. In this case library(clpfd) will help:
?- Xs = [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], Xs ins 1..16, all_different(Xs).
Xs = [A, B, C, D, E, F, G, H, I|...],
A in 1..16,
all_different([A, B, C, D, E, F, G, H|...]),
B in 1..16,
C in 1..16,
D in 1..16,
E in 1..16,
F in 1..16,
G in 1..16,
H in 1..16,
I in 1..16,
J in 1..16,
K in 1..16,
L in 1..16,
M in 1..16,
N in 1..16,
O in 1..16,
P in 1..16.
One answer now subsumes all twentytrillion solutions! With further constraints we can now write:
:- use_module(library(clpfd)).
magquad_([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]], Zs) :-
Xs = [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P],
Xs ins 1..16,
all_different(Xs),
Zs = [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P],
A+B+C+D#=34,
E+F+G+H#=34,
I+J+K+L#=34,
M+N+O+P#=34,
A+E+I+M#=34,
B+F+J+N#=34,
C+G+K+O#=34,
D+H+L+P#=34,
A+F+K+P#=34,
D+G+J+M#=34.
magquad(Xss) :-
magquad_(Xss, Zs), % use of core-relation or model
labeling([], Zs). % separate labeling
Let the printing be done by the toplevel!
?- time(magquad(Xss)).
% 106,412 inferences, 0.052 CPU in 0.053 seconds (99% CPU, 2049006 Lips)
Xss = [[1, 2, 15, 16], [12, 14, 3, 5], [13, 7, 10, 4], [8, 11, 6, 9]] ;
% 36,910 inferences, 0.027 CPU in 0.027 seconds (99% CPU, 1384976 Lips)
Xss = [[1, 2, 15, 16], [13, 14, 3, 4], [12, 7, 10, 5], [8, 11, 6, 9]] ;
% 209,488 inferences, 0.089 CPU in 0.089 seconds (100% CPU, 2348606 Lips)
Xss = [[1, 2, 16, 15], [13, 14, 4, 3], [12, 7, 9, 6], [8, 11, 5, 10]] ...
As you can see, Prolog can now ridiculously fast find all those magic squares!