Cryptography/多项式运算

多项式(英语: Polynomial)是代数学中的基础概念, 是由称为未知数的变量和称为系数的常数通过有限次加减法, 乘法以及自然数幂次的乘方运算得到的代数表达式.

给定一个环 R(R 通常是交换环, 可以是有理数, 实数或者复数等等)以及一个未知数 x, 则任何形同:

a₀ + a₁⋅x + a₂⋅x² ... + aₙ⋅xⁿ

的代数表达式叫做 R 上的一元多项式. N 称作该多项式的 degree.

多项式加法和减法

接下来的代码我们使用一个数组来表示一个多项式. 在数组中, 系数从低到高排列, 例如, [1, 2, 3] 表示 1 + 2x + 3x². 考虑如下两个多项式:

px = [4, -2, 5] # p(x) = 5x² - 2x + 4
qx = [2, -5, 2] # q(x) = 2x² - 5x + 2
def polyadd(c1, c2):
    return [c1[i] + c2[i] for i in range(len(c1))]


def polysub(c1, c2):
    return [c1[i] - c2[i] for i in range(len(c1))]

assert polyadd(px, qx) == [6, -7, 7] # 7x² - 7x + 6
assert polysub(px, qx) == [2, 3, 3]  # 3x² + 3x + 2

多项式乘法

def polymul(c1, c2):
    p = [0 for _ in range(len(c1) + len(c2) - 1)]
    for i in range(len(c1)):
        for j in range(len(c2)):
            p[i+j] += c1[i] * c2[j]
    return p

assert polymul(px, qx) == [8, -24, 28, -29, 10] # 10x⁴ - 29x³ + 28x² - 24x + 8

多项式除法

需要使用 Polynomial long division 算法.

def polydivmod(c1, c2):
    # Algorithm: https://en.wikipedia.org/wiki/Polynomial_long_division
    # The code implementation is inspired by numpy.polynomial.polynomial.polydiv
    lc1 = len(c1)
    lc2 = len(c2)
    if lc1 < lc2:
        return [], c1
    if lc2 == 1:
        return [e / c2[0] for e in c1], []
    dif = lc1 - lc2
    scl = c2[-1]
    nc1 = c1.copy()
    nc2 = [e/scl for e in c2[:-1]]
    i = dif
    j = lc1 - 1
    while i >= 0:
        for k in range(lc2 - 1):
            nc1[i+k] -= nc2[k]*nc1[j]
        i -= 1
        j -= 1
    return [i/scl for i in nc1[j+1:]], nc1[:j+1]

def polydiv(c1, c2):
    return polydivmod(c1, c2)[0]


def polymod(c1, c2):
    return polydivmod(c1, c2)[1]

assert polydiv(px, qx) == [2.5]      # 2.5
assert polymod(px, qx) == [-1, 10.5] # 10.5x - 1

多项式求模逆

多项式的模逆要用到扩展欧几里得算法(Extended euclidean algorithm). 所谓的模逆就是 i1 * c1 % c2 == 1, 其中 i1 为 c1 关于 c2 的模逆.

def polydeg(c1):
    d = len(c1) - 1
    while c1[d] == 0 and d:
        d -= 1
    return d

def polyinv(c1, c2):
    # Algorithm: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
    newt, t = [1], [0]
    newr, r = c1, c2
    while polydeg(newr):
        quotient = polydiv(r, newr)
        r, newr = newr, polysub(r, polymul(newr, quotient))
        t, newt = newt, polysub(t, polymul(newt, quotient))
    return [e/newr[0] for e in newt[:polydeg(c2)]]

assert polymod(polymul(polyinv(px, qx), px), qx)[0] == 1

完整代码: https://github.com/mohanson/cryptography-python/blob/master/polynomial.py

使用 Numpy 计算多项式

import numpy

# p(x) = 5x² - 2x + 4
px = [4, -2, 5]

# q(x) = 2x² - 5x + 2
qx = [2, -5, 2]

r = numpy.polynomial.polynomial.polyadd(px, qx)
print('polyadd', r)  # 7x² - 7x + 6
r = numpy.polynomial.polynomial.polysub(px, qx)
print('polysub', r)  # 3x² + 3x + 2
r = numpy.polynomial.polynomial.polymul(px, qx)
print('polymul', r)  # 10x⁴ - 29x³ + 28x² - 24x + 8
quo, rem = numpy.polynomial.polynomial.polydiv(px, qx)
print('polydiv quo', quo)  # 2.5
print('polydiv rem', rem)  # 10.5x - 1

参考