我需要计算所有X
,some_predicate(X)
,并且确实有很多这样的X
。
最好的方法是什么?
第一个线索是找到所有,累积到列表并返回它的长度。
countAllStuff( X ) :-
findall( Y
, permutation( [1,2,3,4,5,6,7,8,9,10], Y )
, List
),
length( List, X ).
(permutation/2
仅是示例,表明存在许多变体,并且收集所有变种的方式不好)
显然,我有堆栈溢出。
?- countAllStuff( X ).
ERROR: Out of global stack
然而,我正在尝试将findall
替换为setof
而没有任何变化。
最后,我创建了aggregate
(可点击的)谓词并尝试使用它。
?- aggregate(count, permutation([1,2,3,4], X), Y ).
X = [1, 2, 3, 4],
Y = 1 .
?- aggregate(count, [1,2,3,4], permutation([1,2,3,4], X), Y ).
X = [1, 2, 3, 4],
Y = 1 ;
X = [1, 2, 4, 3],
Y = 1 ;
我认为这都是错的。我更喜欢像
这样的东西?- aggregate(count, permutation([1,2,3,4], X), Y ).
Y = 24 .
1)我做错了什么?
2)如何声明谓词以获得正确答案?
答案 0 :(得分:9)
使用存在量化变量,就像使用setof
:
?- aggregate(count, X^permutation([1,2,3,4], X), N).
N = 24.
答案 1 :(得分:4)
在SWI-Prolog中,有一个更高效的版本,也可以避免锁定全局存储。因此,简单地使用nb_setval和nb_getval可以获得至少3倍的性能(更多关于多线程)。 就在不久前,另一个问题是计算解决方案。作为聚合的基础,在学习Prolog时,这是一个明显的停止点。为了评估我们使用这些monothread语义等效调用获得的效率增益:
count_solutions(Goal, N) :-
assert(count_solutions_store(0)),
repeat,
( call(Goal),
retract(count_solutions_store(SoFar)),
Updated is SoFar + 1,
assert(count_solutions_store(Updated)),
fail
; retract(count_solutions_store(N))
), !.
:- dynamic count_solutions_store/1.
% no declaration required here
count_solutions_nb(Goal, N) :-
nb_setval(count_solutions_store, 0),
repeat,
( call(Goal),
nb_getval(count_solutions_store, SoFar),
Updated is SoFar + 1,
nb_setval(count_solutions_store, Updated),
fail
; nb_getval(count_solutions_store, N)
), !.
parent(jane,dick).
parent(michael,dick).
parent(michael,asd).
numberofchildren(Parent, N) :-
count_solutions_nb(parent(Parent, _), N).
many_solutions :-
between(1, 500000, _).
time_iso :-
time(count_solutions(many_solutions, N)),
write(N), nl.
time_swi :-
time(count_solutions_nb(many_solutions, N)),
writeln(N).
在我的系统上,我得到了
?- [count_sol].
% count_sol compiled 0,00 sec, 244 bytes
true.
?- time_iso.
tim% 1,000,006 inferences, 2,805 CPU in 2,805 seconds (100% CPU, 356510 Lips)
500000
true.
?- time_swi.
% 2,000,010 inferences, 0,768 CPU in 0,768 seconds (100% CPU, 2603693 Lips)
500000
true.
答案 2 :(得分:2)
还有aggregate_all/3
:
?- aggregate_all(count, permutation([1, 2, 3, 4], _), Total).
Total = 24.
但是,就运行时和堆栈溢出而言,它似乎同样适用于findall
+ length
解决方案:
?- N is 10^7, time(aggregate_all(count, between(1, N, _), Total)).
% 10,000,022 inferences, 5.075 CPU in 5.089 seconds (100% CPU, 1970306 Lips)
N = Total, Total = 10000000.
?- N is 10^7, time((findall(X, between(1, N, _), L), length(L, Total))).
% 10,000,013 inferences, 4.489 CPU in 4.501 seconds (100% CPU, 2227879 Lips)
N = 10000000,
L = [_G30000569, _G30000566, _G30000545|...],
Total = 10000000.
?- N is 10^8, aggregate_all(count, between(1, N, _), Total).
ERROR: Out of global stack
?- N is 10^8, findall(X, between(1, N, _), L), length(L, Total).
ERROR: Out of global stack
您可以使用assert
/ retract
来计算解决方案,这很慢,但可以避免“堆栈外”问题:
?- assert(counter(0)), N is 10^8, between(1, N, _),
retract(counter(C)), C1 is C + 1, assert(counter(C1)), fail
; retract(counter(C)).
C = 100000000.
答案 3 :(得分:1)
这是Kaarels帖子的附录。同时,一些序言系统已更新了aggregate / 3和aggregate_all / 3的实现。因此,不再存在“ Out of global stack”错误:
在SWI-Prolog中:
Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.6)
?- N is 10^8, aggregate_all(count, between(1, N, _), Total).
N = Total, Total = 100000000.
在Jekejeke序言中:
Jekejeke Prolog 3, Runtime Library 1.3.7 (May 23, 2019)
?- use_module(library(advanced/arith)).
% 1 consults and 0 unloads in 16 ms.
Yes
?- use_module(library(advanced/aggregate)).
% 3 consults and 0 unloads in 94 ms.
Yes
?- N is 10^8, aggregate_all(count, between(1, N, _), Total).
N = 100000000,
Total = 100000000
新实现不首先计算列表,然后计算 列表的长度。而是使用某种全局变量 作为柜台。这种方法也用于求和,最大值等。