Compare two lists to see if one is a rotation of the other

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
5
down vote

favorite












Going through a list of common interview question, this was the next one. Any suggestions on performance and improvement? What would be a better implementation of this? Also correct me if I'm wrong, this would be $O(n)$ complexity?



'''
Problem statement: Given 2 integer arrays, determine if the 2nd array is a rotated version of the 1st array.
Ex. Original Array A=1,2,3,5,6,7,8 Rotated Array B=5,6,7,8,1,2,3

@author: Anonymous3.1415
'''

#@cmp_arr, is the array being compared to see if it is a rotation
def is_rotated(int_arr, cmp_arr):

#lengths of arrays are the same and not equal to zero?
if len(int_arr) != len(cmp_arr) or len(int_arr) == 0:
return False

#does array contain same numbers?
if not set(int_arr) <= set(cmp_arr) and not set(int_arr) >= set(cmp_arr):
return False

#Find start of first list in the second
index = 0
while int_arr[0] != cmp_arr[index]:
index += 1

#split list at index point, compare the lists
cmp_arr = cmp_arr[index:] + cmp_arr[:index]
index = 0
for x in int_arr:
if x != cmp_arr[index]:
return False
index += 1

return True

int_arr = [1,2,3,4,6,4,7]
cmp_arr = [6,4,7,1,2,3,4]
print(is_rotated(int_arr, cmp_arr))






share|improve this question

















  • 2




    The code in this post does not work! For example, is_rotated([1, 1, 2], [1, 2, 1]) returns False.
    – Gareth Rees
    Feb 9 at 10:52
















up vote
5
down vote

favorite












Going through a list of common interview question, this was the next one. Any suggestions on performance and improvement? What would be a better implementation of this? Also correct me if I'm wrong, this would be $O(n)$ complexity?



'''
Problem statement: Given 2 integer arrays, determine if the 2nd array is a rotated version of the 1st array.
Ex. Original Array A=1,2,3,5,6,7,8 Rotated Array B=5,6,7,8,1,2,3

@author: Anonymous3.1415
'''

#@cmp_arr, is the array being compared to see if it is a rotation
def is_rotated(int_arr, cmp_arr):

#lengths of arrays are the same and not equal to zero?
if len(int_arr) != len(cmp_arr) or len(int_arr) == 0:
return False

#does array contain same numbers?
if not set(int_arr) <= set(cmp_arr) and not set(int_arr) >= set(cmp_arr):
return False

#Find start of first list in the second
index = 0
while int_arr[0] != cmp_arr[index]:
index += 1

#split list at index point, compare the lists
cmp_arr = cmp_arr[index:] + cmp_arr[:index]
index = 0
for x in int_arr:
if x != cmp_arr[index]:
return False
index += 1

return True

int_arr = [1,2,3,4,6,4,7]
cmp_arr = [6,4,7,1,2,3,4]
print(is_rotated(int_arr, cmp_arr))






share|improve this question

















  • 2




    The code in this post does not work! For example, is_rotated([1, 1, 2], [1, 2, 1]) returns False.
    – Gareth Rees
    Feb 9 at 10:52












up vote
5
down vote

favorite









up vote
5
down vote

favorite











Going through a list of common interview question, this was the next one. Any suggestions on performance and improvement? What would be a better implementation of this? Also correct me if I'm wrong, this would be $O(n)$ complexity?



'''
Problem statement: Given 2 integer arrays, determine if the 2nd array is a rotated version of the 1st array.
Ex. Original Array A=1,2,3,5,6,7,8 Rotated Array B=5,6,7,8,1,2,3

@author: Anonymous3.1415
'''

#@cmp_arr, is the array being compared to see if it is a rotation
def is_rotated(int_arr, cmp_arr):

#lengths of arrays are the same and not equal to zero?
if len(int_arr) != len(cmp_arr) or len(int_arr) == 0:
return False

#does array contain same numbers?
if not set(int_arr) <= set(cmp_arr) and not set(int_arr) >= set(cmp_arr):
return False

#Find start of first list in the second
index = 0
while int_arr[0] != cmp_arr[index]:
index += 1

#split list at index point, compare the lists
cmp_arr = cmp_arr[index:] + cmp_arr[:index]
index = 0
for x in int_arr:
if x != cmp_arr[index]:
return False
index += 1

return True

