排列的枚举与应用-Prolog

 
排列的枚举
n个元素有n!种排列。例如,当n=3时,123的排列有如下6种:

123
132
213
231
312
321

而当n=4时,1234的排列有如下24种:

1234
1243
1324
1342
1423
1432
2134
2143
2314
2341
2413
2431
3124
3142
3214
3241
3412
3421
4123
4132
4213
4231
4312
4321

观察上述排列的生成规律,我们可以得出如下排列的递归求解算法:
任取集合中一个元素作为排列的第一个元素,而剩下的问题是生成剩余元素的排列,例如,当n=4时,集合{1,2,3,4}的排列生成方法可以是:
取1为排列的第一个元素,生成集合{2,3,4}的所有排列:
234、243、324、342、423、432
而1作为首元素的排列为:
1234、1243、1324、1342、1423、1432
同样的道理,当首元素分别取为2、3、4时,我们生成剩余3个元素的排列,再加上首元素,从而得到n=4时的所有排列。
由这一思想也很容易看出n个元素的排列数必为n!.
排列枚举的Prolog程序如下:
%File: perm.pro
 
domains
 ilist=integer*
 
predicates
 nondeterm perm(ilist, ilist)
 nondeterm pick(integer, ilist, ilist)
 
clauses
 perm([], []).
 perm(L, [X|P]):-
      pick(X, L, L1),
      perm(L1, P).
 
 pick(X, [X|L], L).
 pick(X, [Y|L], [Y|L1]):-pick(X, L, L1).
 
goal
 perm([1,2,3], P),
 write(P), nl,
 fail.
执行上述目标可以得到3个元素的所有6个排列:
[1,2,3]
[1,3,2]
[2,1,3]
[2,3,1]
[3,1,2]
[3,2,1]
~~~~~~~~~~~~~~~~~~~~~~~~~
排列枚举算法的应用之一:8皇后问题
8皇后问题的一个解可以对应一个8阶排列,其中排列的第k个元素,表示第k行的皇后所在列的编号,例如,皇后问题的一个解
Q
 
 
 
 
 
 
 
 
 
 
 
Q
 
 
 
 
 
 
 
 
 
 
Q
 
 
 
 
 
Q
 
 
 
 
Q
 
 
 
 
 
 
 
 
 
 
 
Q
 
 
Q
 
 
 
 
 
 
 
 
 
Q
 
 
 
 
对应的排列为15863724,而大量的排列不能构成皇后问题的一个解,例如排列12345678对应皇后的如下放置方式
Q
 
 
 
 
 
 
 
 
Q
 
 
 
 
 
 
 
 
Q
 
 
 
 
 
 
 
 
Q
 
 
 
 
 
 
 
 
Q
 
 
 
 
 
 
 
 
Q
 
 
 
 
 
 
 
 
Q
 
 
 
 
 
 
 
 
Q
可见在对角线方向,皇后相互之间进行攻击,因此12345678不是问题的一个解。
判断一个排列是否8皇后问题的一个解,需要判断对排列中任意两个位置i,j上的值q(i)和q(j),有
|q(i)-q(j)|≠|i-j|
8皇后问题的解可以采用生成测试法进行枚举:
(1)    首先生成一个排列
(2)    利用上述方法,判断该排列是否构成皇后问题的一个解
下面是问题求解的Prolog程序:
%File:queen0.pro
predicates
 nondeterm queen(integer, ilist)
 nondeterm genilist(integer, integer, ilist)
 nondeterm is_solution(ilist)
 nondeterm is_safe(integer, integer, ilist)
 
clauses
 queen(N, Sol):-
     genilist(1, N, L),
     perm(L, Sol),
     is_solution(Sol).
 
 genilist(K, N, [K|L]):-
      K < N,
      K1 = K + 1,
      genilist(K1, N, L).
 genilist(N, N, [N]).
 
 is_solution([]).
 is_solution([X|Sol]):-
      is_safe(1,X,Sol),
      is_solution(Sol).
     
 is_safe(_, _, []).
 is_safe(K, X, [Y|Sol]):-
      abs(X-Y) <> K,
      K1 = K + 1,
      is_safe(K1, X, Sol).
 
goal
 queen(8, Sol),
 write(Sol), nl,
 fail.
其中genilist(K,N,L)生成从K开始到N的所有元素构成的整数表L,is_solution(Sol)判断Sol是否构成问题的一个解,其方法是依次判断Sol的每一个元素(对应一个皇后的位置)是否“安全”,如果所有元素都是“安全”的,则该排列为问题的一个解。而判断一个皇后的放置是否安全,只需要检查它与后续的皇后位置是否相容,is_safe(K,X,Sol)谓词用来执行这一逻辑,其中X为待判断的皇后所在的列,K为后续皇后位置与X皇后的行差(|i-j|的值),而Sol存放待判断的各个皇后的位置。开始时,K=1,以后,K的值每次加1,表示待判断的皇后行号与X皇后的行号之差每次增加1。
对于以上目标,程序生成8皇后问题的92个解:

