Prolog,测试标签启发式

时间:2014-02-10 19:45:01

标签: prolog clpfd sicstus-prolog

我正在进行一些实验,用于比较Sicstus Prolog中的不同标记启发法。

但我一直陷入'资源错误:记忆力不足'。

我很确定我在测试代码中做错了什么。

以下代码将复制我的问题:

:- use_module(library(clpfd)).
:- use_module(library(lists)).

atest( R, C ):-
    X is R * C,
    length( M, X),
    domain( M, 0, X),
    all_distinct( M ),

    statistics(walltime, [_,_SinceLast]),
    labeling( [],M ),
    statistics(walltime, [_,SinceLast]),

    write('Labeling time: '), write(SinceLast),write('ms'),nl.


% Testcode for looping through alot of variations and run one test for each variant
t1:-
    Cm = 1000,
    Cr = 1000,
    (
    for(C,0, Cm),
    param([Cm,Cr])
    do
        (
        for(R, 0, Cr ),
        param([C])
        do
            atest( C, R )
        )
    ).      

在我调用t1谓词后不久,我收到了“资源错误:内存不足”异常。

我想在调用atest释放资源后我应该做些什么?

另外:这是衡量标签时间的正确方法吗?有没有更好的方法呢?

2 个答案:

答案 0 :(得分:3)

你没有做任何严格的错误,但你正试图运行

length(Xs,N), domain(Xs,0,N), all_distinct(Xs), labeling([],Xs).

对于N最多1000000.系统构造深度为N的搜索树,并且必须为每个级别存储变量的中间状态和约束系统。对于大N来说,这会占用大量内存,并且很可能已经为具有大N的运行获得了内存溢出。

第二个问题是您在递归循环中运行基准测试,即您正在有效地创建连接

atest(0,0), ..., atest(1000,1000)

并且由于每次调用atest / 2都使用其第一个解决方案成功并保持其搜索状态,这意味着您正在尝试创建一个250500250000级别的搜索树...

最简单的改进是通过将代码更改为once(atest(C,R))来在第一个解决方案之后删除每个搜索。进一步的改进是在故障驱动的循环

中运行基准测试
(
    between(0,Cm,C),
    between(0,Cr,R), 
    once(atest(C,R)),
    fail
;
    true
)

可以更快,更快地释放内存,并减少由于垃圾收集造成的测量失真。

答案 1 :(得分:1)

Toplevel隐藏了替代答案

如果您在SICStus上测试t1使用较小的值来对shell进行测试,则可能会给出t1只有一个答案/解决方案的错误印象。然而,这种情况并非如此!所以toplevel隐藏了你的其他答案。这是SICStus顶级特殊行为,如果查询不包含变量,则不会显示更多答案。但总共有x!许多解决方案为您的标签,x! 对于每个测试用例,其他解决方案的时间是一些随机值。您的内存不足,因为对于每个测试用例,Prolog会保留记录以继续为每个测试用例生成下一个解决方案。

循环

我不建议使用故障驱动的循环进行测试,而是使用以下非常相似但更安全的循环:

\+ (
      between(0, Cm, C),
      between(0, Cr, R),
      \+ atest(C, R)
   ).

失败驱动循环的最大区别在于atest/2C的{​​{1}}意外失败。在故障驱动的循环中,这将基本上不被注意,而上述构造将失败。

有些系统为此目的提供谓词R

时序

如果您进行计时,最好只使用列表中的 first 元素并计算差异:

forall/2

通过这种方式,对目标的替代答案将为您提供更有意义的价值。