2025-08-08 04:22:49 -05:00

16 lines
36 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"hash": "7a4a253eba46e6fdfc96f2199afec056",
"result": {
"engine": "jupyter",
"markdown": "---\ntitle: \"Exploring Finite Fields: Preliminaries\"\ndescription: |\n How to do arithmetic when there is a finite number of numbers\nformat:\n html:\n html-math-method: katex\ndate: \"2024-01-09\"\ndate-modified: \"2025-07-16\"\ncategories:\n - algebra\n - finite field\n - haskell\n---\n\n<style>\n.cayley-table .header,\n.cayley-table td:nth-child(1),\n.cayley-table th:nth-child(1) {\n color: green;\n}\n</style>\n\n\n\n[Fields](https://en.wikipedia.org/wiki/Field_%28mathematics%29) are a basic structure in abstract algebra.\nRoughly, a field is a collection of elements paired with two operations,\n addition and multiplication, along with particular rules about their interactions.\nThe most important elements of a field are 0 (the additive identity),\n 1 (the multiplicative identity), and -1 (which forms additive inverses).\nMoreover, multiplicative inverses must also exist for everything but 0.\n\nCertain fields are widely-known, such as the rational numbers $\\mathbb{Q}$\n and complex numbers $\\mathbb{C}$.\nFinite fields also exist, such as the field with two elements.\nThis field only contains the elements 0 and 1[^1].\nThe addition and multiplication tables are consequently the simplest possible according to familiar rules.\n\n[^1]: The elements -1 and 1 are identical, unlike in most fields.\n\n:::: {layout-ncol=\"2\"}\n::: {.cayley-table}\n| + | 0 | 1 |\n|---|---|---|\n| 0 | 0 | 1 |\n| 1 | 1 | 0 |\n:::\n\n::: {.cayley-table}\n| × | 0 | 1 |\n|---|---|---|\n| 0 | 0 | 0 |\n| 1 | 0 | 1 |\n:::\n::::\n\nThis field expresses the parity of sums and products of two integers since,\n replacing \"0\" with \"even\" and \"1\" with odd:\n\n:::: {layout-ncol=\"2\"}\n::: {}\n- even + even = even\n- even + odd = odd\n- odd + even = odd\n- odd + odd = even\n:::\n\n::: {}\n- even × even = even\n- even × odd = even\n- odd × even = even\n- odd × odd = odd\n:::\n::::\n\n\nIn One's Prime\n--------------\n\nTwo is not unique as the only possible size for a finite field -- all prime numbers are also candidates.\nSuch fields are referred to as GF(*p*) or $\\mathbb{F}_p$,\n where the prime *p* is the *order* of the field.\n\nThese fields inherit the properties of integer arithmetic.\nAddition is cyclic mod *p*: 0 and *p* are equivalent to one another.\nThe role of -1 taken up by *p* - 1, and the additive inverse of an element *x* can be viewed in two ways:\n\n- Multiplying -1 in the field with x, (i.e., $(p - 1)x \\mod p$)\n- Counting backwards from zero (i.e., $p - x$)\n\nFor this to be a field, the product of any two elements in the field cannot be a multiple of *p*,\n since that would be congruent to 0.\nIf there were such a product, then *p* would share factors (other than 1) with one of the two terms.\nBut the order is prime, so this is impossible.\nMore strongly, multiplicative inverses [can be found algorithmically](\n https://en.wikipedia.org/wiki/Modular_multiplicative_inverse#Extended_Euclidean_algorithm\n ), although it is a somewhat tricky task.\n\n\nPolynomials\n-----------\n\nFor a given field *K*, we can also consider polynomials with coefficients\n from elements in the field, K\\[*x*\\].\nThe mathematical structure that polynomials belong to is called a\n [*ring*](https://en.wikipedia.org/wiki/Ring_%28mathematics%29).\nRings are slightly weaker than fields in the sense that there are not generally multiplicative inverses.\nAdditive and multiplicative identities must still exist however, corresponding to\n the zero polynomial and constant polynomial 1, respectively.\n\nSince GF(*p*) has a finite number of elements to consider, there are only so many choices\n for polynomial coefficients for a given degree.\nContrast this with for polynomials over the integers, where there are infinitely many monomials $mx + n$.\nLooking at polynomials over GF(2), we have:\n\n| Degree | Polynomial *q(x)* | List of coefficients of *q(x)* (ascending) | *q*(2) | *q*(2) (Binary) |\n|--------|-------------------|--------------------------------------------|--------|-----------------|\n| 1 | $x$ | [0, 1] | 2 | 10 |\n| 1 | $1 + x$ | [1, 1] | 3 | 11 |\n| 2 | $x^2$ | [0, 0, 1] | 4 | 100 |\n| 2 | $1 + x^2$ | [1, 0, 1] | 5 | 101 |\n| 2 | $x + x^2$ | [0, 1, 1] | 6 | 110 |\n| 2 | $1 + x + x^2$ | [1, 1, 1] | 7 | 111 |\n| 3 | $x^3$ | [0, 0, 0, 1] | 8 | 1000 |\n| 3 | $1 + x^3$ | [1, 0, 0, 1] | 9 | 1001 |\n| 3 | $x + x^3$ | [0, 1, 0, 1] | 10 | 1010 |\n| 3 | $1 + x + x^3$ | [1, 1, 0, 1] | 11 | 1011 |\n| ... | ... | ... | ... | ... |\n\n\n### The Base-ics\n\nThere is a very close correspondence between binary expansions and polynomials over GF(2).\nThis is evident by comparing the list of coefficients in the polynomial (column 3)\n with the binary expansions of the polynomial evaluated at 2 (column 5).\nThis gives a handy way of referring to polynomials (mod *p*) without having to write out\n each individual \"×\" or \"+\".\nIn fact, this is commonly used to compactly compute with (and refer to)\n [polynomials used in cyclic redundancy checks](\n https://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks\n ).\n\nAgain, 2 is not unique among primes.\nPolynomials over any prime field GF(*p*) can be expressed as integers in base *p*.\nTo make the correspondence more explicit, I'll use the $_p m$ to denote a decimal integer *m*\n whose base *p* expansion should be interpreted as a polynomial (mod *p*).\n*m* is also the value of the polynomial at *p*.\nFor example, $_2 11 = 1 + x + x^3$, and 1 + 2 + 2^3^ = 11.\n\n<details>\n<summary>\nHaskell implementation of duality between polynomials mod *p* and base *p* expansions of integers\n</summary>\n<br>\nThis implementation actually works for any base *b*, which is not necessarily prime.\n The only difference is that the coefficients lose \"field-ness\" for composite *b*.\n\n::: {#3b5ca3e7 .cell execution_count=3}\n``` {.haskell .cell-code}\nimport Data.List (unfoldr)\nimport Data.Tuple (swap)\n\n-- A polynomial is its ascending list of coefficients (of type a)\nnewtype Polynomial a = Poly { coeffs :: [a] } deriving Functor\n\n-- Interpret a number's base-b expansion as a polynomial\nasPoly :: Int -> Int -> Polynomial Int\n-- Build a list with f, which returns either Nothing\n-- or Just (next element of list, next argument to f)\nasPoly b = Poly . unfoldr f where\n -- Divide x by b. Emit the remainder and recurse with the quotient.\n f x | x /= 0 = Just $ swap $ divMod x b\n -- If there's nothing left to divide out, terminate\n | otherwise = Nothing\n\n-- Horner evaluation of a polynomial at the integer b\nevalPoly :: Int -> Polynomial Int -> Int\n-- Start with the highest coefficient\n-- Multiply by b at each step and add the coefficient of the next term\nevalPoly b (Poly p) = foldr (\\y acc -> acc*b + y) 0 p\n\n-- evalPoly n . asPoly n = id :: Int -> Int\n```\n:::\n\n\nAn interesting detail here is that the duality is expressed through `foldr` using multiplication\n and addition and `unfoldr` using divMod.\n</details>\n\n\n### Mono, not Stereo\n\nWith respect to their roots (which will soon become of primary interest), polynomials are *projective*.\nThat is, any scalar multiple of the polynomial has the same roots.\nFor GF(2), this is insignificant since 1 is the only nonzero element, but over GF(5),\n the following polynomials have the same roots:\n\n$$\n\\begin{align*}\n x^2 + 2 \\text{ over GF$(5)$} \\quad\n &\\longleftrightarrow \\quad {_5 27}\n \\\\\n 2x^2 + 4 \\text{ over GF$(5)$} \\quad\n &\\longleftrightarrow \\quad {_5 54}\n \\\\\n 3x^2 + 1 \\text{ over GF$(5)$} \\quad\n &\\longleftrightarrow \\quad {_5 76}\n \\\\\n 4x^2 + 3 \\text{ over GF$(5)$} \\quad\n &\\longleftrightarrow \\quad {_5 103}\n\\end{align*}\n$$\n\nOnly the first polynomial is a *monic* polynomial, or has a leading coefficient of 1.\nIt is preferable to work with these since the product of two monic polynomials is also monic.\n\nAn equivalent condition to this is that when evaluated with the order of the field,\n the polynomial has a value between 5^2^ and 2×5^2^ - 1 = 49.\nIn general, monic polynomials over GF(*p*) as integers fall in the range *p*^2^ and 2*p*^2^ - 1.\n\n<details>\n<summary>\nHaskell implementation of monic polynomials over GF(*p*)\n</summary>\n\nAgain, nothing about this definition depends on the base being prime.\n\n::: {#80a69734 .cell execution_count=4}\n``` {.haskell .cell-code}\n-- All monic polynomials of degree d with coefficients mod n\nmonics :: Int -> Int -> [Polynomial Int]\nmonics n d = map (asPoly n) [n^d..2*(n^d) - 1]\n\n-- All monic polynomials with coefficients mod n, ordered by degree\nallMonics :: Int -> [Polynomial Int]\nallMonics n = concat [monics n d | d <- [1..]]\n```\n:::\n\n\n</details>\n\nAs an aside, one can also read out monics by counting normally by using the digit alphabet\n {1, 0, -1, ..., -*p* + 2}.\nUnfortunately, these base-*p* expansions are more difficult to obtain algorithmically,\n and I'll leave this as an exercise to the reader.\n\n\n### Sieving out Irreducibles\n\nOver the integers, we can factor a number into primes.\nTo decide if a number is prime, we just divide it (using an algorithm like long division)\n by numbers less than it and see if we get a nonzero remainder.\n\nSimilarly, we can factor polynomials into *irreducible* polynomials,\n which have no \"smaller\" polynomial factors other than 1.\nMore precisely, by \"smaller\", we mean those of lesser degree.\nFor example, over the integers, the polynomial $x^2 - 1$ (degree 2) factors into\n $(x + 1)(x - 1)$ (both degree 1), but $x^2 + 1$ is irreducible.\n\nIn general, a factorization of a polynomial over the integers implies a factorization of one over GF(*p*),\n since the coefficients for each factor may be taken mod *p*.\nHowever, the converse does not hold.\nOver GF(2),\n\n$$\n(x + 1)^2 = x^2 + 2x + 1 \\equiv x^2 + 1 \\text{ over GF$(2)$}\n$$\n\n...but as just mentioned, the right-hand side is irreducible over the integers.\n\nJust like integers, we can use [polynomial long division](\n https://en.wikipedia.org/wiki/Polynomial_long_division\n ) with these objects to decide if a polynomial is irreducible.\n[Synthetic division](https://en.wikipedia.org/wiki/Synthetic_division) is an alternative which is\n slightly easier to implement (especially over GF(2), where it is, again, used in CRCs).\nIt only works for monic polynomials, but this is all we need.\n\n<details>\n<summary>\nHaskell implementation of synthetic division\n</summary>\n\nThe algorithm is similar to table-less algorithms for CRCs, but we don't have the luxury\n of working at the bit level with XOR for addition.\nWe also have to watch out for negation and coefficients other than 1 for when not working over GF(2).\n\n::: {#909bcee8 .cell execution_count=5}\n``` {.haskell .cell-code}\nzipAdd :: Num a => [a] -> [a] -> [a]\nzipAdd [] ds = ds\nzipAdd cs [] = cs\nzipAdd (c:cs) (d:ds) = (c + d):zipAdd cs ds\n\n-- Divide the polynomial ps by qs (coefficients in descending degree order)\nsynthDiv' :: (Eq a, Num a) => [a] -> [a] -> ([a], [a])\nsynthDiv' ps qs\n | head qs /= 1 = error \"Cannot divide by non-monic polynomial\"\n | otherwise = splitAt deg $ doDiv ps deg\n where\n -- Negate the denominator and ignore leading term\n qNeg = map negate $ tail qs\n -- The degree of the result, based on the degrees of the numerator and denominator\n deg = max 0 (length ps - length qs + 1)\n -- Pluck off the head of the list and add a shifted and scaled version of\n -- qs to the tail of the list. Repeat this d times\n doDiv xs 0 = xs\n doDiv (x:xs) d = x:doDiv (zipAdd xs $ map (*x) qNeg) (d - 1)\n\n-- Use Polynomial (coefficients in ascending degree order) instead of lists\nsynthDiv :: (Eq a, Num a) => Polynomial a -> Polynomial a\n -> (Polynomial a, Polynomial a)\nsynthDiv (Poly p) (Poly q) = (Poly $ reverse quot, Poly $ reverse rem)\n where (quot, rem) = synthDiv' (reverse p) (reverse q)\n```\n:::\n\n\n</details>\n\nThen, using our list of monic polynomials, we can use the same strategy\n for sieving out primes to find (monic) irreducibles.\n\n<details>\n<summary>\nHaskell implementation of an irreducible polynomial sieve over GF(*p*)\n</summary>\n\n::: {#5c9c99d0 .cell execution_count=6}\n``` {.haskell .cell-code}\n-- All irreducible monic polynomials with coefficients mod n\nirreducibles :: Int -> [Polynomial Int]\nirreducibles n = go [] $ allMonics n where\n -- Divide the polynomial x by i, then take the remainder mod n\n remModN x i = fmap (`mod` n) $ snd $ synthDiv x i\n -- Find remainders of x divided by every irreducible in \"is\".\n -- If any give the zero polynomial, then x is a multiple of an irreducible\n notMultiple x is = and [not $ all (==0) $ coeffs $ remModN x i | i <- is]\n -- Sieve out by notMultiple\n go is (x:xs)\n | notMultiple x is = x:go (x:is) xs\n | otherwise = go is xs\n```\n:::\n\n\n</details>\n\nSince we can denote polynomials by numbers, it may be tempting to freely switch\n between primes and irreducibles.\nHowever, irreducibles depend on the chosen field and do not generally correspond\n to the base-*p* expansion of a prime.\n\n::: {#b968c88e .cell .plain execution_count=7}\n``` {.haskell .cell-code code-fold=\"true\"}\ntexifyPoly :: (Num a, Eq a, Show a) => Polynomial a -> String\ntexifyPoly (Poly xs) = (\"$\" ++) $ (++ \"$\") $ texify' $ zip xs [0..] where\n texify' [] = \"0\"\n texify' ((c, n):xs)\n | all ((==0) . fst) xs = showPow c n\n | c == 0 = texify' xs\n | otherwise = showPow c n ++ \" + \" ++ texify' xs\n showPow c 0 = show c\n showPow 1 1 = \"x\"\n showPow c 1 = show c ++ showPow 1 1\n showPow 1 n = \"x^{\" ++ show n ++ \"}\"\n showPow c n = show c ++ showPow 1 n\n\n-- Simple prime sieve\nprimes = primes' [] 2 where\n primes' ps n\n | and [n `mod` p /= 0 | p <- ps] = n:primes' (n:ps) (n+1)\n | otherwise = primes' ps (n+1)\n\nsomePrimes = takeWhile (<50) primes\nsomeIrr2 = takeWhile (<50) $ map (evalPoly 2) $ irreducibles 2\n\nirr2PrimeTable = columns (\\(_, f) r -> f r) (\\(c, _) -> Headed c) [\n (\"Irreducible over GF(2), *q*(x)\", texifyPoly . (irreducibles 2 !!)),\n (\"*q*(2), [OEIS A014580](https://oeis.org/A014580)\",\n (\\x -> if x `notElem` somePrimes then redSpan $ show x else show x) .\n (someIrr2 !!)),\n (\"Prime\",\n (\\x -> if x `notElem` someIrr2 then greenSpan $ show x else show x) .\n (primes !!))\n ] where\n greenSpan x = \"<span style=\\\"color: green\\\">\" ++ x ++ \"</span>\"\n redSpan x = \"<span style=\\\"color: red\\\">\" ++ x ++ \"</span>\"\n\nmarkdown $ markdownTable irr2PrimeTable [0..10]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n| Irreducible over GF(2), *q*(x) | *q*(2), [OEIS A014580](https://oeis.org/A014580) | Prime |\n|--------------------------------|--------------------------------------------------|-------|\n| $x$ | 2 | 2 |\n| $1 + x$ | 3 | 3 |\n| $1 + x + x^{2}$ | 7 | <span style=\"color: green\">5</span> |\n| $1 + x + x^{3}$ | 11 | 7 |\n| $1 + x^{2} + x^{3}$ | 13 | 11 |\n| $1 + x + x^{4}$ | 19 | 13 |\n| $1 + x^{3} + x^{4}$ | <span style=\"color: red\">25</span> | <span style=\"color: green\">17</span> |\n| $1 + x + x^{2} + x^{3} + x^{4}$ | 31 | 19 |\n| $1 + x^{2} + x^{5}$ | 37 | <span style=\"color: green\">23</span> |\n| $1 + x^{3} + x^{5}$ | 41 | <span style=\"color: green\">29</span> |\n| $1 + x + x^{2} + x^{3} + x^{5}$ | 47 | 31 |\n\n:::\n:::\n\n\nThe red entry in column 2 is not prime.\nDually, the green entries in column 3 do not have binary expansions which correspond\n to irreducible polynomials over GF(2).\n\n\nMatrices\n--------\n\nAlong with polynomials over a finite field, we can also look at matrices.\nThe most interesting matrices are square ones, since the product of two square matrices\n is another square matrix.\nWith the zero matrix ($\\bf 0_n$) as the additive identity and the identity matrix\n ($\\bf 1_n$) as the multiplicative, square matrices also form a ring over the field *K*,\n denoted $K^{n \\times n}$.\n\nSquare matrices are associated to a [determinant](https://en.wikipedia.org/wiki/Determinant),\n which is an element from the underlying field.\nDeterminants are nice, since the determinant of the product of two matrices is\n the product of the determinants.\nThe determinant can be implemented using [Laplace expansion](\n https://en.wikipedia.org/wiki/Laplace_expansion\n ), which is also useful for inductive proofs.\n\n<details>\n<summary>\nHaskell implementation of Laplace expansion\n</summary>\n\nLaplace expansion is ludicrously inefficient compared to other algorithms,\n and is only shown here due to its \"straightforward\" implementation and use in proof.\nNumeric computation will not be used to keep the arithmetic exact.\n\n::: {#e93821be .cell execution_count=8}\n``` {.haskell .cell-code}\nimport Data.Array\nnewtype Matrix a = Mat { unMat :: Array (Int, Int) a } deriving Functor\n\n-- Simple function for building a Matrix from lists\ntoMatrix :: [[a]] -> Matrix a\ntoMatrix l = Mat $ listArray ((0,0),(n-1,m-1)) $ concat l where\n m = length $ head l\n n = length l\n\ndeterminant :: (Num a, Eq a) => Matrix a -> a\ndeterminant (Mat xs) = determinant' xs where\n -- Evaluate (-1)^i without repeated multiplication\n parity i = if even i then 1 else -1\n -- Map old array addresses to new ones when eliminating row 0, column i\n rowMap i (x,y) = (x+1, if y >= i then y+1 else y)\n -- Recursive determinant Array\n determinant' xs\n -- Base case: 1x1 matrix\n | n == 0 = xs!(0,0)\n -- Sum of cofactor expansions\n | otherwise = sum $ map cofactor [0..n] where\n -- Produce the cofactor of row 0, column i\n cofactor i\n | xs!(0,i) == 0 = 0\n | otherwise = (parity i) * xs!(0,i) * determinant' (minor i)\n -- Furthest extent of the bounds, i.e., the size of the matrix\n (_,(n,_)) = bounds xs\n -- Build a new Array by eliminating row 0 and column i\n minor i = ixmap ((0,0),(n-1,n-1)) (rowMap i) xs\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\n<style>/* Styles used for the Hoogle display in the pager */\n.hoogle-doc {\ndisplay: block;\npadding-bottom: 1.3em;\npadding-left: 0.4em;\n}\n.hoogle-code {\ndisplay: block;\nfont-family: monospace;\nwhite-space: pre;\n}\n.hoogle-text {\ndisplay: block;\n}\n.hoogle-name {\ncolor: green;\nfont-weight: bold;\n}\n.hoogle-head {\nfont-weight: bold;\n}\n.hoogle-sub {\ndisplay: block;\nmargin-left: 0.4em;\n}\n.hoogle-package {\nfont-weight: bold;\nfont-style: italic;\n}\n.hoogle-module {\nfont-weight: bold;\n}\n.hoogle-class {\nfont-weight: bold;\n}\n\n.get-type {\ncolor: green;\nfont-weight: bold;\nfont-family: monospace;\ndisplay: block;\nwhite-space: pre-wrap;\n}\n.show-type {\ncolor: green;\nfont-weight: bold;\nfont-family: monospace;\nmargin-left: 1em;\n}\n.mono {\nfont-family: monospace;\ndisplay: block;\n}\n.err-msg {\ncolor: red;\nfont-style: italic;\nfont-family: monospace;\nwhite-space: pre;\ndisplay: block;\n}\n#unshowable {\ncolor: red;\nfont-weight: bold;\n}\n.err-msg.in.collapse {\npadding-top: 0.7em;\n}\n\n.highlight-code {\nwhite-space: pre;\nfont-family: monospace;\n}\n\n.suggestion-warning { \nfont-weight: bold;\ncolor: rgb(200, 130, 0);\n}\n.suggestion-error { \nfont-weight: bold;\ncolor: red;\n}\n.suggestion-name {\nfont-weight: bold;\n}\n\n</style><div class=\"suggestion-name\" style=\"clear:both;\">Redundant bracket</div><div class=\"suggestion-row\" style=\"float: left;\"><div class=\"suggestion-warning\">Found:</div><div class=\"highlight-code\" id=\"haskell\">(parity i) * xs ! (0, i)</div></div><div class=\"suggestion-row\" style=\"float: left;\"><div class=\"suggestion-warning\">Why Not:</div><div class=\"highlight-code\" id=\"haskell\">parity i * xs ! (0, i)</div></div>\n```\n:::\n:::\n\n\n</details>\n\n\n### Back to Polynomials\n\nThe [characteristic polynomial](https://en.wikipedia.org/wiki/Characteristic_polynomial)\n is a stronger invariant which follows from the determinant.\nIt is defined as, for *λ* a scalar variable:\n\n$$\n\\begin{gather*}\n \\text{charpoly}(A) = p_A(\\lambda)\n = \\left| \\lambda I - A \\right|\n \\\\ \\\\\n = \\left| \\begin{matrix*}\n \\lambda - a_{00} & -a_{01} & ... & -a_{0n} \\\\\n -a_{10} & \\lambda - a_{11} & ... & -a_{1n} \\\\\n \\vdots & \\vdots & \\ddots & \\vdots \\\\\n -a_{n0} & -a_{n1} & ... & \\lambda - a_{nn} \\\\\n \\end{matrix*} \\right|\n\\end{gather*}\n$$\n\nLaplace expansion never gives *λ* a coefficient before recursing,\n so the characteristic polynomial is always monic.\nAlso, the characteristic polynomial is over the same field that the entries of the matrix were from.\n\n<details>\n<summary>\nHaskell implementation of the characteristic polynomial\n</summary>\nSince `determinant` was defined for all `Num` and `Eq`, it can immediately be applied\n if these instances are defined for polynomials.\n\n::: {#ed71ff74 .cell execution_count=9}\n``` {.haskell .cell-code}\ninstance (Eq a, Num a) => (Num (Polynomial a)) where\n (+) x@(Poly as) y@(Poly bs) = Poly $ zipAdd as bs\n\n --convolution\n (*) (Poly []) (Poly bs) = Poly []\n (*) (Poly as) (Poly []) = Poly []\n (*) (Poly as) (Poly bs) = Poly $ zeroize $ finish $ foldl convolve' ([], []) as\n where convolve' (xs, ys) a = (a:xs, sum (zipWith (*) (a:xs) bs):ys)\n finish (xs, ys) = (reverse ys ++) $ finish' xs $ tail bs\n finish' xs [] = []\n finish' xs ys = sum (zipWith (*) xs ys):finish' xs (tail ys)\n zeroize xs = if all (==0) xs then [] else xs\n\n -- this definition works\n negate = fmap negate\n -- but these two might run into some problems\n abs = fmap abs\n signum = fmap signum\n fromInteger 0 = Poly []\n fromInteger a = Poly [fromInteger a]\n\ninstance Eq a => Eq (Polynomial a) where\n (==) (Poly xs) (Poly ys) = xs == ys\n```\n:::\n\n\nThen, along with some helper matrix functions, we can build a function for the characteristic polynomial.\n\n::: {#b67e62af .cell execution_count=10}\n``` {.haskell .cell-code}\n-- Zero matrix\nzero :: Num a => Int -> Matrix a\nzero n = Mat $ listArray ((0,0),(n-1,n-1)) $ repeat 0\n\n-- Identity matrix\neye :: Num a => Int -> Matrix a\neye n = Mat $ unMat (zero n) // take n [((i,i), 1) | i <- [0..]]\n\n-- Maps\nmapRange :: Ix i => (i -> e) -> (i, i) -> [(i, e)]\nmapRange g r = map (\\x -> (x, g x)) $ range r\n\n-- Pointwise application\nzipWithArr :: Ix i => (a -> b -> c)\n -> Array i a -> Array i b -> Array i c\nzipWithArr f a b\n | ab == bb = array ab $ map (\\x -> (x, f (a!x) (b!x))) $ indices a\n | otherwise = error \"Array dimension mismatch\" where\n ab = bounds a\n bb = bounds b\n\n(|+|) (Mat x) (Mat y) = Mat $ zipWithArr (+) x y\n\n-- Characteristic Polynomial\ncharpoly :: Matrix Int -> Polynomial Int\ncharpoly xs = determinant $ eyeLambda |+| negPolyXs where\n -- Furthest extent of the bounds, i.e., the size of the matrix\n (_,(n,_)) = bounds $ unMat xs\n -- Negative of input matrix, after being converted to polynomials\n negPolyXs :: Matrix (Polynomial Int)\n negPolyXs = fmap (\\x -> Poly [-x]) xs\n -- Identity matrix times lambda (encoded as Poly [0, 1])\n eyeLambda :: Matrix (Polynomial Int)\n eyeLambda = (\\x -> Poly [x] * Poly [0, 1]) <$> eye (n+1)\n\nmarkdown $ texifyPoly $ charpoly $ toMatrix [[1,0],[0,1]]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$1 + -2x + x^{2}$\n:::\n:::\n\n\n</details>\n\nComputation using this definition is only good for demonstrative purposes.\nThe [Faddeev-LeVerrier algorithm](\n https://en.wikipedia.org/wiki/Faddeev%E2%80%93LeVerrier_algorithm\n ) circumvents Laplace expansion entirely and happens to generate the determinant along the way.\nHowever, it has some problems:\n\n- It inverts the order in which the determinant and characteristic polynomial are defined\n- It introduces division, which makes it unsuitable for direct use with matrices with entries mod *p*\n\nFortunately, we can just work with a matrix over the integers and mod out at the end instead,\n as the following diagram conveys:\n\n$$\n\\begin{array}{}\n \\mathbb{F}_p ^{n \\times n} & \\textcolor{green}{\\hookrightarrow} &\n \\normalsize \\mathbb{Z}^{n \\times n} &\n \\overset{\\mod p ~~}{\\longrightarrow} &\n \\mathbb{F}_p^{n \\times n} & \\scriptsize \\phantom{text{charpoly}}\n \\\\[10pt]\n & \\scriptsize \\textcolor{green}{\\text{charpoly (FL)}} &\n \\textcolor{green}{\\downarrow} & &\n \\textcolor{red}{\\downarrow} & \\scriptsize \\textcolor{red}{\\text{charpoly (LE)}}\n \\\\[12pt]\n & & \\mathbb{Z}[\\lambda] &\n \\textcolor{green}{\\underset{\\mod p ~~}{\\longrightarrow}} &\n \\mathbb{F}_p[\\lambda] & \\scriptsize \\phantom{text{charpoly}}\n\\end{array}\n$$\n\nThe top row are matrices and the bottom row are polynomials.\nTo get to the bottom-right, which contains the characteristic polynomial over GF(*p*),\n we can avoid the red arrow and follow the path in green instead.\n\n\n### Friends Among Matrices\n\nIn the reverse direction, a matrix with a specific characteristic polynomial\n can be constructed from a polynomal.\nThe matrix is called the [companion matrix](https://en.wikipedia.org/wiki/Companion_matrix),\n and is defined as\n\n$$\n\\begin{gather*}\n p(\\lambda) = \\lambda^n + p_{n-1}\\lambda^{n-1} + ... + p_1 \\lambda + p_0\n \\\\[10pt]\n C_{p(\\lambda)} = \\left( \\begin{matrix}\n 0 & 1 & 0 & ... & 0 \\\\\n 0 & 0 & 1 & ... & 0 \\\\\n \\vdots & \\vdots & \\vdots & \\ddots & \\vdots \\\\\n 0 & 0 & 0 & ... & 1 \\\\\n -p_0 & -p_1 & -p_2 & ... & -p_{n-1}\n \\end{matrix} \\right)\n = \\left( \\begin{matrix}\n \\overrightharpoon 0_{n-1} & \\bold{1}_{n-1} \\\\\n -p_0 & -(\\overrightharpoon{p}_{1:n-1})^T\n \\end{matrix} \\right)\n \\\\ \\\\[1pt]\n \\text{charpoly}(C_{p}) = p_{C_{p}}(\\lambda) = p(\\lambda)\n\\end{gather*}\n$$\n\nThe definition of the companion matrix only depends on elements having an additive inverse,\n which is always true in a field.\nTherefore, there are always matrices over a field that have a monic polynomial\n as their characteristic polynomial.\n\nProving that the companion matrix has the characteristic polynomial it was constructed from\n can be done via Laplace expansion:\n\n$$\n\\begin{gather*}\n p_{0:n-1}(\\lambda)\n = \\left| \\begin{matrix}\n \\textcolor{red}{\\lambda} & -1 & 0 & ... & 0 \\\\\n 0 & \\lambda & -1 & ... & 0 \\\\\n \\vdots & \\vdots & \\vdots & \\ddots & \\vdots \\\\\n 0 & 0 & 0 & ... & -1 \\\\\n \\textcolor{green}{p_0} & p_1 & p_2 & ... & \\lambda + p_{n-1}\n \\end{matrix} \\right|\n \\\\ \\\\[1pt]\n = \\textcolor{green}{p_0} \\cdot (-1)^{n-1}\n \\left| \\begin{matrix}\n -1 & 0 & ... & 0 \\\\\n \\lambda & -1 & ... & 0 \\\\\n \\vdots & \\vdots & \\ddots & \\vdots \\\\\n 0 & 0 & ... & -1\n \\end{matrix} \\right|\n + \\textcolor{red}{\\lambda}\n \\left| \\begin{matrix}\n \\lambda & -1 & ... & 0 \\\\\n \\vdots & \\vdots & \\ddots & \\vdots \\\\\n 0 & 0 & ... & -1 \\\\\n p_1 & p_2 & ... & \\lambda + p_{n-1}\n \\end{matrix} \\right|\n \\\\ \\\\[1pt]\n = \\textcolor{green}{p_0} \\cdot (-1)^{n-1} \\cdot (-1)^{n-1}\n + \\textcolor{red}{\\lambda} \\cdot p_{1:n-1}(\\lambda)\n \\\\\n = p_0 + \\lambda(p_1 + \\lambda ( \\cdots (p_{n-1} + \\lambda ) \\cdots )))\n\\end{gather*}\n$$\n\nPleasantly, this yields the Horner form, which was used above to evaluate polynomials.\n\n<details>\n<summary>\nHaskell implementation of the companion matrix\n</summary>\n\n::: {#04d9e500 .cell execution_count=11}\n``` {.haskell .cell-code}\ncompanion :: Polynomial Int -> Matrix Int\ncompanion (Poly ps)\n | last ps' /= 1 = error \"Cannot find companion matrix of non-monic polynomial\"\n | otherwise = Mat $ array ((0,0), (n-1,n-1)) $ lastRow ++ shiftI where\n -- The degree of the polynomial, as well as the size of the matrix\n n = length ps' - 1\n -- Remove trailing 0s from ps\n ps' = reverse $ dropWhile (==0) $ reverse ps\n -- Address/value tuples for a shifted identity matrix:\n -- 1s on the diagonal just above the main diagonal, 0s elsewhere\n shiftI = map (\\p@(x,y) -> (p, if y == x + 1 then 1 else 0)) $\n range ((0,0),(n-2,n-1))\n -- Address/value tuples for the last row of the companion matrix:\n -- ascending powers of the polynomial\n lastRow = zipWith (\\x y -> ((n-1, x), y)) [0..n-1] $ map negate ps'\n\n-- (charpoly . companion) = id :: Polynomial Int -> Polynomial Int\n```\n:::\n\n\nApplying this to $1 - 2x + x^2$ gives us:\n\n::: {#0cd78ab4 .cell execution_count=12}\n``` {.haskell .cell-code code-fold=\"true\"}\nreshape :: Int -> [a] -> [[a]]\nreshape n = unfoldr (reshape' n) where\n reshape' n x = if null x then Nothing else Just $ splitAt n x\n\nfromMatrix :: Matrix a -> [[a]]\nfromMatrix (Mat m) = let (_,(_,n)) = bounds m in reshape (n+1) $ elems m\n\ntexifyMatrix mat = surround mat' where\n mat' = intercalate \" \\\\\\\\ \" $ map (intercalate \" & \" . map show) $\n fromMatrix mat\n surround = (\"\\\\left( \\\\begin{matrix}\" ++) . (++ \"\\\\end{matrix} \\\\right)\")\n\nmarkdown $ (\"$$\" ++) $ (++ \"$$\") $ texifyMatrix $ companion $ Poly [1, -2, 1]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\left( \\begin{matrix}0 & 1 \\\\ -1 & 2\\end{matrix} \\right)$$\n:::\n:::\n\n\n</details>\n\n\nField Extensions\n----------------\n\nAside from those of degree 1, the irreducible polynomials over a field cannot be factored\n into monomials over the field.\nIn other words, irreducibles have roots which do not exist as elements of the field.\nA *field extension* formalizes the notion by which one can make a larger field from another\n by adding roots of a polynomial.\n\n$x^2 + 1$ is irreducible, both over the integers and over an actual field like $\\mathbb{R}$.\nOn the other hand, it can be factored into $(x + i)(x - i)$ over $\\mathbb{C}$.\nWe can construct the latter field from the former if an extra number *i* exists alongside everything\n in $\\mathbb{R}$ such that *i*^2^ = -1.\nElements of this new field are linear combinations of multiples of powers of *i*\n less than the degree (in this case, 0 and 1; i.e., $a + bi$).\n\nThe equation that *i* obeys can be rewritten as $i^2 + 1 = 0$, which is the original polynomial,\n evaluated at *i*.\nIn order to refer explicitly to the construction of the bigger field from the polynomial,\n we write[^2] $\\mathbb{R}[x] / (x^2 + 1) \\cong \\mathbb{C}$.\n\n[^2]: *Technically*, the left hand side refers to something else\n (cosets of polynomials, from which we extract the canonical member *i*), but this description is good enough.\n\n\n### The Power of Primes\n\nWe can extend a finite field in the same way.\nOver GF(2), the smallest irreducible of degree 2 is $x^2 + x + 1$.\nUsing the same logic as before, we construct\n $\\mathbb{F}_2[x] / (x^2 + x + 1) \\cong \\mathbb{F}_2[\\alpha]$.\nThe new element *α* is a root of the polynomial and obeys the relations:\n\n$$\n\\begin{gather*}\n \\alpha^2 + \\alpha + 1 = 0\n \\\\\n \\alpha^2 = -\\alpha - 1 \\equiv \\alpha + 1 \\mod 2\n \\\\\n \\alpha^3 = \\alpha^2 + \\alpha = (\\alpha + 1) + \\alpha \\equiv 1 \\mod 2\n\\end{gather*}\n$$\n\nJust like *i*, only powers of *α* less than 2 (again, 0 and 1) are necessary\n to express elements of the field.\nSkipping a few steps, we can accumulate all possible sums and products over this new field\n into two new tables:\n\n:::: {layout-ncol=\"2\"}\n::: {.cayley-table}\n| + | 0 | 1 | *α* | *α* + 1 |\n|---------|---------|---------|---------|---------|\n| 0 | 0 | 1 | *α* | *α* + 1 |\n| 1 | 1 | 0 | *α* + 1 | *α* |\n| *α* | *α* | *α* + 1 | 0 | 1 |\n| *α* + 1 | *α* + 1 | *α* | 1 | 0 |\n:::\n\n::: {.cayley-table}\n| × | 0 | 1 | *α* | *α* + 1 |\n|---------|---------|---------|---------|---------|\n| 0 | 0 | 0 | 0 | 0 |\n| 1 | 0 | 1 | *α* | *α* + 1 |\n| *α* | 0 | *α* | *α* + 1 | 1 |\n| *α* + 1 | 0 | *α* + 1 | 1 | *α* |\n:::\n::::\n\nAs you might expect, the resulting field has 4 elements, so it's called GF(4) (or $\\mathbb{F}_4$).\nIn general, when adjoining an irreducible of degree *d* to GF(*p*), the resulting field has *p*^*d*^ elements,\n naturally denoted GF(p^d^) (or $\\mathbb{F}_{p^d}$).\n*p* is still special, and is called the *characteristic* of the field.\nIt denotes how many repeated additions are needed to get to 0.\nFrom the above table, it's clear that the characteristic is 2 since\n $1 + 1 = \\alpha + \\alpha = (\\alpha + 1) + (\\alpha + 1) = 0$.\n\n\n### ...and beyond?\n\nAll of this is manageable when you're adjoining a root of a degree 2 polynomial like *α* or *i*,\n but things get difficult when you start to work with higher degrees.\nThe powers of the root form the basis for a *d*-dimensional vector space over GF(*p*)\n (hence the order of the field being *p*^*d*^).\nProceeding as before, we'd have to be able to:\n\n- recognize equality in the new field based on sums of powers of roots (times elements of the field)\n- have a canonical method of expressing other elements after adjoining a root\n - ideally, handle both with an algorithm that gives canonical forms from noncanonical ones\n- know when we've found every element of the new field\n\nThese problems make it difficult to study prime power fields on a computer without the use of\n a CAS like Maple or Mathematica.\nThey're capable of taking care of these issues symbolically, working with the expressions\n in the same a mathematician would (or at least appearing to do so).\nAs someone who likes to do things himself, implementing a CAS from scratch seemed a little too cumbersome.\nFurthermore, even a more direct approach using the previously-mentioned\n \"canonical members of cosets of polynomials\" was more annoying than I was willing to put up with.\n\nFortunately, there's a detour that makes it much easier to dodge all of these problems,\n and it has some interesting consequences.\nJoin me in [the next post](../2) for a direct, non-symbolic way to work with prime power fields.\n\n",
"supporting": [
"index_files"
],
"filters": [],
"includes": {
"include-in-header": [
"<script src=\"https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js\" integrity=\"sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg==\" crossorigin=\"anonymous\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js\" integrity=\"sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==\" crossorigin=\"anonymous\" data-relocate-top=\"true\"></script>\n<script type=\"application/javascript\">define('jquery', [],function() {return window.jQuery;})</script>\n"
]
}
}
}