Циклотомний многочлен


17

Фон (перехід до визначень)

Ейлер довів гарну теорему про складні числа: e ix = cos (x) + i sin (x).

Це робить теорему де Моєра легко довести:

(e ix ) n = e i (nx)

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

Ми можемо побудувати складні числа за допомогою двовимірної евклідової площини, при цьому горизонтальна вісь представляє реальну частину, а вертикальна вісь представляє уявну частину. Таким чином, (3,4) відповідатиме комплексу числа 3 + 4i.

Якщо ви знайомі з полярними координатами, (3,4) буде (5, арктан (4/3)) в полярних координатах. Перше число, r - відстань точки від початку початку; друге число θ - кут, виміряний від позитивної осі x до точки, проти годинникової стрілки. В результаті 3 = r cosθ і 4 = r sinθ. Тому ми можемо записати 3 + 4i як r cosθ + ri sinθ = r (cosθ + i sinθ) = re .

Розв’яжемо комплексне рівняння z n = 1, де n - натуральне число.

Нехай z = re . Тоді 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 = e 2ikπ / n .

Повторюємо своє відкриття: рішення z n = 1 є z = e 2ikπ / n .

Поліном може бути виражений у його коренях. Наприклад, коріння x 2 -3x + 2 дорівнюють 1 і 2, тому x 2 -3x + 2 = (x-1) (x-2). Аналогічно з нашого відкриття вище:

Однак цей продукт, безумовно, містив коріння інших російських. Наприклад, візьмемо n = 8. Коріння z 4 = 1 також будуть включені всередину коренів z 8 = 1, оскільки z 4 = 1 означає z 8 = (z 4 ) 2 = 1 2 = 1. Візьмемо n як 6 як приклад. Якщо z 2 = 1, то ми також мали б z 6 = 1. Так само, якщо z 3 = 1, то z 6 = 1.

Якщо ми хочемо витягти коріння, унікальні для z n = 1, нам знадобиться k і n, щоб поділити спільний дільник, окрім 1. Або, якщо вони мають спільний дільник d, де d> 1, то z було б (k / г) -й корінь z n / d = 1. Використовуючи вищевказану техніку для написання многочлена з точки зору його коренів, отримуємо многочлен:

Зауважимо, що цей многочлен виконується шляхом видалення коренів z n / d = 1, причому d є дільником n. Ми стверджуємо, що многочлен вище має цілі коефіцієнти. Розглянемо LCM поліномів у вигляді z n / d -1, де d> 1 і d ділить n. Коріння LCM - це саме ті корені, які ми хочемо видалити. Оскільки кожен компонент має цілочисельні коефіцієнти, LCM має також цілі коефіцієнти. Оскільки LCM ділить z n -1, коефіцієнт повинен бути многочленом з цілим числовим коефіцієнтом, а коефіцієнт - поліномом вище.

Коріння z n = 1 усі мають радіус 1, тому вони утворюють коло. Поліном являє собою унікальні для n точки кола, тому в певному сенсі многочлени утворюють розділ кола. Тому поліном вище є n-м циклотомним многочленом. (cyclo- = коло; tom- = вирізати)

Визначення 1

Позначений n-й циклотомний многочлен - це унікальний многочлен з цілими коефіцієнтами, що ділить x n -1, але не x k -1 на k <n.

Визначення 2

Циклотомні многочлени - це набір многочленів, по одному на кожне додатне ціле число, таким чином, що:

де k | n означає k ділить n.

Визначення 3

N-й циклотомний многочлен - це многочлен x n -1, поділений на LCM поліномів у вигляді x k -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 - 2x 41 - x 40 - x 39 + x 36 + x 35 + x 34 + x 33 + x 32 + x 31 - x 28 - х 26 - х 24 - х 22 - х 20 + х 17 + х 16 + х 15 + х 14 + х 13 + х 12 - х9 - х 8 - 2х 7 - х 6 - х 5 + х 2 + х + 1

