演習 パート 2 - 量子乱数ジェネレーターを作成する

完了

このユニットでは、量子乱数ジェネレーターの 2 番目の部分を実装します。 複数のランダム ビットを組み合わせて、より大きな乱数を形成します。 このパートは、前のユニットで既に作成したランダム ビット ジェネレーターに基づいています。

複数のランダム ビットを結合して大きな数を形成する

前のユニットでは、量子ビットを重ね合わせ状態にするランダム ビット ジェネレーターを作成し、その量子ビットを測定して、それぞれ 50% 確率で 0 または 1 のランダム ビット値を生成しました。 このビットの値は本当にランダムです。測定結果が何であるかを事前に知る方法はありません。 しかし、この動作をどのように使えば、より大きな乱数を生成できるのでしょうか?

プロセスを 4 回繰り返す場合は、次のバイナリ数字のシーケンスを生成できます。

$${0, 1, 1, 0}$$

これらのビットをビット文字列に結合すると、より大きな数値を形成できます。 この例では、バイナリのビット シーケンス ${0110}$ は、10 進数の数値 6 に相当します。

$${0110_{\ 2 進} \equiv 6_{\ 10 進}}$$

任意に大きな乱数を生成するには、このプロセスを何度も繰り返します。 次に、すべてのビットを 2 進数に結合し、その 2 進数を 10 進数に変換します。

乱数ジェネレーターのロジックを定義する

Q# コードを記述する前に、乱数を生成するロジックの概要を説明します。

  1. 生成したい最大の10進数としてmaxを定義します。
  2. nBitsを生成するために必要なランダム ビット (max) の数を決定します。
  3. 長さが nBits の乱数ビット文字列を生成します。
  4. ビット文字列が maxより大きい数値を表す場合は、前の手順に戻ります。
  5. それ以外の場合、プロセスは終了です。 生成された数値を 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.MathMicrosoft.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 ステートメントで、samplemaxより大きいかどうかを確認します。 samplemaxより大きい場合は、もう一度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) を超える必要が生じるように、プログラムを変更してみてください。