[1,5,8,6,3,7,2,4]
[1,6,8,3,7,4,2,5]
[1,7,4,6,8,2,5,3]
[1,7,5,8,2,4,6,3]
[2,4,6,8,3,1,7,5]
[2,5,7,1,3,8,6,4]
[2,5,7,4,1,8,6,3]
[2,6,1,7,4,8,3,5]
[2,6,8,3,1,4,7,5]
[2,7,3,6,8,5,1,4]
[2,7,5,8,1,4,6,3]
[2,8,6,1,3,5,7,4]
[3,1,7,5,8,2,4,6]
[3,5,2,8,1,7,4,6]
[3,5,2,8,6,4,7,1]
[3,5,7,1,4,2,8,6]
[3,5,8,4,1,7,2,6]
[3,6,2,5,8,1,7,4]
[3,6,2,7,1,4,8,5]
[3,6,2,7,5,1,8,4]
[3,6,4,1,8,5,7,2]
[3,6,4,2,8,5,7,1]
[3,6,8,1,4,7,5,2]
[3,6,8,1,5,7,2,4]
[3,6,8,2,4,1,7,5]
[3,7,2,8,5,1,4,6]
[3,7,2,8,6,4,1,5]
[3,8,4,7,1,6,2,5]
[4,1,5,8,2,7,3,6]
[4,1,5,8,6,3,7,2]
[4,2,5,8,6,1,3,7]
[4,2,7,3,6,8,1,5]
[4,2,7,3,6,8,5,1]
[4,2,7,5,1,8,6,3]
[4,2,8,5,7,1,3,6]
[4,2,8,6,1,3,5,7]
[4,6,1,5,2,8,3,7]
[4,6,8,2,7,1,3,5]
[4,6,8,3,1,7,5,2]
[4,7,1,8,5,2,6,3]
[4,7,3,8,2,5,1,6]
[4,7,5,2,6,1,3,8]
[4,7,5,3,1,6,8,2]
[4,8,1,3,6,2,7,5]
[4,8,1,5,7,2,6,3]
[4,8,5,3,1,7,2,6]
[5,1,4,6,8,2,7,3]
[5,1,8,4,2,7,3,6]
[5,1,8,6,3,7,2,4]
[5,2,4,6,8,3,1,7]
[5,2,4,7,3,8,6,1]
[5,2,6,1,7,4,8,3]
[5,2,8,1,4,7,3,6]
[5,3,1,6,8,2,4,7]
[5,3,1,7,2,8,6,4]
[5,3,8,4,7,1,6,2]
[5,7,1,3,8,6,4,2]
[5,7,1,4,2,8,6,3]
[5,7,2,4,8,1,3,6]
[5,7,2,6,3,1,4,8]
[5,7,2,6,3,1,8,4]
[5,7,4,1,3,8,6,2]
[5,8,4,1,3,6,2,7]
[5,8,4,1,7,2,6,3]
[6,1,5,2,8,3,7,4]
[6,2,7,1,3,5,8,4]
[6,2,7,1,4,8,5,3]
[6,3,1,7,5,8,2,4]
[6,3,1,8,4,2,7,5]
[6,3,1,8,5,2,4,7]
[6,3,5,7,1,4,2,8]
[6,3,5,8,1,4,2,7]
[6,3,7,2,4,8,1,5]
[6,3,7,2,8,5,1,4]
[6,3,7,4,1,8,2,5]
[6,4,1,5,8,2,7,3]
[6,4,2,8,5,7,1,3]
[6,4,7,1,3,5,2,8]
[6,4,7,1,8,2,5,3]
[6,8,2,4,1,7,5,3]
[7,1,3,8,6,4,2,5]
[7,2,4,1,8,5,3,6]
[7,2,6,3,1,4,8,5]
[7,3,1,6,8,5,2,4]
[7,3,8,2,5,1,6,4]
[7,4,2,5,8,1,3,6]
[7,4,2,8,6,1,3,5]
[7,5,3,1,6,8,2,4]
[8,2,4,1,7,5,3,6]
[8,2,5,3,1,7,4,6]
[8,3,1,6,2,5,7,4]
[8,4,1,3,6,2,7,5]

~~~~~~~~~~~~~~~~~~~~~~~~~~
学生考试名次猜测(1)
下面的问题是一道IMO试题:
五个学生A,B,C,D,E参加考试,有两人对他们的名次作了如下猜测:
    甲:五个学生的名次为A,B,C,D,E;
    乙:五个学生的名次为D,A,E,C,B。
