使用pyplot实现quicksort柱状图动画时长列表出现空白伪影
解决Matplotlib快速排序动画500+元素时的空白伪影问题
核心问题分析
- 柱子宽度过小:500个元素时,默认柱子宽度会变得极窄,Matplotlib渲染时容易出现空白间隙或伪影。
- 帧保存数量不足:
save_frames=1000远小于快速排序500元素时的实际迭代次数,导致大量关键帧丢失,动画出现跳变和空白。 - 引用传递导致帧数据异常:生成器直接返回原列表引用,后续排序修改会覆盖所有历史帧的数据,导致动画显示异常。
- 单元素更新效率低:逐个更新柱子高度的方式在元素量较大时,重绘开销极高,容易导致渲染丢帧。
- GIF保存参数不合理:PillowWriter默认设置无法处理高分辨率、多帧的动画,导致保存后伪影残留。
具体修复方案
1. 调整柱子宽度,消除渲染间隙
创建柱状图时显式设置width=1.0,让每个柱子占满x轴单位空间,避免间隙:
bar_rects = ax.bar(range(len(_list)), _list, align="edge", width=1.0, antialiased=False)
2. 动态匹配保存帧数量
快速排序500元素的迭代次数远超1000,可通过遍历生成器统计实际帧数,确保所有帧都被保存:
from itertools import tee # 复制生成器(生成器只能遍历一次) gen1, gen2 = tee(generator) save_frames = sum(1 for _ in gen1) generator = gen2
3. 返回列表副本,避免帧数据覆盖
生成器中yield时返回列表副本,确保每帧数据都是当前排序状态的快照:
# quicksortP函数中 yield numberList.copy() # partition函数中 yield numberlist.copy()
4. 优化渲染与保存参数
- 调整画布大小,适配大量元素:
fig, ax = plt.subplots(figsize=(10, 6)) - 关闭blit提升渲染稳定性:在
FuncAnimation中添加blit=False - 优化GIF保存参数,提升画质:
writer = PillowWriter(fps=30, dpi=100, bitrate=10000, palette="rgb") anim.save(title + ".gif", writer=writer, dpi=100)
5. 优化文本显示,避免遮挡
给迭代次数文本添加白色背景,确保在柱子上方清晰可见:
text = ax.text(0.01, 0.95, "", transform=ax.transAxes, bbox=dict(facecolor='white', alpha=0.8))
完整修改后的代码
import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation, PillowWriter import numpy as np import matplotlib as mpl from itertools import tee # 全局渲染优化 mpl.rcParams['animation.embed_limit'] = 100.0 mpl.rcParams['figure.dpi'] = 100 def animate(_list, generator, title, save, save_frames=None): n = len(_list) fig, ax = plt.subplots(figsize=(10, 6)) ax.set_title(title) # 设置柱子宽度为1,消除间隙 bar_rects = ax.bar(range(len(_list)), _list, align="edge", width=1.0, antialiased=False) ax.set_xlim(0, n) ax.set_ylim(0, int(1.1*n)) # 添加文本背景,避免遮挡 text = ax.text(0.01, 0.95, "", transform=ax.transAxes, bbox=dict(facecolor='white', alpha=0.8)) iteration = [0] def _animate(array, rects, iteration): # 批量更新柱子高度 for rect, val in zip(rects, array): rect.set_height(val) iteration[0] += 1 text.set_text(f"iterations: {iteration[0]}") fig.canvas.draw() # 强制重绘,确保帧完整 # 统计实际帧数 if save_frames is None: gen1, gen2 = tee(generator) save_frames = sum(1 for _ in gen1) generator = gen2 anim = FuncAnimation(fig, func=_animate, fargs=(bar_rects, iteration), frames=generator, interval=5, repeat=False, save_count=save_frames, blit=False) if save: writer = PillowWriter(fps=30, dpi=100, bitrate=10000, palette="rgb") anim.save(title + ".gif", writer=writer, dpi=100) plt.show() def quicksortP(numberList: list, end: int = None, start: int = 0): yield numberList.copy() if end is None: end = len(numberList) - 1 if start < end: pivotPos = yield from partition(numberList, start=start, end=end) yield from quicksortP(numberList, start=start, end=pivotPos - 1) yield from quicksortP(numberList, start=pivotPos + 1, end=end) def partition(numberlist: list, start: int, end: int): pivot = numberlist[end] biggestpos = start - 1 for i in range(start, end): if numberlist[i] <= pivot: biggestpos += 1 numberlist[i], numberlist[biggestpos] = numberlist[biggestpos], numberlist[i] yield numberlist.copy() biggestpos += 1 numberlist[biggestpos], numberlist[end] = numberlist[end], numberlist[biggestpos] yield numberlist.copy() return biggestpos if __name__=="__main__": nums=list(np.random.randint(0,500,500)) animate(nums.copy(), quicksortP(nums.copy()), "QuickSort Animation", save=True)
内容的提问来源于stack exchange,提问作者RobertS
相关产品推荐
相关产品推荐

