IndexError: list assignment index out of range , Python

Viewed 1246

I am trying to achieve functionality. It's working should be this way:

  1. It takes two lists.
  2. Mark some indexes, preferably center few.
  3. Both parents switches marked indexes.
  4. Other indexes go sequentially to their parent element.
  5. If the same element is already present in that parent, it maps and check where other parent same element was and goes there.
import random
def pm(indA, indB):
    size = min(len(indA), len(indB))
    c1, c2 = [0] * size, [0] * size

    # Initialize the position of each indices in the individuals
    for i in range(1,size):
        c1[indA[i]] = i
        c2[indB[i]] = i

    crosspoint1 = random.randint(0, size)
    crosspoint2 = random.randint(0, size - 1)
    if crosspoint2 >= crosspoint1:
        crosspoint2 += 1
    else:  # Swap the two cx points
        crosspoint1, crosspointt2 = crosspoint2, crosspoint1


    for i in range(crosspoint1, crosspoint2):
        # Keep track of the selected values
        temp1 = indA[i]
        temp2 = indB[i]
        # Swap the matched value
        indA[i], indA[c1[temp2]] = temp2, temp1
        indB[i], indB[c2[temp1]] = temp1, temp2
        # Position bookkeeping
        c1[temp1], c1[temp2] = c1[temp2], c1[temp1]
        c2[temp1], c2[temp2] = c2[temp2], c2[temp1]
        return indA, indB

a,b = pm([3, 4, 8, 2, 7, 1, 6, 5],[4, 2, 5, 1, 6, 8, 3, 7])

Error:

in pm
    c1[indA[i]] = i
IndexError: list assignment index out of range
2 Answers

Not sure whether there are other errors in your code (I didn't run it), but here's the explanation for this one. In Python (as most of other languages), lists (sequences to be more precise) index is 0 based:

>>> l = [1, 2, 3, 4, 5, 6]
>>>
>>> for e in l:
...     print(e, l.index(e))
...
1 0
2 1
3 2
4 3
5 4
6 5
>>>
>>> l[0]
1
>>> l[5]
6
>>> l[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

To summarize your problem:

  1. Your indA and indB lists have each 6 elements ([1..6]), and their indexes: [0..5]
  2. Your c1 and c2 lists also have 6 elements (indexes also [0..5])
  3. But, your using values from #1. as indexes in lists from #2., and the value 6 is a problem, as there's no such index

To fix your problem, you should use valid index values. Either:

  • Have the proper values in indA and indB (this is the one I'd chose):

    a, b = pmxCrossover([0, 3, 1, 2, 5, 4], [4, 0, 2, 3, 5, 1])
    
  • Subtract 1, wherever you encounter values from indA or indB used as indexes:

    c1[indA[i] - 1] = i
    

As a general advice: whenever you encounter errors, add print statements before the faulty line (printing (partial) stuff from it), and that might give you clues that could lead to solving the problem yourself.

@EDIT0

Posting (a slightly modified version of) the original code, with the index conversion:

  • Before the algorithm: subtract 1 (from each element) to have valid indexes
  • After the algorithm: add 1 to come back to 1 based indexes

code00.py:

#!/usr/bin/env python3

import sys
import random


def pmx_crossover(ind_a, ind_b):
    size = min(len(ind_a), len(ind_b))
    c1, c2 = [0] * size, [0] * size

    # Initialize the position of each indices in the individuals
    for i in range(1, size):
        c1[ind_a[i]] = i
        c2[ind_b[i]] = i
    # Choose crossover points
    crosspoint1 = random.randint(0, size)
    crosspoint2 = random.randint(0, size - 1)
    if crosspoint2 >= crosspoint1:
        crosspoint2 += 1
    else:  # Swap the two cx points
        crosspoint1, crosspointt2 = crosspoint2, crosspoint1

    # Apply crossover between cx points
    for i in range(crosspoint1, crosspoint2):
        # Keep track of the selected values
        temp1 = ind_a[i]
        temp2 = ind_b[i]
        # Swap the matched value
        ind_a[i], ind_a[c1[temp2]] = temp2, temp1
        ind_b[i], ind_b[c2[temp1]] = temp1, temp2
        # Position bookkeeping
        c1[temp1], c1[temp2] = c1[temp2], c1[temp1]
        c2[temp1], c2[temp2] = c2[temp2], c2[temp1]
    return ind_a, ind_b


def main():
    #initial_a, initial_b = [1, 2, 3, 4, 5, 6, 7, 8], [3, 7, 5, 1, 6, 8, 2, 4]
    initial_a, initial_b = [1, 4, 2, 3, 6, 5], [5, 1, 3, 4, 6, 2]
    index_offset = 1
    temp_a = [i - index_offset for i in initial_a]
    temp_b = [i - index_offset for i in initial_b]
    a, b = pmx_crossover(temp_a, temp_b)
    final_a = [i + index_offset for i in a]
    final_b = [i + index_offset for i in b]
    print("Initial: {0:}, {1:}".format(initial_a, initial_b))
    print("Final:   {0:}, {1:}".format(final_a, final_b))


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("\nDone.")

Output (one of the possibilities (due to random.randint)):

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058424002]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32

Initial: [1, 4, 2, 3, 6, 5], [5, 1, 3, 4, 6, 2]
Final:   [1, 3, 2, 4, 6, 5], [5, 1, 4, 3, 6, 2]

Done.

c1 is out of range because in your for at the fourth index the value of indA[4] is 6. And the range of c1 index it's 0-5 (it's lengh is 6).

With c1[indA[i]] = i you try to do c1[6] = 4

Related