演習 パート 2 - 量子乱数ジェネレーターを作成する
このユニットでは、量子乱数ジェネレーターの 2 番目の部分を実装します。 複数のランダム ビットを組み合わせて、より大きな乱数を形成します。 このパートは、前のユニットで既に作成したランダム ビット ジェネレーターに基づいています。
複数のランダム ビットを結合して大きな数を形成する
前のユニットでは、量子ビットを重ね合わせ状態にするランダム ビット ジェネレーターを作成し、その量子ビットを測定して、それぞれ 50% 確率で 0 または 1 のランダム ビット値を生成しました。 このビットの値は本当にランダムです。測定結果が何であるかを事前に知る方法はありません。 しかし、この動作をどのように使えば、より大きな乱数を生成できるのでしょうか?
プロセスを 4 回繰り返す場合は、次のバイナリ数字のシーケンスを生成できます。
$${0, 1, 1, 0}$$
これらのビットをビット文字列に結合すると、より大きな数値を形成できます。 この例では、バイナリのビット シーケンス ${0110}$ は、10 進数の数値 6 に相当します。
$${0110_{\ 2 進} \equiv 6_{\ 10 進}}$$
任意に大きな乱数を生成するには、このプロセスを何度も繰り返します。 次に、すべてのビットを 2 進数に結合し、その 2 進数を 10 進数に変換します。
乱数ジェネレーターのロジックを定義する
Q# コードを記述する前に、乱数を生成するロジックの概要を説明します。
- 生成したい最大の10進数として
maxを定義します。 -
nBitsを生成するために必要なランダム ビット (max) の数を決定します。 - 長さが
nBitsの乱数ビット文字列を生成します。 - ビット文字列が
maxより大きい数値を表す場合は、前の手順に戻ります。 - それ以外の場合、プロセスは終了です。 生成された数値を 10 進整数として返します。
たとえば、 max を 12 に定義します。 つまり、乱数ジェネレーターが出力する最大の数値は 12 です。
次の式を使用して、数値 12 をバイナリで表すために必要なビット数を決定します。
$${\lfloor ln(12) / ln(2) + 1 \rfloor}$$
この式によると、0 から 12 までの数値を表すには 4 ビットが必要です。
たとえば、ランダム ビットを 4 回生成し、ビット文字列 ${1101_{\ binary}} $ を取得するとします。 バイナリのこの値は、10 進数で 13 に等しくなります。 13 は 12 より大きいため、この処理を繰り返します。
次に、ビット文字列 ${0110_{\ binary}$ を生成します。これは ${6_{\ decimal}} $ と等しくなります。 6 は 12 未満であるため、この処理を終了します。
量子乱数ジェネレーターは数値 6 を返します。
Q で完全な乱数ジェネレーターを作成する#
ここでは、前のレッスンの Main.qs ファイルを展開して、乱数ジェネレーターを構築します。
必要なライブラリをインポートする
まず、プログラムを記述するために必要な関数と操作を含む Q# 標準ライブラリから名前空間をインポートします。 Q# コンパイラは、多くの一般的な関数と操作を自動的に読み込みます。 ただし、量子乱数ジェネレーターの場合は、2 つの Q# 名前空間 ( Microsoft.Quantum.Math と Microsoft.Quantum.Convert) からいくつかの追加の関数と操作が必要です。
次の import ディレクティブをコピーして、 Main.qs ファイルの先頭に貼り付けます。
import Std.Convert.*;
import Std.Math.*;
注
Stdの代わりにMicrosoft.Quantumを使用して、標準ライブラリから関数と操作をインポートできます。
Main 演算の名前を GenerateRandomBit に変更する
乱数ジェネレーター プログラムは、前のユニットで記述した Main 演算を使用してランダム ビットを生成します。
Main操作の名前をGenerateRandomBitに変更して、この操作の名前をわかりやすい名前にし、プログラムへのエントリ ポイントとしないようにします。
次のコードをコピーして Main.qsに貼り付けます。
import Std.Convert.*;
import Std.Math.*;
operation GenerateRandomBit() : Result {
// Allocate a qubit.
use q = Qubit();
// Set the qubit into superposition of 0 and 1 using the Hadamard
H(q);
// Measure the qubit and store the result.
let result = M(q);
// Reset qubit to the |0〉 state.
Reset(q);
// Return the result of the measurement.
return result;
}
乱数ジェネレーターの操作を定義する
GenerateRandomNumberInRangeという新しい操作を作成します。 この演算は GenerateRandomBit 演算を繰り返し呼び出して、ビット文字列を構築します。
次のコードをコピーし、GenerateRandomBit ファイル内のMain.qs操作の直前に配置します。
/// Generates a random number between 0 and `max`.
operation GenerateRandomNumberInRange(max : Int) : Int {
// Determine the number of bits needed to represent `max` and store it
// in the `nBits` variable. Then generate `nBits` random bits which will
// represent the generated random number.
mutable bits = [];
let nBits = BitSizeI(max);
for idxBit in 1..nBits {
set bits += [GenerateRandomBit()];
}
let sample = ResultArrayAsInt(bits);
// Return random number if it's within the requested range.
// Generate it again if it's outside the range.
return sample > max ? GenerateRandomNumberInRange(max) | sample;
}
GenerateRandomNumberInRangeのコードの概要を次に示します。
-
BitSizeIライブラリからStd.Math関数を呼び出して、maxに格納されている整数を表すために必要なビット数を計算します。 -
forループを使用して、nBitsと等しい多数のランダム ビットを生成します。GenerateRandomBit操作を呼び出してランダム ビットを生成します。 -
forループ内で、setステートメントを使用して、新しいランダム ビットごとにbits変数を更新します。 変数bitsは変更可能な変数です。つまり、bitsの値は計算中に変更される可能性があります。 -
ResultArrayAsIntライブラリからStd.Convert関数を呼び出して、bits内のビットの配列を、sampleに格納されている正の整数に変換します。 -
returnステートメントで、sampleがmaxより大きいかどうかを確認します。sampleがmaxより大きい場合は、もう一度GenerateRandomNumberInRange呼び出してやり直します。 それ以外の場合は、sampleに格納されている乱数を返します。
エントリ ポイントを追加する
最後に、コンパイラがプログラムを実行できるように、エントリ ポイント操作をコードに追加します。 既定では、Q# コンパイラはMain操作を検索し、Mainがファイル内のどこにあるかにかかわらず、エントリ ポイントとしてMainを使用します。 ここで、 Main 操作では、 max の値を設定し、 GenerateRandomNumberInRange 操作を呼び出して、0 ~ maxの乱数を生成します。
たとえば、0 から 100 までの乱数を生成するには、次のコードを Main.qs ファイルにコピーします。
operation Main() : Int {
let max = 100;
Message($"Generating a random number between 0 and {max}: ");
// Generate random number in the 0..max range.
return GenerateRandomNumberInRange(max);
}
最終プログラム
Main.qsのプログラムの完全な Q# コードを次に示します。
import Std.Convert.*;
import Std.Math.*;
operation Main() : Int {
let max = 100;
Message($"Generating a random number between 0 and {max}: ");
// Generate random number in the 0..max range.
return GenerateRandomNumberInRange(max);
}
/// Generates a random number between 0 and `max`.
operation GenerateRandomNumberInRange(max : Int) : Int {
// Determine the number of bits needed to represent `max` and store it
// in the `nBits` variable. Then generate `nBits` random bits which will
// represent the generated random number.
mutable bits = [];
let nBits = BitSizeI(max);
for idxBit in 1..nBits {
set bits += [GenerateRandomBit()];
}
let sample = ResultArrayAsInt(bits);
// Return random number if it's within the requested range.
// Generate it again if it's outside the range.
return sample > max ? GenerateRandomNumberInRange(max) | sample;
}
operation GenerateRandomBit() : Result {
// Allocate a qubit.
use q = Qubit();
// Set the qubit into superposition of 0 and 1 using the Hadamard operation
H(q);
// Measure the qubit value using the `M` operation, and store the
// measurement value in the `result` variable.
let result = M(q);
// Reset qubit to the |0〉 state.
Reset(q);
// Return the result of the measurement.
return result;
}
プログラムを実行する
新しい量子乱数ジェネレーターを試してみてください。
プログラムを実行するには、操作の上にあるコマンドの一覧から [コードのMain] レンズを選択します。 または、 Ctrl + F5 キーを押します。 出力がデバッグ コンソールに表示されます。 プログラムを複数回実行し、結果がどのように変化するかに注目します。
お疲れさまでした。 Q# で真にランダムな量子数ジェネレーターを作成しました。
ボーナス演習
生成された乱数が 0 ではなく、いくつかの最小正の数 ( min) を超える必要が生じるように、プログラムを変更してみてください。