You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 21:05:07