Когда не можешь выбрать, куда пойти на свидание, можно мучиться между кофейней, баром и прогулкой. А можно поступить взросло: отправить задачу на квантовый компьютер IBM и переложить ответственность на физику. Внутри — Qiskit, 8 кубитов, реальный job_id и самый пафосный способ заменить подбрасывание обычной монетки.
GitHub репо по ссылке
Проблема выбора
Да, это максимально избыточный способ заменить random.choice(). В этом и смысл. Зато в конце будет настоящий job_id, запуск на IBM Quantum и моральное право сказать: «Это не я выбрал. Это квантовая механика так решила».
Обычная монетка vs Квантовая монетка
Характеристика | Обычная монетка | Квантовая монетка |
Количество вариантов | 2 | до 256 в этой версии |
Job ID | Нет | Job ID — подтверждение, что задача была отправлена и выполнена на выбранном IBM Quantum backend. |
Стоимость | 0 ₽ | 0 ₽, но нужно 5 минут на регистрацию |
Практическая польза | высокая | сомнительная |
Уровень избыточности | Нормальный | Божественный |
Победитель очевиден - выбираем, конечно же, квантовую монетку, сегодня мы не мыслим практично :)
Что понадобится
Python и библиотека
qiskitpip install qiskit qiskit-aer qiskit-ibm-runtimeБесплатный токен IBM Quantum. Регистрируетесь на quantum.ibm.com, получаете токен, копируете в переменную окружения
IBM_QUANTUM_TOKEN. Всё.

