【问题标题】:Interpolate polynomial over a finite field在有限域上插值多项式
【发布时间】:2018-01-02 17:33:56
【问题描述】:

我想在有限域的点上使用 python 插值多项式,并得到一个具有该域系数的多项式。 目前我正在尝试使用 SymPy 并专门进行插值(来自sympy.polys.polyfuncs),但我不知道如何强制插值发生在特定的 gf 中。如果没有,可以用另一个模块来完成吗?

编辑:我对 Python 实现/库感兴趣。

【问题讨论】:

    标签: python sympy polynomial-math galois-field finite-field


    【解决方案1】:

    SymPy 的interpolating_poly 不支持有限域上的多项式。但是在 SymPy 的底层有足够的细节来组合一个有限域类,并以一种直接的方式找到Lagrange polynomial 的系数。

    像往常一样,有限域 GF(pn) 的元素是度数小于 n 的represented by polynomials,系数在 GF(p) 中。乘法以 n 次减少多项式为模完成,该多项式在字段构建时选择。反演是通过扩展欧几里得算法完成的。

    多项式由系数列表表示,最高次数在前。比如GF(32)的元素是:

    [], [1], [2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]
    

    空列表代表0。

    GF 类,有限域

    将算术实现为方法addsubmulinv(乘法逆运算)。为方便测试,插值包括eval_poly,它在 GF(pn) 的一点评估具有 GF(pn) 中的系数的给定多项式。

    请注意,构造函数用作 G(3, 2),而不是 G(9),- 素数及其幂是分开提供的。

    import itertools
    from functools import reduce
    from sympy import symbols, Dummy
    from sympy.polys.domains import ZZ
    from sympy.polys.galoistools import (gf_irreducible_p, gf_add, \
                                         gf_sub, gf_mul, gf_rem, gf_gcdex)
    from sympy.ntheory.primetest import isprime
    
    class GF():
        def __init__(self, p, n=1):
            p, n = int(p), int(n)
            if not isprime(p):
                raise ValueError("p must be a prime number, not %s" % p)
            if n <= 0:
                raise ValueError("n must be a positive integer, not %s" % n)
            self.p = p
            self.n = n
            if n == 1:
                self.reducing = [1, 0]
            else:
                for c in itertools.product(range(p), repeat=n):
                  poly = (1, *c)
                  if gf_irreducible_p(poly, p, ZZ):
                      self.reducing = poly
                      break
    
        def add(self, x, y):
            return gf_add(x, y, self.p, ZZ)
    
        def sub(self, x, y):
            return gf_sub(x, y, self.p, ZZ)
    
        def mul(self, x, y):
            return gf_rem(gf_mul(x, y, self.p, ZZ), self.reducing, self.p, ZZ)
    
        def inv(self, x):
            s, t, h = gf_gcdex(x, self.reducing, self.p, ZZ)
            return s
    
        def eval_poly(self, poly, point):
            val = []
            for c in poly:
                val = self.mul(val, point)
                val = self.add(val, c)
            return val
    

    类 PolyRing,字段上的多项式

    这个比较简单:它实现多项式的加法、减法和乘法,参考地面场进行系数运算。有很多列表反转[::-1] 因为 SymPy 的惯例是列出以最高幂开始的单项式。

    class PolyRing():
        def __init__(self, field):
            self.K = field
    
        def add(self, p, q):
            s = [self.K.add(x, y) for x, y in \
                 itertools.zip_longest(p[::-1], q[::-1], fillvalue=[])]
            return s[::-1]       
    
        def sub(self, p, q):
            s = [self.K.sub(x, y) for x, y in \
                 itertools.zip_longest(p[::-1], q[::-1], fillvalue=[])]
            return s[::-1]     
    
        def mul(self, p, q):
            if len(p) < len(q):
                p, q = q, p
            s = [[]]
            for j, c in enumerate(q):
                s = self.add(s, [self.K.mul(b, c) for b in p] + \
                             [[]] * (len(q) - j - 1))
            return s
    

    内插多项式的构造。

    Lagrange polynomial 是针对列表 X 中的给定 x 值和数组 Y 中的相应 y 值构造的。它是基本多项式的线性组合,对应于 X 的每个元素。每个基本多项式通过乘以 @ 得到987654336@多项式,表示为[[1], K.sub([], x_k)]。分母是一个标量,因此更容易计算。

    def interp_poly(X, Y, K):
        R = PolyRing(K)
        poly = [[]]
        for j, y in enumerate(Y):
            Xe = X[:j] + X[j+1:]
            numer = reduce(lambda p, q: R.mul(p, q), ([[1], K.sub([], x)] for x in Xe))
            denom = reduce(lambda x, y: K.mul(x, y), (K.sub(X[j], x) for x in Xe))
            poly = R.add(poly, R.mul(numer, [K.mul(y, K.inv(denom))]))
        return poly
    

    使用示例:

    K = GF(2, 4) 
    X = [[], [1], [1, 0, 1]]                # 0, 1,   a^2 + 1
    Y = [[1, 0], [1, 0, 0], [1, 0, 0, 0]]   # a, a^2, a^3
    intpoly = interp_poly(X, Y, K)
    pprint(intpoly)
    pprint([K.eval_poly(intpoly, x) for x in X])  # same as Y
    

    漂亮的打印只是为了避免在输出上出现一些与类型相关的装饰。多项式显示为[[1], [1, 1, 1], [1, 0]]。为了提高可读性,我添加了一个函数以将其转换为更熟悉的形式,符号a 是有限域的生成器,x 是多项式中的变量。

    def readable(poly, a, x):
        return Poly(sum((sum((c*a**j for j, c in enumerate(coef[::-1])), S.Zero) * x**k \
                   for k, coef in enumerate(poly[::-1])), S.Zero), x)
    

    所以我们可以这样做

    a, x = symbols('a x')
    print(readable(intpoly, a, x))
    

    得到

    Poly(x**2 + (a**2 + a + 1)*x + a, x, domain='ZZ[a]')
    

    这个代数对象不是我们领域的多项式,这只是为了输出可读性。

    贤者

    作为替代方案,或者只是另一种安全检查,可以使用 Sage 的 lagrange_polynomial 来获取相同的数据。

    field = GF(16, 'a')
    a = field.gen()
    R = PolynomialRing(field, "x")
    points = [(0, a), (1, a^2), (a^2+1, a^3)]
    R.lagrange_polynomial(points)
    

    输出:x^2 + (a^2 + a + 1)*x + a

    【讨论】:

    • 谢谢! @if.... 一条评论 - 计算 denom 不必在包括 d 的循环中,因为它不依赖于它。
    • 我添加了一个类PolyRing,它简化了多项式的计算。
    • 顺便说一句,module(稍后发现)支持 GF 上的多项式。但它没有插值。
    • 总的来说,我认为您在这里的回答是 Python 中可能缺少的模块的良好基础。
    • 评论 - 由于这是 python,性能可能不是目标,但对于以性能为目标的情况,拉格朗日插值可以从 O(n^3) 提高到 O(n^2 ) 在 O(n^2) 时间内一次性计算所有 x[i] 的乘积系列,然后只用 O(n) 时间将每个 x[ 的多边形除以 (1-x[i]) i] 在外循环中。如果 x[] 值的集合是固定的,则更快,在这种情况下,产品系列可以预先计算一次。
    猜你喜欢
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-29
    • 2023-03-31
    相关资源
    最近更新 更多