从论文公式到Python实现:用单元测试校验滑动平均算法

论文中的公式和代码经常各自正确,却没有实现同一个方法。本篇用滑动平均作为教学案例,完整展示"变量定义---公式---Python实现---正常测试---失败测试---论文解释"的核验流程。

示例不是笔匠AI接口,也不代表产品实测。对于"技术章节不知道怎样安排公式、代码和测试"的卡点,笔匠AI论文写作可以按理工科学科与学历配置任务,并将技术内容放进可编辑大纲;得到章节结构后,再用本文流程核验真实实现,最后进入初稿。

1. 环境与问题定义

运行环境:

  • Python 3.10及以上;
  • 仅使用标准库;
  • 不依赖NumPy;
  • 测试命令为 python -m unittest test_moving_average.py -v。

目标:输入长度为 n 的数列 x 和窗口 k,输出每个连续窗口的算术平均值。

当 k=3 时,第一个输出使用 x_1,x_2,x_3,第二个输出使用 x_2,x_3,x_4。输出长度应为 n-k+1。

2. 先把公式约束写完整

滑动平均可表达为:

复制代码
m_t = (1 / k) × Σ x_i
求和范围:i = t-k+1, ..., t
有效下标:t = k, ..., n

仅有公式还不够,必须补充:

  1. x_i 为数值;
  2. k 为正整数;
  3. k 不大于输入长度 n;
  4. 输入不能为空;
  5. 输出顺序与窗口移动顺序一致。

这些约束决定代码的校验分支,也是单元测试的来源。

3. Python实现

新建 moving_average.py:

复制代码
from numbers import Real


def moving_average(values: list[Real], window: int) -> list[float]:
    if not values:
        raise ValueError("values cannot be empty")
    if not isinstance(window, int) or isinstance(window, bool):
        raise TypeError("window must be an integer")
    if window <= 0:
        raise ValueError("window must be greater than 0")
    if window > len(values):
        raise ValueError("window cannot exceed input length")
    if any(not isinstance(value, Real) for value in values):
        raise TypeError("all values must be real numbers")

    return [
        sum(values[start : start + window]) / window
        for start in range(len(values) - window + 1)
    ]

切片 values[start:start+window] 对应公式中的连续求和区间,除以 window 对应 1/k。循环上界保证输出数量为 n-k+1。

4. 正常、边界与失败测试

新建 test_moving_average.py:

复制代码
import unittest

from moving_average import moving_average


class MovingAverageTests(unittest.TestCase):
    def test_normal_windows(self) -> None:
        self.assertEqual(
            moving_average([1, 2, 3, 4], 3),
            [2.0, 3.0],
        )

    def test_window_of_one(self) -> None:
        self.assertEqual(
            moving_average([1, 2], 1),
            [1.0, 2.0],
        )

    def test_window_equals_input_length(self) -> None:
        self.assertEqual(
            moving_average([2, 4, 6], 3),
            [4.0],
        )

    def test_empty_input_fails(self) -> None:
        with self.assertRaises(ValueError):
            moving_average([], 1)

    def test_zero_window_fails(self) -> None:
        with self.assertRaises(ValueError):
            moving_average([1, 2], 0)

    def test_large_window_fails(self) -> None:
        with self.assertRaises(ValueError):
            moving_average([1, 2], 3)


if __name__ == "__main__":
    unittest.main()

运行测试后,预期结果是6个测试通过。这里的"预期"需要在自己的环境中实际执行后才能写成真实结果;本文不把未运行的展示当实测。

5. 用人工计算验证正常样例

输入 [1,2,3,4],窗口 k=3:

复制代码
第一个窗口:(1+2+3)/3 = 2
第二个窗口:(2+3+4)/3 = 3
预期输出:[2.0, 3.0]

单元测试的预期值来自公式和人工计算,而不是复制函数输出。否则代码和测试可能带着同一个错误一起通过。

6. 怎样写进论文

方法章节先说明研究问题、变量、公式和约束;实现章节解释关键映射,不必粘贴全部代码;实验章节记录真实数据、环境、参数和测试结果;附录可保留完整代码或仓库说明。

如果使用笔匠AI组织技术章节,可以从论文题目或开题报告生成可编辑大纲,再把公式定义、实现位置、测试用例和预期输出分别放入方法、实现与实验章节。产品能够连接公式、代码、图表和初稿,但真实代码是否运行、数据是否有效仍需作者验证。

7. 这个示例不能证明什么

它只能证明给定实现符合本文定义的滑动平均行为,不能证明滑动平均适合某项研究,也不能证明结果具有预测、因果或实际应用价值。

如果真实研究涉及缺失值、不规则时间间隔、加权窗口、边界填充或大规模数据性能,就需要重新定义公式、增加实现分支,并补充对应测试。

结论

论文代码验证不能止于"程序能运行"。先定义公式语义和边界,再实现代码,并用独立计算得到的正常样例、边界样例和失败样例进行测试,才能说明公式和程序至少完成了同一项任务。方法变化后,还要同步更新代码、测试、图表和论文结论。

相关推荐
Discipline102911 分钟前
流水线运行是否正随时间变得更加集中?
算法
半日闲dusknook12 分钟前
我给 AI 写了个「音频编译器」:把「听起来还行」变成 PASS/FAIL
python
在世修行29 分钟前
干货:信号槽
python·信号槽
Wx-bishekaifayuan32 分钟前
springboot生活易购超市管理系统54086-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
lzhdim32 分钟前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#
weixin_3077791339 分钟前
C++代码实现MATLAB中的pade函数功能
开发语言·c++·算法·matlab
数字融合40 分钟前
透明化数字孪生智慧养老平台运维服务项目
人工智能·python·virtualenv
smj2302_7968265241 分钟前
解决leetcode第4064题至多一次取反能被k整除的最长子数组II
python·算法·leetcode
AIGC小尼1 小时前
ACE-Step-1.5 本地 AI 音乐模型实战|text2music 歌词转人声,部署脚本、参数调优与排坑全记录
人工智能·算法·comfyui·ai漫剧
YOLO数据集集合1 小时前
树木检测数据集 | 树木检测 树种分类 航拍林业 森林监测9131期
人工智能·算法·机器学习·分类·数据挖掘·林业·树种分类