当前位置:首页 » 操作系统 » 蒙哥马利算法

蒙哥马利算法

发布时间: 2022-08-12 15:44:28

‘壹’ 蒙哥马利约减

资料上说:
“模乘过程中复杂度最高的环节是求模运算,因为一次除法实际上包含了多次加法、减法和乘法,如果在算法中能够尽量减少除法甚至避免除法,则算法的效率会大大提高。“
“我们最终实现了不含除法的模幂算法,这就是着名的蒙哥马利算法”

速幂运算的好处是减少了运算量,极大地提高了速度。例如A^65525(A的65535次幂),原始算法要做65535-1=65534次乘法,而快速幂运算只需要做(16-1)×2=30次乘法。

原理其实很好懂,假设要计算A^B,即底数是A,指数是B。把B写成二进制形式,拿4位来举例:B=b4b3b2b1(二进制)。
先用B=1111(二进制)来做解释。显然A^B = A × A^2 × A^4 × A^8。
又显然,
A^2 = A × A,
A^4 = A^2 × A^2,
A^8 = A^4 × A^4。
假如B的二进制位数更多,则依此类推。
上面这段看懂了吗?如果看懂的,就应该能够写出B=1111(二进制)情况下,A^B的快速幂运算程序。

‘贰’ RSA里的mod计算问题

RSA软件算法以及硬件实现都是利用蒙哥马利模乘实现你的要求的
由于RSA
的核心算法是模幂运算,模幂运算又相当于模乘运算的循环,要提高
RSA
算法的效率,首要问题在于提高模乘运算的效率。不难发现,模乘过程中复杂
度最高的环节是求模运算,因为一次除法实际上包含了多次加法、减法和乘法,如
果在算法中能够尽量减少除法甚至避免除法,则算法的效率会大大提高。
设A=Sum[i=0
to
k](A*2**i),0<=A<=1,则:
C=
A*B
=
Sum[i=0
to
k](A*B*2**i)
可用循环处理为:
C=0
FOR
i
FROM
k
TO
0
C=C*2
C=C+A*B
RETURN
C
若令
C'=
A*B*2**(-k),则:
C'=
Sum[i=0
to
k](A*B*2**(i-k))
用循环处理即:
C'=0
FOR
i
FROM
0
TO
k
C'=C'+A*B
C'=C'/2
RETURN
C'
通过这一算法求A*B*2**(-k)是不精确的,因为在循环中每次除以2都可能有余
数被舍弃了,但是可以通过这一算法求A*B*2**(-k)
%N的精确值,方法是在对C'除
2之前,让C'加上C'[0]*N。由于在RSA中N是两个素数的积,总是奇数,所以当C'是
奇数时,C'[0]=1,C'+C'[0]*N
就是偶数,而当C'为偶数时C'[0]=0,C'+C'[0]*N
还是偶数,这样C'/2
就不会有余数被舍弃。又因为C'+N
%N
=
C'
%N,所以在计算
过程中加若干次N,并不会影响结果的正确性。可以将算法整理如下:
C'=0
FOR
i
FROM
0
TO
k
C'=C'+A*B
C'=C'+C'[0]*N
C'=C'/2
IF
C'>=N
C'=C'-N
RETURN
C'
由于在RSA中A、B总是小于N,又0<=A,C'[0]<=1,所以:
C'
=
(C'+A*B+C'[0]*N)/2
C'
<
(C'+2N)/2
2C'
<
C'+2N
C'
<
2N
既然C'总是小于2N,所以求C'
%N
就可以很简单地在结束循环后用一次减法来
完成,即在求A*B*2**(-k)
%N的过程中不用反复求模,达到了我们避免做除法的目
的。当然,这一算法求得的是A*B*2**(-k)
%N,而不是我们最初需要的A*B
%N。但
是利用A*B*2**(-k)我们同样可以求得A**E
%N。
设R=2**k
%N,R'=2**(-k)
%N,E=Sum[i=0
to
n](E*2**i):
A'=A*R
%N
X=A'
FOR
i
FROM
n
TO
0
X=X*X*R'
%N
IF
E=1
X=X*A'*R'
%N
X=X*1*R'
%N
RETURN
X
最初:
X
=
A*R
%N,
开始循环时:
X
=
X*X*R'
%N
=
A*R*A*R*R'
%N
=
A**2*R
%N
反复循环之后:
X
=
A**E*R
%N
最后:
X
=
X*1*R'
%N
=
A**E*R*R'
%N
=
A**E
%N
如此,我们最终实现了不含除法的模幂算法,这就是着名的蒙哥马利算法,而
X*Y*R'
%N
则被称为“蒙哥马利模乘”。以上讨论的是蒙哥马利模乘最简单,最容
易理解的二进制形式。蒙哥马利算法的核心思想在于将求A*B
%N转化为不需要反复
取模的A*B*R'
%N,但是利用二进制算法求1024位的A*B*R'
%N,需要循环1024次之
多,我么必然希望找到更有效的计算A*B*R'
%N的算法。
考虑将A表示为任意的r进制:
A
=
Sum[i=0
to
k](A*r**i)
0<=A<=r
我们需要得到的蒙哥马利乘积为:
C'=
A*B*R'
%N
R'=r**(-k)
则以下算法只能得到C'的近似值
C'=0
FOR
i
FROM
0
TO
k
C'=C'+A*B
C'=C'/r
IF
C'>=N
C'=C'-N
RETURN
C'
因为在循环中每次C'=C'/r
时,都可能有余数被舍弃。假如我们能够找到一个
系数
q,使得(C'
+
A*B
+
q*N)
%r
=0,并将算法修改为:
C'=0
FOR
i
FROM
0
TO
k
C'=C'+A*B+q*N
C'=C'/r
IF
C'>=N
C'=C'-N
RETURN
C'
则C'的最终返回值就是A*B*R'
%N的精确值,所以关键在于求q。由于:
(C'
+
A*B
+
q*N)
%r
=0
==>
(C'
%r
+
A*B
%r
+
q*N
%r)
%r
=0
==>
(C'[0]
+
A*B[0]
+
q*N[0])
%r
=0
若令N[0]*N[0]'
%r
=1,q=(C'[0]+A*B[0])*(r-N[0]')
%r,则:
(C'[0]
+
A*B[0]
+
q*N[0])
%r
=
(C'[0]+A*B[0]
-
(C'[0]+A*B[0])*N[0]'*N[0])
%r)
%r
=
0
于是我们可以得出r为任何值的蒙哥马利算法:
m=r-N[0]'
C'=0
FOR
i
FROM
0
TO
k
q=(C'[0]+A*B[0])*m
%r
C'=(C'+A*B+q*N)/r
IF
C'>=N
C'=C'-N
RETURN
C'
如果令
r=0x100000000,则
%r

/r
运算都会变得非常容易,在1024位的运
算中,循环次数k
不大于32,整个运算过程中最大的中间变量C'=(C'+A*B+q*N)
<
2*r*N
<
1057位,算法效率就相当高了。唯一的额外负担是需要计算
N[0]',使
N[0]*N[0]'
%r
=1,而这一问题前面已经用欧几里德算法解决过了,而且在模幂运
算转化成反复模乘运算时,N是固定值,所以N[0]'只需要计算一次,负担并不大。

