Polinomio ciclotomico


17

Sfondo (salta alle definizioni)

Eulero dimostrò un bellissimo teorema sui numeri complessi: e ix = cos (x) + i sin (x).

Questo rende facile dimostrare il teorema di De Moivre:

(e ix ) n = e i (nx)

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

Possiamo tracciare numeri complessi usando il piano euclideo bidimensionale, con l'asse orizzontale che rappresenta la parte reale e l'asse verticale che rappresenta la parte immaginaria. In questo modo, (3,4) corrisponderebbe al numero complesso 3 + 4i.

Se hai familiarità con le coordinate polari, (3,4) sarebbe (5, arctan (4/3)) in coordinate polari. Il primo numero, r, è la distanza del punto dall'origine; il secondo numero, θ, è l'angolo misurato dall'asse x positivo al punto, in senso antiorario. Di conseguenza, 3 = r cosθ e 4 = r sinθ. Pertanto, possiamo scrivere 3 + 4i come r cosθ + ri sinθ = r (cosθ + i sinθ) = re .

Risolviamo l'equazione complessa z n = 1, dove n è un numero intero positivo.

Lasciamo z = re . Quindi, z n = r n e inθ . La distanza di z n dall'origine è r n e l'angolo è nθ. Tuttavia, sappiamo che la distanza di 1 dall'origine è 1 e l'angolo è 0. Pertanto, r n = 1 e nθ = 0. Tuttavia, se si ruota di 2π in più, si finisce comunque nello stesso punto, perché 2π è solo un cerchio completo. Pertanto, r = 1 e nθ = 2kπ, dandoci z = e 2ikπ / n .

Riaffermiamo la nostra scoperta: le soluzioni per z n = 1 sono z = e 2ikπ / n .

Un polinomio può essere espresso in termini di radici. Ad esempio, le radici di x 2 -3x + 2 sono 1 e 2, quindi x 2 -3x + 2 = (x-1) (x-2). Allo stesso modo, dalla nostra scoperta sopra:

Tuttavia, quel prodotto conteneva certamente radici di altri n. Ad esempio, prendi n = 8. Le radici di z 4 = 1 verrebbero incluse anche nelle radici di z 8 = 1, poiché z 4 = 1 implica z 8 = (z 4 ) 2 = 1 2 = 1. Prendi n = 6 come esempio. Se z 2 = 1, avremmo anche z 6 = 1. Allo stesso modo, se z 3 = 1, quindi z 6 = 1.

Se vogliamo estrarre le radici uniche di z n = 1, avremmo bisogno di k e n per condividere nessun divisore comune tranne 1. Oppure, se condividono un divisore comune d dove d> 1, allora z sarebbe il (k / d) -th radice di z n / d = 1. Usando la tecnica sopra per scrivere il polinomio in termini di radici, otteniamo il polinomio:

Si noti che questo polinomio viene eseguito rimuovendo le radici di z n / d = 1 con d che è un divisore di n. Sosteniamo che il polinomio sopra abbia coefficienti interi. Considera l'LCM dei polinomi sotto forma di z n / d -1 dove d> 1 e d divide n. Le radici dell'LCM sono esattamente le radici che desideriamo rimuovere. Poiché ogni componente ha coefficienti interi, l'LCM ha anche coefficienti interi. Poiché l'LCM divide z n -1, il quoziente deve essere un polinomio con coefficiente intero e il quoziente è il polinomio sopra.

Le radici di z n = 1 hanno tutte il raggio 1, quindi formano un cerchio. Il polinomio rappresenta i punti del cerchio unici per n, quindi in un certo senso i polinomi formano una partizione del cerchio. Pertanto, il polinomio sopra è l'n-esimo polinomio ciclotomico. (ciclo- = cerchio; tom- = da tagliare)

Definizione 1

Il polinomio n-esimo ciclotomico, indicato , è il polinomio unico con coefficienti interi che dividono x n -1 ma non x k -1 per k <n.

Definizione 2

I polinomi ciclotomici sono un insieme di polinomi, uno per ogni numero intero positivo, tali che:

dove k | n significa che k divide n.

