在 Python 中动态填充字典
populating dynamically dictionaries in Python
我有这个自制的模拟退火算法,用于计算具有 N 个点的系统的最小能量。两点之间的能量计算为 1/r,其中 r 是两点之间的距离。
我正在 运行 循环我的代码,一旦我离开循环,我就会更新字典中的一个新条目。
问题是,在循环的最后一个 运行 中,计算机似乎将最后一个条目保存为所有条目。请参阅下面的代码:
def energy_find(number,Ts=T_s,Tf=T_f):
val_dic = {}
c = 0
radius,theta = generate_random(number)
energy,matrix = total_energy(number,radius,theta)
val_dic[0] = []
val_dic[0] = {"radius":radius,"theta":theta,"energy":energy,"energies":matrix}
m = 5 # number of repetitions per given temperature
for i in range(m):
c +=1
old_theta = val_dic[c-1]["theta"]
old_radius = val_dic[c-1]["radius"]
energy= val_dic[c-1]["energy"]
old_energies = val_dic[c-1]["energies"]
new_theta,new_radius,which = moveCharge(number,old_theta,old_radius)
new_energy,enMatrix= recalculate(number,new_radius,new_theta,old_energies,which)
delta_energy = new_energy-energy
newset = [new_radius,new_theta,new_energy,enMatrix]
val_dic = acceptChange(newset,delta_energy,val_dic,c,Ts)
print(val_dic[c]['radius'])
df = pd.DataFrame(val_dic).T
energy = df.energy.min()
index = pd.to_numeric(df.energy).idxmin()
theta = df.loc[index,"theta"]
radius = df.loc[index,"radius"]
return df,energy,radius,theta,delta,val_dic
如您所见,上面有一个 pring 语句,它正确地打印出点状电荷的径向位置是如何变化的。
但是,一旦函数为 运行:
df,energy,radius,theta,delta,dic= energy_find(5)
print("stop")
print(dic[1]["radius"])
输出为:
[3, 4.95, 6, 9, 2]
[5.05, 4.95, 6, 9, 2]
[3.0, 4.95, 6, 9, 2]
[3.0, 4.95, 6, 9, 4.05]
[5.05, 4.95, 6, 9, 4.05]
stop
[5.05, 4.95, 6, 9, 4.05]
为:
print(dic[2]["radius"])
输出:
[5.05, 4.95, 6, 9, 4.05]
完全正确,就像 dic[1],并且等于函数 运行ning 时在 for 循环中打印的最后一个值。
我是不是用错了字典?
如果需要它们:这些是我在代码中使用的函数:
def uniform(n):
global _first_random
if n==0: random.seed(1234); _first_random=0
if _first_random==1: random.seed(None); _first_random=0
if n==1: return random.random()
else: return floor(n*random.random()+1)
_first_random=1
r = 10
def generate_random(number):
"""
Function for creating random position for n charges
Parameters
---------
number -- number of charges in the system. Takes integer values.
Outputs
--------
charges_radius,
charges_theta
"""
charges_radius = []
charges_theta = []
for i in range(number):
radius = randrange(r)
theta= np.random.random() * 2.0 * np.pi
while theta in charges_theta and radius in charges_radius or radius==0 and radius in charges_radius:
radius = uniform(10)
theta= np.random.random() * 2.0 * np.pi
charges_radius.append(radius)
charges_theta.append(theta)
return charges_radius,charges_theta
def cosRule(rad1,rad2,ang1,ang2):
q = 1.0
net= ang2-ang1
net_distance = sqrt(rad1**2+rad2**2-2*rad1*rad2*cos(net))
try:
energy = q*q*(1.0/net_distance)
except ZeroDivisionError:
energy = 1e12
return energy
def partial_energy(no,radii,thetas,enMatrix):
"""
no- ordinary number of the charge that you moved and calculate the change in energy as a result of displacement"
"""
radiusA = radii[no]
thetaA = thetas[no]
for key,theta in enumerate(thetas):
if key!=no:
radiusB = radii[key]
thetaB = theta
energy = cosRule(radiusB,radiusA,thetaB,thetaA)
enMatrix[key][no]= 0.5*energy
enMatrix[no][key] = enMatrix[key][no]
return enMatrix
def total_energy(n,radius,thetas):
enMatrix = np.zeros([n,n])
energy = None
for i in range(n):
enMatrixNew= partial_energy(i,radius,thetas,enMatrix=enMatrix)
energy = sum(enMatrixNew).sum()
return energy,enMatrix
def recalculate(n,radius,thetas,enMatrix,which):
enMatrixNew = np.zeros([n,n])
enMatrixNew=partial_energy(which,radius,thetas,enMatrix)
energy = sum(enMatrixNew).sum()
return energy,enMatrixNew
def tempScaling(a):
T_s = -a/log(0.7)
T_f = -a/log(0.01)
return T_s, T_f
T_s, T_f = tempScaling(0.2)
def moveCharge(number,thetas,radius):
r = 10
step = 2.05
which = randrange(number)
thetas[which] =2*uniform(1)*np.pi
n = randrange(1,3)
delta_radius = (-1)**n *step
radius[which] +=delta_radius
if radius[which]>r or radius[which]<0.0:
radius[which] +=(-1)**(n+1) * step
return thetas,radius,which
def acceptChange(newset,delta,val_dic,c,Ts):
if delta >0.0:
accept_the_change = uniform(1) # generating a random number to decide if we accept the change
if accept_the_change < exp(-delta/Ts):
val_dic[c]=[]
val_dic[c]={"radius":newset[0],"theta":newset[1],"energy":newset[2],"energies":newset[3]}
else:
val_dic[c]=[]
val_dic[c]=val_dic[c-1]
else:
val_dic[c]=[]
val_dic[c]={"radius":newset[0],"theta":newset[1],"energy":newset[2],"energies":newset[3]}
return val_dic
我认为问题是当你写这样的东西时:
old_theta = val_dic[c-1]["theta"]
你不是在复制你之前制作的 theta old_theta 指的是同一个对象,如果你进一步改变 old_theta 它会改变 val_dic[c-1] ["theta"]还有。
您可以使用复制模块来避免这个问题,并将行更改如下:
import copy
old_theta = copy.copy(val_dic[c-1]["theta"])
old_radius = copy.copy(val_dic[c-1]["radius"])
energy= copy.copy(val_dic[c-1]["energy"])
old_energies = copy.copy(val_dic[c-1]["energies"])
那么old_radius就是前一个半径的值,不是同一个物体
我有这个自制的模拟退火算法,用于计算具有 N 个点的系统的最小能量。两点之间的能量计算为 1/r,其中 r 是两点之间的距离。 我正在 运行 循环我的代码,一旦我离开循环,我就会更新字典中的一个新条目。 问题是,在循环的最后一个 运行 中,计算机似乎将最后一个条目保存为所有条目。请参阅下面的代码:
def energy_find(number,Ts=T_s,Tf=T_f):
val_dic = {}
c = 0
radius,theta = generate_random(number)
energy,matrix = total_energy(number,radius,theta)
val_dic[0] = []
val_dic[0] = {"radius":radius,"theta":theta,"energy":energy,"energies":matrix}
m = 5 # number of repetitions per given temperature
for i in range(m):
c +=1
old_theta = val_dic[c-1]["theta"]
old_radius = val_dic[c-1]["radius"]
energy= val_dic[c-1]["energy"]
old_energies = val_dic[c-1]["energies"]
new_theta,new_radius,which = moveCharge(number,old_theta,old_radius)
new_energy,enMatrix= recalculate(number,new_radius,new_theta,old_energies,which)
delta_energy = new_energy-energy
newset = [new_radius,new_theta,new_energy,enMatrix]
val_dic = acceptChange(newset,delta_energy,val_dic,c,Ts)
print(val_dic[c]['radius'])
df = pd.DataFrame(val_dic).T
energy = df.energy.min()
index = pd.to_numeric(df.energy).idxmin()
theta = df.loc[index,"theta"]
radius = df.loc[index,"radius"]
return df,energy,radius,theta,delta,val_dic
如您所见,上面有一个 pring 语句,它正确地打印出点状电荷的径向位置是如何变化的。 但是,一旦函数为 运行:
df,energy,radius,theta,delta,dic= energy_find(5)
print("stop")
print(dic[1]["radius"])
输出为:
[3, 4.95, 6, 9, 2]
[5.05, 4.95, 6, 9, 2]
[3.0, 4.95, 6, 9, 2]
[3.0, 4.95, 6, 9, 4.05]
[5.05, 4.95, 6, 9, 4.05]
stop
[5.05, 4.95, 6, 9, 4.05]
为:
print(dic[2]["radius"])
输出:
[5.05, 4.95, 6, 9, 4.05]
完全正确,就像 dic[1],并且等于函数 运行ning 时在 for 循环中打印的最后一个值。 我是不是用错了字典?
如果需要它们:这些是我在代码中使用的函数:
def uniform(n):
global _first_random
if n==0: random.seed(1234); _first_random=0
if _first_random==1: random.seed(None); _first_random=0
if n==1: return random.random()
else: return floor(n*random.random()+1)
_first_random=1
r = 10
def generate_random(number):
"""
Function for creating random position for n charges
Parameters
---------
number -- number of charges in the system. Takes integer values.
Outputs
--------
charges_radius,
charges_theta
"""
charges_radius = []
charges_theta = []
for i in range(number):
radius = randrange(r)
theta= np.random.random() * 2.0 * np.pi
while theta in charges_theta and radius in charges_radius or radius==0 and radius in charges_radius:
radius = uniform(10)
theta= np.random.random() * 2.0 * np.pi
charges_radius.append(radius)
charges_theta.append(theta)
return charges_radius,charges_theta
def cosRule(rad1,rad2,ang1,ang2):
q = 1.0
net= ang2-ang1
net_distance = sqrt(rad1**2+rad2**2-2*rad1*rad2*cos(net))
try:
energy = q*q*(1.0/net_distance)
except ZeroDivisionError:
energy = 1e12
return energy
def partial_energy(no,radii,thetas,enMatrix):
"""
no- ordinary number of the charge that you moved and calculate the change in energy as a result of displacement"
"""
radiusA = radii[no]
thetaA = thetas[no]
for key,theta in enumerate(thetas):
if key!=no:
radiusB = radii[key]
thetaB = theta
energy = cosRule(radiusB,radiusA,thetaB,thetaA)
enMatrix[key][no]= 0.5*energy
enMatrix[no][key] = enMatrix[key][no]
return enMatrix
def total_energy(n,radius,thetas):
enMatrix = np.zeros([n,n])
energy = None
for i in range(n):
enMatrixNew= partial_energy(i,radius,thetas,enMatrix=enMatrix)
energy = sum(enMatrixNew).sum()
return energy,enMatrix
def recalculate(n,radius,thetas,enMatrix,which):
enMatrixNew = np.zeros([n,n])
enMatrixNew=partial_energy(which,radius,thetas,enMatrix)
energy = sum(enMatrixNew).sum()
return energy,enMatrixNew
def tempScaling(a):
T_s = -a/log(0.7)
T_f = -a/log(0.01)
return T_s, T_f
T_s, T_f = tempScaling(0.2)
def moveCharge(number,thetas,radius):
r = 10
step = 2.05
which = randrange(number)
thetas[which] =2*uniform(1)*np.pi
n = randrange(1,3)
delta_radius = (-1)**n *step
radius[which] +=delta_radius
if radius[which]>r or radius[which]<0.0:
radius[which] +=(-1)**(n+1) * step
return thetas,radius,which
def acceptChange(newset,delta,val_dic,c,Ts):
if delta >0.0:
accept_the_change = uniform(1) # generating a random number to decide if we accept the change
if accept_the_change < exp(-delta/Ts):
val_dic[c]=[]
val_dic[c]={"radius":newset[0],"theta":newset[1],"energy":newset[2],"energies":newset[3]}
else:
val_dic[c]=[]
val_dic[c]=val_dic[c-1]
else:
val_dic[c]=[]
val_dic[c]={"radius":newset[0],"theta":newset[1],"energy":newset[2],"energies":newset[3]}
return val_dic
我认为问题是当你写这样的东西时:
old_theta = val_dic[c-1]["theta"]
你不是在复制你之前制作的 theta old_theta 指的是同一个对象,如果你进一步改变 old_theta 它会改变 val_dic[c-1] ["theta"]还有。
您可以使用复制模块来避免这个问题,并将行更改如下:
import copy
old_theta = copy.copy(val_dic[c-1]["theta"])
old_radius = copy.copy(val_dic[c-1]["radius"])
energy= copy.copy(val_dic[c-1]["energy"])
old_energies = copy.copy(val_dic[c-1]["energies"])
那么old_radius就是前一个半径的值,不是同一个物体