Полиномиальная факторизация в Haskell

С Хаммар Я сделал шаблон Haskell бит, который компилирует

$(zModP 5)

to

newtype Z5 = Z5 Int
instance Additive.C Z5 where
  (Z5 x) + (Z5 y) = Z5 $ (x + y) `mod` 5
...

теперь я столкнулся с проблемой, которую я не думаю, что смогу решить таким образом.

замечательный факт о многочленах заключается в том, что они неприводимы в рациональных, если они неприводимы по модулю некоторого простого p. У меня уже есть метод, который грубой силой пытается разложить многочлены на заданное (конечное) поле.

I хочется попробовать запустить эту функцию для нескольких полей. Вот что я хочу:

isIrreducible :: (FiniteField.C a) => Poly.T a -> Bool
isIrreducible p = ...

intPolyIrreducible :: Poly.T Int -> Bool
intPolyIrreducible p = isIrreducible (p :: Poly.T Z2) ||
                       isIrreducible (p :: Poly.T Z3) ||
                       isIrreducible (p :: Poly.T Z5) ||
                       ...

в основном, я хочу попробовать мой алгоритм факторинга для большого количества определений понятия "деление".

Я думаю, что это можно сделать с TH, но кажется, что это займет вечность. Мне интересно, если было бы проще просто передать мои арифметические операции в качестве параметра isIrreducible?

в качестве альтернативы кажется, что это может быть что-то Модуль Newtype может помочь, но я не могу думать о том, как он будет работать без использования TH таким образом, что было бы так же сложно...

у кого-нибудь есть мысли о том, как лучше достичь этого?

2 ответов


вы можете выполнять вычисления в конечных полях, используя числительные уровня типа, например, с type-level пакет:

{-# LANGUAGE ScopedTypeVariables #-}
module Mod where
import Data.TypeLevel.Num (Nat,toNum, reifyIntegral)

data Z p = Z Integer

instance Eq (Z p) where Z x == Z y = x == y
instance Ord (Z p) where -- dummy instance
instance Show (Z p) where show (Z n) = show n

instance Nat p => Num (Z p) where
    Z x + Z y = Z $ (x + y) `mod` toNum (undefined :: p)
    Z x - Z y = Z $ (x - y) `mod` toNum (undefined :: p)
    Z x * Z y = Z $ (x * y) `mod` toNum (undefined :: p)
    fromInteger n = Z (n `mod` toNum (undefined :: p))
    -- etc

-- Compute x^2-6 (mod p)
poly :: Nat p => Z p -> Z p
poly x = x*x-6

-- Computes whether x^2-6==0 (mod p), for x=3
checkPoly :: Integer -> Bool
checkPoly n = reifyIntegral n test
  where
    test :: forall p . Nat p => p -> Bool
    test _ = poly (3::Z p) == 0

test1 = map checkPoly [2,3,5]
-- Result: [False,True,False]

этот подход имеет то преимущество, что не требует нового экземпляра шаблона haskell для каждого числового типа. Недостатком является то, что он, вероятно, медленнее, чем решение шаблона haskell, поскольку каждая операция передает размер конечного поля через словарь классов.


это зависит немного от того, что Поли.T выглядит так, но можете ли вы написать функцию типа (например)

fmap :: (a -> b) -> (Poly.T a -> Poly.T b)

? Если это так, то имеет смысл иметь Z тип операции, которых не во время выполнения, когда их модуль не соответствует:

data Z = Z Int Int
instance Applicative.C Z where
    (Z m v) + (Z m' v')
        | m == m' = Z m ((v + v') `mod` m)
        | otherwise = error "mismatched modulus"

тогда вы можете написать что-то вроде этого в простом старом Haskell:

intPolyIrreducible :: Poly.T Int -> Bool
intPolyIrreducible p = any isIrreducible [fmap (Z m) p | m <- [2,3,5,7,11,13]]

конечно, это немного менее безопасный тип. Но это ясно из parametricity, что fmap (Z m) не будет вводить какие-либо несоответствия модули.