教程建议 35 分钟学习等级 5/8

PYTHON LESSON 069

超级大数运算

掌握用数组逐位模拟竖式加法与乘法的高精度思想,能计算任意位数的加减乘。

00 · 学习目标

这一课要解决什么?

先想一想100 位的数字相加,计算机里的 int 装不下怎么办?
完成任务天文数字计算器
学习顺序定义 → 语法 → 最小实例 → 独立练习

学完后,你应该能够

  • 字符串倒序存成数字数组
  • 逐位相加,满十进一
  • 乘法错位相加,最后统一进位

01 · 核心概念

高精度运算:数组模拟竖式加法与乘法

高精度运算:数组模拟竖式加法与乘法:Python 的 int 能自动长大,可很多语言(如 C++)的整数最多只有 20 位左右。竞赛大纲要求掌握“用数组模拟竖式”的通用思想——换任何语言都不怕。

考级对应GESP Python 5 级 · 高精度运算
学习边界

浮点数比较受二进制表示误差影响,需要按问题选择容差。

02 · 语法与规则

先记住这 3 条,再开始写程序

01字符串倒序存成数字数组

先准确读出这条写法的结构与作用。

02逐位相加,满十进一

换一组最小数据,手工推演一次结果。

03乘法错位相加,最后统一进位

再用边界值或反例确认它的适用条件。

03 · 完整实例

代码、运行结果和解释放在一起看

big-number-lab.pyPYTHON 3.12
# 超级大数运算:数组模拟竖式(高精度思想)
def big_add(a, b):
    """两个非负整数(字符串)相加,返回和的字符串"""
    x = [int(c) for c in a[::-1]]   # 倒过来存:个位在 x[0]
    y = [int(c) for c in b[::-1]]
    n = max(len(x), len(y))
    x = x + [0] * (n - len(x))
    y = y + [0] * (n - len(y))
    carry = 0
    res = []
    for i in range(n):
        s = x[i] + y[i] + carry
        res.append(s % 10)
        carry = s // 10
    if carry > 0:
        res.append(carry)
    return "".join(str(d) for d in res[::-1])

def big_mul(a, b):
    """两个非负整数(字符串)相乘,返回积的字符串"""
    x = [int(c) for c in a[::-1]]
    y = [int(c) for c in b[::-1]]
    res = [0] * (len(x) + len(y))
    for i in range(len(x)):
        for j in range(len(y)):
            res[i + j] = res[i + j] + x[i] * y[j]   # 错位相加
    for i in range(len(res) - 1):                   # 统一处理进位
        res[i + 1] = res[i + 1] + res[i] // 10
        res[i] = res[i] % 10
    while len(res) > 1 and res[-1] == 0:            # 去掉前导零
        res.pop()
    return "".join(str(d) for d in res[::-1])

a = "12345678901234567890"
b = "98765432109876543210"
print("a + b =", big_add(a, b))
print("a * b =", big_mul(a, b))

print("——————")

fact = "1"
for i in range(1, 31):
    fact = big_mul(fact, str(i))
print("30! =", fact)
运行结果OUTPUT
a + b = 111111111011111111100
a * b = 1219326311370217952237463801111263526900
——————
30! = 265252859812191058636308480000000

Python 的整数天生支持大数,但 C++ 等语言装不下——数组模拟竖式是通用武功。

在新标签运行和修改这个实例已装入本课代码 · 可自定义输入 · 可提交判题

04 · 逐步理解

每一步只解决一个问题

  1. 01

    为什么要学高精度

    Python 的 int 能自动长大,可很多语言(如 C++)的整数最多只有 20 位左右。竞赛大纲要求掌握“用数组模拟竖式”的通用思想——换任何语言都不怕。

  2. 02

    倒序存储的妙处

    把字符串倒过来:个位存在下标 0、十位在下标 1。这样进位永远“往后走”,数字变长也只要在数组末尾添一位,比正着存好写一百倍。

    PYTHON
    x = [int(c) for c in a[::-1]]
  3. 03

    竖式加法逐位模拟

    和你列竖式一模一样:同一位的两个数字加进位,得数写个位、十位进上去。s % 10 是留下的,s // 10 是进位的——取模和整除又一次联手。

    PYTHON
    s = x[i] + y[i] + carry
    res.append(s % 10)
    carry = s // 10
  4. 04

    位数不齐与最后的进位

    两个加数位数不同时,短的用 0 补齐;循环结束后若 carry 还有值,要再补一位(比如 999 + 1 = 1000)。边界想周全,高精度才靠谱。

    PYTHON
    if carry > 0:
        res.append(carry)
  5. 05

    乘法 = 错位相加

    竖式乘法里,x 的第 i 位乘 y 的第 j 位,结果落在第 i + j 位上。先不管进位,全部错位累加;最后从低位到高位统一进位一次,干净利落。

    PYTHON
    res[i + j] = res[i + j] + x[i] * y[j]
  6. 06

    去前导零与特殊边界

    0 × 任何数都该得 0。结果数组可能前面(倒序下是末尾)挂着一串 0,输出前要把它们 pop 掉,但至少要留一位——想想为什么。

    PYTHON
    while len(res) > 1 and res[-1] == 0:
        res.pop()
  7. 07

    用高精度算 30!

    30! 是一个 33 位的天文数字。从 "1" 开始连乘 1 到 30,全程用我们自己写的 big_mul——自己的工具算出自己的大数,成就感拉满。

    PYTHON
    fact = big_mul(fact, str(i))

05 · 练习与检验

自己写出来,才算真正学会

本课实作步骤
  1. 1运行加法,看两个 20 位大数相加
  2. 2手工验算结果的前几位
  3. 3运行乘法,看积有多少位
  4. 4用高精度阶乘算出 30!
打开本课编程实验室编辑、运行、测试、判题都在一个页面完成

06 · 完整知识

继续理解定义、规则和适用边界

第一次学习先完成上面的六个步骤;需要查定义、核对规则、分析误区或理解“为什么”时,再展开对应知识章。

Python 基础运算符、表达式与优先级完整区分算术、比较、逻辑、成员、身份和位运算,并用优先级表消除歧义。

正式定义

表达式求值得到一个值。运算符规定如何组合操作数;当一个表达式含多个运算符时,优先级和结合方向决定求值顺序,括号可以明确改变顺序。

必须掌握

  • / 总是得到浮点结果;// 是向负无穷方向取整的整除;% 与 // 满足 a == (a // b) * b + a % b。
  • 比较可以链式书写,如 0 <= x < 10;and/or 会短路并返回最后求值的操作数,不一定返回 bool。
  • == 比较值是否相等,is 比较是否为同一个对象;判断 None 应写 is None。
  • in/not in 做成员测试;对 dict 测试的是键。
  • 位运算作用于整数的二进制位;负整数按无限长二进制补码语义理解。
  • 复杂表达式即使能靠优先级正确运行,也应使用括号表达意图。

HIGH → LOW

运算符优先级完整速查

同一行通常优先级相同。复杂表达式仍建议加括号表达意图,不把可读性交给记忆。

Python 运算符优先级,从高到低
级别运算符/结构含义结合
最高(表达式)、[列表]、{字典/集合}分组与容器显示
x[index]、x[start:stop]、x(...)、x.attr下标、切片、调用、属性左到右
await x等待表达式
**乘方右到左
+x、-x、~x正负号、按位取反右到左
*、@、/、//、%乘、矩阵乘、除、整除、取模左到右
+、-加、减左到右
<<、>>移位左到右
&按位与左到右
^按位异或左到右
|按位或左到右
in、not in、is、is not、<、<=、>、>=、!=、==成员、身份与比较(可链式)链式
not x逻辑非右到左
and逻辑与(短路)左到右
or逻辑或(短路)左到右
x if condition else y条件表达式右到左
lambda匿名函数
最低:=赋值表达式

常见误区

  • 把 // 当成简单截断
  • 用 is 比较数字或字符串的值
  • 忘记 and 的优先级高于 or
  • 连续位移、比较和逻辑运算却不加括号

适用边界

  • 浮点数比较受二进制表示误差影响,需要按问题选择容差。
  • 运算符可由自定义类重载,因此相同符号对不同类型可能有不同语义。
打开本章完整示例与独立阅读页 →
编码与文本字符串、转义、切片与格式化从不可变字符序列到检索、拆分、拼接、格式化和常用判断方法。

正式定义

