Encontrar coeficientes en polinomio de manera eficiente

Dada una transitiva GRAMO -colocar METRO . Estoy interesado en encontrar el número de puntos fijos de GRAMO actuando Maceta a ( METRO ) := { norte METRO ; | norte | = a } utilizando la tabla de notas de GRAMO . Dejar tu 1 , , tu s ser representantes de las clases de conjugación de subgrupos de GRAMO . Para cada uno de ellos, un polinomio

pag tu i := yo = 1 s ( j = 0 metro i , yo ( metro i , yo j ) X λ ( i , yo ) j )

se define, donde λ ( i , yo ) , metro i , yo norte 0 . Dejar pag := i = 1 s pag i . El coeficiente de X pag es el número de puntos fijos GRAMO tiene en Maceta pag ( METRO ) . El problema es calcular estos coeficientes de manera eficiente. Mi enfoque fue calcular

pag tu i := yo = 1 s ( j = 0 metro i , yo ( metro i , yo j ) X λ ( i , yo ) j modificación X pag + 1 ) modificación X pag + 1 .

Cada transitiva GRAMO -set corresponde a una fila en la tabla de marcas de GRAMO desde METRO GRAMO / tu k para algunos tu k GRAMO . Resulta que calcular los puntos fijos de S 8 en Maceta 2 ( GRAMO / tu pag ) para pag = 2 148 lleva más de 10 horas (todavía sin terminar), lo cual no es aceptable. Para definir el polinomio utilicé las funciones internas GAP Product, Sum y mod. ¿Tiene alguna sugerencia sobre cómo puedo calcular los coeficientes más rápido?

El cálculo tomó alrededor de 22 horas. El principal problema de GAP es calcular el pag tu i . Lleva mucho tiempo definir este polinomio para GAP.
Para la suma interna, puede usar PolynomialByExtRepo incluso PolynomialByExtRepNC si confía en los argumentos, consulte aquí . Entonces al menos no tienes que usar Sume inner mod. Sin embargo, no estoy seguro de cuánta aceleración obtendrá, ya que espero que sea la multiplicación lo que lleva la mayor parte del tiempo, pero de todos modos esta sería una optimización que vale la pena. Esperamos saber si esto tendrá algún efecto, luego veamos más.
@AlexanderKonovalov ¡Muchas gracias, esto fue realmente útil! Para los ejemplos pequeños, toma aproximadamente un 30% menos de tiempo que antes, los ejemplos grandes todavía se están ejecutando. Tuve que cambiar un poco el código, por ejemplo, reemplazar mod por QuotRemLaurpols y agregar algunos bucles if. Pero tienes razón, la mayor parte del tiempo se usa para calcular el producto. Tienes alguna otra sugerencia?
@AlexanderKonovalov Bueno, ¡resulta que el efecto en grupos más grandes es mucho mejor! El cálculo que antes tomaba 22 horas ahora se completa en 59 minutos. Gracias, estoy muy agradecido. Sin embargo, estaría interesado en cualquier consejo adicional que me puedan dar.
¡Excelente! Para más consejos, para este cálculo de una hora es interesante saber qué es s (el número de polinomios a multiplicar) y cuáles son sus grados? ¿Estos polinomios son densos o dispersos? Si son escasos, ¿cuántos monomios tienen? Finalmente, ¿está interesado en conocer solo un coeficiente para X pag por un fijo pag a la vez, si eso podría ser más rápido, o si desea calcular todo el polinomio de todos modos?
Tenía algunas ideas más, quizás esto podría mejorarse aún más usando la funcionalidad descrita en Aritmética para representaciones externas de polinomios o incluso vectores como coeficientes de polinomios . El objetivo principal es evitar conversiones de ida y vuelta entre polinomios como objetos GAP y los dados por sus representaciones externas.
@AlexanderKonovalov Gracias nuevamente, cambiaré el código hoy usando solo las representaciones externas de polinomios. s es el tamaño de la tabla de marcas ( T ) de GRAMO , entonces s es el número de clases de conjugación de subgrupos de GRAMO . Para cada subgrupo tu i , λ ( i , yo ) := | GRAMO / tu i | / | GRAMO / tu yo | y metro i , yo se obtiene multiplicando el i y yo la fila de T entrada por entrada. Esto te da un vector que luego se multiplica por T 1 . Las entradas de este vector son las metro i , yo .
λ ( i , yo ) Z si y solo si metro i , yo = 0 . Si GRAMO = S norte el polinomio es denso. Todavía no estoy seguro del grado, pero lo pensaré. Por ahora, solo estoy interesado en el coeficiente de algunos X pag en i = 1 s pag tu i dónde pag se sabe antes. No estoy interesado en todo el polinomio.
¡Bien! Solo mantén la versión anterior como respaldo. Me pondré en contacto más tarde hoy, trataré de organizar todos los comentarios en una especie de respuesta adecuada. También existe la idea de calcular solo el coeficiente deseado, omitiendo la multiplicación completa de todos los polinomios.
He hecho algunos experimentos con ProductCoeffs, tal vez eso podría ser más eficiente que usar iteradores, por favor, eche un vistazo.