Код
Схема такая: 8 кубитов, гейт Адамара на каждом, измерение, 256 повторных запусков. На выходе получаем набор битстрингов, берём один из реально измеренных результатов, превращаем его в число и маппим на индекс в списке вариантов.
В квантовые вычисления я заходил как разработчик, поэтому часть терминов пришлось отдельно разобрать по документации и примерам. Но в итоге схема оказалась вполне понятной.
Код под спойлером:
```python
import os
import sys
import secrets
from dataclasses import dataclass
from collections import Counter
# pip install qiskit qiskit-aer qiskit-ibm-runtime
#
# Для реального IBM Quantum:
# export IBM_QUANTUM_TOKEN="ваш_api_key"
#
# Опционально, но желательно:
# export IBM_QUANTUM_INSTANCE="ваш_CRN_или_service_name"
SHOTS = 256
QUBITS = 8
REGISTER_NAME = "coin"
# ========================
# ВАШ СПИСОК ВАРИАНТОВ
# ========================
OPTIONS = [
"Кофейня",
"Бар",
"Кино",
"Прогулка",
"Не идти никуда и наконец-то выспаться",
]
# ========================
@dataclass(frozen=True)
class CoinRun:
bitstrings: list[str]
counts: dict[str, int]
backend: str
job_id: str | None = None
def validate_options() -> None:
if not OPTIONS:
raise ValueError("Список OPTIONS пуст. Даже квантовая механика тут бессильна.")
max_values = 2 ** QUBITS
if len(OPTIONS) > max_values:
raise ValueError(
f"Слишком много вариантов: {len(OPTIONS)}. "
f"При {QUBITS} кубитах доступно максимум {max_values} базовых значений."
)
def make_circuit():
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
q = QuantumRegister(QUBITS, "q")
c = ClassicalRegister(QUBITS, REGISTER_NAME)
qc = QuantumCircuit(q, c, name="quantum_coin")
# Каждый кубит отправляем в состояние, где при измерении
# 0 и 1 выпадают примерно с равной вероятностью.
for qubit in q:
qc.h(qubit)
qc.measure(q, c)
return qc
def get_job_id(job) -> str | None:
job_id = getattr(job, "job_id", None)
if callable(job_id):
return job_id()
if job_id:
return str(job_id)
return None
def get_bits_local() -> CoinRun:
"""
Локальный режим:
1. Сначала пробуем AerSimulator.
2. Если qiskit-aer не установлен — используем secrets как честный fallback.
"""
try:
from qiskit_aer import AerSimulator
qc = make_circuit()
simulator = AerSimulator()
job = simulator.run(qc, shots=SHOTS, memory=True)
result = job.result()
bitstrings = result.get_memory(qc)
counts = result.get_counts(qc)
return CoinRun(
bitstrings=bitstrings,
counts=dict(counts),
backend="AerSimulator локально",
job_id=get_job_id(job),
)
except ImportError:
bitstrings = [
"".join(secrets.choice("01") for _ in range(QUBITS))
for _ in range(SHOTS)
]
return CoinRun(
bitstrings=bitstrings,
counts=dict(Counter(bitstrings)),
backend="Python secrets fallback",
job_id=None,
)
def get_bits_ibm() -> CoinRun:
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
token = os.getenv("IBM_QUANTUM_TOKEN")
instance = os.getenv("IBM_QUANTUM_INSTANCE")
if not token:
raise RuntimeError(
"Токен не найден. Укажите IBM_QUANTUM_TOKEN в переменной окружения."
)
service_kwargs = {
"channel": "ibm_quantum_platform",
"token": token,
}
if instance:
service_kwargs["instance"] = instance
service = QiskitRuntimeService(**service_kwargs)
backend = service.least_busy(
operational=True,
simulator=False,
min_num_qubits=QUBITS,
)
qc = make_circuit()
pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=1,
)
isa_circuit = pass_manager.run(qc)
sampler = Sampler(mode=backend)
job = sampler.run([isa_circuit], shots=SHOTS)
job_id = get_job_id(job)
print(f"\nОтправили задачу на IBM Quantum backend: {backend.name}")
if job_id:
print(f"Job ID: {job_id}")
print("Ждём ответ от Вселенной...")
result = job.result()
pub_result = result[0]
register_data = getattr(pub_result.data, REGISTER_NAME)
bitstrings = register_data.get_bitstrings()
counts = register_data.get_counts()
return CoinRun(
bitstrings=bitstrings,
counts=dict(counts),
backend=backend.name,
job_id=job_id,
)
def choose_option(bitstrings: list[str]) -> tuple[str, int, str, int]:
"""
Выбираем вариант по одному измеренному битстрингу.
Важно:
простой value % len(OPTIONS) может давать небольшой перекос,
если число вариантов не делит 2**QUBITS без остатка.
Поэтому используем rejection sampling:
берём только значения из диапазона, который ровно делится
на количество вариантов.
"""
variants_count = len(OPTIONS)
quantum_space = 2 ** QUBITS
usable_space = (quantum_space // variants_count) * variants_count
for bitstring in bitstrings:
value = int(bitstring, 2)
if value < usable_space:
index = value % variants_count
return OPTIONS[index], index, bitstring, value
# Практически сюда попасть почти невозможно при SHOTS=256,
# но пусть fallback будет.
index = secrets.randbelow(variants_count)
return OPTIONS[index], index, "fallback", index
def print_top_counts(counts: dict[str, int], selected_bitstring: str) -> None:
if not counts:
return
print("\nСтатистика измерений:")
print("-" * 40)
selected_count = counts.get(selected_bitstring, 0)
total = sum(counts.values())
if total > 0 and selected_count > 0:
selected_percent = selected_count / total * 100
print(
f"Выбранный битстринг встретился {selected_count} раз "
f"из {total} ({selected_percent:.2f}%)."
)
print("\nТоп-5 самых частых битстрингов:")
for bitstring, count in sorted(
counts.items(),
key=lambda item: item[1],
reverse=True,
)[:5]:
print(f" {bitstring}: {count}")
def main() -> None:
validate_options()
print("\n⚛️ КВАНТОВАЯ МОНЕТКА ⚛️")
print("=" * 40)
print(f"Кубитов: {QUBITS}")
print(f"Измерений: {SHOTS}")
print(f"Вариантов: {len(OPTIONS)}")
print("=" * 40)
use_ibm = input("Дёрнуть реальный IBM Quantum? (y/n): ").strip().lower() == "y"
if use_ibm:
try:
run = get_bits_ibm()
print(f"\n✅ Ответ получен с backend: {run.backend}")
except Exception as error:
print(f"\n❌ IBM Quantum не ответил: {error}")
print("Переключаемся на локальный режим...")
run = get_bits_local()
else:
run = get_bits_local()
print(f"\n💻 Считаем локально: {run.backend}")
choice, index, selected_bitstring, raw_value = choose_option(run.bitstrings)
print("\n🎲 РЕЗУЛЬТАТ 🎲")
print("=" * 40)
print(f"Источник: {run.backend}")
if run.job_id:
print(f"Job ID: {run.job_id}")
print(f"Битстринг: {selected_bitstring}")
print(f"Число: {raw_value}")
print(f"Вариант: {index + 1} из {len(OPTIONS)}")
print(f"ВЫБОР: {choice}")
print("=" * 40)
print_top_counts(run.counts, selected_bitstring)
print(
"\nЭто всё ещё максимально избыточная замена random.choice(). "
"Но теперь с кубитами, job_id и чувством научной важности."
)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nВыбор отменён. Судьба подождёт.")
sys.exit(130)
```
Не забудьте выставить ключ и запустить
$env:IBM_QUANTUM_TOKEN="ваштокен"
python quantum.pyВ итоге Вселенная ответила и запрос был отработан на реальном квантовом сервере:

