量子生成随机数,真正的随机!
大家已经成功创建并运行一个Q#量子程序了,之后就可以通过代码来探索量子计算的宇宙了。
本章开启之前,还请大家看以下几行代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 int main() 4 { 5 int a=rand(); 6 printf("%d",a); 7 return 0; 8 }
想必大家已经看出来了这是C语言中经典的生成随机数的方法,大家在自己本机运行时会发现,无论我们运行几次,输出的都是相同的数字(但是不同电脑输出的数字可能会不同),所以 rand() 函数生成的随机数是伪随机数。
实际上,rand() 函数产生的随机数是伪随机数,是根据一个数值按照某个公式推算出来的,这个数值我们称之为“种子”。种子和随机数之间的关系是一种正态分布,如下图:

虽然种子在每次启动计算机时是随机的,但是一旦计算机启动以后,它就不再变化了;也就是说,每次启动计算机以后,种子就固定了。
而C语言中另一种生成随机数 srand() 函数则是通过重新“播种”来解决上述问题:它需要一个 unsigned int 类型的参数。我们可以用时间作为参数,只要每次播种的时间不同,生成的种子就不同,最终的随机数也就不同了。具体代码如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 int main() { 5 int a; 6 srand((unsigned)time(NULL)); 7 a = rand(); 8 printf("%d\n", a); 9 return 0; 10 }
多次运行程序,我们会发现虽然每次生成的随机数都不一样了。但是,这些随机数会呈现逐渐增大或者逐渐减小的趋势,这是因为我们以时间为种子,而时间是逐渐增大的,结合上面的正态分布图,很容易推断出随机数也会逐渐增大或者减小。这样也只是做到了相对的随机。
接下来让我们一起探索量子程序是如何做到真正产生随机数的吧!
首先,大家按照上篇文章的方法创建一个命名为“QuantumRNG”的新项目,然后将下面代码代码替换到Program.qs文件中。
1 namespace QuantumRNG { 2 3 open Microsoft.Quantum.Canon; 4 open Microsoft.Quantum.Intrinsic; 5 open Microsoft.Quantum.Measurement; 6 7 @EntryPoint() 8 operation GenerateRandomBit() : Result { 9 use q = Qubit();// 10 H(q); 11 return M(q); 12 } 13 }
接下来让我们慢慢理解。
首先,EntryPoint 表示Q# 编译器从这里开始执行程序。
然后,我们定义了 GenerateRandomBit 方法用来随机生成数字,该方法没有任何输入,但是返回类型为 Result 的值,表示度量的结果,可以是“Zero”或“One”。“use”关键字用来分配单个量子比特(Qubit),此时的量子比特还处于 “Zero”状态。
之后,把这个量子比特作用于H门,使得该量子比特置于相等的叠加态中。
PS:在第一篇文章中介绍过量子叠加的数学表现形式,|ψ⟩=a|ψ1⟩+b|ψ2⟩。而H门(hadamardgate)的将量子比特置于相等的叠加态这一作用实质上是赋予a,b的值为1/√2(根号二分之一)。这样经过H门的量子比特被检测出“Zero”态或“One”态的概率就相等了,这也算真正生成随机数的本质。
最后,我们把这个量子比特作用于M门,用于度量并返回量子的值。
做到这,我们可以运行程序检验一下随机性是否能体现出来(如下图):

我们可以看出,“Zero”和“One”是随机出现的。
虽然目前我们只能随机输出两个数字,但是有了这个基础,接下来就可以通过组合多个随机位来输出更大的随机数了。
具体的算法思想主要是:先设定一个随机数的最大值max(这里以50举例),然后按照上述思想,生成一系列的“0”、“1”的数位,之后连在一起构成一个由“0”、“1”组成的字符串,最后把该字符串转为Int型数值,并将之与我们最初设置的最大值max进行比较(注意:这里得到的数值可能比预设的要大,因为“0”和“1”的位置是彻底随机的可能会导致“1”占比比较多且位置相对靠前,注意对导致数值偏大)此时我们可以重新运行一遍次算法即可。具体代码如下:
1 namespace QuantumRNG { 2 3 open Microsoft.Quantum.Canon; 4 open Microsoft.Quantum.Intrinsic; 5 open Microsoft.Quantum.Measurement; 6 open Microsoft.Quantum.Math; 7 open Microsoft.Quantum.Convert; 8 9 operation GenerateRandomBit() : Result { 10 use q = Qubit(); 11 H(q); 12 return M(q); 13 } 14 15 operation SampleRandomNumberInRange(max : Int) : Int { 16 mutable output = 0; 17 repeat { 18 mutable bits = []; 19 for idxBit in 1..BitSizeI(max) { 20 set bits += [GenerateRandomBit()]; 21 } 22 set output = ResultArrayAsInt(bits); 23 } until (output <= max); 24 return output; 25 } 26 27 @EntryPoint() 28 operation SampleRandomNumber() : Int { 29 let max = 50; 30 Message($"Sampling a random number between 0 and {max}: "); 31 return SampleRandomNumberInRange(max); 32 } 33 }
其中,mutable 关键字用来定义一个变量,repeat-until 的用法与我们其他编程语言中经典的for循环的作用是相同的,let 关键字用于声明在计算过程中不更改的变量
程序运行结果如下:

我们可以看到我们成功实现了使用量子程序真正随机生成了在一定范围内随机数了。
PS:值得注意的是:在 mutable bits = [] 中,我们定义的bits变量要是一个数组,用于存储“0”、“1”组成的二进制型的字符串并转换成 Int类型数字。
我们不能将这一变量直接定义成 Int 类型的变量,然后再利用 GenerateRandomBit()方法生成max(max这里=50)个“0”或“1”进行逐个相加,从而得到 0~50 之间的数字。这里需要思考的是,虽然这种方法能够生成 0~50 之间的某一数值,但是这些数字是真正随机的吗?换句话说,他们出现的概率相等吗?显然不是的,利用排列组合的数学思想,我们可以得到靠近中位数的数值出现的概率会更高一些,所以不能做到随机生成区间数值。
这样我们就完全理解了量子程序是究竟是怎样真正生成随机数的了!

浙公网安备 33010602011771号