Как ускорить разложение очень больших многочленов в sympy?

#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 было взято отсюда с некоторыми изменениями)