python函数正在更改全局变量

python函数正在更改全局变量,python,genetic-algorithm,Python,Genetic Algorithm,我正在尝试用python实现一个遗传算法。 我最初有六个人。我测量了它们的适合度和下一代的概率,并挑选了三对交配,交配概率为0.7。为了进行交叉,我将配对和交配概率传递给一个函数 new_population = cross_over(pairsg, mating_prob) 其中pairsg是为交配挑选的配对,交配prob是一个二进制列表(如果1交叉,如果0不交叉)问题在于,交叉函数改变了原始总体,尽管其中从未使用总体变量 def cross_over(prs, mp): new =

我正在尝试用python实现一个遗传算法。 我最初有六个人。我测量了它们的适合度和下一代的概率,并挑选了三对交配,交配概率为0.7。为了进行交叉,我将配对和交配概率传递给一个函数

new_population = cross_over(pairsg, mating_prob)
其中pairsg是为交配挑选的配对,交配prob是一个二进制列表(如果1交叉,如果0不交叉)问题在于,交叉函数改变了原始总体,尽管其中从未使用总体变量

def cross_over(prs, mp):
    new = []
    for pr in prs:
        if mp[prs.index(pr)] == 1:
            index = np.random.choice([1,2,3], p=[1/3, 1/3, 1/3])
            pr[0][:index], pr[1][:index] = pr[1][:index], pr[0][:index]

    for pr in prs:
        new.append(pr[0])
        new.append(pr[1])

    return new
这是完整的代码:

from random import *
import numpy as np

#fitness function
def fit(x):
    return 15*x -x**2

#covert binary list to decimal number
def to_dec(x):
    return int("".join(str(e) for e in x), 2)

#picks pairs from the original population
def gen_pairs(populationl, prob):
    pairsl = []
    test = [0, 1, 2, 3, 4, 5]
    for i in range(3):
        pair = []
        for j in range(2):
            temp = np.random.choice(test, p=prob)
            pair.append(populationl[temp])
        pairsl.append(pair)

    return pairsl

#mating function
def cross_over(prs, mp):
    new = []
    for pr in prs:
        if mp[prs.index(pr)] == 1:
            index = np.random.choice([1,2,3], p=[1/3, 1/3, 1/3])
            pr[0][:index], pr[1][:index] = pr[1][:index], pr[0][:index]

    for pr in prs:
        new.append(pr[0])
        new.append(pr[1])

    return new


population = [[1,0,1,0], [0,1,1,0], [1,0,0,1], [1,1,1,0],[1,1,0,0],[1,0,0,0]]
fittness = [fit(to_dec(y)) for y in population]

s = sum(fittness)
prob = [e/s for e in fittness]
pairsg = gen_pairs(population.copy(), prob)

mating_prob = []
for i in pairsg:
    mating_prob.append(np.random.choice([0,1], p=[0.3,0.7]))

new_population = cross_over(pairsg, mating_prob)

问题是,您正在中进行
复制

gen_pairs(population.copy(), prob)
但您复制的是外部列表,而不是子列表数据(它是一个浅拷贝)。所以

更改原始数据(因为
pair.append(populationl[temp])
也不复制,但使用子列表的相同引用)

你必须遵循子列表的参考来解决这个问题

一旦你理解了这个问题,有很多方法可以避免它。在某个时刻复制子列表

你可以做:

gen_pairs([x.copy() for x in population], prob)
创建“深度复制”(或
copy.deepcopy(填充))

或替换

pair.append(populationl[temp])


问题是,您正在中进行
复制

gen_pairs(population.copy(), prob)
但您复制的是外部列表,而不是子列表数据(它是一个浅拷贝)。所以

更改原始数据(因为
pair.append(populationl[temp])
也不复制,但使用子列表的相同引用)

你必须遵循子列表的参考来解决这个问题

一旦你理解了这个问题,有很多方法可以避免它。在某个时刻复制子列表

你可以做:

gen_pairs([x.copy() for x in population], prob)
创建“深度复制”(或
copy.deepcopy(填充))

或替换

pair.append(populationl[temp])


每当您向python函数传递参数时,基本上只需传递对该对象的引用,并且您对函数内的对象所做的一切都会反映在函数外。即使对象的外部名称不同,也会发生这种情况
pairsg
prs
仍然引用相同的列表。这类似于在
a=[]
b=a
之后,
a
b
指的是完全相同的对象

因此,将可变参数(如列表)传递给函数时要小心。参数传递对不可变参数的作用相同,但您不会遇到此问题,因为不可变参数无法更改

因此,解决方案是不修改prs。这应该起作用:

def cross_over(prs, mp):
    new = []
    for pr in prs:
        if mp[prs.index(pr)] == 1:
            index = np.random.choice([1,2,3], p=[1/3, 1/3, 1/3])
            new.append(pr[1][:index])
            new.append(pr[0][:index])
        else:
            new.append(pr[0])
            new.append(pr[1])

    return new

每当您向python函数传递参数时,基本上只需传递对该对象的引用,并且您对函数内的对象所做的一切都会反映在函数外。即使对象的外部名称不同,也会发生这种情况
pairsg
prs
仍然引用相同的列表。这类似于在
a=[]
b=a
之后,
a
b
指的是完全相同的对象

因此,将可变参数(如列表)传递给函数时要小心。参数传递对不可变参数的作用相同,但您不会遇到此问题,因为不可变参数无法更改

因此,解决方案是不修改prs。这应该起作用:

def cross_over(prs, mp):
    new = []
    for pr in prs:
        if mp[prs.index(pr)] == 1:
            index = np.random.choice([1,2,3], p=[1/3, 1/3, 1/3])
            new.append(pr[1][:index])
            new.append(pr[0][:index])
        else:
            new.append(pr[0])
            new.append(pr[1])

    return new

当您传递
population.copy()
时,将传递一个浅拷贝。因此,它是一个新列表,与复制它的子列表具有相同的引用。当您修改这些子列表时,由于它们是相同的列表,您将看到这些更改反映在原始的
填充
列表中。当您传递
填充.copy()
时,您将传递一个浅层副本。因此,它是一个新列表,与复制它的子列表具有相同的引用。当您修改这些子列表时,由于它们是相同的列表,您将看到这些更改反映在原始的
总体
列表中。这些更改的易变性或缺乏易变性是一种误导。这不会改变python参数传递的语义。Python变量总是像对象的引用一样,函数使用求值策略——这与大多数现代语言中使用的策略相同(尽管很少使用这个技术名称)。我承认这里有点迂腐,但人们通常认为python中有两种不同的求值策略,一种用于可变对象,另一种用于不可变对象,只是做了一次编辑。我想现在更准确了。如果我仍然犯了错误,请随意改进它。易变性或缺乏易变性是一种误导。这不会改变python参数传递的语义。Python变量总是像对象的引用一样,函数使用求值策略——这与大多数现代语言中使用的策略相同(尽管很少使用这个技术名称)。我承认这里有点迂腐,但人们通常认为python中有两种不同的求值策略,一种用于可变对象,另一种用于不可变对象,只是做了一次编辑。我想现在更准确了。如果我仍然有问题,请随时改进。