已知甲的猜测没有猜对任何一个学生的名次,也没有猜对名次相邻的情况,而乙的猜测中猜对了两个学生的名次,还猜对了两对名次相邻学生的先后次序。问A,B,C,D,E,五名学生的名次是怎样排列的?
利用排列的生成测试法,容易找到问题的解。我们首先生成集合{1,2,3,4,5}的一个排列[X1,X2,X3,X4,X5](1、2、3、4、5分别表示A、B、C、D、E),然后用对甲、乙两人所作猜测的评价进行解的判断
由于甲没有猜对任何一个学生的名次,因此,所有可能的名次排列为:

[2,1,4,5,3]
[2,1,5,3,4]
[2,3,1,5,4]
[2,3,4,5,1]
[2,3,5,1,4]
[2,4,1,5,3]
[2,4,5,1,3]
[2,4,5,3,1]
[2,5,1,3,4]
[2,5,4,1,3]
[2,5,4,3,1]
[3,1,2,5,4]
[3,1,4,5,2]
[3,1,5,2,4]
[3,4,1,5,2]
[3,4,2,5,1]
[3,4,5,1,2]
[3,4,5,2,1]
[3,5,1,2,4]
[3,5,2,1,4]
[3,5,4,1,2]
[3,5,4,2,1]
[4,1,2,5,3]
[4,1,5,2,3]
[4,1,5,3,2]
[4,3,1,5,2]
[4,3,2,5,1]
[4,3,5,1,2]
[4,3,5,2,1]
[4,5,1,2,3]
[4,5,1,3,2]
[4,5,2,1,3]
[4,5,2,3,1]
[5,1,2,3,4]
[5,1,4,2,3]
[5,1,4,3,2]
[5,3,1,2,4]
[5,3,2,1,4]
[5,3,4,1,2]
[5,3,4,2,1]
[5,4,1,2,3]
[5,4,1,3,2]
[5,4,2,1,3]
[5,4,2,3,1]

关于甲的猜测的第二句评价“没有猜对名次相邻的情况”可以有两种理解,例如甲的话中包含了1、2相邻的涵义,一种是不考虑两人名次相邻的先后次序,这样1、2和2、1都认为是相邻的,另一种是只认为1、2才是相邻,2、1不算相邻,例如在上面筛选出的排列[4,3,2,5,1],按前一种理解4、3两人名次相邻,3、2两人名次也相邻,而按后一种理解,它们的名次都不相邻。
按第一种理解,上面筛选出的排列中仅仅有两个排列符合对甲的评价:
[2,4,1,5,3]
[3,1,5,2,4]
对于乙的猜测的评价也是分为两个部分:1、猜中了两个学生的名次;2、猜对了两对名次相邻学生的先后次序。由第一部分,只有[3,1,5,2,4]能够通过,而由第二部分,知乙的猜测41532中,恰好猜对了两对名次相邻的学生15及52的先后次序。
而按第二种理解,下列排列都满足对甲的评价要求:

[2,4,1,5,3]
[2,5,4,1,3]
[2,5,4,3,1]
[3,1,5,2,4]
[3,5,2,1,4]
[3,5,4,2,1]
[4,1,5,3,2]
[4,3,1,5,2]
[4,3,2,5,1]
[4,3,5,2,1]
[5,1,4,3,2]
[5,3,2,1,4]
[5,4,1,3,2]
[5,4,2,1,3]

而按对乙评价的第一部分,下列排列满足要求:
[3,1,5,2,4]
[4,3,1,5,2]
[4,3,5,2,1]
[5,4,1,3,2]
而按对乙第二部分的评价,排列[3,1,5,2,4]和[4,3,5,2,1]均能满足要求。
为了保证问题只有一个解,对名次相邻关系的理解应当是只要两个名次排在一起就算相邻,而不必计较其先后关系,这样筛选掉的排列更多,从而出现唯一结果的可能性就大(当然一个解也得不到的可能性也大)。
甲乙两位的话分别对应两个检查谓词。甲的话对应于:
乙的话对应于:
问题求解的程序如下:
%File: TestScore1.pro
predicates
 nondeterm solver(ilist)
 nondeterm is_solution(ilist)
 nondeterm index(integer, ilist, integer)
 nondeterm index_4(integer, ilist, integer, integer)
 nondeterm eq(integer, integer, integer)
 nondeterm gr(integer, integer, integer)
 
