Polinomio ciclotómico

17

Antecedentes (salte a las definiciones)

Euler demostró un hermoso teorema sobre los números complejos: e ix = cos (x) + i sin (x).

Esto hace que el teorema de de Moivre sea fácil de probar:

(e ix ) n = e i (nx)

(cos (x) + i sen (x)) n = cos (nx) + i sin (nx)

Podemos trazar números complejos usando el plano euclidiano bidimensional, donde el eje horizontal representa la parte real y el eje vertical representa la parte imaginaria. De esta manera, (3,4) correspondería al número complejo 3 + 4i.

Si está familiarizado con las coordenadas polares, (3,4) sería (5, arctan (4/3)) en coordenadas polares. El primer número, r, es la distancia del punto desde el origen; el segundo número, θ, es el ángulo medido desde el eje x positivo hasta el punto, en sentido antihorario. Como resultado, 3 = r cosθ y 4 = r sinθ. Por lo tanto, podemos escribir 3 + 4i como r cosθ + ri sinθ = r (cosθ + i sinθ) = re .

Resolvamos la ecuación compleja z n = 1, donde n es un número entero positivo.

Dejamos z = re . Entonces, z n = r n e inθ . La distancia de z n desde el origen es r n , y el ángulo es nθ. Sin embargo, sabemos que la distancia de 1 desde el origen es 1 y el ángulo es 0. Por lo tanto, r n = 1 y nθ = 0. Sin embargo, si giras 2π más, aún terminas en el mismo punto, porque 2π es solo un círculo completo. Por lo tanto, r = 1 y nθ = 2kπ, dándonos z = e 2ikπ / n .

Replanteamos nuestro descubrimiento: las soluciones para z n = 1 son z = e 2ikπ / n .

Un polinomio se puede expresar en términos de sus raíces. Por ejemplo, las raíces de x 2 -3x + 2 son 1 y 2, entonces x 2 -3x + 2 = (x-1) (x-2). Del mismo modo, de nuestro descubrimiento anterior:

Sin embargo, ese producto ciertamente contenía raíces de otros n. Por ejemplo, tome n = 8. Las raíces de z 4 = 1 también se incluirían dentro de las raíces de z 8 = 1, ya que z 4 = 1 implica z 8 = (z 4 ) 2 = 1 2 = 1. Tome n = 6 como ejemplo. Si z 2 = 1, entonces también tendríamos z 6 = 1. Del mismo modo, si z 3 = 1, entonces z 6 = 1.

Si queremos extraer las raíces exclusivas de z n = 1, necesitaríamos k y n para no compartir ningún divisor común excepto 1. O bien, si comparten un divisor común d donde d> 1, entonces z sería el (k / d) enésima raíz de z n / d = 1. Usando la técnica anterior para escribir el polinomio en términos de sus raíces, obtenemos el polinomio:

Tenga en cuenta que este polinomio se realiza eliminando las raíces de z n / d = 1 con d siendo un divisor de n. Afirmamos que el polinomio anterior tiene coeficientes enteros. Considere el MCM de los polinomios en forma de z n / d -1 donde d> 1 yd divide n. Las raíces del LCM son exactamente las raíces que deseamos eliminar. Como cada componente tiene coeficientes enteros, el LCM también tiene coeficientes enteros. Como el MCM divide z n -1, el cociente debe ser un polinomio con coeficiente entero, y el cociente es el polinomio anterior.

Las raíces de z n = 1 tienen radio 1, por lo que forman un círculo. El polinomio representa los puntos del círculo únicos de n, por lo que, en cierto sentido, los polinomios forman una partición del círculo. Por lo tanto, el polinomio anterior es el enésimo polinomio ciclotómico. (ciclo- = círculo; tom- = cortar)

Definición 1

El enésimo polinomio ciclotómico, denotado , es el polinomio único con coeficientes enteros que divide x n -1 pero no x k -1 para k <n.

Definición 2

Los polinomios ciclotómicos son un conjunto de polinomios, uno para cada entero positivo, de modo que:

donde k | n significa k divide n.

Definición 3

El polinomio ciclotómico n-ésimo es el polinomio x n -1 dividido por el LCM de los polinomios en la forma x k -1 donde k divide n y k <n.