int_arr = [1,2,3,4,6,4,7]
cmp_arr = [6,4,7,1,2,3,4]
print(is_rotated(int_arr, cmp_arr))






share|improve this question













Going through a list of common interview question, this was the next one. Any suggestions on performance and improvement? What would be a better implementation of this? Also correct me if I'm wrong, this would be $O(n)$ complexity?



'''
Problem statement: Given 2 integer arrays, determine if the 2nd array is a rotated version of the 1st array.
Ex. Original Array A=1,2,3,5,6,7,8 Rotated Array B=5,6,7,8,1,2,3

@author: Anonymous3.1415
'''

#@cmp_arr, is the array being compared to see if it is a rotation
def is_rotated(int_arr, cmp_arr):

#lengths of arrays are the same and not equal to zero?
if len(int_arr) != len(cmp_arr) or len(int_arr) == 0:
return False

#does array contain same numbers?
if not set(int_arr) <= set(cmp_arr) and not set(int_arr) >= set(cmp_arr):
return False

#Find start of first list in the second
index = 0
while int_arr[0] != cmp_arr[index]:
index += 1

#split list at index point, compare the lists
cmp_arr = cmp_arr[index:] + cmp_arr[:index]
index = 0
for x in int_arr:
if x != cmp_arr[index]:
return False
index += 1

return True

int_arr = [1,2,3,4,6,4,7]
cmp_arr = [6,4,7,1,2,3,4]
print(is_rotated(int_arr, cmp_arr))








share|improve this question












share|improve this question




share|improve this question








edited Feb 9 at 10:53









Gareth Rees

41.1k394168




41.1k394168









asked Feb 9 at 3:29









Anonymous3.1415

376212




376212







  • 2




    The code in this post does not work! For example, is_rotated([1, 1, 2], [1, 2, 1]) returns False.
    – Gareth Rees
    Feb 9 at 10:52












  • 2




    The code in this post does not work! For example, is_rotated([1, 1, 2], [1, 2, 1]) returns False.
    – Gareth Rees
    Feb 9 at 10:52







2




2




The code in this post does not work! For example, is_rotated([1, 1, 2], [1, 2, 1]) returns False.
– Gareth Rees
Feb 9 at 10:52




The code in this post does not work! For example, is_rotated([1, 1, 2], [1, 2, 1]) returns False.
– Gareth Rees
Feb 9 at 10:52










2 Answers
2






active

oldest

votes

















up vote
5
down vote



accepted










Naming



Your 2 arrays are used the same way, it is a bit weird to have 2 different names for them. Maybe int_arr1 and int_arr2 would be better names. Also the fact that they contain integers may be irrelevant for the naming: arr1 and arr2 would do the trick (or lst1 and lst2 because the corresponding Python primitive data structure is list).



Problem with the check for length equal to 0



Checking for identical lengths as the beginning of the function is a nice touch.



However, the or len(lst1) == 0 condition makes me uneasy. It only makes a different when the first part of the check was false and the second part is true which means: len(lst1) == len(lst2) == 0 which really means: lst1 == lst2 == .



I'd expect the empty array to be a rotation of the empty array. The alternative would be to document the function to precise that only non-trivial rotations are considered (which is not the case at the moment). The easy fix is probably to remove this test or to return True in that case (or whenver lst1 == lst2).



Comments



Commenting your code is nice but adding obvious comments doesn't help anyone. There is a fine line between too many comments and not enough comments.



I'd get rid of #lengths of arrays are the same and not equal to zero? for instance.



On the other hand, #@cmp_arr, is the array being compared to see if it is a rotation could be kept somehow. This should be moved in the function docstring. Also, Python has a Docstring convention called PEP 257. You could write:



def is_rotated(lst1, lst2):
"""Test whether list lst1 is a rotation of list lst2."""


Same value computed multiple times



You compute set(lst1) multiple times which is not as efficient as it could be (which is sad for something meant to be an optimisation).



#does array contain same numbers?
s1 = set(lst1)
s2 = set(lst2)
if not s1 <= s2 and not s1 >= s2:
return False


Optimisation does not work



I guess the point of the optimisation would be to detect situations like:



lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
print(is_rotated(lst1, lst2))

lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
print(is_rotated(lst1, lst2))


Unfortunately, this is not detected.



