16

I have an array of set of permutations, and I want to remove isomorphic permutations.

We have S sets of permutations, where each set contain K permutations, and each permutation is represented as and array of N elements. I'm currently saving it as an array int pset[S][K][N], where S, K and N are fixed, and N is larger than K.

Two sets of permutations, A and B, are isomorphic, if there exists a permutation P, that converts elements from A to B (for example, if a is an element of set A, then P(a) is an element of set B). In this case we can say that P makes A and B isomorphic.

My current algorithm is:

  1. We choose all pairs s1 = pset[i] and s2 = pset[j], such that i < j
  2. Each element from choosen sets (s1 and s2) are numered from 1 to K. That means that each element can be represented as s1[i] or s2[i], where 0 < i < K+1
  3. For every permutation T of K elements, we do the following:
    • Find the permutation R, such that R(s1[1]) = s2[1]
    • Check if R is a permutation that make s1 and T(s2) isomorphic, where T(s2) is a rearrangement of the elements (permutations) of the set s2, so basically we just check if R(s1[i]) = s2[T[i]], where 0 < i < K+1
    • If not, then we go to the next permutation T.

This algorithms works really slow: O(S^2) for the first step, O(K!) to loop through each permutation T, O(N^2) to find the R, and O(K*N) to check if the R is the permutation that makes s1 and s2 isomorphic - so it is O(S^2 * K! * N^2).

Question: Can we make it faster?

Community
  • 1
  • 1
Chan Kha Vu
  • 9,834
  • 6
  • 32
  • 64
  • 1
    I think you can improve `K!` multiplier to polynomial: for every i and j: find permutation R such that R(s1[i]) = s2[j], mark j as "used", then for every k != i, find a non-"used" m, such that R(s1[k]) = s2[m], and mark m as "used". If for some i and j you can "mark" all `m` from 1 to K, then R makes s1 and s2 isomorphic. – Kolmar May 11 '15 at 18:54
  • How stable does this has to be? You could sort them all, O(n*mlgm), where n is the number of sequences and m is the length of the sequence. Then you can add them all to a set, which (if compare is O(m)) will be O(n*lg(n)*m), making the total cost O(n*m*lg(n))). – IdeaHat May 11 '15 at 19:00
  • Is the definition of the problem correctly expressed? Because I think you should write "...if there exists a ``function`` ``P``, that converts elements from ``A`` to ``B`` and viceversa..." – optimusfrenk May 11 '15 at 19:28
  • @frenk I think the function is induced by a permutation, i.e. `Pa` is defined as a permutation `p` composed with `a`. – vsoftco May 11 '15 at 19:30
  • I'm agree with you, by the way in the definition it should be added the piece "... and ``viceversa``...". Just another thing, do you consider the sets ``{123,111,321}`` and ``{321,333,123}`` isomorphic? – optimusfrenk May 11 '15 at 19:53
  • 1
    How is your map defined? `A\isomorphic B` under left-multiplication by a permutation? Or under conjugation, i.e. `B = p A inverse(p)`? – vsoftco May 11 '15 at 20:17
  • @frenk a permutation is a funtion, but not every function is a permutation. The case {123, 111, 321} and {321, 333, 123} is not correct for my question because 111 and 333 are not permutations. – Chan Kha Vu May 11 '15 at 20:29
  • @FalconUA, In a cursory brute-force search in Haskell, I could not find a counter-example or false positive to my algorithm - since you are actually applying this, I was wondering if you might let me know if you do? – גלעד ברקן May 14 '15 at 11:54

5 Answers5

6

You can sort and compare:

// 1 - sort each set of permutation
for i = 0 to S-1
    sort(pset[i])
// 2 - sort the array of permutations itself
sort(pset)
// 3 - compare
for i = 1 to S-1 {
    if(areEqual(pset[i], pset[i-1]))
        // pset[i] and pset[i-1] are isomorphic
}

A concrete example:

0: [[1,2,3],[3,2,1]]
1: [[2,3,1],[1,3,2]]
2: [[1,2,3],[2,3,1]]
3: [[3,2,1],[1,2,3]]

