python函数正在更改全局变量
我正在尝试用python实现一个遗传算法。 我最初有六个人。我测量了它们的适合度和下一代的概率,并挑选了三对交配,交配概率为0.7。为了进行交叉,我将配对和交配概率传递给一个函数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 =
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中有两种不同的求值策略,一种用于可变对象,另一种用于不可变对象,只是做了一次编辑。我想现在更准确了。如果我仍然有问题,请随时改进。