I'm attempting to write a (naive, or semi-naive) program that given a set of elements and a number of players divides it to this number of players, and for each such division takes the minimal value (by sum) subset. Then, I want to calculate the maximum of all these minimal divisions.
This is known as the https://en.wikipedia.org/wiki/Maximin_share .
For example, if we look at the set {1,4,6} we can divide it for 2 players such that one receives {1,4} and the other {6}, the minimalk value here is 5. For all other divisions, it is easy to see that the minimal value will be less than 5.
I want to write a prolog predicate maximin(+Elements,+Players,-Value) that given a list of Elements (positive integers) and a number of Players returns the maximin Value.
I tried a very naive approach:
- Wrote a predicate that calculates a certain division.
- Used
find allto find all the divisions. - For each division returned the value of the minimal subset.
- Calculated the maximum of them.
However, this program runs only for small inputs. For example, if I take a 10 elements list Elements and attempt to divide it to 3 players, I get an error of out of stack, even though I tried to increase the program memory as best as I could with set_prolog_stack.
My code:
% returns the smaller item
mini(A,B,B):- A > B.
mini(A,B,A):- A =< B.
% Returns the Sum of the minimal subset in a division (+,-)
min_subset_value([P1],Sum1):-subset_value(P1,Sum1).
min_subset_value([P1|RestP],Min) :- RestP \= [], min_subset_value(RestP, OtherMin), subset_value(P1,A1)
, mini(A1, OtherMin, Min).
% divide_to_players(+,+,-) : Generate a division of all the Elements to N subsets
divide_to_players([],N,EmptySets):- N>=1, generate_empty_sets(EmptySets,N).
divide_to_players(Elements, 1, [Elements]):-Elements \= [].
divide_to_players(Elements, N, [P1|RestP]):- N>1, Elements \= [], subseti(P1,Elements)
, remove_subset(Elements,P1,OtherElements)
, N1 is N-1
, divide_to_players(OtherElements, N1, RestP).
% Generates all divisions (+,+,-)
generate_all_divisions(Elements,N,AllDivs) :- findall(Div,divide_to_players(Elements,N,Div),AllDivs).
% From all the given divisions, which one has the maximal minimal subset
find_max_division([],0).
find_max_division([Set1|RestSets],Max) :- find_max_division(RestSets, OtherMin)
, min_subset_value(Set1,LocalMin)
, maxi(LocalMin, OtherMin, Max).
% uses the previous functions as described above (+,+,-)
maximin_player(Elements,N,Maximin) :- generate_all_divisions(Elements,N,AllDiv)
, find_max_division(AllDiv,Maximin).
However, I am wandering if there is another way to go on it? Maybe finding the maximin value without using findall? I only used findall because I did not have any better idea for an approach, so I'd appreciate to hear other ideas or approaches to this problem.