Алгоритм Гровера: поиск за корень из N

Поиск иголки в стоге сена за ~корень из N шагов вместо N — за счёт амплитудного усиления.

Алгоритм Гровера находит выделенный элемент в неструктурированном наборе из N вариантов примерно за корень из N запросов вместо N в классическом переборе.

Задача и обещание

Есть N вариантов и оракул, умеющий сказать «да/нет» для каждого: это искомый элемент или нет. Структуры никакой — отсортировать или схитрить нельзя. Классически в среднем нужно проверить N/2 вариантов. Гровер находит ответ за порядка корня из N обращений. Для N = миллион это разница между ~500 000 и ~1000 шагами. Ускорение квадратичное (не экспоненциальное!), но универсальное: применимо к любому перебору, включая взлом симметричных шифров.

Амплитудное усиление по шагам

Идея: начать с равной суперпозиции всех N вариантов, затем повторять два действия — оракул помечает нужный вариант сменой знака амплитуды, а «диффузор» отражает все амплитуды относительно среднего. Каждый такой раунд чуть увеличивает амплитуду нужного варианта. Промоделируем это прямо на массиве амплитуд.

import math

N = 16
target = 11
amp = [1/math.sqrt(N)] * N        # равная суперпозиция

def oracle(a):
    a[target] = -a[target]        # пометить нужный сменой знака

def diffuse(a):
    avg = sum(a) / len(a)
    for i in range(len(a)):
        a[i] = 2*avg - a[i]       # отражение относительно среднего

steps = round(math.pi/4 * math.sqrt(N))   # оптимальное число итераций
for _ in range(steps):
    oracle(amp)
    diffuse(amp)

probs = [round(x*x, 3) for x in amp]
print('итераций:', steps)
print('p(target=%d) = %.3f' % (target, amp[target]**2))
print('макс. p у элемента:', probs.index(max(probs)))

Вывод:

итераций: 3
p(target=11) = 0.961
макс. p у элемента: 11

За 3 итерации вероятность найти нужный элемент при измерении выросла с 1/16 (0.0625) до ~0.96.

Как работает под капотом

Геометрически каждая пара «оракул + диффузор» поворачивает вектор состояния на маленький угол в сторону искомого. Через ~ (пи/4)·корень из N поворотов вектор почти совпадёт с целевым — тогда и надо измерять. Важная тонкость: итераций должно быть именно столько, не больше. Если перестараться, вектор «проскочит» цель, и вероятность снова упадёт. Это контринтуитивно: лишние шаги вредят. Поэтому число итераций считают заранее по формуле.

Частые ошибки

  • Ждать экспоненциального ускорения. У Гровера оно квадратичное (корень из N).
  • Делать слишком много итераций — вероятность успеха падает после оптимума.
  • Думать, что Гровер «взломает» AES мгновенно. Он лишь вдвое уменьшает эффективную длину ключа — лечится удлинением ключа.

Итог

  • Гровер ищет среди N вариантов за ~корень из N шагов (квадратичное ускорение).
  • Механизм — амплитудное усиление: оракул помечает, диффузор отражает относительно среднего.
  • Число итераций строго оптимально; перебор шагов вредит.
Проверьте себя
1. Какое ускорение даёт алгоритм Гровера?
AЭкспоненциальное
BКвадратичное (~корень из N вместо N)
CЛинейное
DНикакого
2. Из каких двух операций состоит итерация Гровера?
AИзмерение и сброс
BОракул (пометка знаком) и диффузор (отражение относительно среднего)
CДва Адамара
DCNOT и Z
3. Что будет, если сделать слишком много итераций Гровера?
AТочность вырастет
BВектор проскочит цель и вероятность успеха упадёт
CНичего не изменится
DАлгоритм зациклится