str 是不可变的 Unicode 字符序列。下标访问单个字符,切片生成新字符串;任何看似“修改字符串”的方法都会返回新对象。

必须掌握

  • 下标从 0 开始,负下标从末尾开始;切片 s[start:stop:step] 不包含 stop,step 不能为 0。
  • 转义序列用于在字面量中表示换行、制表、引号、反斜杠或码点;原始字符串仍有末尾反斜杠限制。
  • find 找不到返回 -1,index 找不到抛 ValueError;count 统计不重叠出现次数。
  • split 把字符串拆成列表,join 用一个字符串连接可迭代对象中的字符串,strip 只删除两端字符。
  • f-string 的格式说明可控制宽度、对齐、精度、进制和百分比;格式化不改变原值。
  • isalpha/isdigit 等按 Unicode 定义,不只识别英文字母和 ASCII 数字。

常见误区

  • 尝试 s[0] = 'A' 原地修改字符串
  • 把 strip('ab') 误解为删除完整子串 'ab'
  • find 返回 -1 后直接拿去当有效下标
  • 把字节长度与字符长度混为一谈

适用边界

  • 正则表达式不在低等级字符串必修范围,但复杂模式匹配时应使用 re,而不是堆叠大量 split/find。
  • 面向用户的字符计数还可能涉及组合字符和字素簇,len 统计的是 Unicode 码点序列长度。
打开本章完整示例与独立阅读页 →
Python 基础条件、循环与程序流程准确理解 if、for、while、range、break、continue 和循环嵌套,而不是背代码模板。

正式定义

控制流决定下一条要执行的语句。分支依据布尔条件选择路径;循环在满足规则时重复执行代码块。Python 用冒号和缩进界定代码块。

必须掌握

  • if/elif/else 从上到下判断,只执行第一个为真的分支;else 不写条件。
  • for 依次取得可迭代对象中的元素;range(start, stop, step) 包含 start、不包含 stop,step 不能为 0。
  • while 在每轮开始前检查条件;循环体必须让状态向终止条件推进。
  • break 结束最内层循环,continue 跳过本轮剩余语句,循环的 else 仅在没有被 break 终止时执行。
  • 嵌套循环的总执行次数通常需要把各层次数相乘;内层 break 不会结束外层循环。
  • 边界测试至少覆盖空范围、单个元素、第一项命中、最后一项命中和始终不命中。

常见误区

  • range 的右端点多算或少算一次
  • while 忘记更新状态造成死循环
  • 把两个互斥条件写成两个独立 if
  • 误以为 break 会跳出所有嵌套循环

适用边界

  • 流程图是算法的表示方法,不是 Python 语法。
  • 递归也能表达重复,但有调用开销和递归深度限制,不能无条件代替循环。
打开本章完整示例与独立阅读页 →
算法方法算法、复杂度与解题验证把题意转成输入、状态、规则与输出,用正确性和复杂度共同评价解法。

正式定义

算法是解决一类问题的有限、明确步骤。正确性说明算法对所有满足前置条件的输入都得到规定结果;时间和空间复杂度描述输入规模增长时资源使用的增长量级。

必须掌握

  • 先明确输入规模 n、数据范围、目标和允许误差,再选择数据结构与算法。
  • O(1)、O(log n)、O(n)、O(n log n)、O(n²)、O(2ⁿ) 表示增长量级,不是精确运行秒数。
  • 顺序代码复杂度取较大项,嵌套循环常相乘,二分每步把范围缩小一半。
  • 正确性可用循环不变量、数学归纳、交换论证、反证或状态定义来说明。
  • 样例只验证少量输入;必须自己设计边界、极端、重复、有序/逆序和无解数据。
  • 优化前先得到正确基线并测量瓶颈,不为小数据盲目增加复杂实现。

常见误区

  • 只看样例通过就宣称正确
  • 不看数据范围使用 O(n²)
  • 二分区间开闭混用
  • 把 O(n) 当成永远比 O(log n) 慢固定倍数

适用边界

  • 复杂度隐藏常数与硬件差异,但仍是比较规模增长的核心工具。
  • 考场策略、课程完成度和算法能力是不同证据,任何单项都不能保证考级通过。
打开本章完整示例与独立阅读页 →

完成检查

确认自己会解释、会编写、会验证