Завдання

Давши додатне ціле число n, поверніть n-мий циклотомний многочлен, як визначено вище, у розумному форматі (тобто список переліку коефіцієнтів дозволений).

Правила

Ви можете повертати числа з плаваючою точкою / комплексними, доки вони округляються до потрібного значення.

Оцінка балів

Це . Найкоротша відповідь у байтах виграє.

Список літератури


1
Може додати 105 як тест?
Джонатан Аллан

@JonathanAllan Я не хочу вводити 48 термінів
Leaky Nun

1
Чи допускаються неточності з плаваючою комою?
милі

3
@miles Я ненавиджу поплавців із пристрастю>. <, але я захищаю до смерті ваше право використовувати floats.
Leaky Nun

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до неточності поплавця. Прямий метод множення для відновлення многочлена з його коренів.

Це fromIntegerвелика поступка системі типів 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]

Спробуйте в Інтернеті!

Ніякого імпорту.

Використовує формулу

Обчислює Φ_n (x) шляхом ділення LHS на кожен з інших доданків у RHS.

Оператор %робить поділ на поліноми, спираючись на решту, що дорівнює нулю. Дільник вважається монічним і задається без провідного 1, а також з нескінченними відсталими нулями, щоб уникнути обрізання при виконанні zipWith.


[0,0..]- байт коротший, ніж repeat 0.
Лайконі

@flawr Розділяє многочлени. Я думаю, що це той самий метод, що і ваше рішення.
xnor

Це виглядає проклято елегантно, мені доведеться детальніше ознайомитися завтра :)
недолік

ця відповідь змушує мене навчитися Хаскелл.
Джузеппе

1
@xnor -1 байт
H.PWiz

7

Математика, 43 41 байт

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

Звичайно, ми завжди можемо використовувати вбудований, але якщо цього не зробити, це ділить x n -1 на Φ k ( x ) (обчислюється рекурсивно) для кожного власного дільника k of n .

Ми використовуємо Factorдля отримання многочлена в кінці. Я думаю, що причина цього працює в тому, що x^#-1входять у всі циклотомічні поліноми дільників n , а потім ми ділимо ті, яких ми не хочемо.

-2 байти завдяки Jenny_mathy, переписавши, що Factorстосується лише чисельника.


2
Це чудово! ви можете зберегти байт, скориставшисьFactor@
J42161217

@Jenny_mathy Робить це, здається, розбирається Factor[x^#-1]/Times@@...натомість; якби у нас не було дужок, ми б хотіли дужки.
Міша Лавров

1
ок ... але я мушу сказати, що коли я тестував це, він давав правильні результати ...
J42161217

Це цікаво. Це означає, що ми можемо зберегти ще один байт, записавши його Factor[x^#-1]/Times@@..., а це також означає, що я не маю поняття, як це працює.
Міша Лавров


4

Haskell , 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.
недолік

Е ... я не думаю, що це проблема.
H.PWiz

3

Желе , 23 байти

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

Спробуйте в Інтернеті!

Виходи як перелік коефіцієнтів.

Має плаваючу крапку І складні неточності. Нижній колонтитул робить округлення, щоб зробити вигляд красивішим.



2

Математика, 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

Оскільки мені потрібно використовувати функції 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]);

Використовувана формула така

enter image description here

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 байти, але додав 3 для дужок for-loop та змінної z

264> 263 (-1): Далі гольфували за останньою поправкою

263> 262 (-1): Гольф на петлі

262> 260 (-2): Поміщено пункт «if»

260> 258 (-2): Далі поєднуються декларації

258> 252 (-6): Гольф при повторному використанні посилань на масив

252> 250 (-2): Замініть деякі одинарні оператори як двійкові оператори

250> 245 (-5): перемістіть приріст у Array.map () до останньої посилання лічильника, щоб видалити байти

245> 242 (-3): використовуйте синтаксис розповсюдження замість Array.push ()

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.