Definizione 3

Il polinomio n-esimo ciclotomico è il polinomio x n -1 diviso per il LCM dei polinomi nella forma x k -1 dove k divide n e k <n.

Esempi

  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 - x 8 - 2x 7 - x 6 - x 5 + x 2 + x + 1

Compito

Dato un numero intero positivo n, restituisce il npolinomio ciclotomico -th come definito sopra, in un formato ragionevole (è consentito un elenco di coefficienti ieeg).

Regole

È possibile restituire i numeri in virgola mobile / complessi purché arrotondati al valore corretto.

punteggio

Questo è . Vince la risposta più breve in byte.

Riferimenti


1
Forse aggiungi 105 come test?
Jonathan Allan,

@JonathanAllan Non voglio scrivere 48 termini
Leaky Nun,

1
Sono consentite inesattezze in virgola mobile?
miglia,

3
@miles Odio i galleggianti con passione>. <ma difenderò fino alla morte il tuo diritto di usare i galleggianti.
Leaky Nun,

1
Possiamo generare numeri in virgola mobile complessi purché producano la risposta corretta quando arrotondati al numero intero / intero gaussiano più vicino?
fireflame241,

Risposte:


12

Haskell , 120 byte

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]

Provalo online!

Fornisce un elenco di float complessi con voci come a 1.0000000000000078 :+ 3.314015728506092e-14causa dell'inacurracy del float. Un metodo diretto di moltiplicazione per recuperare il polinomio dalle sue radici.

La fromIntegerè una grande concessione al sistema di tipi di Haskell. Deve esserci un modo migliore. Suggerimenti sono ben accetti Anche trattare simbolicamente le radici dell'unità potrebbe funzionare.


Haskell , 127 byte

(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]

Provalo online!

Nessuna importazione.

Usa la formula

Calcola Φ_n (x) dividendo l'LHS per ciascuno degli altri termini nell'RHS.

L'operatore %esegue la divisione sui polinomi, basandosi sul resto su zero. Si presume che il divisore sia monico e viene dato senza il 1 iniziale e anche con infiniti zero finali per evitare di troncare quando lo si fa zipWith.


[0,0..]è un byte più corto di repeat 0.
Laikoni,

@flawr Divide i polinomi. Penso che sia lo stesso metodo della tua soluzione.
xnor

Sembra dannatamente elegante, dovrò dare un'occhiata più da vicino domani :)
flawr

questa risposta mi fa venire voglia di imparare Haskell.
Giuseppe,

1
@xnor -1 byte
H.Pwiz

7

Mathematica, 43 41 byte

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

Ovviamente, possiamo sempre usare il built-in, ma se non lo facciamo, questo divide x n -1 per Φ k ( x ) (calcolato ricorsivamente) per ogni vero divisore k di n .

Usiamo Factorper ottenere un polinomio alla fine. Penso che la ragione per cui funziona sia che i x^#-1fattori in tutti i polinomi ciclotomici dei divisori di n , e quindi dividiamo quelli che non vogliamo.

-2 byte grazie a Jenny_mathy, riscrivendo il Factorda applicare solo al numeratore.


2
Questo è fantastico! puoi salvare un byte usandoFactor@
J42161217 il

