Тема «Гадалка угадает число» часто встречается в учебных курсах по программированию, на форумах о Python и в конкурсах по алгоритмике. Суть проста: пользователь задумывает целое число в известном диапазоне, а программа (или «гадалка») определяет его, задавая минимум вопросов. В этой статье мы разберём, как построить такую гадалку, какие алгоритмы используют для минимального количества попыток, и покажем готовый пример на Python.
Содержание
- Что такое «гадалка» в программировании?
- Математическая модель задачи
- Бинарный поиск – оптимальная стратегия
- Реализация на Python
- Вариации: магический квадрат, деление диапазона, адаптивные стратегии
- Тестирование и отладка
Что такое «гадалка» в программировании?
Термин «гадалка» (англ. guessing game) в контексте программирования обозначает интерактивное приложение, которое пытается определить заранее неизвестное значение, опираясь только на ответы пользователя. Чаще всего речь идёт о числах, но аналогичные задачи решаются для слов, цветов, фигур и т.д.
Ключевые требования к гадалке:
- Работать в заранее заданном диапазоне (например, от 1 до 100).
- Определять число за минимальное количество вопросов.
- Вести диалог с пользователем, предоставляя понятные подсказки.
Почему «гадалка» интересна?
- Позволяет познакомиться с алгоритмами поиска (бинарный поиск, интервальный поиск).
- Учит писать интерактивный ввод‑вывод в консольных приложениях.
- Служит отличной базой для изучения тестирования и обработки ошибок.
Математическая модель задачи
Пусть пользователь загадывает целое число n из диапазона [L, R] (включительно). Программа может задавать один из трёх вопросов:
- «Ваше число больше k?»,
- «Ваше число меньше k?»,
- «Ваше число равно k?».
Ответы «да»/«нет» (или «больше», «меньше», «равно») позволяют сузить диапазон. В идеальном случае каждый вопрос делит текущий диапазон пополам – тогда количество вопросов q удовлетворяет условию:
Отсюда получаем минимальное количество шагов:
Для диапазона 1…100 это ⌈log₂100⌉ = 7 вопросов.
Бинарный поиск – оптимальная стратегия
Самый простой и эффективный способ реализовать «деление пополам» – использовать бинарный поиск. Алгоритм:
- Установить
left = L,right = R; - Пока
left ≤ right:- Вычислить среднее:
mid = (left + right) // 2. - Спросить пользователя: «Ваше число больше
mid?».
Если ответ «да», установитьleft = mid + 1.
Если ответ «нет», спросить «Ваше число меньшеmid?».- Если «да», установить
right = mid ‒ 1. - Если «нет», значит
mid– загаданное число, вывести результат и завершить.
- Если «да», установить
- Вычислить среднее:
Таким образом каждый шаг сокращает диапазон ровно вдвое, а количество шагов достигает теоретического минимума.
Реализация на Python
Ниже представлен полностью рабочий пример программы‑гадалки, которая угадывает число от 0 до 1000 за ≤ 10 попыток.
#!/usr/bin/env python3
"""
Гадалка угадает число, задуманное пользователем.
Диапазон: от 0 до 1000 (включительно).
Алгоритм – бинарный поиск, минимальное количество вопросов.
"""
def ask(question: str) -> str:
answer = input(question + " (y/n): ").strip.lower
while answer not in ("y", "n"):
answer = input("Пожалуйста, введите 'y' (yes) или 'n' (no): ").strip.lower
return answer
def guess_number(left: int = 0, right: int = 1000) -> None:
attempts = 0
print(f"Загадайте целое число от {left} до {right}. Я попытаюсь его угадать.")
while left <= right:
attempts += 1
mid = (left + right) // 2
# 1) Больше?
if ask(f"Ваше число больше {mid}?") == "y":
left = mid + 1
continue
# 2) Меньше?
if ask(f"Ваше число меньше {mid}?") == "y":
right = mid ‒ 1
continue
# Если ответ «нет» на оба вопроса – значит ровно mid
print(f"
Я угадал! Ваше число – {mid}.")
print(f"Потребовалось попыток: {attempts}")
return
# Если цикл завершился без совпадения (корректность ввода нарушена)
print("
Что‑то пошло не так: диапазон исчерпан. Проверьте свои ответы.")
if __name__ == "__main__":
guess_number
Что делает код?
- Функция
askгарантирует, что пользователь вводит толькоy(yes) илиn(no). - Переменные
leftиrightхранят текущие границы диапазона. - Внутри цикла вычисляется
mid– середина диапазона. - Последовательно задаются два вопроса: «больше?» и «меньше?». Если оба ответа «нет», значит
mid– загаданное число. - Счётчик
attemptsфиксирует количество сделанных вопросов.
Запустив программу, пользователь получит диалог, похожий на:
Загадайте целое число от 0 до 1000. Я попытаюсь его угадать. Ваше число больше 500? (y/n): n Ваше число меньше 500? (y/n): y ... Я угадал! Ваше число – 317. Потребовалось попыток: 7
Вариации и альтернативные подходы
Магический квадрат
Один из интересных способов «угадывать» числа – использовать магический квадрат. В нём числа располагаются так, что суммы по строкам, столбцам и диагоналям одинаковы. При правильном построении пользователь может «выбрать» ячейку, а система по её координатам быстро вычисляет задуманное число. Однако такой метод требует предварительного построения таблицы и удобен лишь для небольших диапазонов (до 9×9).
Деление диапазона не пополам, а по «правилу 3‑х»
Если хочется уменьшить количество вопросов за счёт более «интеллектуального» деления, можно задавать не один, а два сравнения одновременно:
- Выбираем две границы
aиb(например, 1/3 и 2/3 от текущего диапазона). - Спросить: «Ваше число ≤ a?», «Ваше число ≤ b?». По двум ответам получаем три поддиапазона, каждый в среднем в 1/3 от исходного.
Теоретически это даёт ⌈log₃(N)⌉ шагов, но в реальности из‑за необходимости задавать два вопроса за один «раунд» сложность интерфейса возрастает.
Адаптивные стратегии с учётом вероятностных предположений
Если известна априорная вероятность (например, пользователь чаще загадывает числа ближе к середине), можно использовать информационную энтропию и выбирать точку разделения, минимизирующую ожидаемое количество вопросов. Это уже выходит за рамки простых учебных примеров, но иллюстрирует связь задачи с теорией информации.
Тестирование и отладка
Для уверенности в корректности гадалки рекомендуется написать автоматические тесты. Ниже показан простой подход с использованием unittest и имитации ввода через io.StringIO:
import unittest
from unittest.mock import patch
import io
class TestGuessNumber(unittest.TestCase):
def simulate(self, secret, left=0, right=100):
# Генерируем последовательность ответов «y/n» для бинарного поиска
answers = []
l, r = left, right
while l <= r:
mid = (l + r) // 2
if secret > mid:
answers.append('y') # больше
answers.append('n') # не меньше
l = mid + 1
elif secret < mid:
answers.append('n') # не больше
answers.append('y') # меньше
r = mid ⎼ 1
else:
answers.append('n')
answers.append('n')
break
return '\n'.join(answers) + '\n'
@patch('sys.stdout', new_callable=io.StringIO)
def test_random_numbers(self, mock_stdout):
for secret in [0, 1, 50, 99, 100]:
user_input = self;simulate(secret)
with patch('sys.stdin', new=io.StringIO(user_input)):
from guess_game import guess_number # предполагаем, что код в guess_game.py
guess_number(0, 100)
output = mock_stdout.getvalue
self.assertIn(f'Ваше число – {secret}', output)
if __name__ == '__main__':
unittest.main
Тест генерирует ответы «y/n» автоматически, имитируя поведение реального пользователя, и проверяет, что программа корректно выводит задуманное число.
«Гадалка угадает число» – классический учебный пример, который одновременно знакомит с:
- Алгоритмами бинарного поиска и их теоретическим минимумом.
- Практикой написания интерактивных консольных приложений.
- Тестированием пользовательского ввода.
- Различными вариациями (магический квадрат, деление на три, вероятностные подходы).
С помощью представленного кода любой желающий может быстро реализовать свою собственную гадалку, адаптировать её под любой диапазон и добавить дополнительные фичи (например, визуальное оформление, запись статистики, поддержку нескольких игроков).
Помните: главное – задавать вопросы, которые максимально «разрезают» пространство вариантов. И тогда ваша гадалка действительно будет угадывать числа за минимальное количество попыток!
Дата публикации: 28 апреля 2026 г.