algorithmsgrovertutorialcircuitsqiskit

Grover's Algorithm: A Developer's Complete Guide with Circuit Implementation

quantumcomputer.dev
quantumcomputer.dev
May 17, 2026 · 367 views

Grover's algorithm is one of two quantum algorithms with proven, unambiguous quantum speedup (the other being Shor's). It searches an unstructured database of N items in O(√N) time, compared to O(N) for classical algorithms. This is a quadratic speedup — not exponential, but provably optimal for unstructured search.

This tutorial builds Grover's algorithm from scratch: the problem it solves, the mathematical mechanism behind the speedup, the circuit implementation, and a complete Qiskit implementation.

The Problem

You have a function f: {0,1}ⁿ → {0,1} where exactly one input x satisfies f(x) = 1. You want to find x*.

Classically, with no structure to exploit, you must evaluate f on average N/2 times to find x* (where N = 2ⁿ). This is optimal — no classical algorithm can do better in the worst case.

Grover's algorithm finds x* in O(√N) evaluations of f. For N = 1,000,000 items, classical search requires ~500,000 evaluations. Grover requires ~1,000.

Concrete example: Suppose f checks whether a candidate password matches a target hash (brute-force attack). With a 256-bit key space, N = 2²⁵⁶. Classically: 2²⁵⁵ evaluations expected. With Grover's: 2¹²⁸ evaluations. This is why AES-256 is recommended for quantum-resistant symmetric encryption — Grover halves the effective key length.

The Core Mechanism

Grover's algorithm works by amplitude amplification — a technique that increases the probability amplitude of the target state while decreasing the amplitudes of all other states.

Step 1: Initialize a uniform superposition

Start with all n qubits in |0⟩ and apply Hadamard to each:

|s⟩ = H⊗ⁿ|0⟩ⁿ = (1/√N) Σ|x⟩

Every possible input has equal amplitude 1/√N. The target state x* has amplitude 1/√N — no better than random guessing.

Step 2: Oracle application

The oracle Oₓ flips the sign of the target state's amplitude:

Oₓ|x⟩ = -|x⟩  if x = x*
Oₓ|x⟩ =  |x⟩  if x ≠ x*

In matrix form, this is a phase kickback operation. The oracle does not reveal which state x* is — it just negates that state's amplitude.

After oracle application, x has amplitude -1/√N. All other states still have amplitude +1/√N. The total probability of measuring x is still 1/N — we have not improved the measurement odds yet.

Step 3: Diffusion operator (inversion about the mean)

The diffusion operator D = 2|s⟩⟨s| - I reflects all amplitudes about their mean value.

Mean amplitude before diffusion:
μ = (1/N)[(N-1)(1/√N) + (-1/√N)]
  = (1/N)[(N-2)/√N]
  ≈ 1/√N   (for large N)

After inversion about the mean, the target state's amplitude increases from -1/√N to approximately 3/√N, while all other amplitudes decrease slightly.

Repeat oracle + diffusion ~π√N/4 times

Each iteration amplifies x's amplitude by approximately 2/√N. After π√N/4 iterations, x's amplitude is close to 1, giving a measurement probability close to 1.

This is the √N speedup: you need O(√N) repetitions, each requiring one oracle call.

The Circuit

For a 3-qubit example searching for x* = |101⟩ among 8 states:

q₀: ──H──[Oracle]──[Diffusion]──[Oracle]──[Diffusion]──M──
q₁: ──H──[Oracle]──[Diffusion]──[Oracle]──[Diffusion]──M──
q₂: ──H──[Oracle]──[Diffusion]──[Oracle]──[Diffusion]──M──
ancilla: ──X──H──[Oracle]──────────────────────────────────

For 3 qubits, the optimal number of iterations is π√8/4 ≈ 2.2, so 2 iterations.

The Oracle Implementation

The oracle marks x* = |101⟩ by applying a phase of -1 to that state. The standard technique uses a multi-controlled Z gate on the target state:

from qiskit import QuantumCircuit
import numpy as np

def grover_oracle(qc, target, n_qubits):
    """
    Apply oracle for target bitstring.
    target: string like '101'
    """
    # Flip qubits where target has '0'
    for i, bit in enumerate(reversed(target)):
        if bit == '0':
            qc.x(i)

    # Multi-controlled Z (MCZ)
    qc.h(n_qubits - 1)
    qc.mcx(list(range(n_qubits - 1)), n_qubits - 1)
    qc.h(n_qubits - 1)

    # Unflip qubits where target has '0'
    for i, bit in enumerate(reversed(target)):
        if bit == '0':
            qc.x(i)

    return qc

The Diffusion Operator

The diffusion operator 2|s⟩⟨s| - I can be implemented as H⊗ⁿ · (2|0⟩⟨0| - I) · H⊗ⁿ:

def diffusion_operator(qc, n_qubits):
    """
    Grover diffusion operator (inversion about mean).
    """
    # Apply H to all qubits
    qc.h(range(n_qubits))

    # Apply X to all qubits
    qc.x(range(n_qubits))

    # Multi-controlled Z
    qc.h(n_qubits - 1)
    qc.mcx(list(range(n_qubits - 1)), n_qubits - 1)
    qc.h(n_qubits - 1)

    # Uncompute X
    qc.x(range(n_qubits))

    # Uncompute H
    qc.h(range(n_qubits))

    return qc