clauses
 solver(Sol):-
      perm([1,2,3,4,5], Sol),
      is_solution(Sol).
     
 is_solution([X1,X2,X3,X4,X5]):-
      index(1, [X1,X2,X3,X4,X5], Ind1),
      index(2, [X1,X2,X3,X4,X5], Ind2),
      index(3, [X1,X2,X3,X4,X5], Ind3),
      index(4, [X1,X2,X3,X4,X5], Ind4),
      index(5, [X1,X2,X3,X4,X5], Ind5),
      % check1
      X1 <> 1, X2<> 2, X3 <>3, X4<> 4, X5 <> 5,
      abs(Ind1 - Ind2) <> 1,
      abs(Ind2 - Ind3) <> 1,
      abs(Ind3 - Ind4) <> 1,
      abs(Ind4 - Ind5) <> 1,
      % check2
      eq(X1, 4, B1),
      eq(X2, 1, B2),
      eq(X3, 5, B3),
      eq(X4, 3, B4),
      eq(X5, 2, B5),
      B1+B2+B3+B4+B5 = 2,
      index(X1, [4, 1, 5, 3, 2], IndX1),
      index(X2, [4, 1, 5, 3, 2], IndX2),
      index(X3, [4, 1, 5, 3, 2], IndX3),
      index(X4, [4, 1, 5, 3, 2], IndX4),
      index(X5, [4, 1, 5, 3, 2], IndX5),
      gr(IndX2, IndX1, A1),
      gr(IndX3, IndX2, A2),
      gr(IndX4, IndX3, A3),
      gr(IndX5, IndX4, A4),
      A1+A2+A3+A4=2.
 
 index(X, L, Ind):-index_4(X, L, 0, Ind).
 
 index_4(X, [X|_], Ind, Ind):-!.
 index_4(X, [_|L], Ind0, Ind):-
      Ind1 = Ind0 + 1,
      index_4(X, L, Ind1, Ind).   
 
 eq(X,X,1):-!.
 eq(_, _, 0).
 
 gr(X, Y, 1):-X > Y, !.
 gr(_,_,0).
 
goal
 solver(Sol),
 write(Sol), nl,
 fail.
 执行目标找到问题的唯一解:
[3,1,5,2,4]
学生考试名次猜测(2)
五个学生A,B,C,D,E参加考试,有5个人对他们的考试名次进行了猜测:
    甲:B为第2名,A为第3名;
    乙:D为第2名,B为第4名;
    丙:E为第1名,C为第5名;
    丁:D为第3名,C为第4名;
    戊:A为第2名,B为第5名。
已知五个人中每个人都猜对了一句,又猜错了一句,问A,B,C,D,E名次排列是怎样的?
假设5个学生的名次为[X1,X2,X3,X4,X5],其中,X1指第一名的编号,X2指第二名的编号,如此等等,而[X1,X2,X3,X4,X5]为[1,2,3,4,5]的一个排列。我们对其中甲所说的话进行逻辑分析
    甲:B为第2名,A为第3名;
表示第二名为B,即X2=2,第三名为A,即X3=1,而最后又说每个人都猜对了一句,猜错了一句,意味着X2=2和X3=1恰好有一个式子成立。我们定义eq谓词如下:
 eq(X,X,1):-!.
 eq(_, _, 0).
eq(X,Y,B)表示当X=Y时,B=1,反之B=0。因此,甲所说的话恰好有一句正确在逻辑上等价于:
      eq(X2, 2, A1) 且 eq(X3, 1, B1) 且 A1+B1=1
由此不难得到问题求解的Prolog程序:
% File: testscore2.pro
predicates
 nondeterm solver(ilist)
 nondeterm is_solution(ilist)
 nondeterm eq(integer, integer, integer)
 
clauses
 solver(Sol):-
      perm([1,2,3,4,5], Sol),
      is_solution(Sol).
     
 is_solution([X1,X2,X3,X4,X5]):-
      eq(X2, 2, A1), eq(X3, 1, B1), A1+B1=1,
      eq(X2, 4, A2), eq(X4, 2, B2), A2+B2=1,
      eq(X1, 5, A3), eq(X5, 3, B3), A3+B3=1,
      eq(X3, 4, A4), eq(X4, 3, B4), A4+B4=1,
      eq(X2, 1, A5), eq(X5, 2, B5), A5+B5=1.
 
 eq(X,X,1):-!.
 eq(_, _, 0).
 
goal
 solver(Sol),
 write(Sol), nl,
 fail.
执行以上目标找到问题的唯一解:
[5,4,1,3,2]
即E为第一名,D为第二名,A为第三名,C为第四名,B为第五名。对甲乙丙丁戊等5人的话进行对照可得下表:
B为第2名
错误
A为第3名
正确
D为第2名
正确
B为第4名
错误
E为第1名
正确
C为第5名
错误
D为第3名
错误
C为第4名
正确
A为第2名
错误
B为第5名
正确
~~~~~~~~~~~~~~~~~~~~~~~~~~
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值