Prolog斑马问题谓词无限运行,如何修复?
修复Prolog斑马问题代码的关键步骤
核心问题分析
- 线索对应混乱:代码中的线索编号完全错位,比如将线索17(40岁在第4位)错误标记为Clue11,遗漏了线索11(第5位演奏drum and bass)和线索12(中间位置在Asgard舞台),直接导致逻辑链断裂。
- 命名不匹配:苏格兰人的国籍应为
scottish,但代码中写成scott,导致无法匹配问题定义的属性。 - 低效位置谓词:原
left_of和next_to用append+member实现,会产生大量无效回溯,是程序无限运行的核心原因。 - 缺少唯一性约束:仅检查属性存在,未确保每个类别(如国籍、衬衫颜色)的属性在5个DJ中唯一,冗余搜索拖慢运行速度。
- 字段顺序不一致:部分线索的DJ字段使用未严格遵循
[国籍, 衬衫颜色, 音乐类型, 舞台, 年龄, 爱好]的结构,导致匹配错误。
修复方案
1. 修正基础命名与线索对应
- 将苏格兰人标识从
scott改为scottish,匹配问题中的国籍定义。 - 重新对应所有线索,确保代码实现与问题描述完全一致,比如补充线索11(第5位演奏drum and bass)、线索12(第3位在Asgard舞台)的代码。
2. 用索引实现高效位置谓词
替换低效的位置判断逻辑,使用1-5的位置索引定义关系,大幅减少回溯:
% 定位DJ在列表中的位置(索引从1开始) at_position(DJ, Pos, DJs) :- nth1(Pos, DJs, DJ). % A在B的正左侧(相邻) immediately_left_of(A, B, DJs) :- at_position(A, PosA, DJs), at_position(B, PosB, DJs), PosB is PosA + 1. % A在B的左侧某处(不要求相邻) left_of(A, B, DJs) :- at_position(A, PosA, DJs), at_position(B, PosB, DJs), PosA < PosB. % A和B相邻 next_to(A, B, DJs) :- immediately_left_of(A, B, DJs) ; immediately_left_of(B, A, DJs).
3. 提前绑定已知位置的线索
直接将确定位置的属性绑定到DJ列表对应位置,缩小搜索空间:
DJs = [ DJ1, % 第1位:30岁(线索3) DJ2, DJ3, % 第3位:Asgard舞台(线索12) DJ4, % 第4位:Arcadia舞台(线索2)、40岁(线索17) DJ5 % 第5位:drum_and_bass(线索11) ], DJ1 = [_, _, _, _, 30, _], DJ3 = [_, _, _, asgard, _, _], DJ4 = [_, _, _, arcadia, 40, _], DJ5 = [_, _, drum_and_bass, _, _, _],
4. 添加唯一性约束
确保每个属性类别下的所有值唯一,避免冗余搜索:
% 国籍唯一 maplist(nth1(1), DJs, Nations), sort(Nations, [american, canadian, dutch, french, scottish]), % 衬衫颜色唯一 maplist(nth1(2), DJs, Shirts), sort(Shirts, [black, blue, green, red, white]), % 音乐类型唯一 maplist(nth1(3), DJs, Genres), sort(Genres, [drum_and_bass, dubstep, edm, house, trance]), % 舞台唯一 maplist(nth1(4), DJs, Stages), sort(Stages, [arcadia, asgard, shangri_la, valhalla, xibalba]), % 年龄唯一 maplist(nth1(5), DJs, Ages), sort(Ages, [25, 30, 35, 40, 45]), % 爱好唯一 maplist(nth1(6), DJs, Hobbies), sort(Hobbies, [camping, juggling, painting, singing, surfing]),
5. 修正所有线索的实现
严格按照问题描述实现每条线索,比如:
- 线索10:绿衬衫→绘画→蓝衬衫(连续顺序),用两次
immediately_left_of确保位置关系。 - 线索18:荷兰人→25岁→40岁,结合40岁在第4位,直接绑定25岁和荷兰人的位置。
完整修复代码
% 位置相关谓词,使用索引1-5 at_position(DJ, Pos, DJs) :- nth1(Pos, DJs, DJ). immediately_left_of(A, B, DJs) :- at_position(A, PosA, DJs), at_position(B, PosB, DJs), PosB is PosA + 1. left_of(A, B, DJs) :- at_position(A, PosA, DJs), at_position(B, PosB, DJs), PosA < PosB. next_to(A, B, DJs) :- immediately_left_of(A, B, DJs) ; immediately_left_of(B, A, DJs). solve(DJs) :- % 定义DJ列表结构,提前绑定已知位置的属性 DJs = [DJ1, DJ2, DJ3, DJ4, DJ5], DJ1 = [_, _, _, _, 30, _], % 线索3:第1位30岁 DJ3 = [_, _, _, asgard, _, _], % 线索12:中间位置(第3位)在Asgard舞台 DJ4 = [_, _, _, arcadia, 40, _], % 线索2:第4位在Arcadia;线索17:第4位40岁 DJ5 = [_, _, drum_and_bass, _, _, _], % 线索11:第5位演奏drum and bass % 线索1:苏格兰人在白衬衫DJ左侧某处 at_position([scottish, _, _, _, _, _], ScottPos, DJs), at_position([_, white, _, _, _, _], WhitePos, DJs), ScottPos < WhitePos, % 线索4:EDM在加拿大人正右侧 immediately_left_of([canadian, _, _, _, _, _], [_, _, edm, _, _, _], DJs), % 线索5:绘画爱好者与dubstep演奏者相邻 next_to([_, _, _, _, _, painting], [_, _, dubstep, _, _, _], DJs), % 线索6:苏格兰人 → 黑衬衫 → dubstep(顺序固定,中间隔黑衬衫) immediately_left_of([scottish, _, _, _, _, _], [_, black, _, _, _, _], DJs), immediately_left_of([_, black, _, _, _, _], [_, _, dubstep, _, _, _], DJs), % 线索7:法国人穿蓝衬衫的DJ相邻 next_to([french, _, _, _, _, _], [_, blue, _, _, _, _], DJs), % 线索8:露营爱好者在队伍两端 (DJ1 = [_, _, _, _, _, camping] ; DJ5 = [_, _, _, _, _, camping]), % 线索9:Asgard舞台的DJ穿蓝衬衫(结合线索12,DJ3穿蓝衬衫) DJ3 = [_, blue, _, asgard, _, _], % 线索10:绿衬衫 → 绘画 → 蓝衬衫(连续顺序) immediately_left_of([_, green, _, _, _, _], [_, _, _, _, _, painting], DJs), immediately_left_of([_, _, _, _, _, painting], [_, blue, _, _, _, _], DJs), % 线索13:trance与dubstep相邻 next_to([_, _, trance, _, _, _], [_, _, dubstep, _, _, _], DJs), % 线索14:加拿大人在杂耍爱好者正左侧 immediately_left_of([canadian, _, _, _, _, _], [_, _, _, _, _, juggling], DJs), % 线索15:唱歌爱好者在黑衬衫DJ正右侧 immediately_left_of([_, black, _, _, _, _], [_, _, _, _, _, singing], DJs), % 线索16:35岁与杂耍爱好者相邻 next_to([_, _, _, _, 35, _], [_, _, _, _, _, juggling], DJs), % 线索18:荷兰人 → 25岁 → 40岁(顺序固定,40岁在DJ4) immediately_left_of([dutch, _, _, _, _, _], [_, _, _, _, 25, _], DJs), immediately_left_of([_, _, _, _, 25, _], DJ4, DJs), % 线索19:蓝衬衫DJ在Xibalba舞台DJ左侧某处 left_of([_, blue, _, _, _, _], [_, _, _, xibalba, _, _], DJs), % 线索20:冲浪爱好者在Valhalla舞台 at_position([_, _, _, valhalla, _, surfing], _, DJs), % 线索21:穿红衬衫的DJ在法国人右侧某处 left_of([french, _, _, _, _, _], [_, red, _, _, _, _], DJs), % 唯一性约束:所有属性类别下的值唯一 maplist(nth1(1), DJs, Nations), sort(Nations, [american, canadian, dutch, french, scottish]), maplist(nth1(2), DJs, Shirts), sort(Shirts, [black, blue, green, red, white]), maplist(nth1(3), DJs, Genres), sort(Genres, [drum_and_bass, dubstep, edm, house, trance]), maplist(nth1(4), DJs, Stages), sort(Stages, [arcadia, asgard, shangri_la, valhalla, xibalba]), maplist(nth1(5), DJs, Ages), sort(Ages, [25, 30, 35, 40, 45]), maplist(nth1(6), DJs, Hobbies), sort(Hobbies, [camping, juggling, painting, singing, surfing]). % 辅助打印格式化结果 print_solution :- solve(DJs), nl, write('位置 | 国籍 | 衬衫颜色 | 音乐类型 | 舞台 | 年龄 | 爱好 '), nl, write('-----|------------|----------|----------------|--------------|------|------------'), nl, print_dj(1, DJs). print_dj(_, []). print_dj(Pos, [DJ|Rest]) :- DJ = [Nation, Shirt, Genre, Stage, Age, Hobby], format('~4d | ~-12w | ~-8w | ~-14w | ~-12w | ~4d | ~-12w~n', [Pos, Nation, Shirt, Genre, Stage, Age, Hobby]), NextPos is Pos + 1, print_dj(NextPos, Rest).
使用说明
运行print_solution.即可得到格式化的结果,程序不会再无限运行,原因如下:
- 提前绑定已知位置属性,大幅缩小搜索空间。
- 索引式位置谓词避免了
append带来的无效回溯。 - 唯一性约束排除了重复属性的无效分支。
内容的提问来源于stack exchange,提问作者guy43535453
相关产品推荐
相关产品推荐

