Polynôme cyclotomique

17

Contexte (passez aux définitions)

Euler a démontré un beau théorème sur les nombres complexes: e ix = cos (x) + i sin (x).

Cela rend le théorème de de Moivre facile à prouver:

(e ix ) n = e i (nx)

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

Nous pouvons tracer des nombres complexes en utilisant le plan euclidien à deux dimensions, avec l'axe horizontal représentant la partie réelle et l'axe vertical représentant la partie imaginaire. De cette façon, (3,4) correspondrait au nombre complexe 3 + 4i.

Si vous connaissez les coordonnées polaires, (3,4) serait (5, arctan (4/3)) en coordonnées polaires. Le premier nombre, r, est la distance du point à l'origine; le deuxième nombre, θ, est l'angle mesuré à partir de l'axe x positif jusqu'au point, dans le sens antihoraire. En conséquence, 3 = r cosθ et 4 = r sinθ. Par conséquent, nous pouvons écrire 3 + 4i comme r cosθ + ri sinθ = r (cosθ + i sinθ) = re .

Résolvons l'équation complexe z n = 1, où n est un entier positif.

On laisse z = re . Alors, z n = r n e inθ . La distance de z n à l'origine est r n et l'angle est nθ. Cependant, nous savons que la distance de 1 à l'origine est 1 et l'angle est 0. Par conséquent, r n = 1 et nθ = 0. Cependant, si vous tournez de 2π de plus, vous vous retrouvez toujours au même point, car 2π n'est qu'un cercle complet. Par conséquent, r = 1 et nθ = 2kπ, nous donnant z = e 2ikπ / n .

Rappelons notre découverte: les solutions à z n = 1 sont z = e 2ikπ / n .

Un polynôme peut être exprimé en termes de racines. Par exemple, les racines de x 2 -3x + 2 sont 1 et 2, donc x 2 -3x + 2 = (x-1) (x-2). De même, à partir de notre découverte ci-dessus:

Cependant, ce produit contenait certainement des racines d'autres n. Par exemple, prenez n = 8. Les racines de z 4 = 1 seraient également incluses à l'intérieur des racines de z 8 = 1, car z 4 = 1 implique z 8 = (z 4 ) 2 = 1 2 = 1. Prenez n = 6 comme exemple. Si z 2 = 1, alors nous aurions également z 6 = 1. De même, si z 3 = 1, alors z 6 = 1.

Si nous voulons extraire les racines uniques à z n = 1, nous aurions besoin de k et n pour ne partager aucun diviseur commun sauf 1. Ou bien, s'ils partagent un diviseur commun d où d> 1, alors z serait le (k / d) -th racine de z n / d = 1. En utilisant la technique ci-dessus pour écrire le polynôme en termes de racines, nous obtenons le polynôme:

Notez que ce polynôme se fait en supprimant les racines de z n / d = 1, d étant un diviseur de n. Nous affirmons que le polynôme ci-dessus a des coefficients entiers. Considérons le LCM des polynômes sous la forme de z n / d -1 où d> 1 et d divise n. Les racines du LCM sont exactement les racines que nous souhaitons supprimer. Étant donné que chaque composant a des coefficients entiers, le LCM a également des coefficients entiers. Étant donné que le LCM divise z n -1, le quotient doit être un polynôme à coefficient entier, et le quotient est le polynôme ci-dessus.

Les racines de z n = 1 ont toutes un rayon de 1, elles forment donc un cercle. Le polynôme représente les points du cercle uniques à n, donc en un sens les polynômes forment une partition du cercle. Par conséquent, le polynôme ci-dessus est le n-ième polynôme cyclotomique. (cyclo- = cercle; tom- = couper)

Définition 1

Le nième polynôme cyclotomique, noté , est le polynôme unique avec des coefficients entiers qui divisent x n -1 mais pas x k -1 pour k <n.

Définition 2

Les polynômes cyclotomiques sont un ensemble de polynômes, un pour chaque entier positif, tels que:

où k | n signifie que k divise n.

Définition 3

Le nième polynôme cyclotomique est le polynôme x n -1 divisé par le LCM des polynômes sous la forme x k -1 où k divise n et k <n.

Exemples

  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

Tâche

