0

How to remove 'circularly-similar' lists from a nested list. Two lists are 'circularly-similar' if they are the same after some circular rotation. For example [1,2,3,4] is circularly-similar to [3,4,1,2] because [1,2,3,4] rotated by 2 is [3,4,1,2]

Let's say I have the following list:

list = [[1, 1, 0], [0, 1, 1], [1, 1, 1]]

I would like to have [0, 1, 1] removed, because it is circularly-similar to [1, 1, 0] after rotation by 2. How should I approach this problem?

Aven Desta
  • 2,114
  • 12
  • 27

3 Answers3

1

You first need to check if two lists are the same under rotation

# not very efficient algorithm but it works
# you can also import deque from collections for rotation operation

def is_circular_equal(a,b):
   if len(a) != len(b):
      return False  # if they are not of equal length
   for i in range(len(a)):
      if b == a[i:]+a[:i]:
         return True
   return False   

Here is another post on how to do it.

Then loop through the list of lists to check if each is is_circular_equal as the other

Aven Desta
  • 2,114
  • 12
  • 27
  • 1
    Could be considered out of scope, but I would at least make sure that they have the same length. – L3viathan Jan 21 '21 at 21:11
  • Using `collections.deque` is better, esp. if there are multiple lists with many elements. See https://stackoverflow.com/q/2150108/7884305 – Chayim Friedman Jan 21 '21 at 21:14
  • @L3viathan ya, I don't wanna make the code longer. But its necessary to check that they have the same length, and return False otherwise – Aven Desta Jan 21 '21 at 21:18
1

Here's a bad solution (quadratic complexity):

def rotated_eq(a, b):
    return len(a) == len(b) and any(a == b[i:]+b[:i] for i in range(len(a)))


def prune_rotated_dupes(ls):
    result = []
    for element in ls:
        for existing in result:
            if rotated_eq(element, existing):
                break
        else:
            result.append(element)
    return result


list_in = [[1, 1, 0], [0, 1, 1], [1, 1, 1]]

assert prune_rotated_dupes(list_in) == [[1, 1, 0], [1, 1, 1]]

For a good solution, I would think about whether you can somehow generate a unique value for each rotated-identical list, and then use that value as an element of a set to test for existence.

L3viathan
  • 26,748
  • 2
  • 58
  • 81
  • Would it help to make the sub-lists colection.deques? – wwii Jan 21 '21 at 21:14
  • I guess it would help with the cost of the rotation, but it wouldn't solve the issue of the nested for-loops. It's quadratic even if you assume `rotated_eq` is O(1). – L3viathan Jan 21 '21 at 21:15
  • It would be better if you would divide into groups by list length – Chayim Friedman Jan 21 '21 at 21:18
  • The best for complexity is using tuples and sets. The conversion (list->tuple->back to list) can be more expensive, but if there are many lists this can still be better – Chayim Friedman Jan 21 '21 at 21:19
  • @ChayimFriedman Sure. But all this doesn't change the asymptotic complexity, which is why I consider it premature optimization. Once the main problem is solved (generating a key value for each list) this probably won't even be necessary. – L3viathan Jan 21 '21 at 21:19
  • itertools.product/combinations would push the complexity to C. – wwii Jan 21 '21 at 21:19
  • @L3viathan a) Asymptotic complexity isn't all in life. b) Grouping by length does change the average complexity – Chayim Friedman Jan 21 '21 at 21:20
  • @ChayimFriedman In general I agree with that, but when it seems so tangible to make it linear I don't see the point in improving average complexity (yet). Same comment wrt. wwii's remark. – L3viathan Jan 21 '21 at 21:22
0

I'd do something like:

xs = [[1, 1, 0], [0, 1, 1], [1, 1, 1]]                                                                                                                                                              

sorted_list = list(map(lambda x: sorted(x), xs))

final_list = []

for x in sorted_list:

    if x in final_list:
        continue
    final_list.append(x)

Giovanni Rescia
  • 605
  • 5
  • 15
  • This does not solve the above but compares unordered list (which is already covered at https://stackoverflow.com/q/7828867/7884305) – Chayim Friedman Jan 21 '21 at 21:12