У меня есть неполное решение. Но я надеюсь, что этого достаточно, чтобы дать вам надежду разобраться с остальными. Сначала я немного пересмотрел вашу модель данных:
family(antonella,1).
family(domenico,1).
family(raffaella,1).
family(tommaso,1).
family(vincenzo,1).
family(azzurra,2).
family(cristiano,2).
family(francesca,2).
family(luigi,2).
Также я удалил notinfamily/1
, так как у вас был один человек, которого не было ни в семье, ни в семье, и это стоило мне времени на отладку. :) Лучше сделать вывод.
Далее нам нужна пара помощников. Сначала несколько помощников для вашей модели данных, чтобы было легче определить, совпадают ли семьи или сражаются люди. Это должно быть довольно просто понять, я просто гарантирую, что либо семьи действительно совпадают, либо одна из них не принадлежит семье (что мы проверяем с отрицанием, а не notinfamily/1
):
family_matches(P1, P2) :-
family(P1, Family),
family(P2, Family).
family_matches(P1, P2) :-
\+ family(P1, _) ; \+ family(P2, _).
Борьба в настоящее время не ассоциативна, поэтому я сделал отдельный предикат, который ассоциативен:
fighting(X, Y) :- fight(X, Y) ; fight(Y, X).
Изначально я использовал select/3
, который является действительно хорошим способом вытащить различные элементы из списка, но я понял, что выполнение различных перестановок убивает меня, поэтому я придумал эту версию, которую я могу использовать, чтобы генерировать только комбинации :
select_inorder(X, [X|Xs], Xs).
select_inorder(X, [_|Rem], Xs) :-
select_inorder(X, Rem, Xs).
Использование этого предиката станет понятным всего за секунду.
Я предпочитаю не вовлекать арифметику в Пролог, если могу избежать этого. На первый взгляд это похоже на случай, когда вы можете предположить, что будет три стола по пять, и все будет отлично. Оказывается, это не так, но я все равно поделился кодом, над которым работал, на случай, если он поможет вам решить проблему. Мой план был таким: у меня будет предикат, который знает, что он собирает список из пяти человек. Первоначально я превращу вашу базу данных в список людей. Я выберу людей из этого списка. Когда у меня будет пять, я проверю, что таблица действительна. У меня будет основной предикат, который ведет учет и запускает этот генератор таблиц три раза.
seating([T1,T2,T3]) :-
findall(X, person(X), People),
make_table(People, T1, R1),
make_table(R1, T2, R2),
make_table(R2, T3, []).
Вероятно, это можно было бы более четко переформулировать с помощью синтаксиса DCG. Но это должно выглядеть разумно для вас. Оформление стола имеет похожий вкус:
make_table(People, [A,B,C,D,E], Unseated) :-
select_inorder(A, People, P1),
select_inorder(B, P1, P2),
select_inorder(C, P2, P3),
select_inorder(D, P3, P4),
select_inorder(E, P4, Unseated),
safe_table([A,B,C,D,E]).
select_inorder/3
используется здесь, чтобы убедиться, что мы выбираем людей по порядку, так как в противном случае мы просто тратим время на обработку. Как только у нас будет пять человек, мы проверяем это. Это классический цикл «генерация-тест».
safe_table(Table) :-
forall((member(L, Table), member(R, Table)),
(family_matches(L, R), \+ fighting(L, R))).
Мне нравится логическое чтение здесь. Это не супер эффективно, это будет перекрестное произведение всех элементов в списке. Тем не менее, я думаю, что такая вещь может быть оправдана, поскольку family_matches не является транзитивным (family_matches(antonella, giovanni), family_matches(giovanni, azzurra)
верно, но family_matches(antonella, azzurra)
не верно).
Теперь, конечно, облом в том, что это не работает. Но причина, по которой это не работает, довольно проста: после того, как мы разместили всю семью 1 за одним столом и всю семью 2 за другим столом, у нас есть один человек, чтобы перейти к их столу, а затем мы должны найти комбинацию из пяти остальные люди сидят вместе. Но, к сожалению, трое людей не в семье борются друг с другом. Если вы сможете заставить Марселлу и Даниэлу поладить, вы получите ровно одно решение:
?- seating([T1,T2,T3]).
T1 = [antonella, domenico, raffaella, tommaso, vincenzo],
T2 = [azzurra, cristiano, francesca, luigi, giovanni],
T3 = [marcella, daniela, nunzio, leonardo, silvia] ;
false.
Заставить эту работу для неполных таблиц звучит немного утомительно для меня, поэтому я собираюсь оставить ее там, где есть надежда, что она вам как-то пригодится.