IBM в панели отчитался о выполненной задаче.

Как это работает
Честно - сначала сам не знал, но разобрался.
Схема такая:
Создаём 8 кубитов.
На каждый кубит применяем гейт Адамара. Если совсем грубо: после этого при измерении каждый кубит может дать
0или1с примерно равной вероятностью.Измеряем схему 256 раз и получаем набор битстрингов вроде
01010110,11100001,00011010.Берём один реально измеренный битстринг.
Превращаем его в число.
Маппим число на индекс в списке вариантов.
Какие списки можно подставить
```python
# Мягкий режим: написать или не написать
OPTIONS = [
"Написать Ане",
"Написать Кате",
"Написать Маше",
"Написать Лене",
"Не писать никому и героически лечь спать в 23:00",
]
# Свидание без выбора людей как пунктов меню
OPTIONS = [
"Позвать в кофейню",
"Позвать в бар",
"Позвать в кино",
"Позвать просто погулять",
"Не устраивать социальный эксперимент и спокойно пережить вечер",
]
# Режим «я голодный, но решения принимать не способен»
OPTIONS = [
"Пицца",
"Суши",
"Бургер",
"Шаурма",
"Гречка: скучно, зато без архитектурных рисков",
]
# Для тех, кто в субботу за ноутом
OPTIONS = [
"Закрыть баг",
"Написать тесты",
"Дописать README",
"Рефакторить то, что никто не просил трогать",
"Пойти гулять, пока проект не превратился в свой фреймворк",
]
# Для разработчика перед релизом
OPTIONS = [
"Задеплоить и сделать вид, что всё под контролем",
"Сначала всё-таки прогнать тесты",
"Посмотреть логи и пожалеть об этом",
"Откатиться, пока никто не заметил",
"Сказать: «у меня локально работало»",
]
# Для выбора пет-проекта
OPTIONS = [
"Сделать маленькую полезную утилиту",
"Начать новый SaaS и страдать",
"Написать плагин для IDE",
"Сделать локальный AI-инструмент",
"Закрыть ноутбук и не плодить ещё один репозиторий",
]
```
Подставьте что угодно: вакансии, фильмы, страны для отпуска. Скрипт одинаково беспристрастен к выбору между шаурмой и рефакторингом.
В комментариях жду
Ваши самые безумные списки
OPTIONSСкриншоты job'ов из IBM Quantum
Священные холивары, куда без них :-)
Любые претензии к выбору направлять не мне, а в Институт Нильса Бора. Я только скрипт написал :-)
GitHub репо по ссылке
Удачи в экспериментах!
