Étant donné un entier positif n, renvoyer le n-e polynôme cyclotomique tel que défini ci-dessus, dans un format raisonnable (la liste ieeg des coefficients est autorisée).

Règles

Vous pouvez renvoyer des nombres à virgule flottante / complexes tant qu'ils arrondissent à la valeur correcte.

Notation

C'est du . La réponse la plus courte en octets l'emporte.

Les références

Leaky Nun
la source
1
Peut-être ajouter 105 comme test?
Jonathan Allan
@JonathanAllan Je ne veux pas taper 48 termes
Leaky Nun
1
Les inexactitudes en virgule flottante sont-elles autorisées?
miles
3
@miles Je déteste les flotteurs avec passion>. <mais je défendrai jusqu'à la mort votre droit d'utiliser des flotteurs.
Leaky Nun
1
Pouvons-nous produire des nombres à virgule flottante complexes tant qu'ils donnent la bonne réponse lorsqu'ils sont arrondis à l'entier / entier gaussien le plus proche?
fireflame241

Réponses:

12

Haskell , 120 octets

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]

Essayez-le en ligne!

Donne une liste de flottants complexes qui ont des entrées comme en 1.0000000000000078 :+ 3.314015728506092e-14raison de l'inactivité de flotteur Une méthode directe de multiplication pour récupérer le polynôme de ses racines.

Le fromIntegerest une grande concession au système de type Haskell. Il doit y avoir un meilleur moyen. Les suggestions sont les bienvenues. Traiter symboliquement les racines de l'unité pourrait également fonctionner.


Haskell , 127 octets

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

Essayez-le en ligne!

Aucune importation.

Utilise la formule

Calcule Φ_n (x) en divisant le LHS par chacun des autres termes du RHS.

L'opérateur %fait la division sur les polynômes, en se basant sur le reste étant nul. Le diviseur est supposé être monique, et est donné sans le 1 de tête, ainsi qu'avec des zéros de fin infinis pour éviter de tronquer lors de l'exécution zipWith.

xnor
la source
[0,0..]est un octet plus court que repeat 0.
Laikoni
@flawr Divise les polynômes. Je pense que c'est la même méthode que votre solution.
xnor
Ça a l'air sacrément élégant, je vais devoir regarder de plus près demain :)
flawr
cette réponse me donne envie d'apprendre Haskell.
Giuseppe
1
@xnor -1 byte
H.PWiz
7

Mathematica, 43 41 octets

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

Bien sûr, nous pouvons toujours utiliser la fonction intégrée, mais si ce n'est pas le cas, cela divise x n -1 par Φ k ( x ) (calculé récursivement) pour chaque diviseur k approprié de n .

Nous utilisons Factorpour obtenir un polynôme à la fin. Je pense que la raison pour laquelle cela fonctionne est que les x^#-1facteurs dans tous les polynômes cyclotomiques des diviseurs de n , puis nous divisons ceux que nous ne voulons pas.

-2 octets grâce à Jenny_mathy, réécriture du Factorpour ne s'appliquer qu'au numérateur.

