Python - 一个变量在不该相等时等于另一个变量
这是我的示例代码。它是一个用于高斯-赛德尔方法的迭代过程(矩阵求解器)。简单来说,当误差足够小的时候,它会跳出循环。
i=1
while (i>0):
x_past = x_present
j=0
while(j<3):
value=0
k=0
while(k<3):
if(k!=j):
if(i==1):
if(k>j):
value=value+0
else:
value=value+x_present[k]*eqn[j][k]
else:
value=value+x_present[k]*eqn[j][k]
else:
value=value+eqn[j][k]
k=k+1
x_present[j:j+1]=[value]
j=j+1
print "X_PAST"
print x_past
print "X_PRESENT"
print x_present
if(error(x_past, x_present)<10**-2):
break;
i=i+1
我把代码简化了一下,这样更容易管理。如果你不明白它在做什么,其实对解决这个问题来说并不是特别重要。
现在说说我遇到的问题。每次运行
x_present[j:j+1]=[value]
时,x_past 都会被设置为 x_present。我不知道为什么会这样,因为我只在循环的开头把 x_past 设置为 x_present。如果我去掉
x_past=x_present
这一句,x_past 就不会再等于 x_present。这让我觉得可能是这两句代码的某种组合导致了这个问题。
这真是个大问题,因为如果 x_past 等于 x_present,那么每次的误差就会等于 0,循环在第一次迭代后就会结束。代码确实能工作,比如如果我让代码运行 8 次迭代,然后再跳出循环,它会给我正确的答案。
我已经试着解决这个问题 4 个小时了,完全搞不懂。我学 Python 的时间不长,所以在语法方面的排错能力也不是很好。任何帮助都非常感谢!!
6 个回答
正如其他人提到的,解决办法是把 x_past = x_present
改成 x_past = x_present[:]
。一般来说,你可以使用一个叫 copy
的模块来复制Python中的对象。
>>> import copy
>>> a = range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> b = a
>>> a += 10, 11
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> c = copy.copy(a) # shallow copy
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> del a[3:]
>>> a
[0, 1, 2]
>>> b
[0, 1, 2]
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
你的代码至少可以说是不够Python风格的。
可以用下面这样的代码来替代:
import copy
# assert(len(x_present) >= len(eqn))
first = True
while True:
x_past = copy.copy(x_present) # copy
for j, eqj in enumerate(eqn):
x_present[j] = sum(x_present[k] * eqj[k]
for k in range(j if first else len(eqj))
if k != j)
x_present[j] += eqj[j]
print "X_PAST\n%s\nX_PRESENT\n%s" % (x_past, x_present)
if allequal(x_past, x_present, tolerance=10**-2):
break
first = False
这里有一个 allequal()
的定义(使用绝对误差。根据你的情况,这可能是个好主意,也可能不是(你也可以使用相对误差)):
def allequal(x, y, tolerance):
return (len(x) == len(y) and
all(-tolerance < (xx - yy) < tolerance
for xx, yy in zip(x, y)))
x_past和x_present是什么?我对Python了解不多,但从.NET或Java的角度来看,如果它们是某种数据结构的引用(比如一个映射或其他),那么一开始把它们指向同一个对象,就意味着通过一个变量所做的任何更改,另一个变量也能看到。这听起来像是你需要复制这个数据结构,而不仅仅是做一个引用赋值。你正在使用的数据结构有没有什么“克隆”功能可以用?
不过如我所说,我对Python了解不多,所以这可能完全是错的……
是的,我觉得这里的回答能说明你的问题。让我稍微解释一下。
你在引用一个列表,所以当这个列表发生变化时,所有引用这个列表的地方都会看到这个变化。为了演示这一点:
>>> x_present = [4,5,6]
>>>
>>> x_past = x_present
>>>
>>> x_past
[4, 5, 6]
>>>
>>> x_present.append(7)
>>>
>>> x_past
[4, 5, 6, 7]
>>>
如果你想要一个列表的副本,你需要这样做,listcopy = mylist[:]。(或者可以用 import copy;listcopy = copy.copy(mylist)
)
>>> x_past = x_present[:]
>>> x_past
[4, 5, 6, 7]
>>>
>>> x_present.append(8)
>>>
>>> x_past
[4, 5, 6, 7]