Марковские цепи

Иногда будущее системы зависит только от того, где она находится сейчас, и совсем не зависит от того, как она туда попала. Такие системы описываются марковскими цепями — и они на удивление мощный инструмент, от прогноза погоды до ранжирования веб-страниц.

Марковская цепь — это математическая модель системы, которая в каждый момент находится в одном из конечного набора состояний и переходит между ними с фиксированными вероятностями. Главное свойство (марковское): вероятность следующего состояния зависит только от текущего состояния, а не от всей предыдущей истории.

Зачем это нужно? Огромное число процессов «не помнит» прошлого: погода завтра зависит в основном от погоды сегодня, страница, на которую перейдёт сёрфер, — от текущей страницы. Марковские цепи позволяют такие процессы аккуратно описать и предсказать их долгосрочное поведение.

Состояния и матрица переходов

Пусть у системы есть состояния 0, 1, 2. Поведение задаётся матрицей переходов P, где P[i][j] — вероятность за один шаг перейти из состояния i в состояние j. Каждая строка — это распределение вероятностей выхода из одного состояния, поэтому сумма каждой строки равна 1.

          в 0     в 1     в 2
  из 0 [  0.7     0.2     0.1  ]  сумма = 1
  из 1 [  0.3     0.4     0.3  ]  сумма = 1
  из 2 [  0.2     0.3     0.5  ]  сумма = 1

Возьмём метеорологическую интерпретацию: 0 — солнечно, 1 — облачно, 2 — дождь. Тогда P[0][0] = 0.7 означает «если сегодня солнечно, завтра снова солнечно с вероятностью 0.7», а P[2][0] = 0.2 — «после дождя солнце наступает с вероятностью 0.2».

Марковское свойство

«Будущее зависит только от настоящего» — звучит как ограничение, но это и есть источник простоты. Нам не нужно хранить всю историю: достаточно текущего состояния (или текущего распределения вероятностей по состояниям). Цепь без памяти — как игральная кость, которой всё равно, что выпадало раньше.

Шаг во времени = умножение вектора на матрицу

Состояние знания о системе удобно хранить как вектор вероятностей v, где v[i] — вероятность находиться в состоянии i. Один шаг во времени — это умножение этого вектора на матрицу P: новая вероятность оказаться в j складывается из всех способов туда попасть, v_new[j] = сумма по i от v[i] * P[i][j].

Если повторять шаг много раз, распределение обычно сходится к одному и тому же вектору независимо от старта. Этот предельный вектор называют стационарным распределением: применив к нему ещё один шаг, мы получаем его же. Посчитаем его, стартовав из «точно солнечно» и сделав 50 шагов.

P = [[0.7, 0.2, 0.1],
     [0.3, 0.4, 0.3],
     [0.2, 0.3, 0.5]]
v = [1.0, 0.0, 0.0]
for _ in range(50):
    v = [sum(v[i] * P[i][j] for i in range(3)) for j in range(3)]
print("Стационарное распределение:", [round(x, 4) for x in v])
print("Сумма:", round(sum(v), 4))

Вывод:

Стационарное распределение: [0.4565, 0.2826, 0.2609]
Сумма: 1.0

Итог: в долгосрочной перспективе около 45.65% дней солнечные, 28.26% облачные и 26.09% дождливые. Мы стартовали из «точно солнечно» (вектор [1, 0, 0]), но после многих шагов это начальное условие забылось — система пришла в своё устойчивое распределение. Сумма осталась равна 1: вероятности никуда не делись, они лишь перераспределились.

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

Разберём ключевые места.

Внутренний comprehension — это умножение вектор×матрица. Выражение sum(v[i] * P[i][j] for i in range(3)) при фиксированном j перебирает все исходные состояния i и складывает «сколько вероятности перетекает из i в j». Внешний comprehension [... for j in range(3)] делает это для каждого целевого состояния j, собирая новый вектор.

Почему распределение сходится. Если из любого состояния можно (хотя бы за несколько шагов) попасть в любое другое и цепь не «зациклена» строго периодически, она называется эргодической. У эргодической цепи есть единственное стационарное распределение, и итерации к нему сходятся из любого старта. Наша матрица именно такая: все вероятности положительны, перейти можно куда угодно.

Сумма сохраняется. Поскольку каждая строка P суммируется в 1, операция «вектор×матрица» сохраняет суммарную вероятность: стартовали с суммой 1 — на каждом шаге сумма остаётся 1. Если в выводе сумма «уплыла», значит, в матрице ошибка — строка не суммируется в единицу.

Геометрически шаги сходятся к одной точке так:

старт [1.00, 0.00, 0.00]
шаг 1 [0.70, 0.20, 0.10]
шаг 2 [0.55, 0.25, 0.20]
 ...        сходится
предел [0.4565, 0.2826, 0.2609]  <-- стационарное

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

  • Перепутать направление в P[i][j]. Здесь i — откуда, j — куда. Если случайно транспонировать матрицу, получится цепь с другими переходами и неверным результатом.
  • Строки не суммируются в 1. Каждая строка — распределение вероятностей выхода из состояния. Если сумма не равна 1, вероятность будет «утекать» или «возникать из ниоткуда».
  • Ждать, что старт повлияет на предел. Для эргодической цепи стационарное распределение одно и то же из любого начального вектора. Старт влияет лишь на скорость сходимости, а не на предел.
  • Считать слишком мало шагов. Для одних матриц хватает 5 итераций, для других нужны десятки. Если результат ещё «дрожит», увеличьте число шагов.
  • Забыть про марковское свойство. Если реальный процесс зависит от двух последних состояний, обычная цепь его не опишет — нужно расширять пространство состояний.

Итоги

  • Марковская цепь — система из конечного набора состояний с фиксированными вероятностями переходов между ними.
  • Марковское свойство: будущее зависит только от текущего состояния, а не от всей истории — поэтому хранить нужно лишь «настоящее».
  • Поведение задаётся матрицей переходов P, где P[i][j] — вероятность перейти из i в j, а каждая строка суммируется в 1.
  • Один шаг во времени — это умножение вектора вероятностей на матрицу; повторяя его, мы прокручиваем цепь вперёд.
  • У эргодической цепи итерации сходятся к стационарному распределению, единому для любого старта — оно и описывает долгосрочное поведение системы.
Проверьте себя
1. Что такое марковское свойство?
AСумма всех вероятностей в матрице равна 1
BВероятность следующего состояния зависит только от текущего, а не от всей предыдущей истории
CВсе состояния равновероятны
DСистема обязательно возвращается в исходное состояние
2. Что означает элемент P[i][j] в матрице переходов?
AЧисло посещений состояния i
BВероятность перейти из состояния i в состояние j за один шаг
CВероятность остаться в состоянии j навсегда
DСумму вероятностей строки j
3. К чему сходится вектор вероятностей после многих умножений на матрицу переходов (для эргодической цепи)?
AК нулевому вектору
BК стационарному распределению, не зависящему от начального состояния
CК вектору, где вся вероятность в стартовом состоянии
DК случайному вектору, своему на каждом запуске
4. Почему сумма элементов вектора вероятностей остаётся равной 1 после каждого шага?
AПотому что мы делим вектор на его сумму вручную
BПотому что каждая строка матрицы переходов суммируется в 1, и операция сохраняет суммарную вероятность
CПотому что Python округляет значения
DПотому что стартовый вектор был [1, 0, 0]