【问题标题】:Intersection of two lists of variables两个变量列表的交集
【发布时间】:2015-01-04 01:40:29
【问题描述】:

如何在 ISO Prolog 中为在 linear 时间内运行的两个变量列表的交集定义一个(元逻辑)谓词?变量可以以任何确定的顺序出现。没有像变量“年龄”这样的实现相关属性必须影响结果。

类似于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

也就是说,第三个参数是一个输出参数,它与前两个参数的交集统一。

参见当前 ISO 标准中的 list of the built-ins(ISO/IEC 13211-1:1995 包括 Cor.2)。

(请注意,我确实在几年前的另一个问题中回答了这个问题。但是,它对 Google 来说仍然是隐藏和不可见的。)

【问题讨论】:

  • 第一个查询(varset_intersection([A,B], [B,A], []).)的答案是假的,对吧?
  • 以下查询的结果应该是什么:varset_intersection([A,B],[A,B],[A,C]).varset_intersection([A,B,C],[A,B],[A,C]). 是否应该通过将 real 交集与第三个参数统一来满足这些目标?
  • 现在应该清楚了。
  • 我需要进一步理解的一件事:将 放在不同的结果之间意味着目标应该只满足这两个结果中的任何一个吗?
  • 我放弃了... O(|L1|+|L2|) 怎么办?等待一些聪明的“把戏”......

标签: prolog iso-prolog


【解决方案1】:

如果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 可能会有所帮助)。

【讨论】:

  • 奇怪的结果是这个谓词是元逻辑的结果。
  • 标准是否对term_variables/2等谓词强制执行任何复杂性?
  • 无,但该接口允许在 SICStus、YAP、SWI 等当前系统中有效实施。
  • 有效表示线性
  • 是的,但从某种意义上说,我的意思是额外的实施工作是有回报的。对于term_variables(Vs, Ws), Vs == Ws 的情况,它甚至可以消耗更少的空间。
【解决方案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 时复制两个列表以保留它们之间的共享变量,然后将第一个副本的变量接地,然后挑选出第二个列表中对应于接地位置的原始变量第二份。

【讨论】:

  • 非常惊喜的答案!
  • @false:谢谢,感谢您的慷慨赏金!
【解决方案3】:

如果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] ;

已编辑:将 bagof/3 更改为手动编写的过滤器,这要归功于下面 cmets 中 @false 的观察。

【讨论】:

  • 不错。如果任何实现实际上能够在恒定时间内完成,那么在 unify_with_occurs_check 中解除扫描可能会起作用——我认为这不太可能。也许 permutation/2 它只是分散注意力......
  • unify_with_occurs_check 在最坏的情况下需要与整个项成正比的时间。
  • bagof/3 是二次的:变量的见证需要包含所有AsBs。也就是说,对于单个变量V |As|+|Bs|需要空间。这种考虑完全独立于unify_with_occurs_check/2!
  • 谢谢,我删除了bagof/3,我写了一个不同的谓词来收集解决方案。
【解决方案4】:

一个可能的解决方案是使用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/4include/3 不是ISO,但它们的实现很容易。

【讨论】:

  • 请注意,在符合标准的系统 write(Var) 中,Var 是一个未实例化的变量,可能总是给出相同的文本,例如 _1。所以这个解决方案依赖于实现相关的特性(变量的命名)。此外,名称可能会由于其间的垃圾收集而改变。它要简单得多!
猜你喜欢
  • 2012-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-19
  • 2015-01-06
  • 1970-01-01
相关资源
最近更新 更多