Complete Qiskit Implementation

from qiskit import QuantumCircuit, transpile
from qiskit.primitives import StatevectorSampler
import numpy as np
import matplotlib.pyplot as plt

def build_grover_circuit(n_qubits, target, n_iterations=None):
    """
    Build complete Grover's search circuit.

    Args:
        n_qubits: Number of search qubits
        target: Target bitstring to find (e.g., '101')
        n_iterations: Number of Grover iterations (default: optimal)
    """
    N = 2 ** n_qubits
    if n_iterations is None:
        n_iterations = int(np.round(np.pi / 4 * np.sqrt(N)))

    qc = QuantumCircuit(n_qubits, n_qubits)

    # Step 1: Initialize uniform superposition
    qc.h(range(n_qubits))
    qc.barrier()

    # Step 2: Grover iterations
    for _ in range(n_iterations):
        # Oracle
        for i, bit in enumerate(reversed(target)):
            if bit == '0':
                qc.x(i)
        qc.h(n_qubits - 1)
        qc.mcx(list(range(n_qubits - 1)), n_qubits - 1)
        qc.h(n_qubits - 1)
        for i, bit in enumerate(reversed(target)):
            if bit == '0':
                qc.x(i)
        qc.barrier()

        # Diffusion
        qc.h(range(n_qubits))
        qc.x(range(n_qubits))
        qc.h(n_qubits - 1)
        qc.mcx(list(range(n_qubits - 1)), n_qubits - 1)
        qc.h(n_qubits - 1)
        qc.x(range(n_qubits))
        qc.h(range(n_qubits))
        qc.barrier()

    # Measure
    qc.measure(range(n_qubits), range(n_qubits))

    return qc, n_iterations


# Example: Search for '101' in 3-qubit space (8 items)
n = 3
target = '101'
qc, n_iter = build_grover_circuit(n, target)

print(f"Searching for: |{target}⟩")
print(f"Search space size: N = {2**n}")
print(f"Grover iterations: {n_iter}")
print(f"Classical expected evaluations: {2**(n-1)}")
print()
print(qc.draw())

# Simulate
sampler = StatevectorSampler()
job = sampler.run([qc], shots=1024)
result = job.result()
counts = result[0].data.meas.get_counts()

# Sort and display
sorted_counts = dict(sorted(counts.items(), key=lambda x: x[1], reverse=True))
print("\nMeasurement results (top 5):")
for state, count in list(sorted_counts.items())[:5]:
    print(f"  |{state}⟩: {count} ({count/1024*100:.1f}%)")

print(f"\nTarget state |{target}⟩ probability: {counts.get(target, 0)/1024*100:.1f}%")

Expected output:

Searching for: |101⟩
Search space size: N = 8
Grover iterations: 2
Classical expected evaluations: 4

Measurement results (top 5):
  |101⟩: 971 (94.8%)
  |000⟩: 7 (0.7%)
  |011⟩: 7 (0.7%)
  ...

Target state |101⟩ probability: 94.8%

The target state is found with ~95% probability using only 2 oracle calls, versus 4 expected classical evaluations for this 8-item search space.

Limitations and Practical Considerations

The oracle must be efficient: Grover's speedup assumes oracle evaluation is O(1). If the oracle itself requires O(N) classical computation, there is no net speedup.

The speedup is quadratic: Grover reduces O(N) to O(√N), not O(log N). For N = 10¹² items, classical needs 10¹² evaluations, Grover needs 10⁶. Significant, but not the exponential speedup of Shor's algorithm.

NISQ circuit depth: For large search spaces, the number of iterations grows as √N, each requiring multi-controlled gates that are expensive on NISQ hardware. Practical Grover circuits are limited to small search spaces on current hardware.

Multiple solutions: If there are k solutions, optimal iterations is ~π√(N/k)/4. You can estimate k by running a short Grover circuit and measuring the success probability.

Where Grover's Algorithm Actually Matters

Despite being "only" quadratic, Grover's speedup is significant in several real contexts:

Symmetric key cryptography: Grover halves effective key length. AES-128 drops to 64-bit security. This is why NIST recommends AES-256 for post-quantum security.

Hash function preimage resistance: Grover reduces SHA-256 preimage resistance to 128-bit security — still safe, but worth noting in security planning.

Constraint satisfaction: Grover can be composed with classical preprocessing to speed up certain NP problem searches — not solving NP in polynomial time, but providing a √N speedup over exhaustive search.

Amplitude estimation: Grover's core technique (amplitude amplification) underlies quantum amplitude estimation algorithms used in financial modeling (Monte Carlo acceleration) and quantum chemistry. These are arguably the more practically important near-term applications.

The Bell State template in the playground demonstrates entanglement. Load the Grover's template to see the complete 3-qubit circuit and simulate it in the browser.

quantumcomputer.dev
quantumcomputer.dev
The editorial team at quantumcomputer.dev