我能熟练使用Java和C#,因此Prolog中的编码对我来说一直很困难,因为它似乎是一种完全不同的思维方式。
我需要解决的问题很简单,我可以在十分钟内用Java来解决它。我老实说,即使在这里开始也很麻烦。我得到一份代表选民“投票”的十个数字的清单。投票是0,-1或1.然后我也得到一个列表列表,每个列表是候选人的列表。每个候选人的列表包括一个名称,后面跟选民名单中的十个分数。
我的目标是将选民名单与每个候选人名单进行比较,并根据相同的投票返回最佳匹配列表。
这就是我给出的:
?- best_candidates(
[ 0, 0, 0, 1, 1, 1, -1, -1, -1, 1],
[[adams 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[grant -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[polk 1, -1, 1, -1, 1, -1, 1, -1, 1, -1],
[jackson 1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[taft 0, -1, 0, -1, 0, -1, 0, -1, 0, -1],
[ford 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[madison 0, 0, 0, 1, -1, 0, 0, -1, 1, 1]],
BestMatches).
这应该返回BestMatches = [adams,ford,madison]。
到目前为止,我没有太多东西。我只想弄明白我应该怎么做。我是否需要多种方法,或者我是否应该在best_candidates方法中完成所有这些?
在回应埃德蒙的建议时,我到目前为止就是这样:
% match V1 and V2 and unify M with the match score
% match(V1, V2, M)
match([], [], 0).
match([H1|T1], [H2|T2], M) :-
match_single_entry(H1, H2, M1),
match(T1, T2, M2),
M is M1+M2.
% match_single_entry(I, J, M)
match_single_entry(X, Y, M) :-
X =\= 0,
Y =\= 0,
I =:= J,
M is 1.
答案 0 :(得分:1)
问题的关键似乎是一个函数,它接受10个数字的两个向量,并返回总匹配分数。
首先是Prolog没有功能;它有谓词。但是您可以通过提供可以绑定到“函数”输出的变量,轻松地将函数概念转换为谓词概念:
% Match V1 and V2, and unify M with the match score.
match(V1, V2, M) :- ...
目前尚不清楚向量是如何匹配的(但我认为它是相同条目的数量?或每对条目之间的绝对差值之和?)。但是这个谓词可能会用一个基本情况定义(对于长度为0的列表),以及为每个列表的头部计算它的一般情况,以及列表尾部的递归。
match([], [], 0). % I'm assuming the match score for empty lists is 0.
match([H1|T1], [H2|T2], M) :-
match_single_entry(H1, H2, M1), % Somehow compute the score for two single entries.
match(T1, T2, M2), % Recurse on the tails.
M is M1+M2. % Combine the two scores and bind to the output variable M.
我已经match_single_entry
未定义。一旦你定义了它,你应该练习match
运行% Let's try getting the match score for adams:
?- match([1,1,1,1,1,1,1,1,1,1], [0,0,0,1,1,1,-1,-1,-1,1], AdamsScore).
AdamsScore = 3 ;
No
与候选人的矢量对应的各种候选人:
best_candidates
下一个挑战是编写另一个谓词BestMatches
,它接受选民的向量加上一组候选向量,每一个得分,然后返回 - 也就是说,绑定到对应{{1}的输出变量 - 那些得分最高的人。同样,这个谓词可以通过定义一个基本案例(没有候选者,因此没有最好的候选者)和一次处理一个候选者的递归案例来迭代候选载体集。
如您所述,候选向量的名称后跟值。这意味着可以通过[Name|Values] = V
轻松分割向量,并将Values
传递给match
以与选民向量进行比较。
另一件事是将名称存储在BestMatches
列表中。 best_candidates
谓词必须对每个候选矢量进行评分,将这些分数保持在某个位置,然后必须找到最佳分数,然后必须通过原始候选名称并添加最佳分数。分数。
建议添加一个谓词来完成第一部分:
% Score all vectors, returning a list of scores.
% score_vectors(VoterVector, AllVectors, AllScores)
score_vectors(V, [], []).
score_vectors(V, [H|T], [SH, ST]) :-
... score V against H, matching the result against SH.
... recurse on T and ST.
(使用...
填写两行)然后使用简单谓词从AllScores
中找到最高分数(或者可能有内置谓词来执行此操作)。
然后创建另一个谓词,迭代所有的向量和分数,并选择那些符合最佳分数的谓词:
% Pick best candidates.
% pick_best(AllVectors, AllScores, BestScore, BestNames).
pick_best([], [], BS, []).
pick_best([H|T], [SH|ST], BS, [Name|NT]) :-
H = [Name|Values],
SH >= BS.
pick_best([H|T], [SH|ST], BS, NT) :-
H = [Name|Values],
SH < BS.
然后从这三个步骤构建best_candidates
:
best_candidates(VoterVector, CandidateVectors, BestNames) :-
score_vectors(VoterVector, CandidateVectors, Scores),
maximum(Scores, BestScore),
pick_best(CandidateVectors, Scores, BestScore, BestNames).
答案 1 :(得分:1)
我将展示在SWI-Prolog库(aggregate)的帮助下实施的解决方案。
:- [library(aggregate)].
best_candidates(Votes, Candidates, Best) :-
maplist(count_matched(Votes), Candidates, NamesCounted),
keysort(NamesCounted, BestDown),
reverse(BestDown, Best).
count_matched(Votes, [Name|ThisVotes], MatchCount-Name) :-
aggregate_all(sum(V * T),
( nth1(I, Votes, V),
nth1(I, ThisVotes, T)
), MatchCount).
test(BestMatches) :-
best_candidates(
[ 0, 0, 0, 1, 1, 1, -1, -1, -1, 1],
[[adams , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[grant , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[polk , 1, -1, 1, -1, 1, -1, 1, -1, 1, -1],
[jackson , 1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[taft , 0, -1, 0, -1, 0, -1, 0, -1, 0, -1],
[ford , 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[madison , 0, 0, 0, 1, -1, 0, 0, -1, 1, 1]],
BestMatches),
BestMatches = [_-A, _-B, _-C|_],
writeln([A, B, C]).
测试输出:
?- test(L).
[madison,ford,adams]
L = [1-madison, 1-ford, 1-adams, -1-jackson, -1-grant, -2-taft, -3-polk].