Python 实例|支持计算最近速度和平均速度的计时器工具

文章介绍了如何通过实现高效的计时器类SpeedTracker,以O(1)的时间复杂度计算平均速度,针对高频调用场景,讨论了两种记录和查询性能方案,并选择了空间复杂度为O(s)的方案。详细分析了时间复杂度和空间复杂度,以及类的方法和使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现原则

尽可能提高在高频调用前提下的记录和查询性能。

实现逻辑

在查询性能方面,实现在 O ( 1 ) O(1) O(1) 的时间复杂度内计算任意时间段内的平均速度,我们记录每一条记录数量的前缀和,从而使计算任意时间段内的完成数量时,只需要将两个元素相减即可。

在记录性能方面,我们有两种方案:

  1. 构造包含每一秒都前缀和序列,然后二分查找目标元素,查询性能为 O ( s ) O(s) O(s),其中 s s s 为运行总时长(单位为秒),插入性能为 O ( 1 ) O(1) O(1)
  2. 构造仅包含有记录的秒的前缀和序列,查询性能为 O ( 1 ) O(1) O(1),插入性能为 O ( t ) O(t) O(t),其中 t t t 为距离上一次记录的时间间隔(单位为秒)

因为我们主要优化高频调用前提下的性能,所以采用方案 2。

综上所述,总体时间、空间复杂度如下:

  • 时间复杂度:初始化、启动定时器、停止定时器、查询速度均为 O ( 1 ) O(1) O(1),插入记录的时间复杂度为 O ( t ) O(t) O(t),其中 t t t 为距离上一次记录的时间间隔(单位为秒);
  • 空间复杂度: O ( s ) O(s) O(s),其中 s s s 为运行总时长(单位为秒)。

具体实现如下:

from typing import Optional, List


class TimerError(Exception):
    """计时器异常"""


class SpeedTracker:
    """支持计算平均速度和最近速度的计时器

    空间复杂度:O(s),其中 s 为计时器总运行时间
    """

    def __init__(self):
        """实例化计时器

        时间复杂度:O(1)
        """
        self._t_start: Optional[int] = None  # 开始时间的时间戳
        self._t_end: Optional[int] = None  # 结束时间的时间戳
        self._record: List[int] = [0, 0]  # 每一秒记录数量的前缀和(开头包含额外的一个 0)

    def start(self) -> None:
        """启动计时器(根据调用此方法的时间计算平均速度,调用此方法前,不允许调用其他方法)

        时间复杂度:O(1)
        """
        self._t_start = time.time()

    def end(self) -> None:
        """停止计时器(调用此方法后,不允许调用 record 方法)

        时间复杂度:O(1)
        """
        if self._t_start is None:
            raise TimerError("未启动定时器,无法停止计时器")
        self._t_end = time.time()

    def record(self) -> None:
        """记录当前时间(通常每完成一次任务调用一次)

        时间复杂度:O(t),其中 t 为距离上一次记录的时间间隔(单位为秒)
        """
        if self._t_start is None:
            raise TimerError("未启动定时器,无法记录时间")
        if self._t_end is not None:
            raise TimerError("已停止定时器,无法记录时间")
        idx = int(time.time() - self._t_start) + 1  # 计算下标
        while len(self._record) <= idx:
            self._record.append(self._record[-1])  # 补齐前缀和的长度
        self._record[-1] += 1  # 累加前缀和

    def get_run_time(self) -> float:
        """获取总运行时间(单位:秒)

        时间复杂度:O(1)

        Returns
        -------
        run_time : float
            总运行时间
        """
        if self._t_start is None:
            return 0.0  # 如果计时器没有启动,那么总运行时间为 0 秒
        if self._t_end is None:
            t_end = time.time()  # 如果计时器暂未停止,那么总运行时间为从开启计时器到当前时间之间的时间
        else:
            t_end = self._t_end  # 如果计时器已经停止,那么总运行时间为从开启计时器到停止计时器之间的时间
        return t_end - self._t_start

    def get_average_speed(self) -> float:
        """获取总平均速度(单位:次/秒)
        
        时间复杂度:O(1)

        Returns
        -------
        speed : float
            平均速度
        """
        if self._t_start is None:
            return float("NaN")  # 如果计时器暂未启动,那么总平均速度为 NaN
        if self._t_end is not None:
            run_time = self.get_run_time()
            if run_time > 0:
                return self._record[-1] / self.get_run_time()  # 如果计时器已经停止,那么总平均速度为 总任务数 / 总运行时间
            else:
                return float("NaN")  # 如果计时器已经停止,且总运行时间为 0,那么总平均速度为 NaN

        # 如果计时器暂未停止,那么总平均速度就是所有已结束秒的平均速度
        return self._get_speed(idx_start=0, idx_end=max(len(self._record) - 2, 0))

    def get_recent_speed(self, n: int) -> float:
        """获取最近 n 秒(已结束秒)的平均速度(单位:次/秒)

        如果启动时间不足 1 秒,则平均速度为 NaN
        
        时间复杂度:O(1)

        Parameters
        ----------
        n : int
            计算平均速度的秒数范围

        Returns
        -------
        speed : float
            平均速度
        """
        if self._t_start is None:
            return float("NaN")  # 如果计时器暂未启动,那么平均速度为 NaN

        # 计算最近 n 个已结束秒的平均速度
        return self._get_speed(idx_start=max(len(self._record) - 2 - n, 0), idx_end=max(len(self._record) - 2, 0))

    def _get_speed(self, idx_start: int, idx_end: int) -> float:
        """获取从 idx_start 到 idx_end 之间的平均速度(单位:次/秒)

        如果开始下标等于截止下标,则平均速度为 NaN
        
        时间复杂度:O(1)

        Parameters
        ----------
        idx_start : int
            开始下标(包含)
        idx_end : int
            截止下标(不包含)

        Returns
        -------
        speed : float
            平均速度
        """
        if idx_start < idx_end:  # 已经至少完整地运行过 1 秒
            return (self._record[idx_end] - self._record[idx_start]) / (idx_end - idx_start)
        else:  # 暂未完整地运行过 1 秒
            return float("NaN")
使用样例
if __name__ == "__main__":
    import time

    speed_tracker = SpeedTracker()
    speed_tracker.start()
    for _ in range(100):
        time.sleep(0.1)  # 模拟任务执行时间
        speed_tracker.record()  # 记录任务时间
        print("recent_speed:", speed_tracker.get_recent_speed(1))  # 查看当前平均速度

    print("run_time:", speed_tracker.get_run_time())  # 查看总运行时间
    print("average_speed:", speed_tracker.get_average_speed())  # 查看总平均速度

    speed_tracker.end()

    print("run_time:", speed_tracker.get_run_time())  # 查看总运行时间
    print("average_speed:", speed_tracker.get_average_speed())  # 查看总平均速度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值