サイクロトミック多項式


17

背景(定義にスキップ)

オイラーは、複素数に関する美しい定理を証明しました:e ix = cos(x)+ i sin(x)。

これにより、de Moivreの定理が証明しやすくなります。

(e ixn = e i(nx)

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

2次元ユークリッド平面を使用して複素数をプロットできます。水平軸は実数部を表し、垂直軸は虚数部を表します。このように、(3,4)は複素数3 + 4iに対応します。

極座標に精通している場合、極座標では(3,4)は(5、arctan(4/3))になります。最初の数値rは、原点からのポイントの距離です。2番目の数値θは、正のx軸からポイントまでの反時計回りの角度です。その結果、3 = rcosθおよび4 = rsinθです。したがって、我々は再び= R +cosθとsinθとRI = R(cosθとsinθと+ I)として3 + 4Iを書き込むことができるIθを

複素方程式z n = 1を解きましょう。nは正の整数です。

z = reiθとします。次いで、Z N = R N E inθ。原点からのz nの距離はr nで、角度はnθです。ただし、原点から1の距離は1であり、角度は0であることがわかっています。したがって、r n = 1およびnθ= 0です。ただし、2πをさらに回転させても、2πは完全な円であるため、同じポイントに到達します。したがって、r = 1およびnθ=2kπとなり、z = e2ikπ/ nが得られます。

私たちは、私たちの発見を言い換える:Zへの解決策のn = 1は、z = eは2ikπ/ N

多項式は、その根で表現できます。たとえば、x 2 -3x + 2 の根は1と2なので、x 2 -3x + 2 =(x-1)(x-2)です。同様に、上記の発見から:

ただし、その製品には確かに他のnのルートが含まれていました。たとえば、n = 8を使用します。z 4 = 1はz 8 =(z 42 = 1 2 = 1を意味するため、z 4 = 1 の根もz 8 = 1の根の中に含まれます。z 2 = 1の場合、z 6 = 1になります。同様に、z 3 = 1の場合、z 6 = 1です。

z n = 1に固有の根を抽出したい場合、1以外の共通の除数を共有するためにkとnが必要です。または、d> 1で共通の除数dを共有する場合、zは(k / d)zのルートn / d = 1。上記の手法を使用して、その根に関して多項式を書くと、多項式が得られます。

この多項式は、dがnの約数であるz n / d = 1の根を削除することによって行われることに注意してください。上記の多項式には整数係数があると主張します。z n / d -1の形式の多項式のLCMを考えます。d> 1で、dはnを除算します。LCMのルートは、まさに削除したいルートです。各コンポーネントには整数係数があるため、LCMには整数係数もあります。LCMはz n -1を除算するため、商は整数係数を持つ多項式でなければならず、商は上記の多項式です。

z n = 1 の根はすべて半径1であるため、円を形成します。多項式は、nに固有の円の点を表すため、ある意味で、多項式は円のパーティションを形成します。したがって、上記の多項式は、n番目のサイクロトミック多項式です。(シクロ-=円;トム-=切断)

定義1

で示されるn番目の循環多項式は、x n -1 を除算する整数係数を持つ一意の多項式であり、k <nの場合はx k -1ではありません。

定義2

サイクロトミック多項式は、次のような、正の整数ごとに1つずつの多項式のセットです。

ここでk | nは、kがnを除算することを意味します。

定義3

n番目のサイクロトミック多項式は、多項式x n -1をx k -1 の形式の多項式のLCMで除算したものです。ここで、kはnとk <nを除算します。

  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 - 2× 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 - 2× 7 - X 6 - X 5 + X 2 + X + 1

仕事

正の整数を指定するとnn上記で定義した-thサイクロトミック多項式を妥当な形式で返します(つまり、係数のリストが許可されます)。

ルール

正しい値に丸める限り、浮動小数点/複素数を返すことができます。

得点

これはです。バイト単位の最短回答が優先されます。

参照資料


1
たぶんテストとして105を追加しますか?
ジョナサンアラン

@JonathanAllan 48語を入力したくない
リーキー修道女

1
浮動小数点の不正確さは許可されますか?
マイル

3
@miles私は情熱を持ってフロートが嫌いです。<<しかし、フロートを使用するあなたの権利を死から守ります。
リーキー修道女

1
最も近い整数/ガウス整数に丸めたときに正しい答えが得られる限り、複雑な浮動小数点数を出力できますか?
fireflame241

回答:


12

Haskell、120バイト

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]

オンラインでお試しください!

1.0000000000000078 :+ 3.314015728506092e-14浮動小数点の不正確さによるエントリのような複雑な浮動小数点のリストを提供します。乗算してその根から多項式を回復する直接的な方法。

fromIntegerHaskellの型システムに大きな譲歩です。もっと良い方法があるはずです。提案を歓迎します。結束の根を象徴的に扱うこともうまくいくかもしれません。


Haskell、127バイト

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

オンラインでお試しください!

インポートなし。

数式を使用します

LHSをRHSの他の各項で除算して、Φ_n(x)を計算します。

演算子%は、剰余がゼロであることに依存して、多項式で除算を行います。除数は、monicであると想定され、先頭に1を付けずに、無限大の末尾ゼロを付けて、を行うときに切り捨てを回避しzipWithます。


[0,0..]は、より短いバイトですrepeat 0
ライコニ

@flawr多項式を除算します。あなたのソリューションと同じ方法だと思います。
XNOR

それはいまいましい優雅に見えます、私は明日もっとよく見る必要があります:)
flawr

