两个变量列表的交集
Intersection of two lists of variables
如何在 ISO Prolog 中为在 线性 时间内运行的两个变量列表的交集定义一个(元逻辑)谓词?变量可以以任何确定的顺序出现。没有像变量 "age" 这样的依赖于实现的属性必须影响结果。
类似于library(ordsets)
,我们称关系为varset_intersection(As, Bs, As_cap_Bs).
?- varset_intersection([A,B], [C,D], []).
true.
?-varset_intersection([A,B], [B,A], []).
false.
?- varset_intersection([A,B,C], [C,A,D], Inter).
Inter = [A,C].
or
Inter = [C,A].
?- varset_intersection([A,B],[A,B],[A,C]).
B = C
or
A = B, A = C
?- varset_intersection([A,B,C],[A,B],[A,C]).
idem
也就是说,第三个参数是一个输出参数,它与前两个参数的交集相统一。
看到这个 list of the built-ins from the current ISO standard (ISO/IEC 13211-1:1995 including Cor.2)。
(请注意,我在几年前的另一个问题中确实回答过这个问题。但是,它仍然隐藏并且 Google 看不到。)
如果 unify_with_occurs_check(Var, ListOfVars)
在恒定时间内失败或成功,此实现应在线性时间内产生答案:
filter_vars([], _, Acc, Acc).
filter_vars([A|As], Bs, Acc, As_cap_Bs):-
(
\+ unify_with_occurs_check(A, Bs)
->
filter_vars(As, Bs, [A|Acc], As_cap_Bs)
;
filter_vars(As, Bs, Acc, As_cap_Bs)
).
varset_intersection(As, Bs, As_cap_Bs):-
filter_vars(As, Bs, [], Inter),
permutation(Inter, As_cap_Bs).
当给定列表包含重复项时,此实现会出现问题:
?- varset_intersection1([A,A,A,A,B], [B,A], L).
L = [B, A, A, A, A] ;
?- varset_intersection1([B,A], [A,A,A,A,B], L).
L = [A, B] ;
已编辑 : 由于@false 在下面评论中的观察,将 bagof/3
更改为手动编写的过滤器。
一个可能的解决方案是使用 Bloom filter。如果发生碰撞,结果可能是错误的,但存在更好的抗碰撞功能。这是一个使用单个哈希函数的实现。
sum_codes([], _, Sum, Sum).
sum_codes([Head|Tail], K, Acc,Sum):-
Acc1 is Head * (256 ** K) + Acc,
K1 is (K + 1) mod 4,
sum_codes(Tail, K1, Acc1, Sum).
hash_func(Var, HashValue):-
with_output_to(atom(A), write(Var)),
atom_codes(A, Codes),
sum_codes(Codes, 0, 0, Sum),
HashValue is Sum mod 1024.
add_to_bitarray(Var, BAIn, BAOut):-
hash_func(Var, HashValue),
BAOut is BAIn \/ (1 << HashValue).
bitarray_contains(BA, Var):-
hash_func(Var, HashValue),
R is BA /\ (1 << HashValue),
R > 0.
varset_intersection(As, Bs, As_cap_Bs):-
foldl(add_to_bitarray, As, 0, BA),
include(bitarray_contains(BA), Bs, As_cap_Bs).
我知道 foldl/4
和 include/3
不是 ISO,但它们的实现很容易。
如果 term_variables/2
的工作时间与其第一个参数的大小成线性关系,那么这可能会工作:
varset_intersection(As, Bs, As_cap_Bs):-
term_variables([As, Bs], As_and_Bs),
term_variables(As, SetAs),
append(SetAs, OnlyBs, As_and_Bs),
term_variables([OnlyBs, Bs], SetBs),
append(OnlyBs, As_cap_Bs, SetBs).
无论在给定的两个列表中出现多少次,每个公共变量在结果列表中只出现一次。
?- varset_intersection2([A,_C,A,A,A], [A,_B,A,A,A], L).
L = [A].
此外,它可能会给出奇怪的结果,如本例所示:
?- varset_intersection([A,_X,B,C], [B,C,_Y,A], [C, A, B]).
A = B, B = C.
(permutation/2
在这里可能会有帮助)。
如果copy_term/2
使用线性时间,我相信以下工作:
varset_intersection(As, Bs, Cs) :-
copy_term(As-Bs, CopyAs-CopyBs),
ground_list(CopyAs),
select_grounded(CopyBs, Bs, Cs).
ground_list([]).
ground_list([a|Xs]) :-
ground_list(Xs).
select_grounded([], [], []).
select_grounded([X|Xs], [_|Bs], Cs) :-
var(X),
!,
select_grounded(Xs, Bs, Cs).
select_grounded([_|Xs], [B|Bs], [B|Cs]) :-
select_grounded(Xs, Bs, Cs).
想法是在对 copy_term/2
的一次调用中复制两个列表以保留它们之间的共享变量,然后将第一个副本的变量接地,然后挑选出第二个列表对应的原始变量第二个副本的接地位置。
如何在 ISO Prolog 中为在 线性 时间内运行的两个变量列表的交集定义一个(元逻辑)谓词?变量可以以任何确定的顺序出现。没有像变量 "age" 这样的依赖于实现的属性必须影响结果。
类似于library(ordsets)
,我们称关系为varset_intersection(As, Bs, As_cap_Bs).
?- varset_intersection([A,B], [C,D], []).
true.
?-varset_intersection([A,B], [B,A], []).
false.
?- varset_intersection([A,B,C], [C,A,D], Inter).
Inter = [A,C].
or
Inter = [C,A].
?- varset_intersection([A,B],[A,B],[A,C]).
B = C
or
A = B, A = C
?- varset_intersection([A,B,C],[A,B],[A,C]).
idem
也就是说,第三个参数是一个输出参数,它与前两个参数的交集相统一。
看到这个 list of the built-ins from the current ISO standard (ISO/IEC 13211-1:1995 including Cor.2)。
(请注意,我在几年前的另一个问题中确实回答过这个问题。但是,它仍然隐藏并且 Google 看不到。)
如果 unify_with_occurs_check(Var, ListOfVars)
在恒定时间内失败或成功,此实现应在线性时间内产生答案:
filter_vars([], _, Acc, Acc).
filter_vars([A|As], Bs, Acc, As_cap_Bs):-
(
\+ unify_with_occurs_check(A, Bs)
->
filter_vars(As, Bs, [A|Acc], As_cap_Bs)
;
filter_vars(As, Bs, Acc, As_cap_Bs)
).
varset_intersection(As, Bs, As_cap_Bs):-
filter_vars(As, Bs, [], Inter),
permutation(Inter, As_cap_Bs).
当给定列表包含重复项时,此实现会出现问题:
?- varset_intersection1([A,A,A,A,B], [B,A], L).
L = [B, A, A, A, A] ;
?- varset_intersection1([B,A], [A,A,A,A,B], L).
L = [A, B] ;
已编辑 : 由于@false 在下面评论中的观察,将 bagof/3
更改为手动编写的过滤器。
一个可能的解决方案是使用 Bloom filter。如果发生碰撞,结果可能是错误的,但存在更好的抗碰撞功能。这是一个使用单个哈希函数的实现。
sum_codes([], _, Sum, Sum).
sum_codes([Head|Tail], K, Acc,Sum):-
Acc1 is Head * (256 ** K) + Acc,
K1 is (K + 1) mod 4,
sum_codes(Tail, K1, Acc1, Sum).
hash_func(Var, HashValue):-
with_output_to(atom(A), write(Var)),
atom_codes(A, Codes),
sum_codes(Codes, 0, 0, Sum),
HashValue is Sum mod 1024.
add_to_bitarray(Var, BAIn, BAOut):-
hash_func(Var, HashValue),
BAOut is BAIn \/ (1 << HashValue).
bitarray_contains(BA, Var):-
hash_func(Var, HashValue),
R is BA /\ (1 << HashValue),
R > 0.
varset_intersection(As, Bs, As_cap_Bs):-
foldl(add_to_bitarray, As, 0, BA),
include(bitarray_contains(BA), Bs, As_cap_Bs).
我知道 foldl/4
和 include/3
不是 ISO,但它们的实现很容易。
如果 term_variables/2
的工作时间与其第一个参数的大小成线性关系,那么这可能会工作:
varset_intersection(As, Bs, As_cap_Bs):-
term_variables([As, Bs], As_and_Bs),
term_variables(As, SetAs),
append(SetAs, OnlyBs, As_and_Bs),
term_variables([OnlyBs, Bs], SetBs),
append(OnlyBs, As_cap_Bs, SetBs).
无论在给定的两个列表中出现多少次,每个公共变量在结果列表中只出现一次。
?- varset_intersection2([A,_C,A,A,A], [A,_B,A,A,A], L).
L = [A].
此外,它可能会给出奇怪的结果,如本例所示:
?- varset_intersection([A,_X,B,C], [B,C,_Y,A], [C, A, B]).
A = B, B = C.
(permutation/2
在这里可能会有帮助)。
如果copy_term/2
使用线性时间,我相信以下工作:
varset_intersection(As, Bs, Cs) :-
copy_term(As-Bs, CopyAs-CopyBs),
ground_list(CopyAs),
select_grounded(CopyBs, Bs, Cs).
ground_list([]).
ground_list([a|Xs]) :-
ground_list(Xs).
select_grounded([], [], []).
select_grounded([X|Xs], [_|Bs], Cs) :-
var(X),
!,
select_grounded(Xs, Bs, Cs).
select_grounded([_|Xs], [B|Bs], [B|Cs]) :-
select_grounded(Xs, Bs, Cs).
想法是在对 copy_term/2
的一次调用中复制两个列表以保留它们之间的共享变量,然后将第一个副本的变量接地,然后挑选出第二个列表对应的原始变量第二个副本的接地位置。