@Jenny_mathy Fare ciò sembra analizzare come Factor[x^#-1]/Times@@...invece; se non avessimo parentesi lì, vorremmo parentesi.
Misha Lavrov,

1
ok ... ma devo dire che quando l'ho provato, stava dando i risultati giusti ...
J42161217,

Interessante. Significa che possiamo salvare un altro byte scrivendolo Factor[x^#-1]/Times@@..., e significa anche che non ho idea di come funzioni.
Misha Lavrov,

5

MATL , 32 31 27 25 byte

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

L'output può essere non intero a causa di inesattezze in virgola mobile (che sono consentite). Il piè di pagina fa l'arrotondamento per chiarezza.

Provalo online!


4

Haskell , 250 236 233 218 216 byte

Questa è una versione dettagliata, (@xnor può farlo in quasi la metà del punteggio ) ma è garantito che funzioni per tutto nil tempo in cui hai memoria sufficiente, ma non usa un builtin per generare l'ennesimo polinomio ciclotomico. L'input è un numero intero di dimensioni arbitrarie e l'output è di tipo polinomiale con coefficienti razionali (esatti).

L'idea approssimativa qui è di calcolare ricorsivamente i polinomi. Per n=1o nprime è banale. Per tutti gli altri numeri questo approccio utilizza sostanzialmente la formula dalla definizione 2

risolto per . Grazie @ H.PWiz per un bel po 'di byte!

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]

Per n=105questo cede il seguente polinomio (ho riordinato tutti i %1denominatori):

[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]

Il polinomio per n=15015può essere trovato qui (il coefficiente più grande è 23).

Provalo online!


+1per non essere incorporato.
DJMcMayhem

@flawr Perché stai usando Rationals? Sembra che funzioni bene senza di loro
H.Piz,

Vero? Ho avuto problemi con quotRemPoly, fammi riprovare allora!
Flawr,

Ah, il "problema" era che produce Doublecoefficienti se non si utilizza Ratio Integerinvece, il che potrebbe causare problemi per (molto) molto grandi n.
Flawr,

Eh ... Non penso che sia un problema.
H.Piz,

3

Gelatina , 23 byte

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

Provalo online!

Output come un elenco di coefficienti.

Ha virgola mobile e inesattezze complesse. Il piè di pagina esegue l'arrotondamento per rendere l'output più bello.


3

J , 36 byte

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

Provalo online!

Usa la formula

formula

Ci sono alcune imprecisioni in virgola mobile, ma è consentito.


2

Mathematica, 81 byte

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

2

R , 176 171 139 112 byte

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}

Provalo online!

Versione enormemente più semplice; utilizza un forciclo anziché un Reduce.


2

Pari / GP , 8 byte

Un built-in.

polcyclo

Provalo online!


Pari / GP , 39 byte, senza built-in

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

Utilizzando la formula:

Φn(X)=Xn-1Πd<nd|nΦd(X)

Provalo online!


1

CJam ( 52 51 byte)

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

Demo online . Questo è un blocco (funzione) anonimo che accetta un numero intero nello stack e lascia una matrice big-endian di coefficienti nello stack.

Dissezione

{                    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
}

0

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

(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]))}

Spiegazione (selezionato):

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

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

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

Calcolo GCD.

h=Math

Dato che ho bisogno di usare parecchie funzioni matematiche, ho usato un'altra variabile qui.

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

Moltiplicazione polinomiale.

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

La formula utilizzata è

inserisci qui la descrizione dell'immagine

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

Comprime l'output in un array intero.

Uscite:

Un array di numeri interi, con l'elemento nella posizione i che rappresenta il coefficiente di x ^ i.

Uno dei problemi che JS ha è che poiché JS non fornisce una libreria nativa per i calcoli su polinomi e numeri complessi, dovevano essere implementati in modo simile ad un array.

console.log (phi (105)) dà

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): modificato il codice per il controllo di un valore indefinito

333> 284 (-49): modificata la funzione di moltiplicazione polinomiale perché può essere semplificata

284> 277 (-7): eliminato del codice ridondante

277> 265 (-12): utilizzare 2 variabili anziché una matrice a 2 elementi per eliminare alcuni byte durante l'utilizzo della matrice

265> 264 (-1): utilizzare Array.push () invece di Array.concat () per ridurre 4 byte, ma aggiunto 3 per le parentesi graffe for-loop e la variabile z

264> 263 (-1): ulteriormente approfondito sull'ultimo emendamento

263> 262 (-1): golfato sul ciclo for

262> 260 (-2): annullata la clausola if

260> 258 (-2): combinate ulteriormente le dichiarazioni

258> 252 (-6): golfato al riutilizzo dei riferimenti di array

252> 250 (-2): sostituisce alcuni operatori unari come operatori binari

250> 245 (-5): spostare l'incremento in Array.map () sull'ultimo riferimento del contatore per rimuovere i byte

245> 242 (-3): usa la sintassi diffusa invece di Array.push ()

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.