Respuestas (1)

Gracias, esa es realmente una pregunta interesante!

0 -th enfoque

Al multiplicar polinomios con coeficientes enteros, el valor absoluto de los coeficientes puede aumentar bastante rápido. Es importante verificar que GAP esté compilado con la biblioteca GMP para una aritmética rápida de enteros. Debería ver gmpen la línea 4 de la pantalla de inicio de GAP:

 Libs used:  gmp, readline

La compatibilidad con GMP se introdujo en GAP 4.5 (2012) y ahora es una opción predeterminada, por lo que lo más probable es que así sea.

Paso 1

El primer paso es ensamblar directamente el polinomio de la lista de coeficientes en lugar de calcularlo como la suma de monomios. En el siguiente ejemplo creo una lista aleatoria lde un 1000 enteros entre 10 y 10 , y luego compare estas dos formas de crear un polinomio con coeficientes dados por esta lista:

gap> x:=Indeterminate(Rationals,"x");
gap> l:=List([1..10000],i->Random(Integers));;
gap> f:=Sum([1..10000], i-> l[i]*x^i);;time;
2268
gap> f1:=LaurentPolynomialByCoefficients(FamilyObj(1),l,1);;time;
1
gap> f=f1;
true

timedevuelve el tiempo de CPU del último comando en milisegundos, por lo que verá que LaurentPolynomialByCoefficientslo hace instantáneamente, mientras que el otro enfoque tarda más de dos segundos. Consulte más en "Creación de funciones racionales" del capítulo Polinomios y funciones racionales del manual de referencia de GAP; también hay más funciones de bajo nivel como PolynomialByExtRepy PolynomialByExtRepNC, LaurentPolynomialByExtRepy LaurentPolynomialByExtRepNC, donde NCsignifica "sin verificación" y solo debe usarse si la velocidad es requerida y se sabe que los argumentos están en forma correcta. A menos que esto esté garantizado para los parámetros, LaurentPolynomialByCoefficientsdebe usarse para estar seguro.

Obviamente, ahora ya no es necesario realizar modeste paso, ya que uno podría simplemente truncar la lista de coeficientes (o no calcular la lista completa en absoluto).

Observación: se verificó el paso 1 (consulte los comentarios del autor de la pregunta más arriba) y se redujo en un ejemplo el tiempo de ejecución general de 22 horas a 59 minutos. Los pasos a continuación son solo sugerencias, que requieren más experimentación y ajuste del código.

Paso 2(a)

Quizás esto podría mejorarse aún más usando la funcionalidad descrita en Aritmética para representaciones externas de polinomios o incluso vectores como coeficientes de polinomios . El objetivo principal es evitar conversiones de ida y vuelta entre polinomios como objetos GAP y polinomios dados por sus representaciones externas.

