Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python

Derek Derek
Derek Derek
8,744 Points

[python] minimum number of swaps to equalise two lists that contain the same numbers but in different orders

My idea was to create and populate lst3 with indexes of items in lst1 in lst2. For example,

lst1 = [1, 2, 3, 4] lst2 = [1, 2, 4, 3]

lst3 = [0, 1, 3, 2]

And figure out how many swaps are needed in lst3 to sort it. In this case, 1.

Below is my implementation in python. It seems to be working well except the line that calculates swaps needed in lst3.

'''
Given two lists, find the minimum number of swaps 
needed to make them identical.

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

swap 2, 4
swap 1, 3

min num of swaps: 2
'''

def MiniSwaps(lst1, lst2):

    lst3 = [None] * len(lst1)
    for i in range(len(lst1)):

        lst3[i] = lst2.index(lst1[i])


    swapCount = 0


    for k in range(len(lst3)):

        if k is not lst3[k]:
            lst3[k], lst3[lst3[k]] = lst3[lst3[k]], lst3[k] # <- this line doesn't do what it should. 
            swapCount += 1
        pass

    return swapCount

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

print MiniSwaps(A, B)

How can I fix it to make it work? Thank you.

Edit:

I tried the below approach and it works. But if I don't use the variable whereIsIndexMatchingNum, and just plug in lst.index(index) in the line below, it does not work, giving me an incorrect number of swaps needed. Why is this happening if I'm only plugging in the value of a variable instead of the variable itself?

'''
Given two lists, find the minimum number of swaps 
needed to make them identical.

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

2
'''

def MinSwaps(lst1, lst2):
    lst3 = [None] * len(lst1)

    for i in range(len(lst1)):
        lst3[i] = lst2.index(lst1[i])

    return calculateSwapsToSort(lst3)


def calculateSwapsToSort(lst):
    numOfSwaps = 0
    for index in range(len(lst)):
        if index != lst[index]:
            whereIsIndexMatchingNum = lst.index(index) # this variable!
            lst[index], lst[whereIsIndexMatchingNum] = lst[whereIsIndexMatchingNum], lst[index]

            #This is incorrect but the above is correct. I don't see the difference...
            #lst[index], lst[lst.index(index)] = lst[lst.index(index)], lst[index] 

            numOfSwaps +=1
        pass
    return numOfSwaps


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

print MinSwaps(A, B)

Thanks.

Hi Derek,

Out of curiosity, where are these problems coming from?

In your example as well as the example in the problem statement, no number needs to be swapped more than once.

Do you know if it needs to work for lists like the following?

lst1 = [1, 2, 3, 4, 5, 6]
lst2 = [1, 5, 2, 4, 6, 3]

That's an example where 4 numbers are in the wrong spot and it takes 3 swaps.

2 Answers

You could try this. I don't actually have Python on the machine that I on right now, so I couldn't test it. But it's a different angle than what you are taking and it's a bit shorter. Have fun! You're writing some complicated code. :)

def mini_swaps(list_one, list_two):
    # Add a running tally
    wrong_count = 0

    # Iterate over the items
    for item in list_one:
        # Index the item in both lists
        list_one_index = list_one.index(item)
        list_two_index = list_two.index(item)

        # If the item is not in the same place in both lists
        if list_two_index != list_one_index:
            # Add one to the tally
            wrong_count += 1

    # Since you're swapping these around, every swap will correct 2
    # items; so divide the count by two.
    return wrong_count / 2

P.S. Python convention is to use snake casing for everything except class names. Just FYI.

Hi jacinator,

You may have over-simplified the problem.

If you have the following lists,

a = [1, 2, 3, 4, 5, 6]
b = [1, 5, 2, 4, 6, 3]

I think your code is going to say that 4 numbers are wrong and it would take 2 swaps to fix. But 3 swaps are needed as far as I can tell. Every swap doesn't necessarily correct 2 items.

Also, if you needed to count how many are wrong it would be more efficient to compare corresponding values from both lists and increment whenever the values don't match.