Ejemplos

  1. Φ 1 (x) = x - 1
  2. Φ 2 (x) = x + 1
  3. Φ 3 (x) = x 2 + x + 1
  4. Φ 30 (x) = x 8 + x 7 - x 5 - x 4 - x 3 + x + 1
  5. Φ 105 (x) = x 48 + x 47 + x 46 - x 43 - x 42 - 2x 41 - x 40 - x 39 + x 36 + x 35 + x 34 + x 33 + x 32 + x 31 - x 28 - x 26 - x 24 - x 22 - x 20 + x 17 + x 16 + x 15 + x 14 + x 13 + x 12 - x9 - x8 - 2x 7 - x 6 - x 5 + x 2 + x + 1

Tarea

Dado un entero positivo n, devuelve eln polinomio ciclotómico -th como se definió anteriormente, en un formato razonable (es decir, se permite una lista de coeficientes).

Reglas

Puede devolver números flotantes / complejos siempre que se redondeen al valor correcto.

Puntuación

Esto es . La respuesta más corta en bytes gana.

Referencias

Monja permeable
fuente
1
¿Quizás agregar 105 como prueba?
Jonathan Allan
@JonathanAllan No quiero escribir 48 términos
Leaky Nun
1
¿Se permiten imprecisiones de punto flotante?
millas
3
@miles Odio las carrozas con pasión>. <pero defenderé hasta la muerte tu derecho a usar carrozas.
Leaky Nun
1
¿Podemos generar números complejos de coma flotante siempre que den la respuesta correcta cuando se redondean al entero más cercano / entero gaussiano?
fireflame241

Respuestas:

12

Haskell , 120 bytes

import Data.Complex
p%a=zipWith(\x y->x-a*y)(p++[0])$0:p
f n=foldl(%)[1][cis(2*pi/fromInteger n)^^k|k<-[1..n],gcd n k<2]

Pruébalo en línea!

Da una lista de flotadores complejos que tiene entradas como 1.0000000000000078 :+ 3.314015728506092e-14debido a la falta de capacidad de flotación. Un método directo de multiplicación para recuperar el polinomio desde sus raíces.

El fromIntegeres una gran concesión al sistema de tipos de Haskell. Tiene que haber una mejor manera. Las sugerencias son bienvenidas. Tratar simbólicamente con las raíces de la unidad también podría funcionar.


Haskell , 127 bytes

(h:t)%q|all(==0)t=[]|1>0=h:zipWith(\x y->x-h*y)t q%q
f n=foldl(%)(1:(0<$[2..n])++[-1])[tail$f k++[0,0..]|k<-[1..n-1],mod n k<1]

Pruébalo en línea!

Sin importaciones

Usa la fórmula

Calcula Φ_n (x) dividiendo el LHS por cada uno de los otros términos en el RHS.

El operador %hace la división en polinomios, confiando en que el resto sea cero. Se supone que el divisor es monic, y se da sin el 1 inicial, y también con ceros finales infinitos para evitar truncarse al hacerlo zipWith.

xnor
fuente
[0,0..]es un byte más corto que repeat 0.
Laikoni
@flawr Divide polinomios. Creo que es el mismo método que tu solución.
xnor
Parece maldita elegante, voy a tener que conseguir un vistazo más de cerca mañana :)
flawr
Esta respuesta me da ganas de aprender Haskell.
Giuseppe
1
@xnor -1 byte
H.PWiz
7

Mathematica, 43 41 bytes

