假设我有一个形式的表达式。我知道我可以像这样简化表达式:。但是,sympy.simplify
和sympy.factor
都返回原始表达式。
要解决此问题,我一直在较低级别上对该表达式进行操作:
factor_map = defaultdict(set)
additive_terms = expr.as_coeff_add()[-1]
for term1, term2 in combinations(additive_terms, 2):
common_terms = (
set(term1.as_coeff_mul()[-1])
& set(term2.as_coeff_mul()[-1])
)
if common_terms:
common_factor = sympy.Mul(*common_terms)
factor_map[common_factor] |= {term1, term2}
factor_map
现在看起来像这样:
{
a: {a⋅x, -a⋅y},
b: {b⋅x, -b⋅y},
c: {-c⋅x, c⋅y},
x: {a⋅x, b⋅x, -c⋅x},
y: {-a⋅y, -b⋅y, c⋅y}
}
我按照术语表示的操作次数对其进行排序:
factor_list = sorted(
factor_map.items(),
key = lambda i: (i[0].count_ops() + 1) * len(i[1])
)[::-1]
然后我重新构建表达式:
used = set()
new_expr = 0
for item in factor_list:
factor = item[0]
appearances = item[-1]
terms = 0
for instance in appearances:
if instance not in used:
terms += instance.as_coefficient(factor)
used.add(instance)
new_expr += factor * terms
for term in set(additive_terms) - used:
new_expr += term
这得到new_expr = d + x*(a + b - c) + y*(-a - b + c)
。不是很好,但是更好。
我还可以通过将加法项的每种组合相互除法,检查结果是否为数字并使用该信息将输出进一步减少到new_expr = d + (x - y)*(a + b - c)
来进行改进。
我还尝试将sympy.factor
应用于加法项的每种可能组合,但显然,对于任何相当大的表达式,它都会迅速爆炸。
编辑:以下是在一组附加项的所有分区(从this answer借用的分区函数)上使用sympy.factor
的实现:
def partition(collection):
if len(collection) == 1:
yield [ collection ]
return
first = collection[0]
for smaller in partition(collection[1:]):
# insert `first` in each of the subpartition's subsets
for n, subset in enumerate(smaller):
yield smaller[:n] + [[ first ] + subset] + smaller[n+1:]
# put `first` in its own subset
yield [ [ first ] ] + smaller
def partial_factor(expr):
args = list(expr.as_coeff_add()[-1])
# Groupings is just a cache to avoid duplicating factor operations
groupings = {}
unique = set()
for p in partition(args):
new_expr = 0
for group in p:
add_group = sympy.Add(*group)
new_expr += groupings.setdefault(add_group, sympy.factor(add_group))
unique.add(new_expr)
return sorted(unique, key=sympy.count_ops)[0]
对于像a*x + b*y + c*z + d + e*x + f*y + h*z
这样的表达式,在我的计算机上运行需要7.8秒,而另一种方法则需要378微秒,并且得出相同的结果。似乎应该有一种方法比第一种方法更严格,而无需花费2万倍的时间来解决它。
我觉得得到我想要的东西并不难。有更简单的方法吗?
答案 0 :(得分:2)
This similar question的答案涉及func
的{{1}}参数。尽管您必须明确提到collect()
:
d
这在特定情况下有帮助,但是不存在更通用和自动的解决方案。
除了现有的from sympy import *
a, b, c, d, x, y = symbols('a, b, c, d, x, y')
expr = a * x + b * x - c * x - a * y - b * y + c * y + d
collect(expr, d, func=factor)
=> d + (x - y)*(a + b - c)
参数之外,我也找不到任何文档。
答案 1 :(得分:1)
很难建议一种在大多数时间都有效的“部分分解”策略。考虑到您的示例(这是一个由多个变量组成的多项式),在设计时可以尝试一下。
给出一个表达式:尝试将其分解。如果不成功,请查看其包含的每个符号的系数。方法Expr.coeff(Symbol)
做到了。系数最小的符号(通过包含的符号数来衡量)被认为是分解的障碍,因此已从表达式中删除。重复。
此逻辑在下面进行了编码,partial_factor(a*x + b*x - c*x - a*y - b*y + c*y + d)
确实返回了d + (x - y)*(a + b - c)
。
def partial_factor(expr):
to_factor = expr
non_factorable = 0
while to_factor != 0:
if factor(to_factor) != to_factor:
return factor(to_factor) + non_factorable
coeffs = {v: to_factor.coeff(v) for v in to_factor.free_symbols}
min_size = min([len(coeffs[v].free_symbols) for v in coeffs])
for v in coeffs:
if len(coeffs[v].free_symbols) == min_size:
non_factorable += v*coeffs[v]
to_factor -= v*coeffs[v]
break
return expr