Python 3.7 Jupyter中Numpy数组迭代更新的循环终止异常排查
问题排查与解决方案
看起来你的谣言传播模拟提前终止的核心问题出在onBoard函数的坐标判断逻辑上,另外我们也可以优化循环逻辑让代码更健壮。让我一步步拆解:
1. 核心问题:onBoard函数的坐标范围判断错误
你当前的onBoard函数写的是:
def onBoard(i, j, array): if (i >= 0 and i < array.shape[0]-1 and j >= 0 and j < array.shape[1]-1): return True return False
这里的错误在于,你把棋盘的最后一行(i = array.shape[0]-1)和最后一列(j = array.shape[1]-1)的合法坐标判定为"不在棋盘上"。比如当棋盘高度是100时,合法的行索引是0到99,但你的条件i < array.shape[0]-1等价于i <99,直接把索引99的行排除了。
这会导致什么后果?
- 最后一行的
2无法感染它上方的1(因为检查上方元素的下方邻域时,会错误认为最后一行的坐标越界) - 最后一列的
2无法感染它左侧的1 - 边缘的
1永远无法被这些边缘的2触发转换,当传播到边缘后就没有新的元素可以转换了,循环自然提前终止。
修复后的onBoard函数
把判断条件改成正确的坐标范围:
def onBoard(i, j, array): # 合法坐标是0 <= i < 行数,0 <= j < 列数 return 0 <= i < array.shape[0] and 0 <= j < array.shape[1]
2. 循环逻辑的小优化(可选但更健壮)
你的Step5代码中,只处理了值为1的元素,虽然0和2的元素因为New_City是City_Copy的副本所以会保持原值,但显式处理所有情况会让代码更清晰,避免潜在的bug(比如后续修改副本逻辑时出错):
City_Copy = StartingBoard.copy() iterations = 0 max_iterations = 100 for _ in range(max_iterations): # 每次循环都创建新的副本,避免覆盖 New_City = City_Copy.copy() for i in range(City_Copy.shape[0]): for j in range(City_Copy.shape[1]): current_val = City_Copy[i,j] if current_val == 1: # 检查邻域是否有2 if 2 in getNeighborValues(i,j, City_Copy): New_City[i,j] = 2 # 否则保持1,无需额外赋值 # 0和2保持原值,无需修改 # 检查是否有变化 if np.array_equal(New_City, City_Copy): break City_Copy = New_City iterations += 1 print("This rumor has been around for", iterations, "days.")
这里我把New_City的创建移到了循环内部,每次迭代都基于当前的City_Copy创建新副本,逻辑更清晰,也避免了之前可能存在的副本复用问题。
3. 辅助函数的简化(可选)
getNeighborValues可以用列表推导式简化,让代码更简洁高效:
def getNeighborValues(i, j, board): neighborhood_indices = [(i-1,j),(i,j-1),(i+1,j),(i,j+1)] # 直接过滤合法坐标并提取值 return [board[x,y] for x,y in neighborhood_indices if onBoard(x,y,board)]
验证效果
修复onBoard函数后,边缘的2可以正常感染相邻的1,传播会尽可能覆盖所有可达的1,直到没有新的元素可以转换,此时循环才会终止。如果你的棋盘设置中1的分布足够分散,传播可能会接近100次迭代才停止。
内容的提问来源于stack exchange,提问作者Cornel Westside
相关产品推荐
相关产品推荐

