简体   繁体   English

如何让这个 prolog 代码在合理的时间内运行?

[英]How to have this prolog code run in sensible time?

I encountered a maths puzzle recently which goes as follows:我最近遇到了一个数学难题,内容如下:

Imagine 13 envelopes, each numbered from 1 to 13. Take 13 index cards, numbered from 528 to 540. Find a possible arrangement of cards in envelopes such that the number on each card can be divided by the containing envelope with no remainder.想象一下 13 个信封,每个信封编号从 1 到 13。取 13 张索引卡,编号从 528 到 540。找出信封中卡片的可能排列方式,使得每张卡片上的数字可以除以包含的信封而没有余数。

I've solved this by hand, and found more than one solution.我已经手动解决了这个问题,并找到了不止一个解决方案。 The manual technique I used was to list the divisors of each index card and find those with just one divisor.我使用的手动技术是列出每张索引卡的除数并找到只有一个除数的除数。 Then I cross off that divisor from other numbers and continued the process, looking for numbers with just 1 divisor.然后我从其他数字中划掉那个除数并继续这个过程,寻找只有 1 个除数的数字。 (Occasionally there were some with two, and this either resulted in deadends or resulted in multiple solutions) (偶尔有一些有两个,这要么导致死角,要么导致多个解决方案)

I've no idea how to do this sensibly in prolog.我不知道如何在 prolog 中明智地做到这一点。 I've only managed to do a brute-force, ie check every possible ordering我只设法进行了蛮力,即检查所有可能的订购

valid([], []).
valid([EHEAD|ETAIL], [CHEAD|CTAIL]) :- 0 is mod(EHEAD, CHEAD), valid(ETAIL, CTAIL).

numlist(1,13,Envelopes),
permutation(Contents, C), numlist(528, 540, C),
valid(Contents, Envelopes).

This solution doesn't run in sensible time so I can't verify it (but it looks about right).这个解决方案没有在合理的时间内运行,所以我无法验证它(但它看起来是正确的)。 How can I force it to backtrack earlier, eg I know instinctively it's pointless to bother searching solutions which try to pair an odd number with any even number (and this generalises to all evelope-numbers) but don't know how to implement this.我怎么能强迫它早点回溯,例如,我本能地知道寻找尝试将奇数与任何偶数配对的解决方案是没有意义的(这适用于所有 evelope 数)但不知道如何实现这一点。

Just use nondeterminism, here represented by select /3:只需使用非确定性,这里用select /3 表示:

:- module(p13, [p13/1]).

p13(L) :-
    numlist(1,13,Envelopes),
    numlist(528,540,Cards),
    associate(Cards,Envelopes,L).

associate([],[],[]).
associate([Card|Cards],Envelopes,[Card/Env|Rest]) :-
    select(Env,Envelopes,Envelopes1),
    Card mod Env =:= 0,
    associate(Cards,Envelopes1,Rest).

yields产量

?- p13(L).
L = [528/4, 529/1, 530/10, 531/9, 532/7, 533/13, 534/6, 535/5, ... / ...|...] .

A typical option would be to use a Constraint Logic Programming over Finite Domains library, ie CLP(FD) for short.一个典型的选择是使用有限域上的约束逻辑编程库,即简称 CLP(FD)。 The specifics will depend on which Prolog you're using.具体情况取决于您使用的 Prolog。 With SWI-Prolog we could solve the problem like this:使用SWI-Prolog ,我们可以解决这样的问题:

:- use_module(library(clpfd)).

envelope(Num, envelope(Num, _)).

index(envelope(Num, Index), Index) :-
    Index in 528..540,
    Index rem Num #= 0.

solve(Envelopes) :-
    % Start by constructing a list of 13 `envelope(Number, Index)` structures.
    numlist(1, 13, Numbers),
    maplist(envelope, Numbers, Envelopes),
    % Constrain each index to be an integer in 528..540, and to be
    % divisible by the number of the envelope. Also collect the Index
    % variables into a list for use below.
    maplist(index, Envelopes, IndexVars),
    % Constrain all Index variables to have a different value.
    all_different(IndexVars),
    % Find values for the indices.
    label(IndexVars).

We can find a solution and print it in a human-readable form:我们可以找到一个解决方案并以人类可读的形式打印出来:

?- time(solve(Envelopes)), 
   forall(member(envelope(Num, Index), Envelopes), 
          format("Envelope ~d: ~d~n", [Num, Index])).
% 59,435 inferences, 0.000 CPU in 0.005 seconds (0% CPU, Infinite Lips)
Envelope 1: 529
Envelope 2: 538
Envelope 3: 537
Envelope 4: 528
Envelope 5: 535
Envelope 6: 534
Envelope 7: 532
Envelope 8: 536
Envelope 9: 531
Envelope 10: 530
Envelope 11: 539
Envelope 12: 540
Envelope 13: 533
Envelopes = ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM