연습 - 양자 난수 생성기 만들기

완료됨

이 단원에서는 양자 난수 생성기의 두 번째 단계, 즉 여러 난수 비트를 결합하여 더 큰 난수를 형성하는 단계를 구현합니다. 이 단계는 이미 만든 임의 비트 생성기를 기반으로 합니다. 이 단계를 위해 일부 클래식 코드를 작성해야 합니다.

여러 임의 비트를 결합하여 더 큰 숫자 형성

이전 단원에서는 큐비트를 중첩에 넣고 측정하여 임의 비트를 생성하는 임의 비트 생성기를 만들었습니다.

큐비트를 측정하면 임의 비트(0 또는 1)가 50% 확률로 표시됩니다. 이 비트의 값은 실제로 임의이며 측정 후 무엇을 얻을지 알 수 있는 방법은 없습니다. 하지만 이 동작을 사용하여 더 큰 난수를 생성하려면 어떻게 해야 하나요?

프로세스를 4번 반복하여 다음과 같은 이진수 시퀀스를 생성한다고 가정하겠습니다.

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

이러한 비트를 비트 문자열로 연결하거나 결합하면 더 큰 숫자를 형성할 수 있습니다. 이 예제에서 비트 시퀀스 ${0110}$는 10진수 6과 같습니다.

$${0110_{\ binary} \equiv 6_{\ decimal}}$$

이 프로세스를 여러 번 반복하면 여러 비트를 결합하여 큰 숫자를 구성할 수 있습니다.

난수 생성기 논리 정의

이전 장치에서 빌드된 임의 비트 생성기를 제공하는 경우 난수 생성기의 논리가 무엇인지 간략히 살펴보겠습니다.

  1. max를 생성하려는 최대 수로 정의합니다.
  2. 최대 max까지의 정수를 표현하는 데 필요한 비트 수(nBits)를 계산하여 생성해야 하는 임의 비트 수를 정의합니다.
  3. 길이가 nBits인 임의 비트 문자열을 생성합니다.
  4. 비트 문자열이 max보다 큰 숫자를 나타내는 경우 3단계로 돌아갑니다.
  5. 그렇지 않으면 프로세스가 완료된 것입니다. 생성된 숫자를 정수로 반환합니다.

예를 들어 max를 12로 설정하겠습니다. 즉, 12는 난수 생성기에서 가져오려는 가장 큰 숫자입니다.

0에서 12 사이의 숫자를 나타내려면 ${\lfloor ln(12) / ln(2) + 1 \rfloor}$, 즉 4비트가 필요합니다. (간결하게 하기 위해 이 수식을 도출하는 방법은 건너뜁니다.)

${13_{\ decimal}}$에 해당하는 비트 문자열 ${1101_{\ binary}}$를 생성한다고 가정하겠습니다. 13은 12보다 크므로 프로세스를 반복합니다.

다음으로 ${6_{\ decimal}}$에 해당하는 비트 문자열 ${0110_{\ binary}}$를 생성합니다. 6은 12보다 작으므로 프로세스는 완료된 것입니다.

양자 난수 생성기는 숫자 6을 반환합니다.

완전한 난수 생성기 만들기

여기서는 RandomNumberGenerator.qs 파일을 확장하여 더 큰 난수를 만듭니다.

필요한 라이브러리 추가

완전한 난수 생성기를 위해서는 세 가지 Q# 라이브러리(Microsoft.Quantum.Math, Microsoft.Quantum.IntrinsicMicrosoft.Quantum.Convert)를 포함해야 합니다. 다음과 같이 open에 다음 RandomNumberGenerator.qs 지시문을 추가합니다.

namespace QuantumRandomNumberGenerator {
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Math;

// The rest of the code goes here.

}

양자 난수 연산 정의

여기에서 GenerateRandomNumberInRange 작업을 정의합니다. 이 작업은 GenerateRandomBit 작업을 반복적으로 호출하여 비트 문자열을 생성합니다.

다음과 같이 RandomNumberGenerator.qs를 수정합니다.

namespace QuantumRandomNumberGenerator {
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;

    /// 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 is within the requested range.
        // Generate it again if it is outside the range.
        return sample > max ? GenerateRandomNumberInRange(max) | sample;
    }
}

잠시 새 코드를 검토해 보겠습니다.

  • 최대 max까지의 정수를 표현하는 데 필요한 비트 수를 계산해야 합니다. Microsoft.Quantum.Math 라이브러리의 BitSizeI 함수는 정수를 이를 표현하는 데 필요한 비트 수로 변환합니다.
  • GenerateRandomNumberInRange 작업은 for 루프를 사용하여 max보다 작거나 같은 값을 생성할 때까지 난수를 생성합니다. for 루프는 다른 프로그래밍 언어의 for 루프와 정확히 동일하게 작동합니다.
  • bits 변수는 변경 가능한 변수입니다. 변경 가능한 변수는 계산 중에 변경될 수 있는 변수입니다. set 지시문을 사용하여 변경 가능한 변수의 값을 변경할 수 있습니다.
  • ResultArrayAsInt 함수는 Microsoft.Quantum.Convert 라이브러리가 제공합니다. 이 함수는 비트 문자열을 양의 정수로 변환합니다.

진입점 정의

이제 프로그램이 난수를 생성할 수 있습니다. 여기에서 프로그램 진입점을 정의합니다.

다음과 같이 RandomNumberGenerator.qs 파일을 수정합니다.

namespace QuantumRandomNumberGenerator {
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;

    @EntryPoint()
    operation Main() : Int {
        let max = 100;
        Message($"Sampling 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 is within the requested range.
        // Generate it again if it is 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);

        // At this point the qubit `q` has 50% chance of being measured in the
        // |0〉 state and 50% chance of being measured in the |1〉 state.
        // 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.
        // Qubits must be in the |0〉 state by the time they are released.
        Reset(q);

        // Return the result of the measurement.
        return result;
    }
}

let 지시문은 계산 도중 변경되지 않는 변수를 선언합니다. 학습 목적을 위해 여기서는 최대 값을 100으로 정의합니다.

프로그램 실행

새 난수 생성기를 사용해 보겠습니다.

  1. 프로그램을 실행하기 전에 대상 프로필을 제한 없음으로 설정해야 합니다. 보기>명령 팔레트를 선택하고 QIR을 검색한 후 Q#: Azure Quantum QIR 대상 프로필을 선택한 다음 Q#: 무제한을 선택합니다.
  2. 프로그램을 실행하려면 @EntryPoint() 아래 명령 목록에서 실행을 선택하거나 Ctrl+F5를 누릅니다. 디버그 콘솔에 출력이 표시됩니다.
  3. 다른 결과를 보려면 프로그램을 다시 실행합니다.

참고 항목

대상 프로필이 제한 없음으로 설정되지 않은 경우 프로그램을 실행할 때 오류가 발생합니다.

축하합니다! 이제 고전 논리를 Q#과 결합하여 양자 난수 생성기를 만드는 방법을 알았습니다.

보너스 연습

생성된 임의 값이 0이 아닌 최소 수 min보다 크도록 프로그램을 수정합니다.