style="width:...;height=...;" I do not know. If you tell the better way, be sure to redo it.
, regarding ordinary school operations of addition and multiplication of polynomials. The ideal of a ring is its subset, the difference of two elements of which lies in it and the product of any of its elements and an arbitrary element of the ring lies in this subset. The simplest example of an ideal is a set of multiples of five numbers as an ideal in the ring of integers. See for yourself that this is ideal. If it worked out, there would be no further problems.
is called an ideal basis if any polynomial from an ideal is represented as
where
- some polynomials. This fact is recorded as:
. Hilbert's theorem on a basis gives a remarkable result — every ideal (in a ring of polynomials) has a finite basis. This allows you to work in any situation just to work with finite bases of ideals.
. It is known from algebra that any polynomial lying in the ideal
, vanishes on any solution of the source system. And further, if
- another ideal basis
then the system
-mold and criterion
-Buchberger pair. Choose some "good" way of arranging monomials (for details - in the book) and denote by
the leading term (with respect to this ordering) of a polynomial
and through
- least common multiple of monomials
and
. Then
polynomial from
and
called the following structure:
-pair), that the ideal basis is its Gröbner basis if and only if
- a polynomial from any pair of its members gives the remainder 0 when dividing by a basis (how this is done will be explained later). This immediately prompts the algorithm for constructing such a basis:
-policy and share it with the remainder on the basis.
,
and so on. Then monomial
has a coefficient of 1 and degree [2,3,1] , while the monomial
- coefficient 2 and degree [0,0,3] . Pay attention to zero degrees! They are critical for implementing all the other methods. In general, we require that the lists of powers of all monomials (within the same task) have the same length. This will greatly simplify our work.c ”) and a list of degrees (each type “ a ”): data Monom ca = M c [a] deriving (Eq) Ord class. I will use the usual lexicographical ordering in powers, since it is very simple and at the same time corresponds to the rules of "good" ordering of monomials. We will also write the embodiment of the Show class just for the convenience of working with our system. instance (Eq c, Ord a) => Ord (Monom ca) where compare (M _ asl) (M _ asr) = compare asl asr instance (Show a, Show c, Num a, Num c, Eq a, Eq c) => Show (Monom ca) where show (M c as) = (if c == 1 then "" else show c) ++ (intercalate "*" $ map showOne $ (filter (\(p,_) -> p /= 0) $ zip as [1..])) where showOne (p,i) = "x" ++ (show i) ++ (if p == 1 then "" else "^" ++ (show p)) show function tries to be “smart”: it does not show the coefficient, if it is equal to 1, and it does not show variables of zero degree, nor does it show the first degree of variables. Like this: *Main> M 2 [0,1] 2x2 *Main> M 1 [2,2] x1^2*x2^2 show , all the time, so it’s worth explaining exactly how it works - I’m sure it scared anyone. Using zip as [1..] glue each power with its own variable number, then with filter (
(p _) -> p /= 0) filter (
(p _) -> p /= 0) filter (
(p _) -> p /= 0) get rid of zero degrees, we turn the description of each variable into a string through showOne and, finally, we glue everything together, interspersed with multiplication icons, using intercalate from Data.Listnewtype wrapper over a regular list will do: newtype Polynom ca = P [Monom ca] deriving (Eq) instance (Show a, Show c, Num a, Num c, Eq a, Eq c) => Show (Polynom ca) where show (P ms) = intercalate " + " $ map show ms show function is simple, since all the dirty work is already hidden in the type of monomials. It works like this: *Main> P [M 1 [3,1], M 1 [2,2], M 1 [1,1]] x1^3*x2 + x1^2*x2^2 + x1*x2 Ord incarnation). This will make it possible to realize some things much easier.head . A monomial is considered to be zero if its coefficient is zero, and a polynomial is considered to contain no monomials. Well, multiplication by a constant simply changes the coefficient: lt :: Polynom ca -> Monom ca lt (P as) = head as zero :: (Num c, Eq c) => Monom ca -> Bool zero (M c _) = c == 0 zeroP :: Polynom ca -> Bool zeroP (P as) = null as scale :: (Num c) => c -> Monom ca -> Monom ca scale c' (M c as) = M (c*c') as similar :: (Eq a) => Monom ca -> Monom ca -> Bool similar (M _ asl) (M _ asr) = asl == asr addSimilar :: (Num c) => Monom ca -> Monom ca -> Monom ca addSimilar (M cl as) (M cr _) = M (cl+cr) as zipWith . I think the code speaks for itself: mulMono :: (Num a, Num c) => Monom ca -> Monom ca -> Monom ca mulMono (M cl asl) (M cr asr) = M (cl*cr) (zipWith (+) asl asr) addPoly :: (Eq a, Eq c, Num c, Ord a) => Polynom ca -> Polynom ca -> Polynom ca addPoly (P l) (P r) = P $ go lr where go [] [] = [] go as [] = as go [] bs = bs go (a:as) (b:bs) = if similar ab then if (zero $ addSimilar ab) then go as bs else (addSimilar ab):(go as bs) else if a > b then a:(go as (b:bs)) else b:(go (a:as) bs) map and mulMono . And then we apply distributivity (“distribution law”, opening brackets) to the product of two polynomials and we obtain that we only need to multiply all the monomials of the first polynomial by the second and add the obtained results. We will execute multiplications with the help of the same map , and foldl' results using foldl' and addPoly . The code of these two operations was surprisingly short - shorter than the type descriptions! mulPM :: (Ord a, Eq c, Num a, Num c) => Polynom ca -> Monom ca -> Polynom ca mulPM (P as) m = P $ map (mulMono m) as mulM :: (Eq c, Num c, Num a, Ord a) => Polynom ca -> Polynom ca -> Polynom ca mulM l@(P ml) r@(P mr) = foldl' addPoly (P []) $ map (mulPM r) ml
divided into monomial
if there is such a monomial
, what
. Obviously, this is true only if each variable is included in
no less than
. Therefore, the check for divisibility can be implemented using the familiar zipWith function and the remarkable function and . And along with the check, it is easy to obtain the division procedure itself: dividable :: (Ord a) => Monom ca -> Monom ca -> Bool dividable (M _ al) (M _ ar) = and $ zipWith (>=) al ar divideM :: (Fractional c, Num a) => Monom ca -> Monom ca -> Monom ca divideM (M cl al) (M cr ar) = M (cl/cr) (zipWith (-) al ar) Fractional class. It is depressing, but nothing can be done.reduceMany , will require two auxiliary ones - reducable and reduce . The first of them checks whether the polynomial is divisible by another, and the second performs division. reducable :: (Ord a) => Polynom ca -> Polynom ca -> Bool reducable lr = dividable (lt l) (lt r) reduce :: (Eq c, Fractional c, Num a, Ord a) => Polynom ca -> Polynom ca -> Polynom ca reduce lr = addPoly lr' where r' = mulPM r (scale (-1) q) q = divideM (lt l) (lt r) reduceMany :: (Eq c, Fractional c, Num a, Ord a) => Polynom ca -> [Polynom ca] -> Polynom ca reduceMany h fs = if reduced then reduceMany h' fs else h' where (h', reduced) = reduceStep h fs False reduceStep h (f:fs) r | zeroP h = (h, r) | otherwise = if reducable hf then (reduce hf, True) else reduceStep h fs r reduceStep h [] r = (h, r) reduceMany function reduceMany to divide the polynomial into a base. If a division has occurred, the process continues; otherwise, it ends. The internal reduceStep function merely searches for the very first polynomial into which it can be divided, and divides, returning a remainder and a flag indicating whether a division has been made.
polynomial Its implementation is very simple, as is the auxiliary function for finding the smallest common multiple of monomials: lcmM :: (Num c, Ord a) => Monom ca -> Monom ca -> Monom ca lcmM (M cl al) (M cr ar) = M (cl*cr) (zipWith max al ar) makeSPoly :: (Eq c, Fractional c, Num a, Ord a) => Polynom ca -> Polynom ca -> Polynom ca makeSPoly lr = addPoly l' r' where l' = mulPM l ra r' = mulPM r la lcm = lcmM (lt l) (lt r) ra = divideM lcm (lt l) la = scale (-1) $ divideM lcm (lt r)
-polynomials from all pairs - “ checked ” - and the one in which only to be done is “ add ”. One step of the algorithm will look like this:
-polynomials between it and all polynomials of the first part and add all non-zero residues to the end of the second part
-polynomials from those pairs from which they are already counted and verified. The key function in this process is checkOne . It takes a polynomial (from the second part), as well as both parts, and returns a list of polynomials that should be added to the base. We use simple recursion on the first part, naturally without adding zero residues: checkOne :: (Eq c, Fractional c, Num a, Ord a) => Polynom ca -> [Polynom ca] -> [Polynom ca] -> [Polynom ca] checkOne f checked@(c:cs) add = if zeroP s then checkOne f cs add else s:(checkOne f cs (add ++ [s])) where s = reduceMany (makeSPoly fc) (checked++add) checkOne _ [] _ = [] foldl , but I will leave this as an exercise. It remains only to construct the Gröbner basis itself for this. The implementation of the algorithm step literally repeats its description, see for yourself: build checked add@(a:as) = build (checked ++ [a]) (as ++ (checkOne a checked add)) build checked [] = checked a passes to the first part, and all its nonzero residues go to the second. Note that it's enough to check
-polynomials of each polynomial of the second part only from the first, due to the displacements of polynomials between parts. It remains to be noted that to obtain the Gröbner basis from this one, it is enough to place one of its polynomials in the first part, the rest in the second and apply the build procedure, which is done in the function makeGroebner . makeGroebner :: (Eq c, Fractional c, Num a, Ord a) => [Polynom ca] -> [Polynom ca] makeGroebner (b:bs) = build [b] bs where build checked add@(a:as) = build (checked ++ [a]) (as ++ (checkOne a checked add)) build checked [] = checked
.
Rational type for greater accuracy): *Main> let f1 = P [M 1 [2,0], M (-2) [1,0], M 1 [0,2], M (-26) [0,1], M 70 [0,0]] :: Polynom Rational Int *Main> let f2 = P [M 1 [2,0], M (-22) [1,0], M 1 [0,2], M (-16) [0,1], M 160 [0,0]] :: Polynom Rational Int *Main> let f3 = P [M 1 [2,0], M (-20) [1,0], M 1 [0,2], M (-2) [0,1], M 76 [0,0]] :: Polynom Rational Int *Main> putStr $ unlines $ map show $ makeGroebner [f1,f2,f3] x1^2 + (-2) % 1x1 + x2^2 + (-26) % 1x2 + 70 % 1 x1^2 + (-22) % 1x1 + x2^2 + (-16) % 1x2 + 160 % 1 x1^2 + (-20) % 1x1 + x2^2 + (-2) % 1x2 + 76 % 1 (-20) % 1x1 + 10 % 1x2 + 90 % 1 15 % 1x2 + (-75) % 1
-polynomials need to be counted only for those pairs of polynomials whose higher members are not mutually simple - that is, their least common multiple is not just their product. In some sources about this case they say "polynomials have a link ". Add this optimization to our makeGroebner function.Source: https://habr.com/ru/post/177237/
All Articles