#python #python-3.x #performance #sympy #polynomials
#python #python-3.x #Производительность #sympy #многочлены
Вопрос:
Я работаю с очень большими многочленами в sympy, и мне нужно иметь их в развернутом виде, чтобы найти определенные термины и коэффициенты. Однако разложение этих многочленов занимает много времени. Есть ли быстрый способ развернуть многочлены или получить определенные члены и коэффициенты другим способом?
Я могу найти термины в расширенном многочлене нормально, но время его расширения является ограничивающим фактором.
Многочлены очень большие, примером может быть:
(x y z a b c) ** 24
Я пробовал как sympy.expand() , так и Add.as_poly() . И обнаружили, что Add.as_poly() работает быстрее, но все еще очень медленно.
my_poly = (x y z a b c) ** 24
# expand using Add.as_poly()
my_poly.as_poly()
# this takes multiple minutes to execute
Я хотел бы иметь возможность выполнять поиск по терминам в расширенном многочлене, чтобы найти те, которые содержат другие термины:
(псевдокод) — это x ** 3 * y z a ** 2, содержащийся в 500 * x ** 5 * y ** 2 * z * a ** 4 * b * c ** 2
, и если он содержится, я хочу получить коэффициент этого члена.
Я хочу либо ускорить расширение, либо использовать другой метод, чтобы найти нужные термины за меньшее время.
Комментарии:
1. В разложении есть
(24 5)! / (24! 5!) = 118755
термины
Ответ №1:
Коэффициенты многочлена могут быть определены без фактического расширения выражения. (a b c x y z)**5
например, будет иметь члены, показатели которых в сумме равны 5. Приведенный вами пример должен быть термином, когда коэффициент равен 15 (но коэффициент был бы 113513400).
def mcoeff(*args):
"""return multinomial coefficient of a terms in the expansion of
(x_1 x_2 x_3 ... x_n)**m from the given exponents in the
term of interest
EXAMPLES
========
>>> from sympy import expand
>>> from sympy.abc import x, y, z
>>> eq = (x y z)**4
>>> eq.expand().coeff(x**2*y**2)
6
>>> mcoeff(2,2)
6
>>>
"""
from sympy import binomial
from sympy.utilities.misc import as_int
assert all(as_int(i)>=0 for i in args)
if len(args) == 2:
return binomial(sum(args), sorted(args)[0])
if len(set(args)) == 1:
return factorial(sum(args))/factorial(args[0])**len(args)
def runsum(a):
t = 0
for i in a:
t = i
yield t
return Mul(*[
binomial(j, k) for j, k in zip(runsum(args), args)])
Если вы хотите найти коэффициент, включающий символ чего-то подобного F = x**3*yza**2
…ну, это сложнее, но не невозможно. Если бы вы искали эти факторы в разложении (a b c x y z)**8
(и я выбрал 8, чтобы было более одного члена, имеющего этот фактор; был бы только один такой член, если бы показатель был равен 7, поскольку 3 1 1 2 = сумма показателей степени членов = 7) тогда этот коэффициентпоявится с любыми другими факторами x,y,z,a,b,c
, которые дадут общий показатель 8. В этом случае это легко, так как нам нужно добавить только 1, поэтому потенциальные члены, имеющие фактор F
, равны
x*F, y*F, z*F, a*F, b*F, c*F
. Будет проще рассмотреть кортежи показателей, для x,y,z,a,b,c
которых in F
равны (3,1,1,2,0,0):
x*F -> (4,1,1,2,0,0)
y*F -> (3,2,1,2,0,0)
z*F -> (3,1,2,2,0,0)
a*F -> (3,1,1,3,0,0)
b*F -> (3,1,1,2,1,0)
c*F -> (3,1,1,2,0,1)
каждый из них будет иметь числовой коэффициент и символический остаток (множитель умножения F
), поэтому коэффициент F
в разложении будет суммой этих:
>>> from sympy.abc import x, y, z, a, b, c, e
>>> coeff = []; t = [3,1,1,2,0,0]
for i,f in enumerate((x, y, z, a, b, c)):
... t[i] = 1
... coeff.append(f*mcoeff(*t))
... t[i] -= 1
>>> Add(*coeff)
1120*a 3360*b 3360*c 840*x 1680*y 1680*z
>>> F = x**3*y*z*a**2
>>> ((x y z a b c)**8).expand().subs(F, e).coeff(e) # wait for it...
1120*a 3360*b 3360*c 840*x 1680*y 1680*z
Все становится сложнее, когда вы хотите найти термины с меньшими коэффициентами, поскольку тогда вам нужно распределить дефицит суммы экспоненты (в данном случае только 1, не считая 8) по всем символам, но идея та же.
( mcoeff
было взято отсюда с некоторыми изменениями)