6.10 Shor算法 ================ 6.10.1 Shor算法介绍 ---------------------- 舒尔算法,即秀尔算法(Shor算法),以数学家彼得·秀尔命名,是一个在1994年发现的,针对整数分解这题目的的量子算法(在量子计算机上面运作的算法)。它解决如下题目:给定一个整数N,找出他的质因数。 在一个量子计算机上面,要分解整数N,秀尔算法的运作需要多项式时间(时间是logN的某个多项式这么长,logN在这里的意义是输入的档案长度)。更精确的说,这个算法花费O((logN))的时间,展示出质因数分解问题可以使用量子计算机以多项式时间解出,因此在复杂度类BQP里面。这比起传统已知最快的因数分解算法,普通数域筛选法还要快了一个指数的差异。 秀尔算法非常重要,因为它代表使用量子计算机的话,我们可以用来破解已被广泛使用的公开密钥加密方法,也就是RSA加密算法。RSA算法的基础在于假设了我们不能很有效率的分解一个已知的整数。就目前所知,这假设对传统的(也就是非量子)电脑为真;没有已知传统的算法可以在多项式时间内解决这个问题。然而,秀尔算法展示了因数分解这问题在量子计算机上可以很有效率的解决,所以一个足够大的量子计算机可以破解RSA。这对于建立量子计算机和研究新的量子计算机算法,是一个非常大的动力。 | 将两个质数乘起来,例如907*641=581387,是一件小学生都能做到的事情,用计算机去处理,看起来也没有什么难度。但是如果我给你581387,让你去找它的质因数,问题就变得很复杂了。也许你可以用计算机一个一个的去尝试,但是当数字变得更大,达到成百上千位的时候,就连计算机也无能为力。世界上面有很多问题都是这样,难以找到答案,但是一旦找到答案就很容易去验证。类似的问题我们称之为NP问题。NP问题之所以难于处理,是因为它的时间复杂度往往具有指数级别。这意味着随着问题规模的线性扩大,需要的时间却是指数增长的。利用这个原理,人们创造了RSA算法,它利用大数难以分解,但是易于验证的原理,对数据进行有效的加密。 | 量子计算机有将问题指数加速的能力,那是否意味着能攻克所有的NP问题呢?很遗憾,不能。但是幸运的是,我们有能力把“质因数分解”的时间复杂度降低到多项式级别,使大数分解问题的解决变为可能。这就是Shor算法。Shor算法的提出意味着RSA密钥的安全性受到了挑战。下面我们就来介绍Shor算法的内容。 问题的转化 ************* | Shor算法首先将质因数分解问题转换成了一个子问题,下面我们来看问题的转换过程。假设我们待分解的数为 N; | STEP 1:随机取一个正整数 :math:`1 a, vector b, qubit c) { let nbit = a.length(); MAJ(c, a[0], b[0]); for(let i=1: 1: nbit) { MAJ(b[i-1], a[i], b[i]); } } //Quantum adder, consists of MAJ and UMA modules, regardless of the carry term circuit Adder(vector a, vector b, qubit c) { let nbit = a.length(); MAJ(c, a[0], b[0]); for(let i=1: 1: nbit) { MAJ(b[i-1], a[i], b[i]); } for(let i=nbit-1: -1: 0) { MAJ(b[i-1], a[i], b[i]); } UMA(c, a[0], b[0]); } //Determine if there is a carry circuit isCarry(vector a, vector b, qubit c, qubit carry) { MAJ2(a, b, c); CNOT(b[-1], carry); MAJ2(a, b, c).dagger(); } //Binding classic data with qubits circuit bindData(vector qlist, int data) { let i = 0; while(data >= 1){ if(data % 2 == 1){ X(qlist[i]); } data = data >> 1; i = i + 1; }; } //Constant modular addition circuit constModAdd(vector qa, int C, int M, vector qb, vector qs1) { let qNum = qa.length(); let tmpValue = (1 << qNum) - M + C; bindData(qb, tmpValue); isCarry(qa, qb, qs1[1], qs1[0]); bindData(qb, tmpValue); circuit qCircuitTmp1; qCircuitTmp1.insert(bindData(qb, tmpValue)); qCircuitTmp1.insert(Adder(qa, qb, qs1[1])); qCircuitTmp1.insert(bindData(qb, tmpValue)); qCircuitTmp1.control([qs1[0]]); X(qs1[0]); circuit qCircuitTmp2; qCircuitTmp2.insert(bindData(qb, C)); qCircuitTmp2.insert(Adder(qa, qb, qs1[1])); qCircuitTmp2.insert(bindData(qb, C)); qCircuitTmp2.control([qs1[0]]); X(qs1[0]); tmpValue = (1 << qNum) - C; bindData(qb, tmpValue); isCarry(qa, qb, qs1[1], qs1[0]); bindData(qb, tmpValue); X(qs1[0]); } //Constant modular multiple circuit constModMul(vector qa, int constNum, int M, vector qs1, vector qs2, vector qs3) { let qNum = qa.length(); for(let i=0: 1: qNum) { let tmp = constNum * pow(2, i) % M; circuit qCircuitTmp; qCircuitTmp.insert(constModAdd(qs1, tmp, M, qs2, qs3)); qCircuitTmp.control([qa[i]]); } for(let i=0: 1: qNum) { CNOT(qa[i], qs1[i]); CNOT(qs1[i], qa[i]); CNOT(qa[i], qs1[i]); } let crev = modReverse(constNum, M); circuit qCircuitTmp1; for(let i=0: 1: qNum) { let tmp = crev * pow(2, i); tmp = tmp % M; circuit qCircuitTmp2; qCircuitTmp2.insert(constModAdd(qs1, tmp, M, qs2, qs3)); qCircuitTmp2.control([qa[i]]); qCircuitTmp1.insert(qCircuitTmp2); qCircuitTmp1.dagger(); } } //Constant modular power operation circuit constModExp(vector qa, vector qb, int base, int M, vector qs1, vector qs2, vector qs3) { let cqNum = qa.length(); let temp = base; for(let i=0: 1: cqNum) { constModMul(qb, temp, M, qs1, qs2, qs3).control([qa[i]]); temp = temp * temp; temp = temp % M; } } //Quantum Fourier transform circuit qft(vector qlist) { let qNum = qlist.length(); for (let i=0: 1: qNum) { H(qlist[qNum-1-i]); for (let j=i+1: 1: qNum) { CR(qlist[qNum-1-j], qlist[qNum-1-i], Pi/(1 << (j-i))); } } for(let i=0: 1: qNum) { CNOT(qlist[i], qlist[qNum - 1 - i]); CNOT(qlist[qNum - 1 - i], qlist[i]); CNOT(qlist[i], qlist[qNum - 1 - i]); } } @script: def gcd(m,n): if not n: return m else: return gcd(n, m%n) def modReverse(c, m): if (c == 0): raise RecursionError('c is zero!') if (c == 1): return 1 m1 = m quotient = [] quo = m // c remainder = m % c quotient.append(quo) while (remainder != 1): m = c c = remainder quo = m // c remainder = m % c quotient.append(quo) if (len(quotient) == 1): return m - quo if (len(quotient) == 2): return 1 + quotient[0] * quotient[1] rev1 = 1 rev2 = quotient[-1] reverse_list = quotient[0:-1] reverse_list.reverse() for i in reverse_list: rev1 = rev1 + rev2 * i temp = rev1 rev1 = rev2 rev2 = temp if ((len(quotient) % 2) == 0): return rev2 return m1 - rev2 def plotBar(xdata, ydata): fig, ax = plt.subplots() fig.set_size_inches(6,6) fig.set_dpi(100) rects = ax.bar(xdata, ydata, color='b') for rect in rects: height = rect.get_height() plt.text(rect.get_x() + rect.get_width() / 2, height, str(height), ha="center", va="bottom") plt.rcParams['font.sans-serif']=['Arial'] plt.title("Origin Q", loc='right', alpha = 0.5) plt.ylabel('Times') plt.xlabel('States') plt.show() def reorganizeData(measure_qubits, quick_meausre_result): xdata = [] ydata = [] for i in quick_meausre_result: xdata.append(i) ydata.append(quick_meausre_result[i]) return xdata, ydata def shorAlg(base, M): if ((base < 2) or (base > M - 1)): raise('Invalid base!') if (gcd(base, M) != 1): raise('Invalid base! base and M must be mutually prime') binary_len = 0 while M >> binary_len != 0 : binary_len = binary_len + 1 machine = init_quantum_machine(QMachineType.CPU_SINGLE_THREAD) qa = machine.qAlloc_many(binary_len*2) qb = machine.qAlloc_many(binary_len) qs1 = machine.qAlloc_many(binary_len) qs2 = machine.qAlloc_many(binary_len) qs3 = machine.qAlloc_many(2) prog = QProg() prog.insert(X(qb[0])) prog.insert(single_gate_apply_to_all(H, qa)) prog.insert(constModExp(qa, qb, base, M, qs1, qs2, qs3)) prog.insert(qft(qa).dagger()) directly_run(prog) result = quick_measure(qa, 100) print(result) xdata, ydata = reorganizeData(qa, result) plotBar(xdata, ydata) return result if __name__=="__main__": base = 7 N = 15 shorAlg(base, N) .. code-tab:: c++ To Be Continue! 6.10.3 Shor算法小结 ---------------------- | Shor算法首先把问题分解为了“经典计算机部分”和“量子计算机部分”。然后利用了量子态的叠加原理,快速取得了函数在一个很大范围内的取值(对于 :math:`n` 个工作比特而言,取值范围为 :math:`0\sim2^n-1` 。由于函数本身是周期的,所以自变量和函数值自动地纠缠了起来,从而对于某一个函数值来说,工作比特上的态就是一组周期数态的叠加态。在取得“周期数叠加态”之后,我们自然可以通过傅里叶变换得到这组周期数的周期,从而快速解决了这个问题。 | 反过来看,之所以找函数周期问题能被量子计算机快速解决,是因为在工作比特上执行了一组Hadamard变换。它在“量子函数”的作用下,相当于同时对指数级别的自变量上求出了函数值。在数据量足够大,周期足够长的情况下,这样执行的操作总量一定会小于逐个取值寻找这个函数值在之前是否出现过——这样的经典计算机“暴力破解”法要快得多。 | Shor算法的难点在于如何通过给出的 :math:`a` , :math:`n` 来得到对应的“量子函数”形式。进一步地讲,是否存在某种方法(准确地说是具有合理时间复杂度的方法)得到任意函数的“量子计算机版本”?限于笔者知识水平不足,我只能给出目前大概的研究结论是存在某些无法表示为量子计算机版本的函数,但是幸运地是Shor算法属于可以表示的那一类里面。 | 最后,我们可以发现,量子计算机之所以快,和量子计算机本身的叠加特性有关,它使得在处理特定问题时,比如数据库搜索,比如函数求周期……有着比经典计算机快得多的方法。但是如果经典计算机在解决某个问题时已经足够快了,我们就不需要用量子计算机来解决了。 | 就像Shor算法里面所描述的那样——我们将问题分解为了量子计算机去处理的“困难问题”和经典计算机去处理的“简单问题”两个部分一样。所以,量子计算机的出现,不代表经典计算机将会退出历史舞台,而是代表着人类将要向经典计算机力所不及的地方伸出探索之手。靠着量子计算机,或许我们能提出新的算法解决化学问题,从而研制出新型药物;或许我们可以建立包含所有信息的数据库,每次只需要一瞬间就能搜索到任何问题……量子云平台是我们帮助量子计算机走出的第一步,但接下来的路怎么走,我们就要和你一同见证了。