I guess instead of not s1 <= s2 and not s1 >= s2, you meant: not s1 <= s2 or not s1 >= s2 which is equivalent to not (s1 <= s2 and s1 >= s2) which is equivalent to the more straight-forward: s1 != s2.



In any case, as pointed out by Oscar Smith, this optimisation may not be worth doing.



Reusing existing functions



Looking for index can be done with the already existing function: index = lst2.index(lst1[0]).



Loop like a native



Your final loop is not really Pythonic. Most of the time in Python, you can avoid messing with indices. I hightly recommand Ned Batchelder's talk: "Loop like a native".



In your case, we can improve your code in multiple steps. (Some of the steps are pretty artificial in your case but I use them to show you the different functions you have in your Python toolbox).



Introducing enumerate to get rid of the code to track index:



#split list at index point, compare the lists
rot2 = lst2[index:] + lst2[:index]
for i, x in enumerate(lst1):
if x != rot2[i]:
return False
return True


Using zip to avoid accessing rot2 by index:



#split list at index point, compare the lists
rot2 = lst2[index:] + lst2[:index]
for x1, x2 in zip(lst1, rot2):
if x1 != x2:
return False
return True


Using all to make things more concise:



return all(x1 == x2 for x1, x2 in zip(lst1, rot2))


The final revelation: all this is not needed:



return lst1 == rot2


At this point, the code looks like:



def is_rotated(lst1, lst2):
"""Test whether list lst1 is a rotation of list lst2."""
if len(lst1) != len(lst2):
return False

if lst1 == lst2:
return True

if set(lst1) != set(lst2):
return False

index = lst2.index(lst1[0])

#split list at index point, compare the lists
rot2 = lst2[index:] + lst2[:index]
return lst1 == rot2


Broken code



As pointed out by Oscar Smith, your code does not work.



For that kind of programming task, it is very easy to write tests to help you catch issues. These can be written before, during and after writing the code. You could use a proper testing framework or just simple assertions like:



# rotation
lst1, lst2 = [1,2,3,4,6,4,7], [6,4,7,1,2,3,4]
assert is_rotated(lst1, lst2)

# rotation with repeated numbers
lst1, lst2 = [1,2,3,4,6,4,7,1], [6,4,7,1,1,2,3,4]
assert is_rotated(lst1, lst2)

# different set
lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
assert not is_rotated(lst1, lst2)
lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
assert not is_rotated(lst1, lst2)

# equal
lst2 = lst1
assert is_rotated(lst1, lst2)

# empty
lst1, lst2 = ,
assert is_rotated(lst1, lst2)

# 1 empty, 1 not empty
lst1, lst2 = , [1]
assert not is_rotated(lst1, lst2)
lst1, lst2 = [1],
assert not is_rotated(lst1, lst2)





share|improve this answer





















  • Wouldn't using deque.rotate be more efficient?
    – hjpotter92
    Feb 9 at 13:09










  • yeah that's the best way to get O(n)
    – Oscar Smith
    Feb 9 at 21:17

















up vote
4
down vote













While this is O(n), it isn't nearly as fast as it could be.



Your first check is a good one because it is O(1), and will be hit for random lists a fair amount. The second however is not. by finding the unique elements, you end up doing a screen that costs an amount of time comparable to solving the actual problem.



The next piece of your code is actually broken, as it only finds the first match instead of all possible matches. In fact, when you fix this bug, your code will be O(n^2) in the worst case (if one is [0]*1000+[1,0] and the other is [0]*1000+[0,1])






