I'm trying to figure out a way to check if two lists are equal regardless of their order of elements.
My first attempt was:
areq([],[]).
areq([],[_|_]).
areq([H1|T1], L):- member(H1, L), areq(T1, L).
However, this only checks if all elements of the list on the left exist in the list on the right; meaning areq([1,2,3],[1,2,3,4]) => true
. At this point, I need to find a way to be able to test thing in a bi-directional sense. My second attempt was the following:
areq([],[]).
areq([],[_|_]).
areq([H1|T1], L):- member(H1, L), areq(T1, L), append([H1], T1, U), areq(U, L).
Where I would try to rebuild the lest on the left and swap lists in the end; but this failed miserably.
My sense of recursion is extremely poor and simply don't know how to improve it, especially with Prolog. Any hints or suggestions would be appreciated at this point.
One possibility, inspired on qsort:
In Prolog you often can do exactly what you say
Rename if necessary.
The other answers are all elegant (way above my own Prolog level), but it struck me that the question stated
The accepted answer is O(max(|A| log(|A|), |B|log(|B|)), irrespective of whether the lists are equal (up to permutation) or not.
At the very least, it would pay to check the lengths before bothering to sort, which would decrease the runtime to something linear in the lengths of the lists in the case where they are not of equal length.
Expanding this, it is not difficult to modify the solution so that its runtime is effectively linear in the general case where the lists are not equal (up to permutation), using random digests.
Suppose we define
This is the Prolog version of the mathematical function Prod_i h(a_i) | p, where h is the hash, and p is a prime. It effectively maps each list to a random (in the hashing sense) value in the range 0, ...., p - 1 (in the above, p is the large prime 1610612741).
We can now check if two lists have the same digest:
If two lists have different digests, they cannot be equal. If two lists have the same digest, then there is a tiny chance that they are unequal, but this still needs to be checked. For this case I shamelessly stole Paulo Moura's excellent answer.
The final code is this:
A simple solution using the
sort/2
ISO standard built-in predicate, assuming that neither list contains duplicated elements:Some sample queries:
a compact form:
but, using member/2 seems inefficient, and leave space to ambiguity about duplicates (on both sides). Instead, I would use select/3
^D here
or, better,
As a starting point, let's take the second implementation of
equal_elements/2
by @CapelliC:Above implementation leaves useless choicepoints for queries like this one:
What could we do? We could fix the efficiency issue by using
selectchk/3
instead ofselect/3
, but by doing so we would lose logical-purity! Can we do better?We can! Introducing
selectd/3
, a logically pure predicate that combines the determinism ofselectchk/3
and the purity ofselect/3
.selectd/3
is based onif_/3
and(=)/3
:selectd/3
can be used a drop-in replacement forselect/3
, so putting it to use is easy!Let's see it in action!
Edit 2015-05-14
The OP wasn't specific if the predicate should enforce that items occur on both sides with the same multiplicities.
equal_elementsB/2
does it like that, as shown by these two queries:If we wanted the second query to succeed, we could relax the definition in a logically pure way by using meta-predicate
tfilter/3
and reified inequalitydif/3
:Let's run two queries like the ones above, this time using
equal_elementsC/2
:Edit 2015-05-17
As it is,
equal_elementsB/2
does not universally terminate in cases like the following:If we flip the first and second argument, however, we get termination!
Inspired by an answer given by @AmiTavory, we can improve the implementation of
equal_elementsB/2
by "sharpening" the solution set like so:To check if non-termination is gone, we put queries using both predicates head to head:
Note that the same "trick" does not work with
equal_elementsC/2
, because of the size of solution set is infinite (for all but the most trivial instances of interest).