この答えにより、Haskellを学びたいと思うようになります。
ジュゼッペ

1
@xnor -1バイト
H.PWiz

7

Mathematica、43 41バイト

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

もちろん、私たちは常にビルトインを使用することができますが、私たちがいない場合は、この格差は、xはN -1ΦのでKX(再帰的に計算))ごとに適切な除数のためのkn個

Factor最後に多項式を取得するために使用します。これが機能する理由はx^#-1nの約数のすべてのサイクロトミック多項式を考慮して、不要なものを分割するためだと思います。

-2バイト、Jenny_mathyのおかげでFactor、分子にのみ適用されるように書き換えました。


2
これは素晴らしい!を使用してバイトを保存できますFactor@
J42161217

@Jenny_mathyそうすることで、Factor[x^#-1]/Times@@...代わりにパースするように見えます。括弧がなければ、括弧が必要です。
ミシャラヴロフ

1
[OK]を...私は...私はそれをテストしたときに、それが正しい結果を与えていたと言わざるを得ない
J42161217

それは面白い。これは、別のバイトを書き込むことFactor[x^#-1]/Times@@...で保存できることを意味し、また、どのように機能するのかわからないことも意味します。
ミシャラヴロフ


4

ハスケル250の236 233 218 216バイト

これは冗長バージョンです(@xnor はスコアのほぼ半分で実行できます)がn、十分なメモリがある限り動作することが保証されていますが、n番目のサイクロトミック多項式の生成に組み込みは使用しません。入力は任意のサイズの整数で、出力は(正確な)有理係数を持つ多項式タイプです。

ここでの大まかな考えは、多項式を再帰的に計算することです。以下のためn=1か、nプライム、それは簡単です。他のすべての数値では、このアプローチは基本的に定義2の式を使用します

解決しました。たくさんのバイトをありがとう@ H.PWiz!

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]

このためn=105、次の多項式が得られます(すべての%1分母を整理しました)。

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

多項式をn=15015見つけることができ、ここで(最大係数は23です)。

オンラインでお試しください!


+1ビルトインではないため。
DJMcMayhem

@flawrなぜ使用しているのRationalsですか?それはそれらなしで作業罰金に思える
H.PWiz

しますか?で問題が発生しquotRemPolyました。もう一度試してみましょう。
flawr

ああ、「問題」は、代わりにDouble使用しない場合Ratio Integer、非常に(非常に)大きい場合に問題を引き起こす可能性がある場合に係数を生成することでしたn
flawr

ええ...私はそれが問題だとは思わない。
H.PWiz



2

Mathematica、81バイト

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



1

CJam(52 51バイト)

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

オンラインデモ。これは、スタック上の整数を取り、スタック上に係数のビッグエンディアン配列を残す匿名ブロック(関数)です。

解剖

{                    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バイト

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

説明(選択):

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

初期化z =(1 + 0i)* x ^ 0

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

GCD計算。

h=Math

数学関数を非常に多く使用する必要があるため、ここで別の変数を使用しました。

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

多項式乗算。

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

出力を圧縮して整数配列に戻します。

出力:

整数の配列。位置iの要素はx ^ iの係数を表します。

JSが抱える問題の1つは、JSが多項式と複素数の計算用のネイティブライブラリを提供しないため、配列のような方法で実装する必要があることです。

console.log(phi(105))は、

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):未定義値をチェックするためのコードを変更しました

333> 284(-49):単純化できるため、多項式乗算関数を変更しました

284> 277(-7):いくつかの冗長コードを削除

277> 265(-12):2要素配列の代わりに2変数を使用して、配列の使用量の一部のバイトを削除します

265> 264(-1):Array.concat()の代わりにArray.push()を使用して4バイトを削減しますが、forループ中括弧とz変数に3を追加しました

264> 263(-1):最後の修正でさらにゴルフ

263> 262(-1):forループでゴルフ

262> 260(-2):if節をゴルフアウト

260> 258(-2):宣言をさらに結合

258> 252(-6):配列参照の再利用でゴルフ

252> 250(-2):一部の単項演算子を二項演算子として置き換え

250> 245(-5):Array.map()の増分をカウンターの最後の参照に移動してバイトを削除します

245> 242(-3):Array.push()の代わりにスプレッド構文を使用

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.