HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#статьи</a></p>
1 <p><a>#статьи</a></p>
2 <ul><li>17 июл 2025</li>
2 <ul><li>17 июл 2025</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><h2>Три математических парадокса, на которых спотыкаются даже самые умные</h2>
4 </ul><h2>Три математических парадокса, на которых спотыкаются даже самые умные</h2>
5 <p>Сушим картошку, спасаем узников и играем с бесконечностью.</p>
5 <p>Сушим картошку, спасаем узников и играем с бесконечностью.</p>
6 <p>Иллюстрация: Оля Ежак для Skillbox Media</p>
6 <p>Иллюстрация: Оля Ежак для Skillbox Media</p>
7 <p>Автор. На 50% состоит из музыки и ещё на 50% - из любви к интересным людям.</p>
7 <p>Автор. На 50% состоит из музыки и ещё на 50% - из любви к интересным людям.</p>
8 <p>Математический парадокс - это логическое противоречие, когда на первый взгляд правильные рассуждения приводят к абсурдным или взаимоисключающим выводам. Например, вот классический парадокс лжеца: если человек говорит "я сейчас лгу", это создаёт противоречие.</p>
8 <p>Математический парадокс - это логическое противоречие, когда на первый взгляд правильные рассуждения приводят к абсурдным или взаимоисключающим выводам. Например, вот классический парадокс лжеца: если человек говорит "я сейчас лгу", это создаёт противоречие.</p>
9 <p>Если утверждение истинно, значит, человек действительно лжёт, и тогда оно становится ложным. И наоборот: если утверждение ложно, значит, человек не лжёт, что делает его истинным. То есть мы попадаем в замкнутый круг, где каждый вывод отрицает сам себя. В подобных противоречиях и заключается суть различных парадоксов.</p>
9 <p>Если утверждение истинно, значит, человек действительно лжёт, и тогда оно становится ложным. И наоборот: если утверждение ложно, значит, человек не лжёт, что делает его истинным. То есть мы попадаем в замкнутый круг, где каждый вывод отрицает сам себя. В подобных противоречиях и заключается суть различных парадоксов.</p>
10 <p>На этом с теорией мы закончим и перейдём к разбору трёх математических парадоксов, в которые нам было трудно поверить.</p>
10 <p>На этом с теорией мы закончим и перейдём к разбору трёх математических парадоксов, в которые нам было трудно поверить.</p>
11 <p><strong>Содержание</strong></p>
11 <p><strong>Содержание</strong></p>
12 <ul><li><a>Парадокс картофеля: как 1% воды уносит 50% массы</a></li>
12 <ul><li><a>Парадокс картофеля: как 1% воды уносит 50% массы</a></li>
13 <li><a>Парадокс 100 заключённых: как одна хитрость превращает 0,00...1% в 31%</a></li>
13 <li><a>Парадокс 100 заключённых: как одна хитрость превращает 0,00...1% в 31%</a></li>
14 <li><a>Парадокс отеля Гильберта: почему ∞ + 1 = ∞, но всё же ∞ ≠ ∞</a></li>
14 <li><a>Парадокс отеля Гильберта: почему ∞ + 1 = ∞, но всё же ∞ ≠ ∞</a></li>
15 <li><a>На подумать: парадокс Монти Холла, где двери сбивают с толку</a></li>
15 <li><a>На подумать: парадокс Монти Холла, где двери сбивают с толку</a></li>
16 </ul><p>В вашем сарае лежит 100 килограммов картофеля. Пусть 99% массы каждой картофелины - это вода. Вы оставляете картошку на ночь, и за это время она немного подсыхает. Теперь картофель состоит из 98% воды. Вопрос: сколько килограммов картофеля осталось в вашем сарае?</p>
16 </ul><p>В вашем сарае лежит 100 килограммов картофеля. Пусть 99% массы каждой картофелины - это вода. Вы оставляете картошку на ночь, и за это время она немного подсыхает. Теперь картофель состоит из 98% воды. Вопрос: сколько килограммов картофеля осталось в вашем сарае?</p>
17 <p>Казалось бы, если влажность уменьшилась всего на 1%, то и вес картошки должен сократиться на 1 кг. Но давайте немного посчитаем.</p>
17 <p>Казалось бы, если влажность уменьшилась всего на 1%, то и вес картошки должен сократиться на 1 кг. Но давайте немного посчитаем.</p>
18 <p><strong>Шаг 1.</strong>Если картофель на 99% состоит из воды, то оставшийся 1% - это твёрдая часть. То есть в 100 кг будет 99 кг воды и 1 кг сухого вещества.</p>
18 <p><strong>Шаг 1.</strong>Если картофель на 99% состоит из воды, то оставшийся 1% - это твёрдая часть. То есть в 100 кг будет 99 кг воды и 1 кг сухого вещества.</p>
19 <p><strong>Шаг 2.</strong>На следующий день состав картофеля меняется: теперь в нём 98% воды, а количество сухого вещества остаётся прежним - 1 кг. Однако теперь этот 1 кг составляет уже 2% от общего веса картофеля.</p>
19 <p><strong>Шаг 2.</strong>На следующий день состав картофеля меняется: теперь в нём 98% воды, а количество сухого вещества остаётся прежним - 1 кг. Однако теперь этот 1 кг составляет уже 2% от общего веса картофеля.</p>
20 <p><strong>Шаг 3.</strong>Решаем простую <a>пропорцию</a>: если 2% массы - это 1 кг сухого вещества, то 100% массы будет составлять 1 кг / 0,02 = 50 кг. То есть после высыхания наш картофель стал весить ровно в два раза меньше.</p>
20 <p><strong>Шаг 3.</strong>Решаем простую <a>пропорцию</a>: если 2% массы - это 1 кг сухого вещества, то 100% массы будет составлять 1 кг / 0,02 = 50 кг. То есть после высыхания наш картофель стал весить ровно в два раза меньше.</p>
21 Слева - 100 кг картофеля: 99% воды и 1 кг твёрдого вещества. В центре - 50 кг после сушки: 98% воды и 1 кг твёрдого вещества. А справа видно, как уменьшение воды удваивает долю твёрдого вещества - с 1% до 2%<em>Изображение: Cmglee /<a>Wikimedia Commons</a></em><p>Начальник тюрьмы предлагает заключённым игру, победа в которой может принести свободу всем участникам. В тюрьме содержится 100 заключённых, и у каждого на футболке уникальный номер от 1 до 100.</p>
21 Слева - 100 кг картофеля: 99% воды и 1 кг твёрдого вещества. В центре - 50 кг после сушки: 98% воды и 1 кг твёрдого вещества. А справа видно, как уменьшение воды удваивает долю твёрдого вещества - с 1% до 2%<em>Изображение: Cmglee /<a>Wikimedia Commons</a></em><p>Начальник тюрьмы предлагает заключённым игру, победа в которой может принести свободу всем участникам. В тюрьме содержится 100 заключённых, и у каждого на футболке уникальный номер от 1 до 100.</p>
22 <p>Перед ними - комната с большим шкафом, в котором находится 100 пронумерованных ящиков (от 1 до 100). В каждый ящик начальник случайным образом положил листок с номером одного из заключённых.</p>
22 <p>Перед ними - комната с большим шкафом, в котором находится 100 пронумерованных ящиков (от 1 до 100). В каждый ящик начальник случайным образом положил листок с номером одного из заключённых.</p>
23 <p>Каждый заключённый по очереди заходит в комнату и может открыть 50 из 100 ящиков, пытаясь найти листок со своим номером. Если все 100 справятся с задачей, начальник тюрьмы отпустит их на свободу. Но если хотя бы один не найдёт свой номер, все возвращаются по своим камерам.</p>
23 <p>Каждый заключённый по очереди заходит в комнату и может открыть 50 из 100 ящиков, пытаясь найти листок со своим номером. Если все 100 справятся с задачей, начальник тюрьмы отпустит их на свободу. Но если хотя бы один не найдёт свой номер, все возвращаются по своим камерам.</p>
24 <p>Заключённые не могут ничего менять в комнате, оставлять пометки или передавать информацию тем, кто ещё не заходил. Однако перед началом игры им разрешено обсудить правила и договориться о стратегии.</p>
24 <p>Заключённые не могут ничего менять в комнате, оставлять пометки или передавать информацию тем, кто ещё не заходил. Однако перед началом игры им разрешено обсудить правила и договориться о стратегии.</p>
25 <p>Вопрос: как в такой ситуации должны действовать заключённые, чтобы получить приемлемые шансы на освобождение?</p>
25 <p>Вопрос: как в такой ситуации должны действовать заключённые, чтобы получить приемлемые шансы на освобождение?</p>
26 Предположим, первый заключённый выбрал ящик №69. Если под ним не окажется нужного номера, то у него останется ещё 49 попыток<em>Скриншот: Pygame / Skillbox Media</em><p>Перед поиском оптимального решения давайте рассмотрим самый очевидный вариант - когда все заключённые открывают 50 ящиков в случайном порядке, без какой-либо стратегии. В этом случае вероятность того, что один заключённый найдёт свой листок, составляет 1/2. Вероятность того, что два заключённых подряд найдут свои листки, равна 1/2 × 1/2 = 1/4. Для трёх подряд - 1/2 × 1/2 × 1/2 = 1/8.</p>
26 Предположим, первый заключённый выбрал ящик №69. Если под ним не окажется нужного номера, то у него останется ещё 49 попыток<em>Скриншот: Pygame / Skillbox Media</em><p>Перед поиском оптимального решения давайте рассмотрим самый очевидный вариант - когда все заключённые открывают 50 ящиков в случайном порядке, без какой-либо стратегии. В этом случае вероятность того, что один заключённый найдёт свой листок, составляет 1/2. Вероятность того, что два заключённых подряд найдут свои листки, равна 1/2 × 1/2 = 1/4. Для трёх подряд - 1/2 × 1/2 × 1/2 = 1/8.</p>
27 <p>Если посчитать вероятность для 100 заключённых, то получится (1/2)¹⁰⁰ ≈ 10⁻³⁰. Это невероятно маленькое число, в котором первая значащая цифра появится через нуль и ещё примерно двадцать восемь нулей после запятой. Такую вероятность можно считать почти нулевой, - видимо, именно поэтому начальник тюрьмы и согласился на игру. Но давайте попробуем увеличить шансы.</p>
27 <p>Если посчитать вероятность для 100 заключённых, то получится (1/2)¹⁰⁰ ≈ 10⁻³⁰. Это невероятно маленькое число, в котором первая значащая цифра появится через нуль и ещё примерно двадцать восемь нулей после запятой. Такую вероятность можно считать почти нулевой, - видимо, именно поэтому начальник тюрьмы и согласился на игру. Но давайте попробуем увеличить шансы.</p>
28 <p>Предположим, перед началом игры все заключённые договорились использовать стратегию "следуй за цепочкой". Каждый действует так:</p>
28 <p>Предположим, перед началом игры все заключённые договорились использовать стратегию "следуй за цепочкой". Каждый действует так:</p>
29 <ul><li>Сначала он открывает ящик с номером, который совпадает с его номером. Например, заключённый №25 открывает ящик №25.</li>
29 <ul><li>Сначала он открывает ящик с номером, который совпадает с его номером. Например, заключённый №25 открывает ящик №25.</li>
30 <li>Затем смотрит, какой номер указан на бумажке внутри. Допустим, в ящике № 25 окажется спрятан номер 73.</li>
30 <li>Затем смотрит, какой номер указан на бумажке внутри. Допустим, в ящике № 25 окажется спрятан номер 73.</li>
31 <li>После этого он открывает ящик №73 и снова смотрит, какой номер внутри. Пусть это будет 14.</li>
31 <li>После этого он открывает ящик №73 и снова смотрит, какой номер внутри. Пусть это будет 14.</li>
32 <li>Далее он открывает ящик №14 - и продолжает цепочку до тех пор, пока не найдёт свой номер или не исчерпает 50 попыток.</li>
32 <li>Далее он открывает ящик №14 - и продолжает цепочку до тех пор, пока не найдёт свой номер или не исчерпает 50 попыток.</li>
33 </ul><p>Эта стратегия работает благодаря тому, что все номера в ящиках распределены без повторений и пропусков - то есть образуют перестановку чисел от 1 до 100. Это значит, что каждый номер встречается ровно один раз, но не обязательно в "своём" ящике.</p>
33 </ul><p>Эта стратегия работает благодаря тому, что все номера в ящиках распределены без повторений и пропусков - то есть образуют перестановку чисел от 1 до 100. Это значит, что каждый номер встречается ровно один раз, но не обязательно в "своём" ящике.</p>
34 <p>Такую перестановку удобно представить как набор замкнутых цепочек, или циклов. Цикл - это последовательность переходов по номерам: участник начинает с какого-то ящика, смотрит, какой номер внутри, открывает следующий ящик с этим номером, снова смотрит, что внутри, и так далее - пока не вернётся к начальному номеру. И если длина такого цикла для какого-либо заключённого не будет превышать 50, то он гарантированно найдёт нужный номер за отведённое число попыток.</p>
34 <p>Такую перестановку удобно представить как набор замкнутых цепочек, или циклов. Цикл - это последовательность переходов по номерам: участник начинает с какого-то ящика, смотрит, какой номер внутри, открывает следующий ящик с этим номером, снова смотрит, что внутри, и так далее - пока не вернётся к начальному номеру. И если длина такого цикла для какого-либо заключённого не будет превышать 50, то он гарантированно найдёт нужный номер за отведённое число попыток.</p>
35 На скриншоте показан пример, где заключённый №69 начал поиск с соответствующего ящика и за 15 шагов смог найти свой номер<em>Скриншот: Pygame / Skillbox Media</em><p>Если все циклы окажутся короче 51 ящика, то каждый заключённый сможет найти свой номер. Именно эта особенность и даёт шанс на всей команде. Удивительно, но вероятность того, что в случайной перестановке из 100 элементов ни один цикл не будет длиннее 50, составляет примерно 31%. Это подтверждается как<a>математическими расчётами</a>, так и многочисленными<a>симуляциями</a>этой задачи.</p>
35 На скриншоте показан пример, где заключённый №69 начал поиск с соответствующего ящика и за 15 шагов смог найти свой номер<em>Скриншот: Pygame / Skillbox Media</em><p>Если все циклы окажутся короче 51 ящика, то каждый заключённый сможет найти свой номер. Именно эта особенность и даёт шанс на всей команде. Удивительно, но вероятность того, что в случайной перестановке из 100 элементов ни один цикл не будет длиннее 50, составляет примерно 31%. Это подтверждается как<a>математическими расчётами</a>, так и многочисленными<a>симуляциями</a>этой задачи.</p>
36 <p>Ещё интереснее, что, если увеличить количество заключённых до 1 000, 10 000 или даже 100 000,<a>вероятность успеха почти не изменится</a>и останется на уровне 31%. То есть даже при очень большом числе участников у команды сохраняются шансы на победу. Низкие, но вполне реальные.</p>
36 <p>Ещё интереснее, что, если увеличить количество заключённых до 1 000, 10 000 или даже 100 000,<a>вероятность успеха почти не изменится</a>и останется на уровне 31%. То есть даже при очень большом числе участников у команды сохраняются шансы на победу. Низкие, но вполне реальные.</p>
37 <p>Но мы не предлагаем слепо верить этим цифрам - лучше убедиться во всём самостоятельно. Для этого вам понадобится:</p>
37 <p>Но мы не предлагаем слепо верить этим цифрам - лучше убедиться во всём самостоятельно. Для этого вам понадобится:</p>
38 <ul><li><a>Установить Python</a>для вашей операционной системы и <a>выбрать редактор кода</a>. Мы будем использовать<a>Visual Studio Code</a>.</li>
38 <ul><li><a>Установить Python</a>для вашей операционной системы и <a>выбрать редактор кода</a>. Мы будем использовать<a>Visual Studio Code</a>.</li>
39 <li>С помощью <a>менеджера пакетов PIP</a>загрузить библиотеку <a>Pygame</a>. Команда: pip install pygame или py -m pip install pygame.</li>
39 <li>С помощью <a>менеджера пакетов PIP</a>загрузить библиотеку <a>Pygame</a>. Команда: pip install pygame или py -m pip install pygame.</li>
40 <li>Создать в VS Code новый файл, например, paradox_game.py, и вставить в него код, который мы спрячем ниже под спойлером.</li>
40 <li>Создать в VS Code новый файл, например, paradox_game.py, и вставить в него код, который мы спрячем ниже под спойлером.</li>
41 <li>Открыть терминал в VS Code и запустить игру. Команда: py paradox_game.py или python paradox_game.py.</li>
41 <li>Открыть терминал в VS Code и запустить игру. Команда: py paradox_game.py или python paradox_game.py.</li>
42 </ul><p>Перед вами откроется простая версия игры, в которой можно выбрать любой ящик и двигаться по цепочке в надежде найти свой номер.</p>
42 </ul><p>Перед вами откроется простая версия игры, в которой можно выбрать любой ящик и двигаться по цепочке в надежде найти свой номер.</p>
43 <p>Код игры "Парадокс 100 заключённых"</p>
43 <p>Код игры "Парадокс 100 заключённых"</p>
44 import pygame import random import sys N_PRISONERS = 100 DEFAULT_BOXES_IN_ROW = 20 MIN_BOX_SIZE = 28 MAX_BOX_SIZE = 100 MARGIN_RATIO = 0.12 SIDE_MARGIN_RATIO = 0.14 TOP_BOTTOM_MARGIN = 80 FPS = 60 WHITE = (245, 245, 245) BLACK = (30, 30, 30) GREEN = (56, 200, 100) RED = (220, 50, 50) GRAY = (180, 180, 180) BLUE = (72, 140, 210) YELLOW = (245, 213, 61) ORANGE = (255, 150, 30) MAX_STEPS = N_PRISONERS // 2 pygame.init() pygame.font.init() BASE_FONT_SIZE = 18 BASE_FONT_SMALL = 14 def create_permutation(n): arr = list(range(1, n+1)) random.shuffle(arr) return arr def find_path_and_result(permutation, prisoner, max_steps): path = [] current = prisoner found = False found_at = -1 for i in range(max_steps): path.append(current) if permutation[current - 1] == prisoner: found = True found_at = i + 1 path.append(prisoner) break current = permutation[current - 1] return path, found, found_at def get_grid_params(window_w, window_h): boxes_in_row = DEFAULT_BOXES_IN_ROW num_rows = (N_PRISONERS - 1) // boxes_in_row + 1 side_margin = int(window_w * SIDE_MARGIN_RATIO) usable_w = window_w - 2 * side_margin margin = max(2, int(usable_w / (boxes_in_row * 9))) # динамичный отступ box_size = int(usable_w / (boxes_in_row + (boxes_in_row-1) * MARGIN_RATIO)) box_size = max(MIN_BOX_SIZE, min(box_size, MAX_BOX_SIZE)) grid_w = boxes_in_row * box_size + (boxes_in_row-1) * margin grid_h = num_rows * box_size + (num_rows-1) * margin offset_x = (window_w - grid_w) // 2 offset_y = TOP_BOTTOM_MARGIN return box_size, margin, boxes_in_row, num_rows, offset_x, offset_y def get_box_coords(idx, box_size, margin, boxes_in_row, offset_x, offset_y): row = (idx - 1) // boxes_in_row col = (idx - 1) % boxes_in_row x = offset_x + col * (box_size + margin) + box_size // 2 y = offset_y + row * (box_size + margin) + box_size // 2 return x, y def draw_boxes(screen, box_colors, visible_path, selected_prisoner, permutation, box_size, margin, boxes_in_row, offset_x, offset_y, font_small): for i in range(N_PRISONERS): row = i // boxes_in_row col = i % boxes_in_row x = offset_x + col * (box_size + margin) y = offset_y + row * (box_size + margin) color = box_colors[i] if selected_prisoner and (i+1) == selected_prisoner: color = BLUE if visible_path: if (i+1) == visible_path[-1]: color = YELLOW elif (i+1) in visible_path: color = ORANGE pygame.draw.rect(screen, color, (x, y, box_size, box_size), border_radius=int(box_size*0.22)) num_text = font_small.render(str(i+1), True, BLACK) tx = x + (box_size - num_text.get_width()) // 2 ty = y + (box_size - num_text.get_height()) // 2 screen.blit(num_text, (tx, ty)) def draw_path(screen, path, step, box_size, margin, boxes_in_row, offset_x, offset_y): if not path or len(path) &lt; 2: return draw_to = min(step, len(path)-1) for i in range(draw_to): x1, y1 = get_box_coords(path[i], box_size, margin, boxes_in_row, offset_x, offset_y) x2, y2 = get_box_coords(path[i+1], box_size, margin, boxes_in_row, offset_x, offset_y) pygame.draw.line(screen, ORANGE, (x1, y1), (x2, y2), max(2, box_size // 17)) dx, dy = x2 - x1, y2 - y1 length = max((dx**2 + dy**2) ** 0.5, 1) ux, uy = dx / length, dy / length tip_x = int(x2 - ux * box_size * 0.25) tip_y = int(y2 - uy * box_size * 0.25) pygame.draw.circle(screen, RED, (tip_x, tip_y), max(4, box_size // 10)) def prepare_ui_lines(selected_prisoner, path, permutation, step, found, found_at_step, max_steps, game_over): lines = [] if selected_prisoner is None: lines.append(("Кликни по ящику: выбери свой номер заключённого!", "main", BLACK)) else: cur_step = min(step, len(path)-1) lines.append((f"Заключённый №{selected_prisoner}", "main", BLUE)) if found and found_at_step != -1 and cur_step &gt;= found_at_step: lines.append((f"УРА! Найдено за {found_at_step} шагов!", "main", GREEN)) elif game_over: lines.append(("Игра окончена: не найдено за 50 шагов!", "main", RED)) if path and cur_step &lt; len(path): last_box = path[cur_step] found_number = permutation[last_box - 1] lines.append((f"В ящике №{last_box} спрятан номер {found_number}", "small", BLACK)) lines.append((f"Шаг: {cur_step+1} из {MAX_STEPS}", "small", BLACK)) lines.append(("ENTER -- следующий шаг | SPACE -- новая игра", "small", BLACK)) return lines def draw_ui(screen, lines, font, font_small): window_w, window_h = screen.get_size() line_step = int(font.get_height() * 1.18) total_height = len(lines) * line_step y0 = window_h - total_height - 12 for i, (text, ftype, color) in enumerate(lines): f = font if ftype == "main" else font_small t_surf = f.render(text, True, color) screen.blit(t_surf, (30, y0 + i * line_step)) def main(): WINDOW_START_W = 1320 WINDOW_START_H = 750 screen = pygame.display.set_mode((WINDOW_START_W, WINDOW_START_H), pygame.RESIZABLE) pygame.display.set_caption("Парадокс 100 заключённых") clock = pygame.time.Clock() permutation = create_permutation(N_PRISONERS) box_colors = [WHITE for _ in range(N_PRISONERS)] selected_prisoner = None path = [] step = 0 found = None found_at_step = None game_over = False running = True while running: window_w, window_h = screen.get_size() box_size, margin, boxes_in_row, num_rows, offset_x, offset_y = get_grid_params(window_w, window_h) font_size = max(16, int(BASE_FONT_SIZE * (box_size / 35))) font_small_size = max(11, int(BASE_FONT_SMALL * (box_size / 35))) font = pygame.font.SysFont("consolas", font_size) font_small = pygame.font.SysFont("consolas", font_small_size) lines = prepare_ui_lines(selected_prisoner, path, permutation, step, found, found_at_step, MAX_STEPS, game_over) screen.fill(GRAY) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.VIDEORESIZE: screen = pygame.display.set_mode(event.size, pygame.RESIZABLE) elif event.type == pygame.MOUSEBUTTONDOWN: if not game_over: mx, my = pygame.mouse.get_pos() for i in range(N_PRISONERS): row = i // boxes_in_row col = i % boxes_in_row x = offset_x + col * (box_size + margin) y = offset_y + row * (box_size + margin) if x &lt;= mx &lt;= x+box_size and y &lt;= my &lt;= y+box_size: selected_prisoner = i + 1 path, found, found_at_step = find_path_and_result(permutation, selected_prisoner, MAX_STEPS) step = 0 game_over = False break elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: permutation = create_permutation(N_PRISONERS) box_colors = [WHITE for _ in range(N_PRISONERS)] selected_prisoner = None path = [] found = None found_at_step = None step = 0 game_over = False elif event.key == pygame.K_RETURN: if selected_prisoner is not None and path and not game_over: max_show = len(path)-1 if found and step &lt; found_at_step: step += 1 elif not found and step &lt; MAX_STEPS-1: step += 1 elif not found and step &gt;= MAX_STEPS-1: game_over = True cur_step = min(step, len(path)-1) if path else 0 visible_path = path[:cur_step+1] if path else [] draw_boxes(screen, box_colors, visible_path, selected_prisoner, permutation, box_size, margin, boxes_in_row, offset_x, offset_y, font_small) draw_path(screen, path, step, box_size, margin, boxes_in_row, offset_x, offset_y) draw_ui(screen, lines, font, font_small) pygame.display.flip() clock.tick(FPS) pygame.quit() sys.exit() if __name__ == "__main__": main()Интерфейс VS Code после добавления кода и запуска игры<em>Скриншот: Pygame / Skillbox Media</em><p><a>Парадокс Гранд-отеля</a> - это мысленный эксперимент, который предложил математик <a>Дэвид Гильберт</a>для иллюстрации необычных свойств бесконечности. Представьте отель с бесконечным числом комнат, в котором нет свободных мест: каждая комната занята, и в каждой живёт по одному постояльцу. Давайте поэкспериментируем с этим отелем и увидим, насколько странными могут быть его свойства.</p>
44 import pygame import random import sys N_PRISONERS = 100 DEFAULT_BOXES_IN_ROW = 20 MIN_BOX_SIZE = 28 MAX_BOX_SIZE = 100 MARGIN_RATIO = 0.12 SIDE_MARGIN_RATIO = 0.14 TOP_BOTTOM_MARGIN = 80 FPS = 60 WHITE = (245, 245, 245) BLACK = (30, 30, 30) GREEN = (56, 200, 100) RED = (220, 50, 50) GRAY = (180, 180, 180) BLUE = (72, 140, 210) YELLOW = (245, 213, 61) ORANGE = (255, 150, 30) MAX_STEPS = N_PRISONERS // 2 pygame.init() pygame.font.init() BASE_FONT_SIZE = 18 BASE_FONT_SMALL = 14 def create_permutation(n): arr = list(range(1, n+1)) random.shuffle(arr) return arr def find_path_and_result(permutation, prisoner, max_steps): path = [] current = prisoner found = False found_at = -1 for i in range(max_steps): path.append(current) if permutation[current - 1] == prisoner: found = True found_at = i + 1 path.append(prisoner) break current = permutation[current - 1] return path, found, found_at def get_grid_params(window_w, window_h): boxes_in_row = DEFAULT_BOXES_IN_ROW num_rows = (N_PRISONERS - 1) // boxes_in_row + 1 side_margin = int(window_w * SIDE_MARGIN_RATIO) usable_w = window_w - 2 * side_margin margin = max(2, int(usable_w / (boxes_in_row * 9))) # динамичный отступ box_size = int(usable_w / (boxes_in_row + (boxes_in_row-1) * MARGIN_RATIO)) box_size = max(MIN_BOX_SIZE, min(box_size, MAX_BOX_SIZE)) grid_w = boxes_in_row * box_size + (boxes_in_row-1) * margin grid_h = num_rows * box_size + (num_rows-1) * margin offset_x = (window_w - grid_w) // 2 offset_y = TOP_BOTTOM_MARGIN return box_size, margin, boxes_in_row, num_rows, offset_x, offset_y def get_box_coords(idx, box_size, margin, boxes_in_row, offset_x, offset_y): row = (idx - 1) // boxes_in_row col = (idx - 1) % boxes_in_row x = offset_x + col * (box_size + margin) + box_size // 2 y = offset_y + row * (box_size + margin) + box_size // 2 return x, y def draw_boxes(screen, box_colors, visible_path, selected_prisoner, permutation, box_size, margin, boxes_in_row, offset_x, offset_y, font_small): for i in range(N_PRISONERS): row = i // boxes_in_row col = i % boxes_in_row x = offset_x + col * (box_size + margin) y = offset_y + row * (box_size + margin) color = box_colors[i] if selected_prisoner and (i+1) == selected_prisoner: color = BLUE if visible_path: if (i+1) == visible_path[-1]: color = YELLOW elif (i+1) in visible_path: color = ORANGE pygame.draw.rect(screen, color, (x, y, box_size, box_size), border_radius=int(box_size*0.22)) num_text = font_small.render(str(i+1), True, BLACK) tx = x + (box_size - num_text.get_width()) // 2 ty = y + (box_size - num_text.get_height()) // 2 screen.blit(num_text, (tx, ty)) def draw_path(screen, path, step, box_size, margin, boxes_in_row, offset_x, offset_y): if not path or len(path) &lt; 2: return draw_to = min(step, len(path)-1) for i in range(draw_to): x1, y1 = get_box_coords(path[i], box_size, margin, boxes_in_row, offset_x, offset_y) x2, y2 = get_box_coords(path[i+1], box_size, margin, boxes_in_row, offset_x, offset_y) pygame.draw.line(screen, ORANGE, (x1, y1), (x2, y2), max(2, box_size // 17)) dx, dy = x2 - x1, y2 - y1 length = max((dx**2 + dy**2) ** 0.5, 1) ux, uy = dx / length, dy / length tip_x = int(x2 - ux * box_size * 0.25) tip_y = int(y2 - uy * box_size * 0.25) pygame.draw.circle(screen, RED, (tip_x, tip_y), max(4, box_size // 10)) def prepare_ui_lines(selected_prisoner, path, permutation, step, found, found_at_step, max_steps, game_over): lines = [] if selected_prisoner is None: lines.append(("Кликни по ящику: выбери свой номер заключённого!", "main", BLACK)) else: cur_step = min(step, len(path)-1) lines.append((f"Заключённый №{selected_prisoner}", "main", BLUE)) if found and found_at_step != -1 and cur_step &gt;= found_at_step: lines.append((f"УРА! Найдено за {found_at_step} шагов!", "main", GREEN)) elif game_over: lines.append(("Игра окончена: не найдено за 50 шагов!", "main", RED)) if path and cur_step &lt; len(path): last_box = path[cur_step] found_number = permutation[last_box - 1] lines.append((f"В ящике №{last_box} спрятан номер {found_number}", "small", BLACK)) lines.append((f"Шаг: {cur_step+1} из {MAX_STEPS}", "small", BLACK)) lines.append(("ENTER -- следующий шаг | SPACE -- новая игра", "small", BLACK)) return lines def draw_ui(screen, lines, font, font_small): window_w, window_h = screen.get_size() line_step = int(font.get_height() * 1.18) total_height = len(lines) * line_step y0 = window_h - total_height - 12 for i, (text, ftype, color) in enumerate(lines): f = font if ftype == "main" else font_small t_surf = f.render(text, True, color) screen.blit(t_surf, (30, y0 + i * line_step)) def main(): WINDOW_START_W = 1320 WINDOW_START_H = 750 screen = pygame.display.set_mode((WINDOW_START_W, WINDOW_START_H), pygame.RESIZABLE) pygame.display.set_caption("Парадокс 100 заключённых") clock = pygame.time.Clock() permutation = create_permutation(N_PRISONERS) box_colors = [WHITE for _ in range(N_PRISONERS)] selected_prisoner = None path = [] step = 0 found = None found_at_step = None game_over = False running = True while running: window_w, window_h = screen.get_size() box_size, margin, boxes_in_row, num_rows, offset_x, offset_y = get_grid_params(window_w, window_h) font_size = max(16, int(BASE_FONT_SIZE * (box_size / 35))) font_small_size = max(11, int(BASE_FONT_SMALL * (box_size / 35))) font = pygame.font.SysFont("consolas", font_size) font_small = pygame.font.SysFont("consolas", font_small_size) lines = prepare_ui_lines(selected_prisoner, path, permutation, step, found, found_at_step, MAX_STEPS, game_over) screen.fill(GRAY) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.VIDEORESIZE: screen = pygame.display.set_mode(event.size, pygame.RESIZABLE) elif event.type == pygame.MOUSEBUTTONDOWN: if not game_over: mx, my = pygame.mouse.get_pos() for i in range(N_PRISONERS): row = i // boxes_in_row col = i % boxes_in_row x = offset_x + col * (box_size + margin) y = offset_y + row * (box_size + margin) if x &lt;= mx &lt;= x+box_size and y &lt;= my &lt;= y+box_size: selected_prisoner = i + 1 path, found, found_at_step = find_path_and_result(permutation, selected_prisoner, MAX_STEPS) step = 0 game_over = False break elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: permutation = create_permutation(N_PRISONERS) box_colors = [WHITE for _ in range(N_PRISONERS)] selected_prisoner = None path = [] found = None found_at_step = None step = 0 game_over = False elif event.key == pygame.K_RETURN: if selected_prisoner is not None and path and not game_over: max_show = len(path)-1 if found and step &lt; found_at_step: step += 1 elif not found and step &lt; MAX_STEPS-1: step += 1 elif not found and step &gt;= MAX_STEPS-1: game_over = True cur_step = min(step, len(path)-1) if path else 0 visible_path = path[:cur_step+1] if path else [] draw_boxes(screen, box_colors, visible_path, selected_prisoner, permutation, box_size, margin, boxes_in_row, offset_x, offset_y, font_small) draw_path(screen, path, step, box_size, margin, boxes_in_row, offset_x, offset_y) draw_ui(screen, lines, font, font_small) pygame.display.flip() clock.tick(FPS) pygame.quit() sys.exit() if __name__ == "__main__": main()Интерфейс VS Code после добавления кода и запуска игры<em>Скриншот: Pygame / Skillbox Media</em><p><a>Парадокс Гранд-отеля</a> - это мысленный эксперимент, который предложил математик <a>Дэвид Гильберт</a>для иллюстрации необычных свойств бесконечности. Представьте отель с бесконечным числом комнат, в котором нет свободных мест: каждая комната занята, и в каждой живёт по одному постояльцу. Давайте поэкспериментируем с этим отелем и увидим, насколько странными могут быть его свойства.</p>
45 <p><strong>Случай первый: приходит один новый гость.</strong>На первый взгляд кажется, что разместить его невозможно, ведь отель уже полностью занят: в каждой из бесконечных комнат живёт по одному постояльцу.</p>
45 <p><strong>Случай первый: приходит один новый гость.</strong>На первый взгляд кажется, что разместить его невозможно, ведь отель уже полностью занят: в каждой из бесконечных комнат живёт по одному постояльцу.</p>
46 <p>Однако если попросить каждого гостя перейти на одну комнату вперёд, то первая комната освободится и в неё можно будет поселить нового гостя.</p>
46 <p>Однако если попросить каждого гостя перейти на одну комнату вперёд, то первая комната освободится и в неё можно будет поселить нового гостя.</p>
47 <p>Получается парадокс: отель полностью заполнен, но для одного нового постояльца место всё равно находится. Это показывает, что у бесконечности нет предела и для неё справедливо равенство ∞ + 1 = ∞.</p>
47 <p>Получается парадокс: отель полностью заполнен, но для одного нового постояльца место всё равно находится. Это показывает, что у бесконечности нет предела и для неё справедливо равенство ∞ + 1 = ∞.</p>
48 <p><strong>Случай второй: приходит бесконечное число новых гостей.</strong>Просим всех постояльцев нашего переполненного отеля переехать из своей комнаты с номером n в комнату с номером 2n. Например, из комнаты 1 - в комнату 2, из 2 - в 4, из 3 - в 6, из 4 - в 8, из 5 - в 10 и так далее.</p>
48 <p><strong>Случай второй: приходит бесконечное число новых гостей.</strong>Просим всех постояльцев нашего переполненного отеля переехать из своей комнаты с номером n в комнату с номером 2n. Например, из комнаты 1 - в комнату 2, из 2 - в 4, из 3 - в 6, из 4 - в 8, из 5 - в 10 и так далее.</p>
49 <p>После такой перестановки все чётные комнаты окажутся заняты, а нечётные - свободны. Поскольку и тех и других бесконечно много, то в освобождённые нечётные комнаты можно заселить всех прибывших гостей. Так мы разместили одну бесконечность внутри другой: ∞ + ∞ = ∞.</p>
49 <p>После такой перестановки все чётные комнаты окажутся заняты, а нечётные - свободны. Поскольку и тех и других бесконечно много, то в освобождённые нечётные комнаты можно заселить всех прибывших гостей. Так мы разместили одну бесконечность внутри другой: ∞ + ∞ = ∞.</p>
50 Схема переселения бесконечного числа новых гостей в бесконечно заполненном отеле<em>Изображение: Jan Beránek /<a>Wikimedia Commons</a></em><p><strong>Случай третий: к отелю подъезжает бесконечное число автобусов с бесконечным числом гостей в каждом.</strong>То есть в заполненном отеле нужно разместить бесконечно много бесконечностей.</p>
50 Схема переселения бесконечного числа новых гостей в бесконечно заполненном отеле<em>Изображение: Jan Beránek /<a>Wikimedia Commons</a></em><p><strong>Случай третий: к отелю подъезжает бесконечное число автобусов с бесконечным числом гостей в каждом.</strong>То есть в заполненном отеле нужно разместить бесконечно много бесконечностей.</p>
51 <p>Чтобы выйти из ситуации, мы можем присвоить каждому гостю уникальный номер. Для этого есть разные способы, и один из них - использование<a>разложения чисел на простые множители</a>. Например, если гость едет в автобусе №i и занимает место №<em>j</em>, то его номер комнаты можно определить по формуле: 2i × 3j. Вот как это вычисляется:</p>
51 <p>Чтобы выйти из ситуации, мы можем присвоить каждому гостю уникальный номер. Для этого есть разные способы, и один из них - использование<a>разложения чисел на простые множители</a>. Например, если гость едет в автобусе №i и занимает место №<em>j</em>, то его номер комнаты можно определить по формуле: 2i × 3j. Вот как это вычисляется:</p>
52 <ul><li>Гость из автобуса №1, место №1: 2¹ × 3¹ = 6.</li>
52 <ul><li>Гость из автобуса №1, место №1: 2¹ × 3¹ = 6.</li>
53 <li>Гость из автобуса №2, место №3: 2² × 3³ = 4 × 27 = 108.</li>
53 <li>Гость из автобуса №2, место №3: 2² × 3³ = 4 × 27 = 108.</li>
54 <li>Гость из автобуса №3, место №2: 2³ × 3² = 8 × 9 = 72.</li>
54 <li>Гость из автобуса №3, место №2: 2³ × 3² = 8 × 9 = 72.</li>
55 </ul><p>Также нам придётся переселить всех нынешних жильцов. Для удобства можно считать, что они приехали в автобусе №0, и расселить их по номерам по той же формуле: 2⁰ × 3ʲ = 3ʲ. Например, постояльцу из комнаты №3 придётся переселиться в комнату №27: 2⁰ × 3³ = 1 × 27 = 27.</p>
55 </ul><p>Также нам придётся переселить всех нынешних жильцов. Для удобства можно считать, что они приехали в автобусе №0, и расселить их по номерам по той же формуле: 2⁰ × 3ʲ = 3ʲ. Например, постояльцу из комнаты №3 придётся переселиться в комнату №27: 2⁰ × 3³ = 1 × 27 = 27.</p>
56 <p>Получается, мы нашли способ присвоить каждому постояльцу и каждому гостю своё уникальное место. То есть даже "бесконечность в квадрате" может уместиться в бесконечном отеле, если речь идёт о счётной бесконечности - о множестве натуральных чисел: ∞ × ∞ = ∞.</p>
56 <p>Получается, мы нашли способ присвоить каждому постояльцу и каждому гостю своё уникальное место. То есть даже "бесконечность в квадрате" может уместиться в бесконечном отеле, если речь идёт о счётной бесконечности - о множестве натуральных чисел: ∞ × ∞ = ∞.</p>
57 <p><strong>Случай четвёртый: мест для гостей не хватает.</strong>Представьте, что к отелю снова подъезжает бесконечное число автобусов и в каждом - бесконечное число гостей. Но теперь у каждого гостя в автобусе уже есть свой уникальный номер - не натуральное число, а любое вещественное число между 0 и 1. То есть это числа, которые можно записать как бесконечные десятичные дроби: 0,333..., 0,5, 0,14159... и так далее.</p>
57 <p><strong>Случай четвёртый: мест для гостей не хватает.</strong>Представьте, что к отелю снова подъезжает бесконечное число автобусов и в каждом - бесконечное число гостей. Но теперь у каждого гостя в автобусе уже есть свой уникальный номер - не натуральное число, а любое вещественное число между 0 и 1. То есть это числа, которые можно записать как бесконечные десятичные дроби: 0,333..., 0,5, 0,14159... и так далее.</p>
58 <p>Поэтому, как бы мы ни нумеровали комнаты, расселить всех гостей не получится: вещественных чисел всегда больше, чем натуральных. Эту особенность впервые сформулировал математик Георг Кантор в <a>теореме о различии мощностей бесконечных множеств</a>. И это ещё один парадокс: не все бесконечности одинаковы - некоторые из них "больше" других.</p>
58 <p>Поэтому, как бы мы ни нумеровали комнаты, расселить всех гостей не получится: вещественных чисел всегда больше, чем натуральных. Эту особенность впервые сформулировал математик Георг Кантор в <a>теореме о различии мощностей бесконечных множеств</a>. И это ещё один парадокс: не все бесконечности одинаковы - некоторые из них "больше" других.</p>
59 <p>Представьте телевикторину с тремя дверями. За одной скрывается роскошный автомобиль, за двумя другими - симпатичные козы.</p>
59 <p>Представьте телевикторину с тремя дверями. За одной скрывается роскошный автомобиль, за двумя другими - симпатичные козы.</p>
60 <p>Вы выбираете, скажем, дверь №1. Прежде чем вы успеете её открыть, ведущий открывает одну из двух оставшихся дверей, за которой точно находится коза, - например, дверь №3. Затем он предлагает вам изменить выбор и выбрать дверь №2. Подумайте, стоит ли соглашаться?</p>
60 <p>Вы выбираете, скажем, дверь №1. Прежде чем вы успеете её открыть, ведущий открывает одну из двух оставшихся дверей, за которой точно находится коза, - например, дверь №3. Затем он предлагает вам изменить выбор и выбрать дверь №2. Подумайте, стоит ли соглашаться?</p>
61 Изначально ваши шансы угадать правильную дверь - 1 к 3. Когда ведущий открывает одну из дверей с козой, кажется, что шансы становятся 50 на 50 и менять выбор нет смысла. Но так ли это?<em>Изображение: Cepheus /<a>Wikimedia Commons</a></em><p>Если вы не уверены в решении - переходите к другой статье, где мы подробно разбираем этот парадокс, предлагаем интерактивную симуляцию на Python и объясняем, почему в него так трудно поверить.</p>
61 Изначально ваши шансы угадать правильную дверь - 1 к 3. Когда ведущий открывает одну из дверей с козой, кажется, что шансы становятся 50 на 50 и менять выбор нет смысла. Но так ли это?<em>Изображение: Cepheus /<a>Wikimedia Commons</a></em><p>Если вы не уверены в решении - переходите к другой статье, где мы подробно разбираем этот парадокс, предлагаем интерактивную симуляцию на Python и объясняем, почему в него так трудно поверить.</p>
62 <a>Курс с трудоустройством: "Профессия Data scientist + ИИ" Узнать о курсе</a>
62 <a>Курс с трудоустройством: "Профессия Data scientist + ИИ" Узнать о курсе</a>