逻辑任务中的互斥

Mutually exclusion in a logic task

有三个朋友 - misha、petya、vova。 姓氏:伊万诺夫、谢苗诺夫、格拉西莫夫。

name(misha).
name(vova).
name(petya).
surname(ivanov).
surname(semyonov).
surname(gerasimov).

misha 不是 gerasimov。

full_name(misha,X) :- 
surname(X),
X \= gerasimov.  

vova 正在读六年级。格拉西莫夫排在第五位。 如何定义 vova 的姓氏(多个)?

vova的父亲是工程师,ivanov的父亲是锁匠。

father(vova,ingeneer).
father(ivanov,locksmith).

vova的(一)姓怎么定义?

首先我们需要了解每个名字都有一个给定的姓氏这一事实。

is_one_of(Name, [ivanov, semyonov, gerasimov]) :-
    member(Name, [misha, vova, petya]).

我们还被赋予了名字和姓氏的特征(学习,父亲),每个特征都有不同的值。这是由以下谓词捕获的。

name_characteristic(vova, studying, sixth_grade).
name_characteristic(vova, father, ingeneer).

surname_characteristic(gerasimov, studying, fifth_grade).
surname_characteristic(ivanov, father, locksmith).

我们还得到gerasimovis_notmisha的姓氏。

is_not(misha, gerasimov).

规则 1:Name 不能有 Surname 如果对于某些共同特征,它们的值不同。

is_not(Name, Surname) :-
    name_characteristic(Name, Characteristic, Value1),
    surname_characteristic(Surname, Characteristic, Value2),
    Value1 \= Value2.

规则 2:如果其他可能姓氏中的 none 是 Name 的姓氏,则 Name 必须有 Surname

is_(Name, Surname) :-
    is_one_of(Name, Surnames),
    select(Surname, Surnames, RemainingSurnames),
    is_none_of(Name, RemainingSurnames).

is_none_of(_, []).
is_none_of(Name, [Surname | RemainingSurnames]) :-
    is_not(Name, Surname),
    is_none_of(Name, RemainingSurnames).

查询 is(vova, Surname) 产生:

Surname = semyonov

查询 is_not(Name, Surname) 产生:

Name = misha,
Surname = gerasimov

Name = vova,
Surname = gerasimov

Name = vova,
Surname = ivanov

此解决方案使用 chr 库,它实际上是一种与 Prolog 稍有不同的语言。我现在自己正在学习 CHR,这实际上是我用它编写的第二个程序。它可能与我在邮件列表中获得的版本或改进版本过于相似。所以对它持保留态度。

在我看来,CHR 非常适合那些从很多可能性开始然后取消不良可能性的程序。也许取消可以继续,直到您找到解决方案。这就是这个程序的工作方式,我们生成所有可能的 name/surname 集合,然后使用我们的不可能约束来减少可能的姓氏集合,直到它们收敛到一个,然后我们将它们从其他 name/surname 集合中删除,直到一切都会收敛。

前两行介绍库和我们将使用的约束:

:- use_module(library(chr)).
:- chr_constraint name/1, surnames/1, impossible/2, matched/2, possible/2.

第一个约束生成我们需要走过的可能性。

gen_possibilities @
  name(Name), surnames(Surnames)
  ==> possible(Name, Surnames).

这里的想法是我们已经将姓氏列在一个列表中,我们只是在每个名字和所有可能的姓氏之间创建一个名为 possible 的对。

impossibility @
  impossible(Name, Surname), possible(Name, Surnames)
  <=> select(Surname, Surnames, Remaining) 
  | possible(Name, Remaining).

一旦我们被告知某件事是不可能的,我们可以简单地将它从该名字的可能姓氏列表中删除。 select/3 对于从列表中删除单个项目并为您提供余数很有用。我们在这里使用一个守卫,以防姓氏已经不存在于可能的姓氏列表中,这可能发生在应用第四个约束之后。

only_possiblity @
  possible(Name, [Surname])
  <=> matched(Name, Surname).

一旦可能性列表减少到一个名字,我们就成功匹配了。

remove_matched @
  matched(_, Surname), possible(Name, _)
  ==> impossible(Name, Surname).

如果我们匹配了,那么所有其他名字都不可能使用该姓氏。

如果因为名称已被处理而无法吸收不可能约束,则可以添加但不是必需的额外清理约束:

irrelevant_impossibility @
  possible(Name, Surnames) 
  \ impossible(Name, Surname)
  <=> \+ memberchk(Surname, Surnames) 
  | true.

现在我们可以运行程序了。我冒昧地为 5 年级/6 年级和不同的父亲内联了逻辑。我觉得通过阅读很容易理解意图,而且我不认为通过专门对其建模我会收获很多。感谢@RobertBaron 在他的解决方案中解决问题![​​=21=]

main :-
    name(misha),
    name(vova),
    name(petya),
    surnames([ivanov, semyonov, gerasimov]),
    impossible(misha, gerasimov),  % stated impossible
    impossible(vova, gerasimov),   % different grades
    impossible(vova, ivanov).      % different fathers

执行结果为:

?- main.
name(petya),
name(vova),
name(misha),
surnames([ivanov, semyonov, gerasimov]),
matched(petya, gerasimov),
matched(misha, ivanov),
matched(vova, semyonov).

我相信这满足了摆在您面前的限制条件。