英文:
Optimize, replace recursion or just fix error with mass recursion
问题
以下是您提供的代码的翻译:
我在Python中遇到了递归深度的问题。我想知道如何使它更高效,或替代它,或增加递归深度。
我尝试通过 `sys.setrecursionlimit` 来增加递归深度,但没有结果。它每次都停在 990。
基本上,我正在为我的Tkinter迷你绘图程序制作泛洪填充。在小区域上,它填充得非常好,但在大区域上会出现错误 `RecursionError: maximum recursion depth exceeded in __instancecheck__`。
此外,还有以下代码:
```py
import tkinter as tk
from random import randint, seed
# 请忽略这些全局变量
blocks = 100
block_size = 5
res = (block_size * blocks,) * 2
color = 'a'
color_dict = {}
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.resizable(False, False)
self.c = tk.Canvas(self, width=res[0], height=res[1], highlightthickness=0, bg='white')
self.c.grid(sticky='w')
self.title('主窗口')
self.bind("<Key>", self.key_handler)
# b-motion是按住按钮并移动时的事件
self.bind("<B1-Motion>", self.left_click)
self.bind("<Button-1>", self.left_click)
self.bind("<B3-Motion>", self.right_click)
self.bind("<Button-3>", self.right_click)
self.field = [[0] * blocks for i in range(blocks)]
self.is_flood_fill = False
# 创建块
def create_block(self, x, y, size, fill='white'):
self.c.create_rectangle(x, y, x + size, y + size, fill=fill, outline='')
def key_handler(self, event):
print(event.keycode)
match event.keycode:
case 82: self.is_flood_fill = False if self.is_flood_fill else True
# 左键单击处理程序
def left_click(self, event):
if self.is_flood_fill:
col, row = self.get_field_pos(event.x, event.y)
self.flood_fill(col, row, self.field[row][col], color)
else: self.draw(event.x, event.y, color)
# 右键单击处理程序
def right_click(self, event): self.draw(event.x, event.y, 0)
def draw(self, cursor_x, cursor_y, color):
col, row = self.get_field_pos(cursor_x, cursor_y)
if self.field[row][col] != color:
try: print(self.field[row][col]);self.field[row][col] = color; print(self.field[row][col])
except Exception: pass; print(Exception)
self.create_block(col * block_size, row * block_size, block_size, self.color_case(color))
def get_field_pos(self, x, y): return ((round(x / block_size), round(y / block_size)))
# 从字典中获取颜色,如果不存在则生成,并放入同一字典
def color_case(self, code):
global color
if code == 0: return 'white'
elif not code in color_dict:
# 使用代码生成随机种子
seed(code)
# 生成十六进制颜色
r = lambda: randint(0, 255)
color_dict[code] = '#{0:02X}{1:02X}{2:02X}'.format(r(), r(), r())
return color_dict[code]
# 主要问题在于这里
def flood_fill(self, col, row, old_color, new_color):
# 检查
if col < 0 or col >= blocks or row < 0 or row >= blocks: return
if self.field[row][col] != old_color: return
self.field[row][col] = new_color
self.create_block(col * block_size, row * block_size, block_size, self.color_case(color))
# 尝试填充相邻位置
self.flood_fill(col + 1, row, old_color, new_color)
self.flood_fill(col - 1, row, old_color, new_color)
self.flood_fill(col, row + 1, old_color, new_color)
self.flood_fill(col, row - 1, old_color, new_color)
if __name__ == "__main__":
app = MainWindow()
app.mainloop()
希望这个翻译对您有所帮助。如果您有任何其他问题,请随时提出。
英文:
I have problem with recursion depth in Python. I want to know how to make it efficient or replace it, or make recursion depth larger.
I tried make it larger by sys.setrecursionlimit
, but no result. It's stopping on 990 every time.
Basicly, I making flood fill for my mini-paint program on Tkinter.
On small fields it's fill prefectly fine, but on larger there error RecursionError: maximum recursion depth exceeded in __instancecheck__
Also, there code:
import tkinter as tk
from random import randint, seed
# don't mind me about these global vars
blocks = 100
block_size = 5
res = (block_size*blocks,)*2
color = 'a'
color_dict = {}
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.resizable(False,False)
self.c = tk.Canvas(self, width=res[0], height=res[1], highlightthickness=0, bg='white')
self.c.grid(sticky='w')
self.title('Main Window')
self.bind("<Key>", self.key_handler)
# b-motion is when holding button and moving
self.bind("<B1-Motion>", self.left_click)
self.bind("<Button-1>", self.left_click)
self.bind("<B3-Motion>", self.right_click)
self.bind("<Button-3>", self.right_click)
self.field = [[0]*blocks for i in range(blocks)]
self.is_flood_fill = False
# creating block
def create_block(self, x, y, size, fill='white'):
self.c.create_rectangle(x,y,x+size,y+size,fill=fill,outline='')
def key_handler(self, event):
print(event.keycode)
match event.keycode:
case 82: self.is_flood_fill=False if self.is_flood_fill else True
# left click handler
def left_click(self, event):
if self.is_flood_fill:
col,row=self.get_field_pos(event.x, event.y)
self.flood_fill(col, row, self.field[row][col], color)
else: self.draw(event.x, event.y, color)
# right click handler
def right_click(self, event): self.draw(event.x, event.y, 0)
def draw(self, cursor_x, cursor_y, color):
col,row=self.get_field_pos(cursor_x, cursor_y)
if self.field[row][col] != color:
try: print(self.field[row][col]);self.field[row][col] = color; print(self.field[row][col])
except Exception: pass; print(Exception)
self.create_block(col*block_size, row*block_size, block_size, self.color_case(color))
def get_field_pos(self, x, y): return((round(x/block_size), round(y/block_size)))
# grabing color from dict, or generating if it's not there and puting in same dict
def color_case(self, code):
global color
if code == 0: return 'white'
elif not code in color_dict:
# seed of random by code
seed(code)
# generating hex color
r = lambda: randint(0,255)
color_dict[code] = '#%02X%02X%02X' % (r(),r(),r())
return color_dict[code]
# main problem is there
def flood_fill(self,col,row,old_color,new_color):
# checks
if col < 0 or col >= blocks or row < 0 or row >= blocks: return
if self.field[row][col] != old_color: return
self.field[row][col] = new_color
self.create_block(col*block_size, row*block_size, block_size, self.color_case(color))
# attempt to fill the neighboring positions
self.flood_fill(col+1, row, old_color, new_color)
self.flood_fill(col-1, row, old_color, new_color)
self.flood_fill(col, row+1, old_color, new_color)
self.flood_fill(col, row-1, old_color, new_color)
if __name__ == "__main__":
app = MainWindow()
app.mainloop()
答案1
得分: 0
你可以将递归转换为使用显式栈(列表)。其次,当旧颜色和新颜色相同时,应跳过整个过程,否则你将陷入循环并最终耗尽内存。
def flood_fill(self, col, row, old_color, new_color):
if old_color == new_color:
return
stack = [(col, row)]
while stack:
col, row = stack.pop()
if 0 <= col < blocks and 0 <= row < blocks and self.field[row][col] == old_color:
self.field[row][col] = new_color
self.create_block(col*block_size, row*block_size, block_size, self.color_case(color))
stack.extend([(col+1, row), (col-1, row), (col, row+1), (col, row-1)])
英文:
You could convert the use of recursion into using an explicit stack (list). Secondly, you should skip the whole thing when the old and new color are equal, as otherwise you'll keep running in circles and eventually run out of memory.
def flood_fill(self, col, row, old_color, new_color):
if old_color == new_color:
return
stack = [(col, row)]
while stack:
col, row = stack.pop()
if 0 <= col < blocks and 0 <= row < blocks and self.field[row][col] == old_color:
self.field[row][col] = new_color
self.create_block(col*block_size, row*block_size, block_size, self.color_case(color))
stack.extend([(col+1, row), (col-1, row), (col, row+1), (col, row-1)])
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论