‘叁’ 如何用python编写一个素数环

此文主要目的,是向大家展示如何才能用python语言,来部署STARK算法。
STARKs(可扩容的透明知识论证)是创建一种证明的技术,这项证明中f(x)=y,其中f可能要花很长的时间来进行计算,但是这个证明可以被很快验证。STARK是“双重扩容”:对于一个需要t步骤的计算,这会花费大约O(t * log(t))步骤才能完成这个证明,这可能是最优的情况,而且这需要通过~O(log2(t))个步骤才能验证,对于中等大小的T值,它比原始计算快得多。STARKs也拥有隐私保护的“零知识证明”的特性,虽然我们将这类使用案例应用到其中,从而完成可验证的延迟功能,不需要这类性质,所以我们不用担心。
首先,先请几项说明:
这个代码还没有完全审核;在实际使用案例中的情况,还不能保证
这部分代码是还没有达到理想状态(是用Python语言写的)
STARKs 的“真实情况” 倾向于使用二进制字段而不是素数域的特定应用程序效率的原因;但是,他们确实也表现出,这里写出的代码是合法并且可用的。
没有一个真实的方法来使用STARK。它是一个非常宽泛的加密和数学架构,同时为不同的应用有不同的设置,以及连续的研究来减少证明者和验证者的复杂性,同时提高可用性。
此文希望大家能够知道,模运算和素数域是如何运行的,
并且和多项式概念,插值和估值进行结合。
现在,让我们一起来了解吧!
MIMC
下面是STARK的功能展示:
def mimc(inp, steps, round_constants): start_time = time.time() for i in range(steps-1): inp = (inp**3 + round_constants[i % len(round_constants)]) % molus print("MIMC computed in %.4f sec" % (time.time() - start_time)) return inp
我们选择MIMC作为案例,因为它(i)很容易理解,(ii)在真实世界使用的很多。函数功能见下图:
注意:在很多关于MIMC的讨论中,你可以典型地看出使用了XOR,而不是+;这是因为MIMC可以在二进制情况下使用,其中添加是XOR;这里我们会在素数领域进行。
在我们的案例中,常数相对而言会是比较小的列表(例如,64位),这会一直连续地进行周期循环(也就说,在k[64]之后)。MIMC自身可以获得这个特性,因为MIMC可以向后进行计算(从相应的输出获得输入),但是往后计算需要比向前计算多花费100倍的时间(并且没有方向可以同步进行)。所以你可以将往后计算的功能想象成计算不能同步的工作量证明,并且往前方向计算的功能可以作为验证的过程。
x -> x(2p-1)/3 是x -> x3 的反函数;根据费马小定理,这是真实的,尽管这个定理没有费马大定理出名,但是依然对数学的贡献很大。
我们尝试使用STARK来进行更加有效的验证,而不是让验证者必须在向前方向运行MIMC,在完成向后计算之后,证明者可以在向前方向进行STARK计算,并且验证者可以很简单地验证STARK。我们希望计算STARK可以比MIMC向前和向后之间的运行速度差别要小,所以证明者的时间仍然是有初始的向后计算来主导的。而并不是STARK计算。STARK的认证会相对较快(在python语言算法中,可以是0.05-0.3秒),不论初始的计算时间有多长。
所有的计算会在2256 – 351 * 232 + 1个模内完成;我们使用素数模,因为它是小于2256 最大的素数,其中乘法群包含了232 个子集(也就是说,有这样一个数g,从而在完全232次循环之后,G素数环的连续幂模绕回到1),而且是按照6k+5的形式。首个特性是保证FFT和FRI算法的有效版本,其次是保证MIMC实际上可以向后计算(请见上面提到的x -> x(2p-1)/3 使用方法)。
素域操作
我们通过建立方便的等级来进行素域的操作,同时也有多项式的操作。代码如下,收首先是小数位数:
class PrimeField(): def __init__(self, molus): # Quick primality test assert pow(2, molus, molus) == 2 self.molus = molus def add(self, x, y): return (x+y) % self.molus def sub(self, x, y): return (x-y) % self.molus def mul(self, x, y): return (x*y) % self.molus
并且使用扩展欧几里得算法,来计算模块逆转(这和在素域中计算1/x相同):
# Molar inverse using the extended Euclidean algorithm def inv(self, a): if a == 0: return 0 lm, hm = 1, 0 low, high = a % self.molus, self.molus while low > 1: r = high//low nm, new = hm-lm*r, high-low*r lm, low, hm, high = nm, new, lm, low return lm % self.molus
上面的算法是相对昂贵的;幸运地是,对于特定的案例,我们需要做很多的模逆计算,有一个数学方法可以让我们来计算很多逆运算,被称为蒙哥马利批量求逆:
使用蒙哥马利批量求逆来计算模逆,其输入为紫色,输出为绿色,乘法门为黑色,红色方块是唯一的模逆。
下面的代码是算法的体现,其中包含一些特别的逻辑。如果我们正在求逆的集合中包含零,那么它会将这些零的逆设置为 0 并继续前进。
def multi_inv(self, values): partials = [1] for i in range(len(values)): partials.append(self.mul(partials[-1], values[i] or 1)) inv = self.inv(partials[-1]) outputs = [0] * len(values) for i in range(len(values), 0, -1): outputs[i-1] = self.mul(partials[i-1], inv) if values[i-1] else 0 inv = self.mul(inv, values[i-1] or 1) return outputs
这部分算法接下来会验证称为非常重要的东西,特别是当我们开始和不同阶的多项式进行计算的时候。
现在我们来看看一些多项式计算。我们把多项式当做一个数据集,其中的i是第i阶(例如,x3 + 2x + 1变成[1, 2, 0, 1])。下面就是在一个点进行多项式估算的方法:
# Evaluate a polynomial at a point def eval_poly_at(self, p, x): y = 0 power_of_x = 1 for i, p_coeff in enumerate(p): y += power_of_x * p_coeff power_of_x = (power_of_x * x) % self.molus return y % self.molus
困难和挑战
f.eval_poly_at([4, 5, 6], 2)的输出是多少?模是31吗?
下面的解释就是答案
.其实也有代码是多项式加法,减法,乘法和除法;这是很长的加减乘除运算。有一个很重要的内容是拉格朗日插值,它将一组 x 和 y 坐标作为输入,并返回通过所有这些点的最小多项式(你可以将其视为多项式求值的逆):
# Build a polynomial that returns 0 at all specified xs def zpoly(self, xs): root = [1] for x in xs: root.insert(0, 0) for j in range(len(root)-1): root[j] -= root[j+1] * x return [x % self.molus for x in root] def lagrange_interp(self, xs, ys): # Generate master numerator polynomial, eg. (x - x1) * (x - x2) * ... * (x - xn) root = self.zpoly(xs) # Generate per-value numerator polynomials, eg. for x=x2, # (x - x1) * (x - x3) * ... * (x - xn), by dividing the master # polynomial back by each x coordinate nums = [self.div_polys(root, [-x, 1]) for x in xs] # Generate denominators by evaluating numerator polys at each x denoms = [self.eval_poly_at(nums[i], xs[i]) for i in range(len(xs))] invdenoms = self.multi_inv(denoms) # Generate output polynomial, which is the sum of the per-value numerator # polynomials rescaled to have the right y values b = [0 for y in ys] for i in range(len(xs)): yslice = self.mul(ys[i], invdenoms[i]) for j in range(len(ys)): if nums[i][j] and ys[i]: b[j] += nums[i][j] * yslice return [x % self.molus for x in b]
相关数学知识请参见此文的M-N部分。需要注意,我们也会有特别的方法lagrange_interp_4和lagrange_interp_2来加速次数小于 2 的拉格朗日插值和次数小于 4 的多项式运算。
快速傅立叶变换
如果你仔细阅读上面的算法,你也许会发现拉格朗日插值和多点求值(即求在N个点处次数小于N的多项式的值)都需要耗费2次时间,例如对于1000个点求拉格朗日插值,需要几百万个步骤,而且100万个点的拉格朗日插值需要万亿个步骤。这是不可接受的低效率,所以我们需要使用更加有效的算法,快速傅立叶变换。
FFT只需要花费O(n * log(n))的时间(也就是说,1000个点的计算需要10,000步,100万个点的计算需要2000步),虽然它的范围更受限制;x坐标必须是单位根部的完全集合,必须满足N = 2k 阶。也就是说,如果有N个点,那么x坐标必须某个P值的连续幂,1, p, p2, p3…,其中pN = 1。这个算法能够用来进行多点计算和插值计算,而且只需要调整一个小参数。
下面就是算法详情(这是个简单的表达方式;更详细内容可以参阅此处代码)
def fft(vals, molus, root_of_unity): if len(vals) == 1: return vals L = fft(vals[::2], molus, pow(root_of_unity, 2, molus)) R = fft(vals[1::2], molus, pow(root_of_unity, 2, molus)) o = [0 for i in vals] for i, (x, y) in enumerate(zip(L, R)): y_times_root = y*pow(root_of_unity, i, molus) o[i] = (x+y_times_root) % molus o[i+len(L)] = (x-y_times_root) % molus return o def inv_fft(vals, molus, root_of_unity): f = PrimeField(molus) # Inverse FFT invlen = f.inv(len(vals)) return [(x*invlen) % molus for x in fft(vals, molus, f.inv(root_of_unity))]
你可以自己通过一些输入来运行代码,并且看看是否能得到想要的结果,当你使用eval_poly_at的时候,给出你期望得到的答案。例如:
>>> fft.fft([3,1,4,1,5,9,2,6], 337, 85, inv=True) [46, 169, 29, 149, 126, 262, 140, 93] >>> f = poly_utils.PrimeField(337) >>> [f.eval_poly_at([46, 169, 29, 149, 126, 262, 140, 93], f.exp(85, i)) for i in range(8)] [3, 1, 4, 1, 5, 9, 2, 6]
傅里叶变换会把[x[0] …. x[n-1]]作为输入,并且它的目标是输出x[0] + x[1] + … + x[n-1]作为首个元素,x[0] + x[1] * 2 + … + x[n-1] * w**(n-1)作为第二个元素,等等;快速傅里叶变换可以通过把数据分为两半,来完成这个,在两边都进行FFT,然后将结果结合在一起。
上图就是信息如何进行FFT运算的解释。请注意FFT是如何进行两次数据复制,并且进行粘合,直到你得到一个元素。
现在,我们把所有部分组合起来,看看整件事情是如何:def mk_mimc_proof(inp, steps, round_constants),它生成运行 MIMC 函数的执行结果的证明,其中给定的输入为步骤数。首先,是一些 assert 函数:
# Calculate the set of x coordinates xs = get_power_cycle(root_of_unity, molus) column = [] for i in range(len(xs)//4): x_poly = f.lagrange_interp_4( [xs[i+len(xs)*j//4] for j in range(4)], [values[i+len(values)*j//4] for j in range(4)], ) column.append(f.eval_poly_at(x_poly, special_x))
扩展因子是我们将要拉伸的计算轨迹(执行 MIMC 函数的“中间值”的集合)。
m2 = merkelize(column) # Pseudo-randomly select y indices to sample # (m2[1] is the Merkle root of the column) ys = get_pseudorandom_indices(m2[1], len(column), 40) # Compute the Merkle branches for the values in the polynomial and the column branches = [] for y in ys: branches.append([mk_branch(m2, y)] + [mk_branch(m, y + (len(xs) // 4) * j) for j in range(4)])
我们需要步数乘以扩展因子最多为 2^32,因为当 k > 32 时,我们没有 2^k 次的单位根。
computational_trace_polynomial = inv_fft(computational_trace, molus, subroot) p_evaluations = fft(computational_trace_polynomial, molus, root_of_unity)
我们首个计算会是得出计算轨迹;也就是说,所有的计算中间值,从输入到输出。
assert steps <= 2**32 // extension_factor assert is_a_power_of_2(steps) and is_a_power_of_2(len(round_constants)) assert len(round_constants) < steps
然后,我们会从将计算轨迹转换为多项式,在单位根 g (其中,g^steps = 1)的连续幂的轨迹上“放下”连续值,然后我们对更大的集合——即单位根 g2 的连续幂,其中 g2^steps * 8 = 1(注意 g2^8 = g)的多项式求值。
# Generate the computational trace computational_trace = [inp] for i in range(steps-1): computational_trace.append((computational_trace[-1]**3 + round_constants[i % len(round_constants)]) % molus) output = computational_trace[-1]
黑色: g1 的幂。紫色: g2 的幂。橙色:1。你可以将连续的单位根看作一个按这种方式排列的圆圈。我们沿着 g1的幂“放置”计算轨迹,然后扩展它来计算在中间值处(即 g2 的幂)的相同多项式的值。
我们可以将MIMC的循环常数转换为多项式。因为这些循环常数链是非常通常发生地(在我们的测试中,每64个步骤都会进行),最终证明他们形成了64阶的多项式,而且外面可以很容易计算出它的表达式,以及扩展式:
skips2 = steps // len(round_constants) constants_mini_polynomial = fft(round_constants, molus, f.exp(subroot, skips2), inv=True) constants_polynomial = [0 if i % skips2 else constants_mini_polynomial[i//skips2] for i in range(steps)] constants_mini_extension = fft(constants_mini_polynomial, molus, f.exp(root_of_unity, skips2))
假设其中有8192个步骤,并且有64个循环常数。这是我们想要做的:我们正在进行FFT,从而计算循环常数来作为g1128 的功能。然后我们在之间加入很多零,来完成g1本身的功能。因为g1128 大约每64步进行循环,我们知道g1这个功能也会同样。我们只计算这个扩展中的512个步骤,因为我们知道这个扩展会在每512步之后重复。现在,我们按照斐波那契案例中那样,计算C(P(x)),除了这次是计算,需要注意,我们不在计算使用系数形式的多项式;而是根据高次单位根的连续幂来对多项式进行求值。
c_of_p需要满足Q(x) = C(P(x), P(g1*x),K(x)) = P(g1*x) – P(x)**3 – K(x);目标是对于任何我们放入计算轨道的x(除了最后一步,因为在最后一步之后,就没有步骤),计算轨迹中的下个数值就和之前的相等,再加上循环常量。与第1部分中的斐波那契示例不同,其中如果某个计算步骤是在k向量,下个就会是k+1向量,我们把低次单位根( g1 )的连续幂放下计算轨迹,所以如果某个计算步骤是在x = g1i ,下个步骤就会在g1i+1 = g1i * g1 = x * g1。因此,对于低阶单位根( g1 )的每一个幂,我们希望最终会是P(x*g1) = P(x)**3 + K(x),或者P(x*g1) – P(x)**3 – K(x) = Q(x) = 0。因此,Q(x) 会在低次单位根 g 的所有连续幂上等于零(除了最后一个)。
# Create the composed polynomial such that # C(P(x), P(g1*x), K(x)) = P(g1*x) - P(x)**3 - K(x) c_of_p_evaluations = [(p_evaluations[(i+extension_factor)%precision] - f.exp(p_evaluations[i], 3) - constants_mini_extension[i % len(constants_mini_extension)]) % molus for i in range(precision)] print('Computed C(P, K) polynomial')
有个代数定理证明,如果Q(x)在所有这些x坐标,都等于零,那么最小多项式的乘积就会在所有这些x坐标等于零:Z(x) = (x – x_1) * (x – x_2) * … * (x – x_n)。通过证明在任何单个的坐标,Q(x)是等于零,我们想要证明这个很难,因为验证这样的证明比运行原始计算需要耗费更长的时间,我们会使用一个间接的方式来证明Q(x)是Z(x)的乘积。并且我们会怎么做呢?通过证明D(x) = Q(x) / Z(x),并且使用FRI来证明它其实是个多项式,而不是个分数。
我们选择低次单位根和高次单位根的特定排列,因为事实证明,计算Z(x),而且除以Z(x)也十分简单:Z 的表达式是两项的一部分。
需要注意地是,直接计算Z的分子和分母,然后使用批量模逆的方法将除以Z转换为乘法,随后通过 Z(X) 的逆来逐点乘以 Q(x) 的值。需要注意,对于低次单位根的幂,除了最后一个,都可以得到Z(x) = 0,所以这个计算包含其逆计算就会中断。这是非常不幸的,虽然我们会通过简单地修改随机检查和FRI算法来堵住这个漏洞,所以就算我们计算错误,也没关系。
因为Z(x)可以简洁地表达,我们也可以获得另个好处:验证者对于任何特别的x,可以快速计算Z(x),而且还不需要任何提前计算。对于证明者来说,我们可以接受证明者必须处理大小等于步数的多项式,但我们不想让验证者做同样的事情,因为我们希望验证过程足够简洁。
# Compute D(x) = Q(x) / Z(x) # Z(x) = (x^steps - 1) / (x - x_atlast_step) z_num_evaluations = [xs[(i * steps) % precision] - 1 for i in range(precision)] z_num_inv = f.multi_inv(z_num_evaluations) z_den_evaluations = [xs[i] - last_step_position for i in range(precision)] d_evaluations = [cp * zd * zni % molus for cp, zd, zni in zip(c_of_p_evaluations, z_den_evaluations, z_num_inv)] print('Computed D polynomial')
在几个随机点上,进行概念检测D(x) * Z(x) = Q(x),从而可以验证转账约束,每个计算步骤是之前步骤的有效结果。但是我们也想验证边界约束,其中计算的输入和输出就是证明者所说的那样。只是要求证明者提供P(1), D(1), P(last_step)还有D(last_step)的数值,这些都是很脆弱的;没有证明,那些数值都是在同个多项式。所以,我们使用类似的多项式除法技巧:
# Compute interpolant of ((1, input), (x_atlast_step, output)) interpolant = f.lagrange_interp_2([1, last_step_position], [inp, output]) i_evaluations = [f.eval_poly_at(interpolant, x) for x in xs] zeropoly2 = f.mul_polys([-1, 1], [-last_step_position, 1]) inv_z2_evaluations = f.multi_inv([f.eval_poly_at(quotient, x) for x in xs]) # B = (P - I) / Z2 b_evaluations = [((p - i) * invq) % molus for p, i, invq in zip(p_evaluations, i_evaluations, inv_z2_evaluations)] print('Computed B polynomial')
那么,我们的论证如下。证明者想要证明P(1) == input和P(last_step) == output。如果我们将I(x)作为插值,那么就是穿越(1, input)和(last_step, output)亮点的线,于是P(x) – I(x)就会在这亮点上等于零。因此,它会证明P(x) – I(x)是P(x) – I(x)的乘积,并且我们通过提高商数来证明这点。
紫色:计算轨迹多项式 (P) 。绿色:插值 (I)(注意插值是如何构造的,其在 x = 1 处等于输入(应该是计算轨迹的第一步),在 x=g^(steps-1) 处等于输出(应该是计算轨迹的最后一步)。红色:P-I。黄色:在x = 1和 x=g^(steps-1)(即 Z2)处等于 0 的最小多项式。粉红色:(P – I) / Z2。
现在,我们来看看将P,D和B的默克尔根部组合在一起。
现在,我们需要证明P,D和B其实都是多项式,并且是最大的正确阶数。但是FRI证明是很大且昂贵的,而且我们不想有三个FRI证明,所以,我们计算 P,D 和 B 的伪随机线性组合,并且基于它来进行FRI证明:
# Compute their Merkle roots mtree = merkelize([pval.to_bytes(32, 'big') + dval.to_bytes(32, 'big') + bval.to_bytes(32, 'big') for pval, dval, bval in zip(p_evaluations, d_evaluations, b_evaluations)]) print('Computed hash root')
除非所有这三个多项式有正确的低阶,不然几乎不可能有随机选择的线性组合,所以这很足够。
我们想要证明D的阶数小于2 * steps,而且P 和 B 的次数小于steps,所以我们其实使用了随机的P, P * xsteps, B, Bsteps 和 D的随机组合,并且可以看出这部分组合是小于2 * steps。
现在,我们来检查下所有的多项式组合。我们先获得很多随机的索引,然后在这些索引上为默克尔树枝提供多项式:
k1 = int.from_bytes(blake(mtree[1] + b'\x01'), 'big') k2 = int.from_bytes(blake(mtree[1] + b'\x02'), 'big') k3 = int.from_bytes(blake(mtree[1] + b'\x03'), 'big') k4 = int.from_bytes(blake(mtree[1] + b'\x04'), 'big') # Compute the linear combination. We don't even bother calculating it # in coefficient form; we just compute the evaluations root_of_unity_to_the_steps = f.exp(root_of_unity, steps) powers = [1] for i in range(1, precision): powers.append(powers[-1] * root_of_unity_to_the_steps % molus) l_evaluations = [(d_evaluations[i] + p_evaluations[i] * k1 + p_evaluations[i] * k2 * powers[i] + b_evaluations[i] * k3 + b_evaluations[i] * powers[i] * k4) % molus for i in range(precision)]
get_pseudorandom_indices函数会回复[0…precision-1]范围中的随机索引,而且exclude_multiples_of参数并不会给出特定参数倍数的值。这就保证了,我们不会沿着原始计算轨迹进行采样,否则就会获得错误的答案。
证明是由一组默克尔根、经过抽查的分支以及随机线性组合的低次证明组成:
# Do some spot checks of the Merkle tree at pseudo-random coordinates, excluding # multiples of `extension_factor` branches = [] samples = spot_check_security_factor positions = get_pseudorandom_indices(l_mtree[1], precision, samples, exclude_multiples_of=extension_factor) for pos in positions: branches.append(mk_branch(mtree, pos)) branches.append(mk_branch(mtree, (pos + skips) % precision)) branches.append(mk_branch(l_mtree, pos)) print('Computed %d spot checks' % samples)
整个证明最长的部分是默克尔树分支,还有FRI证明,这是有更多分支来组成的。这是验证者的实质结果:
o = [mtree[1], l_mtree[1], branches, prove_low_degree(l_evaluations, root_of_unity, steps * 2, molus, exclude_multiples_of=extension_factor)]
在每个位置,证明者需要提供一个默克尔证明,从而让验证者能够检查这个默克尔证明,并且检查C(P(x), P(g1*x), K(x)) = Z(x) * D(x)以及B(x) * Z2(x) + I(x) = P(x)(提醒:对于不在初始计算轨道上的x,Z(x)不会是零,所以C(P(x), P(g1*x), K(x)也不会是零)。验证者也会检查线性组合是正确的,然后调用。
for i, pos in enumerate(positions): x = f.exp(G2, pos) x_to_the_steps = f.exp(x, steps) mbranch1 = verify_branch(m_root, pos, branches[i*3]) mbranch2 = verify_branch(m_root, (pos+skips)%precision, branches[i*3+1]) l_of_x = verify_branch(l_root, pos, branches[i*3 + 2], output_as_int=True) p_of_x = int.from_bytes(mbranch1[:32], 'big') p_of_g1x = int.from_bytes(mbranch2[:32], 'big') d_of_x = int.from_bytes(mbranch1[32:64], 'big') b_of_x = int.from_bytes(mbranch1[64:], 'big') zvalue = f.div(f.exp(x, steps) - 1, x - last_step_position) k_of_x = f.eval_poly_at(constants_mini_polynomial, f.exp(x, skips2)) # Check transition constraints Q(x) = Z(x) * D(x) assert (p_of_g1x - p_of_x ** 3 - k_of_x - zvalue * d_of_x) % molus == 0 # Check boundary constraints B(x) * Z2(x) + I(x) = P(x) interpolant = f.lagrange_interp_2([1, last_step_position], [inp, output]) zeropoly2 = f.mul_polys([-1, 1], [-last_step_position, 1]) assert (p_of_x - b_of_x * f.eval_poly_at(zeropoly2, x) - f.eval_poly_at(interpolant, x)) % molus == 0 # Check correctness of the linear combination assert (l_of_x - d_of_x - k1 * p_of_x - k2 * p_of_x * x_to_the_steps - k3 * b_of_x - k4 * b_of_x * x_to_the_steps) % molus == 0
其实还没有完成成功;证明对跨多项式检查和 FRI 所需的抽查次数的可靠性分析是非常棘手的。但是这些就是所有代码,至少你不用担心进行疯狂的优化。当我运行以上代码的时候,我们会获得STARK证明,会有300-400倍的证明成本例如,一个需要 0.2 秒的 MIMC 计算需要 60 秒来证明)。这就使得4核机器计算MIMC中的 STARK,实际上可以比后向计算 MIMC 更快。也就是说,在python语言,这会相对低效的实现,并且这也会证明运行时间比例会不同。同时,也值得指出,MIMC 的 STARK 证明成本非常低,因为MIMC几乎是完美地可计算,它的数学形式很简单。对于平均计算,会包含更少的清晰计算(例如,检查一个数是大于还是小于另一个),其计算成本可能会更高,会有大约10000-50000倍。

‘肆’ RSA 和 蒙哥马利乘法两者是什么关系

蒙哥马利算法是在RSA密码系统中广泛应用的模乘法算法.

‘伍’ 千禧年七大数学难题是什么

是NP完全问题、霍奇猜想、庞加莱猜想、黎曼假设、杨-米尔斯存在性和质量缺口、纳卫尔-斯托可方程、BSD猜想。其中庞加莱猜想已被解决。

数学难题可以是指那些历经长时间而仍未有解答/完全解答的数学问题。

古今以来,一些特意提出的数学难题有:平面几何三大难题、希尔伯特的23个问题、世界三大数学猜想、千禧年大奖难题等。

费尔马大定理起源于三百多年前,挑战人类3个世纪,多次震惊全世界,耗尽人类众多最杰出大脑的精力,也让千千万万业余者痴迷。终于在1994年被安德鲁·怀尔斯攻克。

古希腊数学家丢番图写过一本着名的《算术》(Arithmetica),经历中世纪的愚昧黑暗到文艺复兴的时候,《算术》的残本重新被发现研究。

1637年,法国业余大数学家费尔马(Pierre de Fremat)在《算术》的关于勾股数问题的页边上,写下猜想:xn+ yn=zn是不可能的(这里n大于2;x,y,z,n都是非零整数)。

此猜想后来就称为费尔马大定理。费尔马还写道“我对此有绝妙的证明,但此页边太窄写不下”。一般公认,他当时不可能有正确的证明。猜想提出后,经欧拉等数代天才努力,200年间只解决了n=3,4,5,7四种情形。

1847年,库默尔创立“代数数论”这一现代重要学科。他还证明了当n﹤100时,除却n=37、59、67这些不规则质数的情况,费尔马大定理都成立,是一次大飞跃。

历史上费尔马大定理高潮迭起,传奇不断。其惊人的魅力,曾在最后时刻挽救自杀青年于不死。他就是德国的沃尔夫斯克勒,他于1908年为费尔马大定理设悬赏10万马克(相当于现时的160万美元多),期限1908-2007年。

无数人耗尽心力,空留浩叹。最现代的电脑加数学技巧,验证了400万以内的n,但这对最终证明无济于事。1983年德国的法尔廷斯证明了:对任一固定的n,最多只有有限多个x,y,z,振动了世界,获得菲尔兹奖(数学界最高奖)。

‘陆’ 跪求蒙哥玛利模幂算法的代码解释

最近在学习RSA算法的相关知识,对于其中的核心模密运算以及模密运算的核心——模乘也开始有一点点了解。蒙哥马利模乘的优点在于减少了取模的次数(在大数的条件下)以及简化了除法的复杂度(在2的k次幂的进制下除法仅需要进行左移操作)。一般的模密是调用模乘运算来实现的(正如你所列的代码),可以看一下下面一段文字(选自hellman2000的博客):

模幂运算是RSA 的核心算法,最直接地决定了RSA 算法的性能。针对快速模幂
运算这一课题,西方现代数学家提出了大量的解决方案,通常都是先将幂模运算转
化为乘模运算。

例如求D=C**15 % N,由于:a*b % n = (a % n)*(b % n) % n,所以:

C1 =C*C % N =C**2 % N
C2 =C1*C % N =C**3 % N
C3 =C2*C2 % N =C**6 % N
C4 =C3*C % N =C**7 % N
C5 =C4*C4 % N =C**14 % N
C6 =C5*C % N =C**15 % N

即:对于E=15的幂模运算可分解为6 个乘模运算,归纳分析以上方法可以发现
对于任意E,都可采用以下算法计算D=C**E % N:

D=1
WHILE E>=0
IF E%2=0
C=C*C % N
E=E/2
ELSE
D=D*C % N
E=E-1
RETURN D

继续分析会发现,要知道E 何时能整除 2,并不需要反复进行减一或除二的操
作,只需验证E 的二进制各位是0 还是1 就可以了,从左至右或从右至左验证都可
以,从左至右会更简洁,设E=Sum[i=0 to n](E*2**i),0<=E<=1,则:

D=1
FOR i=n TO 0
D=D*D % N
IF E=1 D=D*C % N
RETURN D

这样,模幂运算就转化成了一系列的模乘运算。

其他一些关于大数运算、素数测试、模乘、模密运算,hellman2000的一篇文章有比较全面易懂的介绍,链接如下:http://bbs.s.e.cn/pc/pccon.php?id=1308&nid=43763&pid=0&tag=0&tid=0

‘柒’ C#里如何对位数很长的数字(已处理为字符串)转化为16进制

.NET 4.0 中可以引用 System.Numeric 类,用 BigInteger 这个类来进行计算(P.S. 据说有精度上的问题)。

一般的解决办法是自己写一个类型来储存这种超大数,当成 string 类型来处理,并自己写算法来对应加减乘除这些。

暂时只能想到一个效率很低的思路,这么大的数要直接转成16进制的话算法是很麻烦的,建议先转成2进制,用短除法(除法的本质是多次减法,当然如果题主算法好可以用蒙哥马利算法来直接做除法。当作 string 类型来计算,先截取最后一位,看够不够减2,够了就把减去的结果替换掉 string 最后一位;不够就再截取最后两位看,依次类推。除法就是反复做减法,直到最后一次减法结果要变负数为止,商和余数就出来了)。再把2进制转换成16进制就容易多了,从后往前每4位截断一下,最前面不足位用0补齐,然后每四位转换成16进制,最后拼接一下。

‘捌’ 蒙哥马利幂模运算的特点及原理

蒙哥马利模乘的优点在于减少了取模的次数(在大数的条件下)以及简化了除法的复杂度(在2的k次幂的进制下除法仅需要进行左移操作)。模幂运算是RSA 的核心算法,最直接地决定了RSA 算法的性能。
针对快速模幂运算这一课题,西方现代数学家提出了大量的解决方案,通常都是先将幂模运算转化为乘模运算。
例如求D=C^15%N
由于:a*b % n = (a % n)*(b % n) % n
所以令:
C1 =C*C % N =C^2 % N
C2 =C1*C % N =C^3 % N
C3 =C2*C2 % N =C^6 % N
C4 =C3*C % N =C^7 % N
C5 =C4*C4 % N =C^14 % N
C6 =C5*C % N =C^15 % N
即:对于E=15的幂模运算可分解为6 个乘模运算,归纳分析以上方法可以发现:
对于任意指数E,都可采用以下算法计算D=C**E % N:
D=1
WHILE E>0
IF E%2=0
C=C*C % N
E=E/2
ELSE
D=D*C % N
E=E-1
RETURN D
继续分析会发现,要知道E 何时能整除 2,并不需要反复进行减一或除二的操作,只需验证E 的二进制各位是0 还是1 就可以了,从左至右或从右至左验证都可以,从左至右会更简洁,
设E=Sum[i=0 to n](E*2**i),0<=E<=1
则:
D=1
FOR i=n TO 0
C=C*C % N
IF E=1
D=D*C % N
RETURN D这样,模幂运算就转化成了一系列的模乘运算。

‘玖’ c++中如何计算244^847%2773的值 ,最好有一个简单的程序。

模幂算法见高爷爷的大作《计算机程序设计艺术(第二卷)》,指数按二进制位扫描,可以从低到高,也可以从高到低,两者计算法略微不同,这里给出从低到高扫描的方法,遇到bit1,则执行if里面的运算,否则,a平方并模m

程序没做太多的参数检查,例如m为0的情况,也没做优化,比如a是0或者1的情况
unsigned int mod_exp(unsigned int a, unsigned int p, unsigned int m)
{
unsigned int res = 1;

for (a = a % m; p != 0; p >>= 1)
{
if ((p & 0x01) != 0)
{
res = (res * a) % m;
}

a = (a * a) % m;
}

return res;
}
因为不是大数模幂,没有做滑动窗口、蒙哥马利算法之类的优化,直来直去直接计算

‘拾’ 请教RSA加密中大数运算中蒙哥马利算法

http://wenku..com/link?url=-aIgFy7FR29nO3Sz70QbXhh3-2D3xZX6a-

热点内容
服务器可以变电脑使用吗 发布:2025-01-15 19:40:29 浏览:200
传奇手游免费脚本 发布:2025-01-15 19:30:21 浏览:300
我国当前资源配置存在哪些问题 发布:2025-01-15 19:25:03 浏览:513
存储在哪里呀 发布:2025-01-15 19:11:39 浏览:450
pythonuniquelist 发布:2025-01-15 19:10:41 浏览:477
怎么升安卓系统下载 发布:2025-01-15 19:04:27 浏览:893
mcrypt扩展php 发布:2025-01-15 19:01:12 浏览:436
html源码解析 发布:2025-01-15 19:01:10 浏览:222
网络硬盘录像机存储 发布:2025-01-15 18:48:20 浏览:894
大学生java课程培训 发布:2025-01-15 18:45:53 浏览:487