share|improve this answer





















    Your Answer




    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187146%2fcompare-two-lists-to-see-if-one-is-a-rotation-of-the-other%23new-answer', 'question_page');

    );

    Post as a guest






























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    5
    down vote



    accepted










    Naming



    Your 2 arrays are used the same way, it is a bit weird to have 2 different names for them. Maybe int_arr1 and int_arr2 would be better names. Also the fact that they contain integers may be irrelevant for the naming: arr1 and arr2 would do the trick (or lst1 and lst2 because the corresponding Python primitive data structure is list).



    Problem with the check for length equal to 0



    Checking for identical lengths as the beginning of the function is a nice touch.



    However, the or len(lst1) == 0 condition makes me uneasy. It only makes a different when the first part of the check was false and the second part is true which means: len(lst1) == len(lst2) == 0 which really means: lst1 == lst2 == .



    I'd expect the empty array to be a rotation of the empty array. The alternative would be to document the function to precise that only non-trivial rotations are considered (which is not the case at the moment). The easy fix is probably to remove this test or to return True in that case (or whenver lst1 == lst2).



    Comments



    Commenting your code is nice but adding obvious comments doesn't help anyone. There is a fine line between too many comments and not enough comments.



    I'd get rid of #lengths of arrays are the same and not equal to zero? for instance.



    On the other hand, #@cmp_arr, is the array being compared to see if it is a rotation could be kept somehow. This should be moved in the function docstring. Also, Python has a Docstring convention called PEP 257. You could write:



    def is_rotated(lst1, lst2):
    """Test whether list lst1 is a rotation of list lst2."""


    Same value computed multiple times



    You compute set(lst1) multiple times which is not as efficient as it could be (which is sad for something meant to be an optimisation).



    #does array contain same numbers?
    s1 = set(lst1)
    s2 = set(lst2)
    if not s1 <= s2 and not s1 >= s2:
    return False


    Optimisation does not work



    I guess the point of the optimisation would be to detect situations like:



    lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
    print(is_rotated(lst1, lst2))

    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
    print(is_rotated(lst1, lst2))


    Unfortunately, this is not detected.



    I guess instead of not s1 <= s2 and not s1 >= s2, you meant: not s1 <= s2 or not s1 >= s2 which is equivalent to not (s1 <= s2 and s1 >= s2) which is equivalent to the more straight-forward: s1 != s2.



    In any case, as pointed out by Oscar Smith, this optimisation may not be worth doing.



    Reusing existing functions



    Looking for index can be done with the already existing function: index = lst2.index(lst1[0]).



    Loop like a native



    Your final loop is not really Pythonic. Most of the time in Python, you can avoid messing with indices. I hightly recommand Ned Batchelder's talk: "Loop like a native".



    In your case, we can improve your code in multiple steps. (Some of the steps are pretty artificial in your case but I use them to show you the different functions you have in your Python toolbox).



    Introducing enumerate to get rid of the code to track index:



    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    for i, x in enumerate(lst1):
    if x != rot2[i]:
    return False
    return True


    Using zip to avoid accessing rot2 by index:



    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    for x1, x2 in zip(lst1, rot2):
    if x1 != x2:
    return False
    return True


    Using all to make things more concise:



    return all(x1 == x2 for x1, x2 in zip(lst1, rot2))


    The final revelation: all this is not needed:



    return lst1 == rot2


    At this point, the code looks like:



    def is_rotated(lst1, lst2):
    """Test whether list lst1 is a rotation of list lst2."""
    if len(lst1) != len(lst2):
    return False

    if lst1 == lst2:
    return True

    if set(lst1) != set(lst2):
    return False

    index = lst2.index(lst1[0])

    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    return lst1 == rot2


    Broken code



    As pointed out by Oscar Smith, your code does not work.



    For that kind of programming task, it is very easy to write tests to help you catch issues. These can be written before, during and after writing the code. You could use a proper testing framework or just simple assertions like:



    # rotation
    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,7,1,2,3,4]
    assert is_rotated(lst1, lst2)

    # rotation with repeated numbers
    lst1, lst2 = [1,2,3,4,6,4,7,1], [6,4,7,1,1,2,3,4]
    assert is_rotated(lst1, lst2)

    # different set
    lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
    assert not is_rotated(lst1, lst2)
    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
    assert not is_rotated(lst1, lst2)

    # equal
    lst2 = lst1
    assert is_rotated(lst1, lst2)

    # empty
    lst1, lst2 = ,
    assert is_rotated(lst1, lst2)

    # 1 empty, 1 not empty
    lst1, lst2 = , [1]
    assert not is_rotated(lst1, lst2)
    lst1, lst2 = [1],
    assert not is_rotated(lst1, lst2)





    share|improve this answer





















    • Wouldn't using deque.rotate be more efficient?
      – hjpotter92
      Feb 9 at 13:09










    • yeah that's the best way to get O(n)
      – Oscar Smith
      Feb 9 at 21:17














    up vote
    5
    down vote



    accepted










    Naming



    Your 2 arrays are used the same way, it is a bit weird to have 2 different names for them. Maybe int_arr1 and int_arr2 would be better names. Also the fact that they contain integers may be irrelevant for the naming: arr1 and arr2 would do the trick (or lst1 and lst2 because the corresponding Python primitive data structure is list).



    Problem with the check for length equal to 0



    Checking for identical lengths as the beginning of the function is a nice touch.



    However, the or len(lst1) == 0 condition makes me uneasy. It only makes a different when the first part of the check was false and the second part is true which means: len(lst1) == len(lst2) == 0 which really means: lst1 == lst2 == .



    I'd expect the empty array to be a rotation of the empty array. The alternative would be to document the function to precise that only non-trivial rotations are considered (which is not the case at the moment). The easy fix is probably to remove this test or to return True in that case (or whenver lst1 == lst2).



    Comments



    Commenting your code is nice but adding obvious comments doesn't help anyone. There is a fine line between too many comments and not enough comments.



    I'd get rid of #lengths of arrays are the same and not equal to zero? for instance.



    On the other hand, #@cmp_arr, is the array being compared to see if it is a rotation could be kept somehow. This should be moved in the function docstring. Also, Python has a Docstring convention called PEP 257. You could write:



    def is_rotated(lst1, lst2):
    """Test whether list lst1 is a rotation of list lst2."""


    Same value computed multiple times



    You compute set(lst1) multiple times which is not as efficient as it could be (which is sad for something meant to be an optimisation).



    #does array contain same numbers?
    s1 = set(lst1)
    s2 = set(lst2)
    if not s1 <= s2 and not s1 >= s2:
    return False


    Optimisation does not work



    I guess the point of the optimisation would be to detect situations like:



    lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
    print(is_rotated(lst1, lst2))

    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
    print(is_rotated(lst1, lst2))


    Unfortunately, this is not detected.



    I guess instead of not s1 <= s2 and not s1 >= s2, you meant: not s1 <= s2 or not s1 >= s2 which is equivalent to not (s1 <= s2 and s1 >= s2) which is equivalent to the more straight-forward: s1 != s2.



    In any case, as pointed out by Oscar Smith, this optimisation may not be worth doing.



    Reusing existing functions



    Looking for index can be done with the already existing function: index = lst2.index(lst1[0]).



    Loop like a native



    Your final loop is not really Pythonic. Most of the time in Python, you can avoid messing with indices. I hightly recommand Ned Batchelder's talk: "Loop like a native".



    In your case, we can improve your code in multiple steps. (Some of the steps are pretty artificial in your case but I use them to show you the different functions you have in your Python toolbox).



    Introducing enumerate to get rid of the code to track index:



    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    for i, x in enumerate(lst1):
    if x != rot2[i]:
    return False
    return True


    Using zip to avoid accessing rot2 by index:



    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    for x1, x2 in zip(lst1, rot2):
    if x1 != x2:
    return False
    return True


    Using all to make things more concise:



    return all(x1 == x2 for x1, x2 in zip(lst1, rot2))


    The final revelation: all this is not needed:



    return lst1 == rot2


    At this point, the code looks like:



    def is_rotated(lst1, lst2):
    """Test whether list lst1 is a rotation of list lst2."""
    if len(lst1) != len(lst2):
    return False

    if lst1 == lst2:
    return True

    if set(lst1) != set(lst2):
    return False

    index = lst2.index(lst1[0])

    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    return lst1 == rot2


    Broken code



    As pointed out by Oscar Smith, your code does not work.



    For that kind of programming task, it is very easy to write tests to help you catch issues. These can be written before, during and after writing the code. You could use a proper testing framework or just simple assertions like:



    # rotation
    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,7,1,2,3,4]
    assert is_rotated(lst1, lst2)

    # rotation with repeated numbers
    lst1, lst2 = [1,2,3,4,6,4,7,1], [6,4,7,1,1,2,3,4]
    assert is_rotated(lst1, lst2)

    # different set
    lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
    assert not is_rotated(lst1, lst2)
    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
    assert not is_rotated(lst1, lst2)

    # equal
    lst2 = lst1
    assert is_rotated(lst1, lst2)

    # empty
    lst1, lst2 = ,
    assert is_rotated(lst1, lst2)

    # 1 empty, 1 not empty
    lst1, lst2 = , [1]
    assert not is_rotated(lst1, lst2)
    lst1, lst2 = [1],
    assert not is_rotated(lst1, lst2)





    share|improve this answer





















    • Wouldn't using deque.rotate be more efficient?
      – hjpotter92
      Feb 9 at 13:09










    • yeah that's the best way to get O(n)
      – Oscar Smith
      Feb 9 at 21:17












    up vote
    5
    down vote



    accepted







    up vote
    5
    down vote



    accepted






    Naming



    Your 2 arrays are used the same way, it is a bit weird to have 2 different names for them. Maybe int_arr1 and int_arr2 would be better names. Also the fact that they contain integers may be irrelevant for the naming: arr1 and arr2 would do the trick (or lst1 and lst2 because the corresponding Python primitive data structure is list).



    Problem with the check for length equal to 0



    Checking for identical lengths as the beginning of the function is a nice touch.



    However, the or len(lst1) == 0 condition makes me uneasy. It only makes a different when the first part of the check was false and the second part is true which means: len(lst1) == len(lst2) == 0 which really means: lst1 == lst2 == .



    I'd expect the empty array to be a rotation of the empty array. The alternative would be to document the function to precise that only non-trivial rotations are considered (which is not the case at the moment). The easy fix is probably to remove this test or to return True in that case (or whenver lst1 == lst2).



    Comments



    Commenting your code is nice but adding obvious comments doesn't help anyone. There is a fine line between too many comments and not enough comments.



    I'd get rid of #lengths of arrays are the same and not equal to zero? for instance.



    On the other hand, #@cmp_arr, is the array being compared to see if it is a rotation could be kept somehow. This should be moved in the function docstring. Also, Python has a Docstring convention called PEP 257. You could write:



    def is_rotated(lst1, lst2):
    """Test whether list lst1 is a rotation of list lst2."""


    Same value computed multiple times



    You compute set(lst1) multiple times which is not as efficient as it could be (which is sad for something meant to be an optimisation).



    #does array contain same numbers?
    s1 = set(lst1)
    s2 = set(lst2)
    if not s1 <= s2 and not s1 >= s2:
    return False


    Optimisation does not work



    I guess the point of the optimisation would be to detect situations like:



    lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
    print(is_rotated(lst1, lst2))

    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
    print(is_rotated(lst1, lst2))


    Unfortunately, this is not detected.



    I guess instead of not s1 <= s2 and not s1 >= s2, you meant: not s1 <= s2 or not s1 >= s2 which is equivalent to not (s1 <= s2 and s1 >= s2) which is equivalent to the more straight-forward: s1 != s2.



    In any case, as pointed out by Oscar Smith, this optimisation may not be worth doing.



    Reusing existing functions



    Looking for index can be done with the already existing function: index = lst2.index(lst1[0]).



    Loop like a native



    Your final loop is not really Pythonic. Most of the time in Python, you can avoid messing with indices. I hightly recommand Ned Batchelder's talk: "Loop like a native".



    In your case, we can improve your code in multiple steps. (Some of the steps are pretty artificial in your case but I use them to show you the different functions you have in your Python toolbox).



    Introducing enumerate to get rid of the code to track index:



    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    for i, x in enumerate(lst1):
    if x != rot2[i]:
    return False
    return True


    Using zip to avoid accessing rot2 by index:



    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    for x1, x2 in zip(lst1, rot2):
    if x1 != x2:
    return False
    return True


    Using all to make things more concise:



    return all(x1 == x2 for x1, x2 in zip(lst1, rot2))


    The final revelation: all this is not needed:



    return lst1 == rot2


    At this point, the code looks like:



    def is_rotated(lst1, lst2):
    """Test whether list lst1 is a rotation of list lst2."""
    if len(lst1) != len(lst2):
    return False

    if lst1 == lst2:
    return True

    if set(lst1) != set(lst2):
    return False

    index = lst2.index(lst1[0])

    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    return lst1 == rot2


    Broken code



    As pointed out by Oscar Smith, your code does not work.



    For that kind of programming task, it is very easy to write tests to help you catch issues. These can be written before, during and after writing the code. You could use a proper testing framework or just simple assertions like:



    # rotation
    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,7,1,2,3,4]
    assert is_rotated(lst1, lst2)

    # rotation with repeated numbers
    lst1, lst2 = [1,2,3,4,6,4,7,1], [6,4,7,1,1,2,3,4]
    assert is_rotated(lst1, lst2)

    # different set
    lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
    assert not is_rotated(lst1, lst2)
    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
    assert not is_rotated(lst1, lst2)

    # equal
    lst2 = lst1
    assert is_rotated(lst1, lst2)

    # empty
    lst1, lst2 = ,
    assert is_rotated(lst1, lst2)

    # 1 empty, 1 not empty
    lst1, lst2 = , [1]
    assert not is_rotated(lst1, lst2)
    lst1, lst2 = [1],
    assert not is_rotated(lst1, lst2)





    share|improve this answer













    Naming



    Your 2 arrays are used the same way, it is a bit weird to have 2 different names for them. Maybe int_arr1 and int_arr2 would be better names. Also the fact that they contain integers may be irrelevant for the naming: arr1 and arr2 would do the trick (or lst1 and lst2 because the corresponding Python primitive data structure is list).



    Problem with the check for length equal to 0



    Checking for identical lengths as the beginning of the function is a nice touch.



    However, the or len(lst1) == 0 condition makes me uneasy. It only makes a different when the first part of the check was false and the second part is true which means: len(lst1) == len(lst2) == 0 which really means: lst1 == lst2 == .



    I'd expect the empty array to be a rotation of the empty array. The alternative would be to document the function to precise that only non-trivial rotations are considered (which is not the case at the moment). The easy fix is probably to remove this test or to return True in that case (or whenver lst1 == lst2).



    Comments



    Commenting your code is nice but adding obvious comments doesn't help anyone. There is a fine line between too many comments and not enough comments.



    I'd get rid of #lengths of arrays are the same and not equal to zero? for instance.



    On the other hand, #@cmp_arr, is the array being compared to see if it is a rotation could be kept somehow. This should be moved in the function docstring. Also, Python has a Docstring convention called PEP 257. You could write:



    def is_rotated(lst1, lst2):
    """Test whether list lst1 is a rotation of list lst2."""


    Same value computed multiple times



    You compute set(lst1) multiple times which is not as efficient as it could be (which is sad for something meant to be an optimisation).



    #does array contain same numbers?
    s1 = set(lst1)
    s2 = set(lst2)
    if not s1 <= s2 and not s1 >= s2:
    return False


    Optimisation does not work



    I guess the point of the optimisation would be to detect situations like:



    lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
    print(is_rotated(lst1, lst2))

    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
    print(is_rotated(lst1, lst2))


    Unfortunately, this is not detected.



    I guess instead of not s1 <= s2 and not s1 >= s2, you meant: not s1 <= s2 or not s1 >= s2 which is equivalent to not (s1 <= s2 and s1 >= s2) which is equivalent to the more straight-forward: s1 != s2.



    In any case, as pointed out by Oscar Smith, this optimisation may not be worth doing.



    Reusing existing functions



    Looking for index can be done with the already existing function: index = lst2.index(lst1[0]).



    Loop like a native



    Your final loop is not really Pythonic. Most of the time in Python, you can avoid messing with indices. I hightly recommand Ned Batchelder's talk: "Loop like a native".



    In your case, we can improve your code in multiple steps. (Some of the steps are pretty artificial in your case but I use them to show you the different functions you have in your Python toolbox).



    Introducing enumerate to get rid of the code to track index:



    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    for i, x in enumerate(lst1):
    if x != rot2[i]:
    return False
    return True


    Using zip to avoid accessing rot2 by index:



    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    for x1, x2 in zip(lst1, rot2):
    if x1 != x2:
    return False
    return True


    Using all to make things more concise:



    return all(x1 == x2 for x1, x2 in zip(lst1, rot2))


    The final revelation: all this is not needed:



    return lst1 == rot2


    At this point, the code looks like:



    def is_rotated(lst1, lst2):
    """Test whether list lst1 is a rotation of list lst2."""
    if len(lst1) != len(lst2):
    return False

    if lst1 == lst2:
    return True

    if set(lst1) != set(lst2):
    return False

    index = lst2.index(lst1[0])

    #split list at index point, compare the lists
    rot2 = lst2[index:] + lst2[:index]
    return lst1 == rot2


    Broken code



    As pointed out by Oscar Smith, your code does not work.



    For that kind of programming task, it is very easy to write tests to help you catch issues. These can be written before, during and after writing the code. You could use a proper testing framework or just simple assertions like:



    # rotation
    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,7,1,2,3,4]
    assert is_rotated(lst1, lst2)

    # rotation with repeated numbers
    lst1, lst2 = [1,2,3,4,6,4,7,1], [6,4,7,1,1,2,3,4]
    assert is_rotated(lst1, lst2)

    # different set
    lst1, lst2 = [1,2,3,4,6,4,6], [6,4,7,1,2,3,4]
    assert not is_rotated(lst1, lst2)
    lst1, lst2 = [1,2,3,4,6,4,7], [6,4,6,1,2,3,4]
    assert not is_rotated(lst1, lst2)

    # equal
    lst2 = lst1
    assert is_rotated(lst1, lst2)

    # empty
    lst1, lst2 = ,
    assert is_rotated(lst1, lst2)

    # 1 empty, 1 not empty
    lst1, lst2 = , [1]
    assert not is_rotated(lst1, lst2)
    lst1, lst2 = [1],
    assert not is_rotated(lst1, lst2)






    share|improve this answer













    share|improve this answer



    share|improve this answer











    answered Feb 9 at 6:55









    Josay

    23.8k13580




    23.8k13580











    • Wouldn't using deque.rotate be more efficient?
      – hjpotter92
      Feb 9 at 13:09










    • yeah that's the best way to get O(n)
      – Oscar Smith
      Feb 9 at 21:17
















    • Wouldn't using deque.rotate be more efficient?
      – hjpotter92
      Feb 9 at 13:09










    • yeah that's the best way to get O(n)
      – Oscar Smith
      Feb 9 at 21:17















    Wouldn't using deque.rotate be more efficient?
    – hjpotter92
    Feb 9 at 13:09




    Wouldn't using deque.rotate be more efficient?
    – hjpotter92
    Feb 9 at 13:09












    yeah that's the best way to get O(n)
    – Oscar Smith
    Feb 9 at 21:17




    yeah that's the best way to get O(n)
    – Oscar Smith
    Feb 9 at 21:17












    up vote
    4
    down vote













    While this is O(n), it isn't nearly as fast as it could be.



    Your first check is a good one because it is O(1), and will be hit for random lists a fair amount. The second however is not. by finding the unique elements, you end up doing a screen that costs an amount of time comparable to solving the actual problem.



    The next piece of your code is actually broken, as it only finds the first match instead of all possible matches. In fact, when you fix this bug, your code will be O(n^2) in the worst case (if one is [0]*1000+[1,0] and the other is [0]*1000+[0,1])






    share|improve this answer

























      up vote
      4
      down vote













      While this is O(n), it isn't nearly as fast as it could be.



      Your first check is a good one because it is O(1), and will be hit for random lists a fair amount. The second however is not. by finding the unique elements, you end up doing a screen that costs an amount of time comparable to solving the actual problem.



      The next piece of your code is actually broken, as it only finds the first match instead of all possible matches. In fact, when you fix this bug, your code will be O(n^2) in the worst case (if one is [0]*1000+[1,0] and the other is [0]*1000+[0,1])






      share|improve this answer























        up vote
        4
        down vote










        up vote
        4
        down vote









        While this is O(n), it isn't nearly as fast as it could be.



        Your first check is a good one because it is O(1), and will be hit for random lists a fair amount. The second however is not. by finding the unique elements, you end up doing a screen that costs an amount of time comparable to solving the actual problem.



        The next piece of your code is actually broken, as it only finds the first match instead of all possible matches. In fact, when you fix this bug, your code will be O(n^2) in the worst case (if one is [0]*1000+[1,0] and the other is [0]*1000+[0,1])






        share|improve this answer













        While this is O(n), it isn't nearly as fast as it could be.



        Your first check is a good one because it is O(1), and will be hit for random lists a fair amount. The second however is not. by finding the unique elements, you end up doing a screen that costs an amount of time comparable to solving the actual problem.



        The next piece of your code is actually broken, as it only finds the first match instead of all possible matches. In fact, when you fix this bug, your code will be O(n^2) in the worst case (if one is [0]*1000+[1,0] and the other is [0]*1000+[0,1])







        share|improve this answer













        share|improve this answer



        share|improve this answer











        answered Feb 9 at 5:39









        Oscar Smith

        2,625922




        2,625922






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187146%2fcompare-two-lists-to-see-if-one-is-a-rotation-of-the-other%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            Greedy Best First Search implementation in Rust

            Function to Return a JSON Like Objects Using VBA Collections and Arrays

            C++11 CLH Lock Implementation