The problem with the swapping

The reason that your original swap and your commented swap in your update aren't working is because the list gets modified during the assignments.

When you write a, b = b, a it seems as if the assignments happen in parallel but really a gets assigned a value first and then b gets assigned a value.

In the following code, I'll use your lst3 example and we'll assume that we're on the 3rd iteration and k = 2

lst3 = [0, 1, 3, 2]
k = 2
lst3[k], lst3[lst3[k]] = lst3[lst3[k]], lst3[k]

At this point your code is supposed to swap the 3 and 2.

After the right side of the assignment is evaluated you can think of the code like this: (using the current value for k)

lst3[2] = 2
lst3[lst3[2]] = 3

After the first assignment the list has been modified and now looks like this [0, 1, 2, 2]

That means lst3[2] is 2 and you have lst3[2] = 3 for the second assignment.

The 2nd assignment is supposed to assign the 3 to index 3 but it ends up going into index 2 making it look like nothing happened.

As you've discovered, you need to use another variable to prevent this from happening.

is not vs. !=

In your original code you used is not which is comparing identities not values. != is the correct way to compare if values are not equal which you did use in your updated code.

A possible problem with how you are sorting.

I don't think your method of sorting is guaranteed to fully sort the list.

Consider the list [5, 4, 3, 2, 0, 1]. It takes 4 swaps as far as I can tell. Try this out on your working code. I think it's going to report 3 swaps with a final not fully sorted list of [1, 0, 2, 3, 4, 5], meaning 1 more swap was needed.

I would look into implementing an already proven sorting algorithm. You can find a list here: https://en.wikipedia.org/wiki/Sorting_algorithm

You've reframed the problem as sorting lst3 with a minimum number of swaps. I think a possible problem you're going to run into is whether or not any of the sorting algorithms guarantee a minimum number of swaps for any possible list.

You could try starting with selection sort: https://en.wikipedia.org/wiki/Selection_sort

Selection sort is inefficient for large lists but it does seem to try to minimize the number of swaps taking place.

There's an implementation of it on that page which I think is in C code but you can convert it to python code and wrap it into a function. Add a counter to it and increment it whenever a swap takes place.

I worked out selection sort for a few examples with pencil and paper and it does seem to produce the minimum number of swaps but I don't know if it always will.

Creating the list of indexes and then sorting them may be the wrong approach to take here if there's no way to prove that you'll always get the minimum number of swaps.

I did a little research on this and it looks like the approach you want to take is to think of the 2 lists as permutations. Then it becomes a problem of the minimum numbers of swaps to go from one permutation to another. This is called the Cayley distance.

The math is beyond my level at this point but I wanted to give you a few links and some terms you can search for to get you pointed in the right direction.

The answer from Borx here: http://stackoverflow.com/questions/7797540/counting-the-adjacent-swaps-required-to-convert-one-permutation-into-another/20651773#20651773

The first part of the question here, http://math.stackexchange.com/questions/1932991/catalan-number-and-cayley-distance-inequality-in-permutation-group , shows a formula for calculating the cayley distance.

Some terms to search for: cayley distance, permutation cycles, identity permutation, product or composition of two permutations.

Once you get to the point where you have the cycles figured out, then it's a matter of subtracting the number of cycles from the length of the permutation and that gives you the minimum number of swaps.

I know this is a harder way to go than the approach you were taking but I think it will put you in a better spot to solve these types of problems.

In general, if you want to get better at these types of problems you should study the field of combinatorics as much as you can.

Zhonghua Liang
Zhonghua Liang
22,459 Points

//This can solve the problem about jacnator count/2

def mini_swaps(list_one, list_two):

    wrong_count = 0


    for item1 in list_one:
        for item2 in list_two:


            if item1 == item2:

                wrong_count += 1


                return list_one.length-wrong_count-1;

Hi Zhonghua,

I think you meant to leave this as a comment to jacinator's answer.

What level is the return supposed to be at?

As it stands right now, you're going to return as soon as you find 2 equal items.