Misha Lavrov
la source
2
C'est bien! vous pouvez enregistrer un octet en utilisantFactor@
J42161217
@Jenny_mathy Faire cela semble Factor[x^#-1]/Times@@...plutôt analyser comme ; si nous n'avions pas de parenthèses, nous voudrions des parenthèses.
Misha Lavrov
1
ok ... mais je dois dire que quand je l'ai testé, ça donnait les bons résultats ...
J42161217
C'est intéressant. Cela signifie que nous pouvons enregistrer un autre octet en l'écrivant Factor[x^#-1]/Times@@..., et cela signifie également que je n'ai aucune idée de comment cela fonctionne.
Misha Lavrov
5

MATL , 32 31 27 25 octets

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

La sortie peut être non entière en raison d'inexactitudes en virgule flottante (ce qui est autorisé). Le pied de page arrondit pour plus de clarté.

Essayez-le en ligne!

Luis Mendo
la source
4

Haskell , 250 236 233 218 216 octets

Ceci est une version détaillée (@xnor peut le faire dans presque moitié du score ) mais elle est garantie de fonctionner naussi longtemps que vous avez suffisamment de mémoire, mais elle n'utilise pas de fonction intégrée pour générer le nième polynôme cyclotomique. L'entrée est un entier de taille arbitraire et la sortie est un type polynomial avec des coefficients rationnels (exacts).

L'idée approximative ici est de calculer les polynômes récursivement. Pour n=1ou nprime c'est trivial. Pour tous les autres nombres, cette approche utilise essentiellement la formule de la définition 2

résolu pour . Merci @ H.PWiz pour pas mal d'octets!

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]

Pour n=105cela donne le polynôme suivant (j'ai rangé tous les %1dénominateurs):

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

Le polynôme de n=15015peut être trouvé ici (le plus grand coefficient est 23).

Essayez-le en ligne!

flawr
la source
+1pour ne pas être intégré.
DJMcMayhem
@flawr Pourquoi utilisez-vous Rationals? Cela semble bien fonctionner sans eux
H.PWiz
Le fait-il? J'ai eu des problèmes avec quotRemPoly, laissez-moi réessayer alors!
flawr
Ah le "problème" était qu'il donne des Doublecoefficients si vous ne les utilisez pas à la Ratio Integerplace, ce qui pourrait causer des problèmes pour les très (très) grands n.
flawr
Eh ... je ne pense pas que ce soit un problème.
H.PWiz
3

Gelée , 23 octets

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

Essayez-le en ligne!

Sorties sous forme de liste de coefficients.

A une virgule flottante ET des inexactitudes complexes. Le pied de page arrondit pour rendre la sortie plus jolie.

fireflame241
la source
3

J , 36 octets

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

Essayez-le en ligne!

Utilise la formule

formule

Il existe des inexactitudes en virgule flottante, mais cela est autorisé.

miles
la source
2

Mathematica, 81 octets

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

R , 176 171 139 139 112 octets

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}

Essayez-le en ligne!

Version massivement plus simple; utilise une forboucle plutôt qu'un Reduce.

Giuseppe
la source
2

Pari / GP , 8 octets

Un intégré.

polcyclo

Essayez-le en ligne!


Pari / GP , 39 octets, sans intégré

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

En utilisant la formule:

Φn(X)=Xn-1<n|nΦ(X).

Essayez-le en ligne!

alephalpha
la source
1

CJam ( 52 51 octets)

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

Démo en ligne . Il s'agit d'un bloc (fonction) anonyme qui prend un entier sur la pile et laisse un tableau big-endian de coefficients sur la pile.

Dissection

{                    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
la source
0

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

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

Explication (sélectionnée):

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

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

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

Calcul GCD.

h=Math

Comme je dois beaucoup utiliser les fonctions mathématiques, j'ai utilisé une autre variable ici.

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

Multiplication polynomiale.

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

La formule utilisée est

entrez la description de l'image ici

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

Compressez la sortie vers un tableau entier.

Les sorties:

Un tableau d'entiers, l'élément en position i représentant le coefficient de x ^ i.

L'un des problèmes que JS a est que, puisque JS ne fournit pas de bibliothèque native pour les calculs sur les polynômes et les nombres complexes, ils devaient être implémentés de manière similaire à un tableau.

console.log (phi (105)) donne

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): modification du code de vérification de la valeur indéfinie

333> 284 (-49): modification de la fonction de multiplication polynomiale car elle peut être simplifiée

284> 277 (-7): Suppression d'un code redondant

277> 265 (-12): utilisez 2 variables au lieu d'un tableau à 2 éléments pour supprimer certains octets lors de l'utilisation du tableau

265> 264 (-1): utilisez Array.push () au lieu de Array.concat () pour réduire 4 octets, mais a ajouté 3 pour les accolades for-loop et la variable z

264> 263 (-1): Plus loin sur le dernier amendement

263> 262 (-1): Golfé sur la boucle for

262> 260 (-2): a supprimé la clause if

260> 258 (-2): combinaison des déclarations

258> 252 (-6): Golfé lors de la réutilisation des références de tableau

252> 250 (-2): remplacer certains opérateurs unaires en tant qu'opérateurs binaires

250> 245 (-5): déplace l'incrément dans Array.map () vers la dernière référence du compteur pour supprimer les octets

245> 242 (-3): utilisez la syntaxe étendue au lieu de Array.push ()

Shieru Asakoto
la source