如何在SymPy中对乘积和展开式按项数及因子规则排序?
自定义SymPy表达式项的排序规则
问题场景
你通过以下SymPy代码生成矩阵表达式:
import numpy as np import sympy from sympy import * from sympy import simplify b = symbols('b:100') a = symbols('a:100') A = Matrix([[1, 0],[0, 1]]) for i in [10,9,8]: A = A @ Matrix([[1+b[i]-a[i] , -b[i]],[1, 0]]) expr = expand(simplify(A[0,0])) print(expr)
得到输出:
-a10*a8*a9 + a10*a8*b9 + a10*a8 + a10*a9*b8 + a10*a9 - a10*b8*b9 - a10*b8 - a10 + a8*a9*b10 + a8*a9 - a8*b10*b9 - a8*b9 - a8 - a9*b10*b8 - a9*b10 - a9*b8 - a9 + b10*b8*b9 + b8*b9 + b8 + 1
需要对表达式做两类排序:
- 按项的因子数量降序排列(3因子项→2因子项→1因子项→常数项)
- 每个乘积项内的因子遵循:a类符号在前、b类在后;a/b类内部按索引降序排列(如
a10 > a9 > a8,b10 > b9 > b8)
解决方案
通过自定义排序键,对表达式的项和项内因子分别排序后重新构建表达式:
步骤1:定义单因子排序规则
编写函数处理单个项内的因子排序:
def sort_factors(term): # 拆分项的系数与因子部分 coeff, factors = term.as_coeff_mul() # 定义因子排序键:a类优先,再按索引降序;b类随后,同样按索引降序 def factor_key(f): name = str(f) if name.startswith('a'): return (0, -int(name[1:])) elif name.startswith('b'): return (1, -int(name[1:])) return (2, 0) # 排序后重新组合项 sorted_factors = sorted(factors, key=factor_key) return coeff * Mul(*sorted_factors)
步骤2:定义项的整体排序规则
编写函数按因子数量降序排列所有项:
def sort_terms(expr): terms = expr.args # 定义项的排序键:先按因子数量降序,再按因子排序后的字符串保证稳定性 def term_key(term): factor_count = len(term.as_coeff_mul()[1]) return (-factor_count, str(sort_factors(term))) # 对项排序并应用因子排序 sorted_terms = sorted(terms, key=term_key) return Add(*[sort_factors(t) for t in sorted_terms])
步骤3:应用排序规则
将生成的表达式传入函数,得到符合要求的结果:
sorted_expr = sort_terms(expr) print(sorted_expr)
最终输出
运行后会得到严格符合排序要求的表达式:
-a10*a9*a8 + a10*a8*b9 + a10*a9*b8 + a8*a9*b10 - a10*b8*b9 - a8*b9*b10 - a9*b8*b10 + a10*a9 + a10*a8 + a8*a9 - a10*b8 - a10 - a8*b9 - a8 - a9*b10 - a9*b8 - a9 + b8*b9 + b10*b8*b9 + b8 + 1
关键说明
as_coeff_mul()用于拆分项的系数和符号因子,避免系数干扰排序逻辑- 用
-int(name[1:])将索引降序转化为升序排序逻辑,确保大索引的符号排在前面 - 项排序中加入
str(sort_factors(term))是为了保证相同因子数量的项排序稳定,避免SymPy内部的随机顺序
内容的提问来源于stack exchange,提问作者emily20
相关产品推荐
相关产品推荐

