环原子多项式


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)

我们可以使用二维欧几里得平面绘制复数,其中水平轴代表实部,垂直轴代表虚部。这样,(3,4)将对应于复数3 + 4i。

如果您熟悉极坐标,则极坐标中的(3,4)将为(5,arctan(4/3))。第一个数字r是点到原点的距离;第二个数字θ是从x轴正方向到该点逆时针测量的角度。结果,3 = rcosθ和4 = rsinθ。因此,我们可以写成3 + 4i为rcosθ + risinθ = r(cosθ+ isinθ)= reiθ

让我们求解复数方程z n = 1,其中n是一个正整数。

我们令z = reiθ。然后,Z Ñ = R Ñ Ë inθ。z n与原点的距离为r n,角度为nθ。但是,我们知道距原点的距离为1,角度为0。因此,r n = 1,nθ= 0。但是,如果再旋转2π,由于2π只是一个完整的圆,您仍然会在同一点结束。因此,r = 1且nθ=2kπ,得到z = e2ikπ/ n

我们重申我们的发现:z n = 1的解是z = e2ikπ/ n

多项式可以用其根表示。例如,x 2 -3x + 2 的根为1和2,因此x 2 -3x + 2 =(x-1)(x-2)。同样,从我们上面的发现中:

但是,该乘积肯定包含其他n的根。例如,取n = 8。z 4 = 1的根也将包含在z 8 = 1 的根内,因为z 4 = 1意味着z 8 =(z 42 = 1 2 =1。以n = 6为例。如果z 2 = 1,则z 6 = 1。同样,如果z 3 = 1,则z 6 = 1。

如果要提取z n = 1 唯一的根,则需要k和n除1外不共享任何公因数。否则,如果它们共享d> 1的公因数d,则z为(k / d)z n / d = 1的根。使用上面的技术根据多项式的根来写多项式,我们得到多项式:

注意,多项式是通过去除z n / d = 1 的根来完成的,其中d是n的除数。我们声称上面的多项式具有整数系数。考虑z n / d -1 形式的多项式的LCM,其中d> 1,d除以n。LCM的根正是我们希望删除的根。由于每个分量都有整数系数,因此LCM也有整数系数。由于LCM除以z n -1,因此商必须是具有整数系数的多项式,并且商是上面的多项式。

z n = 1 的根都具有半径1,因此它们形成一个圆。多项式代表n唯一的圆的点,因此在某种意义上,多项式形成圆的分区。因此,上面的多项式是第n个环原子多项式。(cyclo- =圆; tom- =切)

定义1

表示为的第n个环原子多项式是唯一的多项式,其整数系数除x n -1而不是k <n的x k -1。

定义2

环多项式是一组多项式,每个正整数一个,因此:

其中k | n表示k除以n。

定义3

第n圆多项式是多项式X Ñ -1由多项式的形式的LCM X划分ķ -1,其中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以合理的格式(即允许使用系数列表)返回如上定义的第-个循环多项式。

规则

您可以返回浮点数/复数,只要它们取整到正确的值即可。

计分

这是。以字节为单位的最短答案将获胜。

参考文献


1
也许加105作为测试?
乔纳森·艾伦,

@JonathanAllan我不想键入48项
破尼姑

1
是否允许浮点数错误?
英里

3
@miles我讨厌花车>。<但我会捍卫您使用花车的权利。
Leaky Nun

1
只要四舍五入到最接近的整数/高斯整数,我们是否可以输出复杂的浮点数?
fireflame241

Answers:


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由于浮动不正确导致的条目。一种直接相乘以从多项式的根中恢复多项式的直接方法。

fromInteger是Haskell字体系统的一大让步。必须有一个更好的方法。欢迎提出建议。象征性地处理团结的根基也可能有效。


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

运算符%依赖于余数为零对多项式进行除法。假定除数是一元数,并且除数不带前导1,并且除以无穷大的尾随零,以避免在执行时被截断zipWith


[0,0..]repeat 0。短一个字节。
Laikoni

@flawr除多项式。我认为这与您的解决方案相同。
xnor

看起来很该死,明天我得仔细看一看:)
瑕疵的

这个答案让我想学习Haskell。
朱塞佩

1
@xnor -1个字节
H.PWiz

7

Mathematica,43个 41字节

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

当然,我们总是可以使用内置的,但如果我们不这样做,这就将X ñ -1:Φ ķX为每一个适当的除数)(递归计算)ķñ

我们Factor用来最后得到一个多项式。我认为这起作用的原因是将x^#-1因子除以所有n的除数的多项式多项式,然后我们将不需要的多项式相除。

感谢Jenny_mathy,将-2个字节重写Factor为仅适用于分子。


2
这很棒!您可以使用Factor@
J42161217 '17

@Jenny_mathy这样做似乎可以解析Factor[x^#-1]/Times@@...;如果那里没有括号,则需要括号。
Misha Lavrov

1
好的...但是我不得不说,当我对其进行测试时,它给出了正确的结果...
J42161217

那很有意思。这意味着我们可以通过写入来保存另一个字节Factor[x^#-1]/Times@@...,这也意味着我不知道它如何工作。
Misha Lavrov


4

Haskell中250个236 233 218 216字节

这是一个冗长的版本(@xnor可以将其得分几乎提高一半),但是n只要您有足够的内存,就可以保证它可以在任何情况下工作,但是它不使用内置函数来生成第n个循环多项式。输入是任意大小的整数,输出是具有(精确)有理系数的多项式类型。

这里的粗略想法是递归计算多项式。对于n=1n素数而言,这是微不足道的。对于所有其他数字,此方法基本上使用定义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,然后让我再试一次!
瑕疵

嗯,“问题”是Double如果不使用它就会产生系数Ratio Integer,这可能会导致很大的问题n
瑕疵

嗯...我不认为这是个问题。
H.PWiz

3

果冻,23个字节

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

在线尝试!

输出为系数列表。

具有浮点数和复杂的错误。页脚会舍入以使输出更漂亮。



2

Mathematica,81个字节

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

2

[R 176个 171 139 112字节

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}

在线尝试!

大大简化的版本;使用for循环而不是Reduce


2

Pari / GP,8字节

内置的。

polcyclo

在线尝试!


Pari / GP,39字节,不带内置

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

使用公式:

ΦñX=Xñ-1个d<ñd|ñΦdX

在线尝试!


1

CJam(52 51字节)

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

在线演示。这是一个匿名块(函数),它在堆栈上取一个整数,并在堆栈上保留一个系数的big-endian数组。

解剖

{                    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存在的问题之一是,由于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.push()代替Array.concat()减少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()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.