Factor[x^#-1]/Times@@#0/@Most@Divisors@#&

Por supuesto, siempre podemos usar el incorporado, pero si no lo hacemos, esto divide x n -1 por Φ k ( x ) (calculado recursivamente) para cada divisor apropiado k de n .

Usamos Factorpara obtener un polinomio al final. Creo que la razón por la que esto funciona es que tiene x^#-1en cuenta todos los polinomios ciclotómicos de los divisores de n , y luego dividimos los que no queremos.

-2 bytes gracias a Jenny_mathy, reescribiendo Factorpara que solo se aplique al numerador.

Misha Lavrov
fuente
2
¡Esto es genial! puede guardar un byte usandoFactor@
J42161217
@Jenny_mathy Hacer eso parece analizarse en su Factor[x^#-1]/Times@@...lugar; si no tuviéramos paréntesis, querríamos paréntesis.
Misha Lavrov
1
ok ... pero tengo que decir que cuando lo probé, estaba dando los resultados correctos ...
J42161217
Eso es interesante. Significa que podemos guardar otro byte escribiéndolo Factor[x^#-1]/Times@@..., y también significa que no tengo idea de cómo funciona.
Misha Lavrov
5

MATL , 32 31 27 25 bytes

Z\"@:@/]XHxvHX~18L*Ze1$Yn

La salida puede ser no entera debido a imprecisiones de punto flotante (que está permitido). El pie de página hace redondeo para mayor claridad.

Pruébalo en línea!

Luis Mendo
fuente
4

Haskell , 250 236 233 218 216 bytes

Esta es una versión detallada, (@xnor puede hacerlo en casi la mitad del puntaje ) pero está garantizado que funcionará para cualquiern siempre que tenga suficiente memoria, pero no utiliza un generador incorporado para generar el polinomio ciclotómico n-ésimo. La entrada es un número entero de tamaño arbitrario y la salida es de tipo polinomial con coeficientes racionales (exactos).

La idea aproximada aquí es calcular los polinomios de forma recursiva. Para n=1o nprimo es trivial. Para todos los demás números, este enfoque utiliza básicamente la fórmula de la definición 2

resuelto por . ¡Gracias @ H.PWiz por un montón de bytes!

import Math.Polynomial
import Data.Ratio
import NumberTheory
p=powPoly x
q=poly LE
c n|n<2=q[-1,1%1]|isPrime n=sumPolys$p<$>[0..n-1]|1>0=fst$quotRemPoly(addPoly(p n)$q[-1])$foldl1 multPoly[c d|d<-[1..n-1],n`mod`d<1]

Para n=105esto se obtiene el siguiente polinomio (ordené todos los %1denominadores):

[1,1,1,0,0,-1,-1,-2,-1,-1,0,0,1,1,1,1,1,1,0,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,1,1,1,1,1,1,0,0,-1,-1,-2,-1,-1,0,0,1,1,1]

El polinomio para n=15015se puede encontrar aquí (el coeficiente más grande es 23).

Pruébalo en línea!

falla
fuente
+1por no ser un empotrado.
DJMcMayhem
@flawr ¿Por qué estás usando Rationals? Parece funcionar bien sin ellos
H.PWiz
¿Lo hace? Tuve problemas con quotRemPoly, ¡déjame intentarlo de nuevo!
flawr
Ah, el "problema" fue que produce Doublecoeficientes si no los usa Ratio Integer, lo que podría causar problemas para muy (muy) grandes n.
flawr
Eh ... no creo que sea un problema.
H.PWiz
3

Jalea , 23 bytes

R÷
ÆḌÇ€FQœ-@Ç×ı2×ØPÆeÆṛ

Pruébalo en línea!

Salidas como una lista de coeficientes.

Tiene coma flotante e inexactitudes complejas. El pie de página se redondea para que la salida sea más bonita.

fireflame241
fuente
3

J , 36 bytes

1+//.@(*/)/@(,.~-)_1^2*%*i.#~1=i.+.]

Pruébalo en línea!

Usa la fórmula

fórmula

Hay algunas imprecisiones de coma flotante, pero está permitido.

millas
fuente
2

Mathematica, 81 bytes

Round@CoefficientList[Times@@(x-E^(2Pi*I#/k)&/@Select[Range[k=#],#~GCD~k<2&]),x]&
J42161217
fuente
2

R , 176 171 139 112 bytes

function(n){for(r in exp(2i*pi*(x=1:n)[(g=function(x,y)ifelse(o<-x%%y,g(y,o),y))(x,n)<2]/n))T=c(0,T)-r*c(T,0)
T}

Pruébalo en línea!

Versión masivamente simple; usa un forbucle en lugar de un Reduce.

Giuseppe
fuente
2

Pari / GP , 8 bytes

Un incorporado.

polcyclo

Pruébalo en línea!


Pari / GP , 39 bytes, sin incorporado

f(n)=p=x^n-1;fordiv(n,d,d<n&&p/=f(d));p

Usando la fórmula:

Φnorte(X)=Xnorte-1re<nortereEl |norteΦre(X).

Pruébalo en línea!

alephalpha
fuente
1

CJam ( 52 51 bytes)

{M{:K,:!W+K,0-{K\%!},{j($,\:Q,-[{(\1$Qf*.-}*;]}/}j}

Demostración en línea . Este es un bloque anónimo (función) que toma un número entero en la pila y deja una matriz big-endian de coeficientes en la pila.

Disección

{                    e# Define a block
  M{                 e#   Memoised recursion with no base cases.
    :K,:!W+          e#     Store argument in K and build (x^K - 1)
    K,0-{K\%!},      e#     Find proper divisors of K
    {                e#     Foreach proper divisor D...
      j              e#       Recursive call to get Dth cyclotomic poly
      ($,\:Q,-       e#       The cleverest bit. We know that it is monic, and the
                     e#       poly division is simpler without that leading 1, so
                     e#       pop it off and use it for a stack-based lookup in
                     e#       calculating the number of terms in the quotient.
                     e#       Ungolfed this was (;:Q1$,\,-
                     e#       Store the headless divisor in Q.
      [              e#       Gather terms into an array...
        {            e#         Repeat the calculated number of times...
          (\         e#           Pop leading term, which goes into the quotient.
          1$Qf*.-    e#           Multiply Q by that term and subtract from tail.
        }*;          e#         Discard the array of Q,( zeroes. 
      ]
    }/
  }j
}
Peter Taylor
fuente
0

JavaScript (ES6), 337 333 284 ... 252 250 245 242 bytes

(v,z=[e=[1,u=0]],g=(x,y)=>y?g(y,x%y):x,h=Math,m=(l,x,p=h.cos(l),q=h.sin(l),i=0)=>x.map(()=>[(i&&(n=x[i-1])[0])-(w=x[i])[0]*p+w[1]*q,(i++&&n[1])-w[1]*p-w[0]*q]))=>{for(;++u<v;z=g(v,u)-1?z:[...m(h.PI*2*u/v,z),e]);return z.map(r=>h.round(r[0]))}

Explicación (Seleccionada):

z=[e=[1,u=0]]

Inicializar z = (1 + 0i) * x ^ 0

g=(x,y)=>y?g(y,x%y):x

Cálculo de MCD.

h=Math

Como necesito usar muchas funciones matemáticas, usé otra variable aquí.

m=(l,x,p=h.cos(l),q=h.sin(l),i=-1)=>blah blah blah

Multiplicación polinómica.

for(;++u<v;z=g(v,u)-1?z:[...m(h.PI*2*u/v,z),e]);

La fórmula utilizada es

ingrese la descripción de la imagen aquí

return z.map(r=>h.round(r[0]))

Comprima la salida nuevamente a una matriz entera.

Salidas:

Una matriz de enteros, con el elemento en la posición i representando el coeficiente de x ^ i.

Uno de los problemas que tiene JS es que, dado que JS no proporciona una biblioteca nativa para los cálculos de polinomios y números complejos, era necesario implementarlos de forma similar a una matriz.

console.log (phi (105)) da

Array(49)
 0:  1    1:  1    2:  1    3: -0    4: -0    5: -1    6: -1 
 7: -2    8: -1    9: -1   10:  0   11: -0   12:  1   13:  1 
14:  1   15:  1   16:  1   17:  1   18:  0   19: -0   20: -1 
21:  0   22: -1   23: -0   24: -1   25:  0   26: -1   27: -0 
28: -1   29:  0   30:  0   31:  1   32:  1   33:  1   34:  1 
35:  1   36:  1   37: -0   38: -0   39: -1   40: -1   41: -2 
42: -1   43: -1   44: -0   45: -0   46:  1   47:  1   48:  1 
length: 49
__proto__: Array(0)

337> 333 (-4): se modificó el código para verificar el valor indefinido

333> 284 (-49): se modificó la función de multiplicación polinómica porque se puede simplificar

284> 277 (-7): eliminado algún código redundante

277> 265 (-12): use 2 variables en lugar de una matriz de 2 elementos para eliminar algunos bytes en el uso de la matriz

265> 264 (-1): use Array.push () en lugar de Array.concat () para reducir 4 bytes, pero agregó 3 para los corchetes for-loop y la variable z

264> 263 (-1): más golf en la última enmienda

263> 262 (-1): Golf en el bucle for

262> 260 (-2): Golfed la cláusula if

260> 258 (-2): combina aún más las declaraciones

258> 252 (-6): Golf en la reutilización de referencias de matriz

252> 250 (-2): reemplazar algunos operadores unarios como operadores binarios

250> 245 (-5): mueva el incremento en Array.map () a la última referencia del contador para eliminar bytes

245> 242 (-3): utilice la sintaxis de propagación en lugar de Array.push ()

Shieru Asakoto
fuente