1

I just started working on a simple genetic algorithm with a bunch of dots which each have a series of instructions to move. The instructions are a list of integers which I compare to a dictionary: movekey = {1:(1,1),2:(1,0),3:(1,-1),4:(0,-1),5:(-1,-1),6:(-1,0),7:(-1,1),8:(0,1)} (where each of the tuples are the change in x and y values). Each individual is a list of lists, one has its current position and the other is its movement list ex: [[0, 0], [4, 4, 8, 1, 7]]. A list of a bunch of these individuals is then created to form a population (pop in code). The function to create an initial population is this:

def createinitpop(size):
    pop = []
    for i in range(size): pop.append([origin,[random.randint(1,8) for i in range(5)]])
    #the variable origin is preset to be [0,0]
    return pop

then, to move all of the individuals one step, i use this function:

def movedots(pop):
    for i in pop:
        print('i:',i)
        i[0][0]+= movekey[i[1][step]][0]
        # the 'step' referenced here begins at zero and counts up, i've only run it on step 0
        i[0][1] += movekey[i[1][step]][1]
        print('i:', i)
    return pop

The problem is that when I run this second function, each iteration changes all the individuals in the population. (However Ii have found that when I hardcode an example population into the movedots function it works just fine). Here is the result of running both of those functions with two individuals (both returned populations are printed)

[[[0, 0], [6, 5, 2, 1, 8]], [[0, 0], [4, 7, 2, 1, 1]]]
i: [[0, 0], [6, 5, 2, 1, 8]]
i: [[-1, 0], [6, 5, 2, 1, 8]]
i: [[-1, 0], [4, 7, 2, 1, 1]]
i: [[-1, -1], [4, 7, 2, 1, 1]]
[[[-1, -1], [6, 5, 2, 1, 8]], [[-1, -1], [4, 7, 2, 1, 1]]]

Ideally the location of the individuals in that final line should be different because their first movement is different, but as you can see the third i is the same as the second i, suggesting that it is using the same value.

I don't know if I just don't understand how lists work or if I'm missing something stupid.

AGN Gazer
  • 8,025
  • 2
  • 27
  • 45
  • 4
    You're reusing the same `origin` list over and over. – user2357112 Jun 13 '18 at 17:06
  • the createinitpop function only runs once at the beginning to create the population in which all individuals start at the origin, the problem i have is more with the second function. If there was something else that you think is a problem with reusing origin could you please expand. –  Jun 13 '18 at 17:10
  • No, your problem is in `createinitpop`. The second function is just where the problem becomes visible. – user2357112 Jun 13 '18 at 17:11
  • Im still not understanding, shouldn't the second function be adding different numbers to the [0,0] in each individual, therefore they will end up with different locations. Is there a specific change that you think I could add to fix this? edit: just figured it out, thank you –  Jun 13 '18 at 17:16
  • `origin` is a "reference" or "pointer" (if you wish) to a data structure holding two integers: 0 and another 0. In your loop `append([origin, ...])` simply copies the reference to _the same object_ instead of creating a new one. Therefore, it should not surprise you that by changing this object in one place of the code this change _becomes visible_ in other places (since `origin` is still pointing to the same object). – AGN Gazer Jun 13 '18 at 17:20

1 Answers1

0

You pointed two references to the same place:

for i in range(size): pop.append([origin,[random.randint(1,8) for i in range(5)]])

You now have three references to the same list. When you change it from any of the three channels, you change the data for each of them. Try an implicit copy operation:

for i in range(size): pop.append([origin[:], ...

This will give you a shallow (one-level) copy of origin for each of your nested references.

Prune
  • 76,765
  • 14
  • 60
  • 81