After 1:

0: [[1,2,3],[3,2,1]]
1: [[1,3,2],[2,3,1]] // order changed
2: [[1,2,3],[2,3,1]]
3: [[1,2,3],[3,2,1]] // order changed

After 2:

2: [[1,2,3],[2,3,1]]
0: [[1,2,3],[3,2,1]]
3: [[1,2,3],[3,2,1]] 
1: [[1,3,2],[2,3,1]]

After 3:

(2, 0) not isomorphic 
(0, 3) isomorphic
(3, 1) not isomorphic

What about the complexity?

  • 1 is O(S * (K * N) * log(K * N))
  • 2 is O(S * K * N * log(S * K * N))
  • 3 is O(S * K * N)

So the overall complexity is O(S * K * N log(S * K * N))

Jean Logeart
  • 52,687
  • 11
  • 83
  • 118
  • I'll have to look more into these complexities: I think they are better than those. – Jean Logeart May 11 '15 at 19:19
  • Wow, it's a really interesting solution, and intuitively it is correct, but are you sure that if they're equal after sorting then we can say that they're "isomorphic" in the way that i've described above? – Chan Kha Vu May 11 '15 at 19:20
  • @JeanLogeart I'm not 100% convinced, you are basically multiplying with the inverse of the element in the first permutation set all of the other sets. But when there are many sets, most of them can map to something quite ugly, and I don't think that by just comparing them lexicographically you'll whether they are isomorphic. – vsoftco May 11 '15 at 19:26
  • 1
    Pretty sure. Think of your problem as the famous "anagram problem" where two words are isomorphic if they are anagram. But in your case the letters are a set of elements (array of ``int`` in your specific example) – Jean Logeart May 11 '15 at 19:26
  • @JeanLogeart thanks, have to think about it more. I know a sol. with `S^2 K^2 N`, but yours is better (if correct). – vsoftco May 11 '15 at 19:27
  • 1
    What about this example: `A = [ [0, 1, 2], [2, 0, 1] ]`, `B = [ [1, 0, 2], [0, 2, 1] ]`. Here `B = [1, 0, 2] * A`, so they are isomorphic under the OP definition. Or are you considering the mapping as taking elements of `A` under conjugation, i.e. `p A inv(p)`? – vsoftco May 11 '15 at 20:16
  • 1
    Sorry last element of `B` is `[2,1,0]`, so `B` is `[ [1, 0, 2], [2, 1, 0] ]` – vsoftco May 11 '15 at 20:24
  • 1
    Why do you assume that to be isomorphic, `pset[i]` must equal `pset[i-1]` ? As vsoftco's example of isomorphic `A = [ [0, 1, 2], [2, 0, 1] ]` and `B = [ [1, 0, 2], [0, 2, 1] ] = [1, 0, 2] * A` shows, your method would miss that. Or am I misunderstanding your test for `areEqual`? (Please see my answer) – גלעד ברקן May 13 '15 at 15:35
  • @FalconUA Consider arbitrary set `S` represented as array `A`. Since order within a set doesn't matter, assume wlog that elements of `S` are sorted within the array `A`. Now consider some `A'` which is a permutation of `A`. Clearly `A'` represents the same set as `S` since all we did was rearrange elements. Every unsorted isomorphism of `A` can be generated by some permutation. If instead we have an unsorted array `B`, we can sort it into `B'`. If `B' = A`, then clearly `B` is a permutation of `A`, so `B` also represents the set `S`. If `B' != A`, then `B` is not a permutation of `A`. – Nicu Stiurca May 16 '15 at 00:18
5

There is a very simple solution for this: transposition.

If two sets are isomorphic, it means a one-to-one mapping exists, where the set of all the numbers at index i in set S1 equals the set of all the numbers at some index k in set S2. My conjecture is that no two non-isomorphic sets have this property.

(1) Jean Logeart's example:

0: [[1,2,3],[3,2,1]]
1: [[2,3,1],[1,3,2]]
2: [[1,2,3],[2,3,1]]
3: [[3,2,1],[1,2,3]]

Perform ONE pass:

Transpose, O(n):
0: [[1,3],[2,2],[3,1]]

Sort both in and between groups, O(something log something):
0: [[1,3],[1,3],[2,2]]

Hash:
"131322" -> 0

...
"121233" -> 1
"121323" -> 2
"131322" -> already hashed.

0 and 3 are isomorphic.

(2) vsoftco's counter-example in his comment to Jean Logeart's answer:

A = [ [0, 1, 2], [2, 0, 1] ]
B = [ [1, 0, 2], [0, 2, 1] ]

"010212" -> A
"010212" -> already hashed.

A and B are isomorphic.

You can turn each set into a transposed-sorted string or hash or whatever compressed object for linear-time comparison. Note that this algorithm considers all three sets A, B and C as isomorphic even if one p converts A to B and another p converts A to C. Clearly, in this case, there are ps to convert any one of these three sets to the other, since all we are doing is moving each i in one set to a specific k in the other. If, as you stated, your goal is to "remove isomorphic permutations," you will still get a list of sets to remove.

Explanation:

Assume that along with our sorted hash, we kept a record of which permutation each i came from. vsoftco's counter-example:

010212  // hash for A and B
100110  // origin permutation, set A
100110  // origin permutation, set B

In order to confirm isomorphism, we need to show that the i's grouped in each index from the first set moved to some index in the second set, which index does not matter. Sorting the groups of i's does not invalidate the solution, rather it serves to confirm movement/permutation between sets.

Now by definition, each number in a hash and each number in each group in the hash is represented in an origin permutation exactly one time for each set. However we choose to arrange the numbers in each group of i's in the hash, we are guaranteed that each number in that group is representing a different permutation in the set; and the moment we theoretically assign that number, we are guaranteed it is "reserved" for that permutation and index only. For a given number, say 2, in the two hashes, we are guaranteed that it comes from one index and permutation in set A, and in the second hash corresponds to one index and permutation in set B. That is all we really need to show - that the number in one index for each permutation in one set (a group of distinct i's) went to one index only in the other set (a group of distinct k's). Which permutation and index the number belongs to is irrelevant.

Remember that any set S2, isomorphic to set S1, can be derived from S1 using one permutation function or various combinations of different permutation functions applied to S1's members. What the sorting, or reordering, of our numbers and groups actually represents is the permutation we are choosing to assign as the solution to the isomorphism rather than an actual assignment of which number came from which index and permutation. Here is vsoftco's counter-example again, this time we will add the origin indexes of our hashes:

110022 // origin index set A
001122 // origin index set B

Therefore our permutation, a solution to the isomorphism, is:

enter image description here

Or, in order:

enter image description here

(Notice that in Jean Logeart's example there is more than one solution to the isomorphism.)

גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
  • Your algorithm seems to work, I'm just trying to understand why :) Did you test it on some other examples with a bit more permutations? – vsoftco May 13 '15 at 22:38
  • @vsoftco according to the OP, FalconUA's, definition, "isomorphic" means one permutation function maps *all* of set `A`'s permutations to *all* of `B`'s permutations (they both contain K permutations). This means simply that each index `i` in `A`'s permutations will go to a specific index `k` in `B`'s permutations (for example, all the `S[A][1]'s` will go to `S[B][5]'s`). All we have to do is check if the grouping *by index* of permutations in `A` and `B` contain the same numbers. One easy way to do that is to sort those groupings. – גלעד ברקן May 13 '15 at 22:55
  • Interesting! Need to think about this some more. – Ami Tavory May 14 '15 at 14:33
  • Hmm, your algorithm seems to work but i'm not sure about the safety of swapping first and second elements of the transposition... – Chan Kha Vu May 14 '15 at 14:54
  • Unfortunately I still don't think it works: `A = { [1,2,0,3], [3,1,0,2] }`, `B = { [2,0,3,1], [1,2,3,0] }`, and `B = [3,2,0,1]A` However the "hash"-es are: `A: 00121323`, `B: 01021233`. – vsoftco May 14 '15 at 14:58
  • @vsoftco I think your permuting is wrong: `[3,2,0,1] * { [1,2,0,3], [3,1,0,2] } => {[0,3,2,1] [0,2,1,3]}` not `{[2,0,3,1], [1,2,3,0] }` hashes are both `00121323` and they are isomorphic – גלעד ברקן May 14 '15 at 16:02
  • 1
    Just to make sure we use the same conventions... Isn't `[3,2,0,1] \composed [1,2,0,3] = [2,0,3,1]`? You do it starting with the right-most permutation, `0->1->2, 1->2->0, 2->0->3, 3->3->1`. – vsoftco May 14 '15 at 16:07
  • 1
    @vsoftco Not sure I understand you. What I do is apply the permutation `[3,2,0,1]` to each of `[1,2,0,3]` and `[3,1,0,2]`: for the first one, `[1,2,0,3]`: 1 goes index 3; 2 goes to index 2; 0 goes to index 0; and 3 goes to index 1. Makes sense? – גלעד ברקן May 14 '15 at 16:12
  • 1
    @גלעדברקן I think you're composing the in reverse http://math.stackexchange.com/questions/549186/the-product-or-composition-of-permutation-groups-in-two-line-notation – vsoftco May 14 '15 at 17:07
  • vsoftco, OP never said it is a permutation product. It's just a permutation function - see here: http://en.wikipedia.org/wiki/Permutation in the section titled "Definition and one-line notation". – גלעד ברקן May 16 '15 at 00:04
  • @FalconUA I added an explanation of why I think sorting is ok. – גלעד ברקן May 16 '15 at 00:15
  • @AmiTavory added an explanation. – גלעד ברקן May 16 '15 at 04:14
4

Take a0 in A. Then find it's inverse (fast, O(N)), call it a0inv. Then choose some i in B and define P_i = b_i * ainv and check that P_i * a generates B, when varying a over A. Do this for every i in B. If you don't find any i for which the relation holds, then the sets are not isomorphic. If you find such an i, then the sets are isomorphic. The runtime is O(K^2) for each pair of sets it checks, and you'd need to check O(S^2) sets, so you end up with O(S^2 * K^2 * N).

PS: I assumed here that by "maps A to B" you mean mapping under permutation composition, so P(a) is actually the permutation P composed with the permutation a, and I've used the fact that if P is a permutation, then there must exist an i for which Pa = b_i for some a.

EDIT I decided to undelete my answer as I am not convinced the previous one (@Jean Logeart) based on searching is correct. If yes, I'll gladly delete mine, as it performs worse, but I think I have a counterexample, see the comments below Jean's answer.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
4

Suppose that two elements of s1, s2 \in S are isomorphic. Then if p1 and p2 are permutations, then s1 is isomorphic to s2 iff p1(s1) is isomorphic to p2(s2) where pi(si) is the set of permutations obtained by applying pi to every element in si.

For each i in 1...s and j in 1...k, choose the j-th member of si, and find the permutation that changes it to unity. Apply it to all the elements of si. Hash each of the k permutations to a number, obtaining k numbers, for any choice of i and j, at cost nk.

Comparing the hashed sets for two different values of i and j is k^2 < nk. Thus, you can find the set of candidate matches at cost s^2 k^3 n. If the actual number of matches is low, the overall complexity is far beneath what you specified in your question.

Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
3

To check if two sets S₁ and S₂ are isomorphic you can do a much shorter search.

If they are isomorphic then there is a permutation t that maps each element of S₁ to an element of S₂; to find t you can just pick any fixed element p of S₁ and consider the permutations

 t₁ = (1/p) q₁
 t₂ = (1/p) q₂
 t₃ = (1/p) q₃
 ...

for all elements q of S₂. For, if a valid t exists then it must map the element p to an element of S₂, so only permutations mapping p to an element of S₂ are possible candidates.

Moreover given a candidate t to check if two sets of permutations S₁t and S₂ are equal you could use an hash computed as the x-or of an hash code for each element, doing the full check of all the permutations only if the hash matches.

6502
  • 112,025
  • 15
  • 165
  • 265