Agregado más adelante: en particular, considere ProductCoeffscuál le permite multiplicar polinomios solo parcialmente: ProductCoeffs( list1[, len1], list2[, len2] )asume que pag 1 (y pag 2 ) son polinomios dados por las primeras len1( len2) entradas de la lista de coeficientes list2( list2). Si len1y len2se omiten, tienen por defecto las longitudes de list1y list2. Luego devuelve la lista de coeficientes del producto de pag 1 y pag 2 .

Si no se restringen las longitudes, el rendimiento es el mismo:

gap> x:=Indeterminate(Rationals,"x");;
gap> deg:=10000;;
gap> l1:=List([1 .. deg],i->Random(Integers));;
gap> f1:=LaurentPolynomialByCoefficients(FamilyObj(1),l1,0);;
gap> l2:=List([1 .. deg],i->Random(Integers));;
gap> f2:=LaurentPolynomialByCoefficients(FamilyObj(1),l2,0);;
gap> f:=f1*f2;;time;
3819
gap> t:=ProductCoeffs(CoefficientsOfUnivariatePolynomial(f1), CoefficientsOfUnivariatePolynomial(f2));;time;
3787
gap> CoefficientsOfUnivariatePolynomial(f)=t;
true

Pero ahora supongamos que queremos calcular el coeficiente para X 10 en el producto de pag 1 y pag 2 . Entonces no tenemos que usar monomios de grado 11 y superior. Así hacemos:

gap> p:=10;
10
gap> tcut:=ProductCoeffs(CoefficientsOfUnivariatePolynomial(f1), p+1, CoefficientsOfUnivariatePolynomial(f2), p+1);;time;
0
gap> CoefficientsOfUnivariatePolynomial(f){[1..p+1]}=tcut{[1..p+1]};
true

así que estamos obteniendo el resultado instantáneamente, saltándonos el trato con todos los monomios de grados superiores. En la misma configuración, tengo 43 ms para pag = 1000 y 977 ms para pag = 5000 , por lo que su millaje puede variar, y cuanto menor sea pag Es decir, mejor debería ser la aceleración.

Paso 2(b)

Se podría intentar experimentar con la multiplicación paralelizante de polinomios (dada por representaciones internas o externas). El número de polinomios a multiplicar es el número de clases de conjugación de GRAMO , p.ej 22 para S 8 . El enfoque más simple sería dividir la lista de polinomios en fragmentos y luego encontrar un producto de cada fragmento en paralelo y luego multiplicar los productos resultantes (posiblemente con otra iteración paralela, según la cantidad de fragmentos y nodos). De todos modos, el grado irá subiendo, y el cuello de botella obvio sería el último paso con la multiplicación secuencial de dos o tres polinomios. Si desea intentar experimentar con esto, mire el paquete SCSCP . También podría proporcionar un código para la multiplicación paralela de polinomios de Karatsuba con el paquete SCSCP: para polinomios de gran grado, también podría acelerarlo un poco.

Paso 2(c)

Otra sugerencia especulativa proviene del hecho de que puede estar interesado solo en un solo coeficiente. Eso sería mucho más útil si los polinomios fueran dispersos, pero aun así puede valer la pena intentarlo en el caso denso. La idea sería iterar sobre el producto cartesiano de grados de monomios (usando IteratorOfCartesianProduct), verificar para cada tupla si la suma de grados de monomios es igual al grado que le interesa, y si es así, sume el producto de los respectivos coeficientes a alguna variable acumulativa. El pseudocódigo para calcular pel coeficiente -th en GAP podría verse así

degs:= < list of degrees of polynomials >
degranges := List( degs, i -> [1..i] ); # list of ranges 
# for sparse polynomial, take the list of degrees of monomials instead of a range

coeff := 0;

iter:=IteratorOfCartesianProduct(degranges);

for t in iter do
  # t is a tuple of degrees of monomials 
  if Sum(t) = p then
    coeff := coeff + Product( List( [1..Length(t)], i -> 
                              < coefficient for x^t[i] in the i-th polynomial > 
  fi;
od;