Теория хаоса и динамические системы: аттракторы, фракталы, чувствительность
В 1961 году Эдвард Лоренц гонял на ламповой машине LGP-30 упрощённую модель конвекции в атмосфере. Захотев повторить кусок прогона, он не стал начинать с нуля: взял распечатку промежуточного состояния и ввёл числа заново. Машина считала с шестью знаками, печатала три. Разница — примерно одна миллионная. Через пару «модельных месяцев» второй прогон не имел с первым ничего общего.
Это не был баг. Это было открытие: полностью детерминированная система из трёх обыкновенных дифференциальных уравнений — ни капли случайности, ни одного rand() — может быть принципиально непредсказуемой на длинном горизонте. Статья Лоренца «Deterministic Nonperiodic Flow» (1963) — один из тех редких текстов, после которых в науке появляется новая категория явлений.
Для программиста тут есть очень конкретная польза, и она не про погоду. Хаос — это про системы с обратной связью, а вся ваша инфраструктура состоит из обратных связей: ретраи усиливают нагрузку, нагрузка увеличивает латентность, латентность вызывает таймауты, таймауты порождают ретраи. Автоскейлер смотрит на метрику, которую сам же и меняет. Кэш вытесняет данные, что меняет паттерн запросов, что меняет вытеснение. Градиентный спуск — это итерация отображения, и «взрыв градиентов» — это буквально положительный показатель Ляпунова.
Эта статья даёт язык, на котором такие вещи описываются точно: неподвижная точка, устойчивость, бифуркация, аттрактор, показатель Ляпунова, фрактальная размерность. Плюс — рабочий код, чтобы всё пощупать руками.
Карта статьи
1. Что такое динамическая система: строгое определение
Определение. Динамическая система — это тройка (X, T, φ), где:
X— пространство состояний (обычно метрическое, частоR^n);T— время:ZилиN(дискретное) либоR(непрерывное);φ: T × X → X— оператор эволюции, удовлетворяющий двум аксиомам:
φ(0, x) = x (тождественность)
φ(t + s, x) = φ(t, φ(s, x)) (полугрупповое свойство)
Вторая аксиома — формализация фразы «состояние содержит всю информацию о будущем». Никакой памяти о прошлом: чтобы шагнуть вперёд, достаточно текущего x.
Два практических случая:
Каскад (дискретное время). Задаётся отображением f: X → X, эволюция — это итерация: x_{n+1} = f(x_n), то есть φ(n, x) = f^n(x). Каждый цикл вашего event loop, каждый шаг градиентного спуска, каждый тик контроллера — это каскад.
Поток (непрерывное время). Задаётся векторным полем dx/dt = F(x). Оператор φ(t, x) — решение задачи Коши. Физические процессы, теория управления, диффуры.
Автономность. Мы требуем, чтобы F (или f) не зависели явно от времени. Если зависят — систему всегда можно сделать автономной, добавив координату s с уравнением ds/dt = 1. Это не бухгалтерский трюк: он объясняет, почему периодически возбуждаемый маятник (формально 2-мерный) может быть хаотичным, а автономная 2-мерная система — нет.
Неподвижные точки и устойчивость
Определение. x* — неподвижная точка каскада, если f(x*) = x*. Для потока — точка равновесия: F(x*) = 0.
Определение (устойчивость по Ляпунову). x* устойчива, если для любого ε > 0 найдётся δ > 0 такое, что из |x - x*| < δ следует |φ(t, x) - x*| < ε для всех t ≥ 0. Если вдобавок φ(t, x) → x*, устойчивость называется асимптотической.
По-человечески: устойчивая точка — та, из окрестности которой вы не убегаете; асимптотически устойчивая — та, в которую вас затягивает.
Критерий через линеаризацию. Разложим f рядом Тейлора возле x*. Отклонение u_n = x_n - x* эволюционирует как u_{n+1} ≈ J u_n, где J = Df(x*) — матрица Якоби. Дальше работает вся машинерия собственных значений из статьи «Собственные значения, SVD и матричные разложения»:
Каскад: |λ_i| < 1 для всех i -> асимптотически устойчива
|λ_i| > 1 хотя бы для одного -> неустойчива
Поток: Re λ_i < 0 для всех i -> асимптотически устойчива
Re λ_i > 0 хотя бы для одного -> неустойчива
Граничный случай (|λ| = 1 или Re λ = 0) линеаризация не решает — там правят нелинейные члены, и именно там рождаются бифуркации.
2. Логистическое отображение: минимальная лаборатория хаоса
Возьмём самую скромную нелинейность, какую можно придумать:
x_{n+1} = r * x_n * (1 - x_n), x ∈ [0, 1], r ∈ [0, 4]
Смысл: x — популяция в долях от максимума. Член r*x — размножение, множитель (1 - x) — конкуренция за ресурс. Одно умножение, одно вычитание. И этого хватает для полного зоопарка динамики — результат, который в 1976-м опубликовал Роберт Мэй в Nature («Simple mathematical models with very complicated dynamics») и который до сих пор считается одной из самых влиятельных страниц в теоретической биологии.
Разбор руками
Неподвижные точки: r x (1 - x) = x даёт x* = 0 и x* = 1 - 1/r.
Производная: f'(x) = r(1 - 2x).
- В нуле:
f'(0) = r. Значит,x* = 0устойчива приr < 1(популяция вымирает) и теряет устойчивость приr > 1. - Во второй точке:
f'(1 - 1/r) = r(1 - 2 + 2/r) = 2 - r. Условие|2 - r| < 1даёт1 < r < 3.
Вот и первые два порога, полученные карандашом. При r = 3 производная равна -1 — линеаризация ломается. Что происходит дальше, посчитаем численно.
import numpy as np
def logistic(x, r):
return r * x * (1 - x)
def attractor(r, burn=20_000, keep=4096):
"""Отбрасываем переходный процесс и возвращаем точки аттрактора."""
x = 0.3
for _ in range(burn):
x = logistic(x, r)
out = np.empty(keep)
for i in range(keep):
x = logistic(x, r)
out[i] = x
return out
def period(r, tol=1e-9, maxp=64):
"""Наименьший p, при котором орбита повторяется. None -> непериодично."""
orb = attractor(r)
for p in range(1, maxp + 1):
if np.max(np.abs(orb[p:] - orb[:-p])) < tol:
return p
return None
for r in (2.5, 3.1, 3.45, 3.55, 3.566, 3.5699456, 3.83, 3.9, 4.0):
print(r, period(r))
Вывод:
2.5 -> 1 неподвижная точка
3.1 -> 2 цикл длины 2
3.45 -> 4
3.55 -> 8
3.566 -> 16
3.5699456 -> None накопление удвоений: конец периодичности
3.83 -> 3 окно периода 3 внутри хаоса
3.9 -> None хаос
4.0 -> None полностью развитый хаос
Сложность: O(burn + keep * maxp) по времени, O(keep) по памяти.
Каскад удвоений периода
Обратите внимание на сжимающиеся интервалы: 3.0 → 3.4495 → 3.5441 → 3.5644 → .... Отношения соседних длин стремятся к константе.
Универсальность Фейгенбаума
Митчелл Фейгенбаум в 1975-м, играя с карманным HP-65, заметил, что отношение длин интервалов между бифуркациями сходится к числу, и это число одно и то же для целого класса отображений — синуса, логистического, чего угодно с одним квадратичным максимумом. Проверим.
Удобнее искать не сами бифуркации (там сходимость мучительно медленная), а суперустойчивые циклы: параметры R_n, при которых критическая точка x = 1/2 принадлежит циклу длины 2^n, то есть f^(2^n)(1/2) = 1/2. Производная вдоль такого цикла равна нулю — сходимость мгновенная.
import numpy as np
def g(r, n):
"""f^(2^n)(0.5) - 0.5; ноль => суперустойчивый цикл периода 2^n."""
x = 0.5
for _ in range(2 ** n):
x = r * x * (1 - x)
return x - 0.5
def superstable(n, lo, hi, steps=20_000):
"""Ищем первую смену знака на [lo, hi], затем бисекция."""
xs = np.linspace(lo, hi, steps)
prev = g(xs[0], n)
for r in xs[1:]:
cur = g(r, n)
if prev * cur < 0:
a, b = r - (hi - lo) / steps, r
for _ in range(80):
m = (a + b) / 2
if g(a, n) * g(m, n) <= 0:
b = m
else:
a = m
return (a + b) / 2
prev = cur
return None
brackets = [(3.10, 3.30), (3.45, 3.52), (3.5500, 3.5570),
(3.5665, 3.5670), (3.56923, 3.56930), (3.56925, 3.56980)]
R = [2.0] + [superstable(n, a, b) for n, (a, b) in enumerate(brackets, start=1)]
for n, v in enumerate(R):
print(f"R_{n} = {v:.9f}")
for i in range(len(R) - 2):
print(f"delta_{i} = {(R[i+1] - R[i]) / (R[i+2] - R[i+1]):.6f}")
Результат:
R_0 = 2.000000000 delta_0 = 4.708943
R_1 = 3.236067977 delta_1 = 4.680771
R_2 = 3.498561699 delta_2 = 4.662960
R_3 = 3.554640863 delta_3 = 4.668404
R_4 = 3.566667380 delta_4 = 4.668954
R_5 = 3.569243532
R_6 = 3.569795294
Последовательность уверенно ползёт к δ = 4.669201609... — константе Фейгенбаума. Приятная проверка: R_1 = 3.23606797... это ровно 1 + √5, что можно вывести на бумаге.
Почему это удивительно? δ не зависит от того, какое конкретно отображение вы взяли — важна только форма максимума (квадратичная). Это тот же тип универсальности, что критические показатели в фазовых переходах: детали микроскопической модели стираются, остаётся класс эквивалентности. Значение δ измерено в лабораторных экспериментах с конвекцией жидкого гелия, с электронными цепями, с колеблющимися химическими реакциями — везде то же число.
Теорема Шарковского (1964) объясняет, почему период 3 в списке появился так поздно. Она вводит порядок на натуральных числах:
3 ▷ 5 ▷ 7 ▷ 9 ▷ ... ▷ 2·3 ▷ 2·5 ▷ ... ▷ 4·3 ▷ 4·5 ▷ ... ▷ 8 ▷ 4 ▷ 2 ▷ 1
Если непрерывное отображение отрезка в себя имеет цикл периода p, оно имеет циклы всех периодов, следующих за p в этом порядке. Тройка стоит первой: период три влечёт все периоды. Отсюда знаменитая статья Ли и Йорка «Period Three Implies Chaos» (Amer. Math. Monthly, 1975), в которой слово «хаос» впервые появилось в математическом смысле.
3. Что такое хаос строго
Слово используют как попало, поэтому зафиксируем определения.
Определение (чувствительная зависимость от начальных условий). f: X → X обладает чувствительной зависимостью, если существует ε > 0 такое, что для любой точки x и любой её окрестности U найдутся y ∈ U и n ≥ 0 с |f^n(x) - f^n(y)| > ε.
Ключевое: ε фиксировано и не зависит от x. Сколь угодно близкие точки рано или поздно расходятся на макроскопическое расстояние.
Определение (топологическая транзитивность). Для любых двух открытых множеств U, V найдётся n с f^n(U) ∩ V ≠ ∅. Система перемешивает: из любого места можно попасть куда угодно.
Определение (хаос по Девани). f хаотична на X, если:
fобладает чувствительной зависимостью;fтопологически транзитивна;- периодические точки
fплотны вX.
Красивый факт: в 1992-м Бэнкс и соавторы («On Devaney’s Definition of Chaos») доказали, что пункт 1 избыточен — он следует из 2 и 3. То есть чувствительность не отдельное свойство, а следствие «плотных циклов + перемешивание».
Третье условие часто удивляет: хаотическая система набита периодическими орбитами под завязку. Просто все они неустойчивы, поэтому численно вы ни одну не увидите — вы всё время скатываетесь с них. Именно эта плотная сеть неустойчивых циклов — то, за что цепляются методы управления хаосом (см. раздел про OGY).
Показатель Ляпунова: измеряем хаос числом
Определение бинарное, но инженеру нужна величина. Возьмём две близкие начальные точки, расстояние δ_0, и посмотрим, как растёт δ_n:
δ_n ≈ δ_0 * e^(λ n)
λ = lim (1/n) * Σ ln |f'(x_i)|, i = 0..n-1
Формула получается по цепному правилу: (f^n)'(x_0) = Π f'(x_i), берём логарифм и усредняем.
λ < 0— соседи сближаются, аттрактор регулярный (точка или цикл);λ = 0— граничный случай (бифуркация, квазипериодичность);λ > 0— хаос.
import numpy as np
def lyapunov_logistic(r, n=200_000, burn=1000):
x = 0.3
for _ in range(burn):
x = r * x * (1 - x)
s = 0.0
for _ in range(n):
s += np.log(abs(r * (1 - 2 * x))) # |f'(x)| = |r(1-2x)|
x = r * x * (1 - x)
return s / n
for r in (2.5, 3.2, 3.5, 3.5699456, 3.83, 3.9, 4.0):
print(r, round(lyapunov_logistic(r), 4))
2.5 -0.6931 устойчивая точка
3.2 -0.9163 цикл 2
3.5 -0.8725 цикл 4
3.5699456 -0.0010 точка накопления: λ = 0
3.83 -0.3697 окно периода 3
3.9 0.4953 хаос
4.0 0.6931 хаос, и это ровно ln 2
Значение λ = ln 2 при r = 4 не совпадение: замена x = sin²(πθ) превращает логистическое отображение в θ → 2θ mod 1, то есть в сдвиг двоичных разрядов. Каждая итерация выбрасывает один бит начального условия и подтягивает следующий. Через 53 шага от вашего float64 не остаётся ничего, кроме мусора округления. Это самая честная модель хаоса, какая бывает: система буквально работает лупой, увеличивающей младшие биты до макроскопического масштаба.
Отсюда же прямая связь с автоматами и формальными языками: динамика на аттракторе кодируется бесконечными словами над конечным алфавитом («левое крыло» / «правое крыло»), а сама система становится сдвигом на пространстве слов. Это направление называется символической динамикой, и через него теория хаоса стыкуется с теорией вычислимости — есть системы, для которых вопрос «попадёт ли траектория в такую-то область» неразрешим.
Горизонт предсказуемости
Практически полезная формула. Если ваша точность знания начального состояния δ_0, а допустимая ошибка прогноза Δ, то время, за которое ошибка вырастет до недопустимой:
t_pred ≈ (1/λ) * ln(Δ / δ_0)
Логарифм — это приговор. Улучшили точность измерений в 1000 раз? Горизонт вырос на ln(1000)/λ ≈ 6.9/λ, то есть на семь ляпуновских времён. Не в 1000 раз — на константу.
Конкретика:
| Система | λ (примерно) | Ляпуновское время 1/λ | Практический горизонт |
|---|---|---|---|
| Логистическое, r = 4 | 0.69 на итерацию | 1.4 итерации | ~50 итераций из float64 |
| Аттрактор Лоренца | 0.906 на ед. времени | 1.1 | ~25 при δ₀ = 1e-9 |
| Погода в средних широтах | ~0.4 / сутки | ~2.5 суток | 10–15 суток (предел ECMWF) |
| Солнечная система | ~1/5 млн лет | 5 млн лет | ~60 млн лет |
Последняя строка — работа Ласкара: положение Меркурия через 100 млн лет непредсказуемо в принципе, хотя ньютоновская механика детерминирована абсолютно.
Картинка выше — суть механизма (подкова Смейла, Smale, 1967). Хаос требует одновременно двух вещей: растяжения (соседи разбегаются экспоненциально) и складывания (иначе всё улетает на бесконечность, а это не хаос, а просто взрыв). Растяжение даёт положительный λ, складывание удерживает траекторию в ограниченной области, а бесконечное повторение пары «растянуть–сложить» порождает канторову слоистую структуру — фрактал.
4. Аттракторы
Определение. Множество A ⊂ X — аттрактор, если:
Aинвариантно:φ(t, A) = A;- существует открытая окрестность
U ⊃ A(бассейн притяжения) такая, чтоφ(t, x) → Aприt → ∞для всехx ∈ U; Aминимально: не содержит собственного подмножества с теми же свойствами.
Третий пункт нужен, чтобы «весь фазовый объём» не считался аттрактором.
ограниченной системы"] --> B{"Наибольший показатель
Ляпунова λ₁?"} B -->|"λ₁ < 0"| C["Аттрактор — неподвижная точка
размерность 0"] B -->|"λ₁ = 0"| D{"Сколько нулевых
показателей?"} B -->|"λ₁ > 0"| E{"Сжимается ли
объём: Σλᵢ < 0?"} D -->|"один"| F["Предельный цикл
размерность 1"] D -->|"два и более"| G["Тор, квазипериодичность
размерность 2+"] E -->|"да"| H["Странный аттрактор
дробная размерность"] E -->|"нет, Σλᵢ = 0"| I["Консервативный хаос
нет аттрактора, есть море хаоса"] C --> J["PID вышел на уставку,
пул стабилизировался"] F --> K["Автоскейлер качается
с постоянным периодом"] G --> L["Две несоизмеримые
периодические нагрузки"] H --> M["Лоренц, Рёсслер,
ретрай-шторм"] I --> N["Гамильтоновы системы,
отображение кота Арнольда"]
Система Лоренца
dx/dt = σ(y - x)
dy/dt = x(ρ - z) - y
dz/dt = xy - βz
Классические параметры: σ = 10, ρ = 28, β = 8/3.
Диссипативность считается в одну строку. Дивергенция векторного поля:
div F = ∂/∂x[σ(y-x)] + ∂/∂y[x(ρ-z)-y] + ∂/∂z[xy-βz]
= -σ - 1 - β = -10 - 1 - 8/3 = -13.667
Она постоянна и отрицательна, значит фазовый объём сжимается как V(t) = V(0) * e^(-13.667 t). За единицу времени объём уменьшается примерно в 860 000 раз. Через десять единиц — множитель e^-137. То есть аттрактор имеет нулевой объём. Но траектории на нём не сходятся к точке и не замыкаются в цикл: λ₁ > 0 их растаскивает. Множество нулевого объёма, на котором соседи расходятся экспоненциально, — это и есть странный аттрактор. Связь div F с определителем матрицы Якоби и с «коэффициентом раздутия объёма» разбиралась в статье про матрицы и определители.
import numpy as np
SIGMA, RHO, BETA = 10.0, 28.0, 8.0 / 3.0
def lorenz(s):
x, y, z = s
return np.array([SIGMA * (y - x), x * (RHO - z) - y, x * y - BETA * z])
def rk4(s, dt):
"""Классический Рунге-Кутта 4-го порядка: локальная ошибка O(dt^5)."""
k1 = lorenz(s)
k2 = lorenz(s + dt / 2 * k1)
k3 = lorenz(s + dt / 2 * k2)
k4 = lorenz(s + dt * k3)
return s + dt / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
dt = 0.001
s = np.array([1.0, 1.0, 1.0])
for _ in range(50_000): # выходим на аттрактор
s = rk4(s, dt)
a = s.copy()
b = s + np.array([1e-9, 0.0, 0.0]) # отклонение в одну миллиардную
print(" t |Δ|")
for step in range(1, 45_001):
a, b = rk4(a, dt), rk4(b, dt)
if step % 5000 == 0:
print(f"{step * dt:5.0f} {np.linalg.norm(a - b):.2e}")
t |Δ|
5 2.61e-08
10 2.05e-06
15 3.19e-04
20 3.51e-02
25 7.79e-01
30 2.30e+01 <- насыщение: размер аттрактора
35 1.17e+01
40 1.87e+01
45 2.51e+01
Ровные шесть порядков за 15 единиц времени — это e^(0.9 * 15) ≈ 7e5, ровно то, что предсказывает λ₁ ≈ 0.9. После t ≈ 28 расхождение упирается в диаметр аттрактора (~30) и дальше просто болтается: две траектории стали статистически независимыми. Прогноз мёртв.
Считаем показатель Ляпунова для потока (метод Беннетина)
Наивно «отпустить две траектории и померить наклон» нельзя: расхождение быстро упирается в размер аттрактора. Трюк — периодически возвращать пробную траекторию на исходное расстояние вдоль текущего направления расхождения, накапливая логарифмы.
def lyapunov_max(T=2000.0, dt=0.01, d0=1e-8):
s = np.array([1.0, 1.0, 1.0])
for _ in range(10_000):
s = rk4(s, dt) # выход на аттрактор
p = s + np.array([d0, 0.0, 0.0])
total, n = 0.0, int(T / dt)
for _ in range(n):
s, p = rk4(s, dt), rk4(p, dt)
d = np.linalg.norm(p - s)
total += np.log(d / d0)
p = s + (p - s) * (d0 / d) # ренормировка: направление то же, длина d0
return total / T
lam = lyapunov_max()
print("λ₁ =", round(lam, 4)) # 0.9069
print("ляпуновское время =", round(1 / lam, 3)) # 1.103
lam3 = -(SIGMA + 1 + BETA) - lam # λ₁+λ₂+λ₃ = div F, λ₂ = 0
print("D_KY =", 2 + lam / abs(lam3)) # 2.0622
Сложность: O(T/dt) шагов, память O(1).
Последняя строка — размерность Каплана–Йорке:
D_KY = k + (λ_1 + ... + λ_k) / |λ_{k+1}|,
где k — наибольшее число, при котором сумма первых k показателей ещё неотрицательна
Для Лоренца: λ = (0.906, 0, -14.57), k = 2, D_KY = 2 + 0.906/14.57 ≈ 2.062. Дробная размерность — не декоративная деталь, а количественное утверждение: аттрактор чуть-чуть больше поверхности и сильно меньше объёма. И заметьте: сумма всех трёх показателей равна div F = -13.667, что даёт бесплатную проверку корректности численного счёта.
Кстати, о строгости: то, что «бабочка» Лоренца действительно является странным аттрактором в математическом смысле, оставалось гипотезой почти сорок лет. Доказательство — компьютерное, с интервальной арифметикой: Warwick Tucker, «A Rigorous ODE Solver and Smale’s 14th Problem» (2002). Это отличная иллюстрация того, что численный счёт и строгое доказательство — не антонимы, если делать всё аккуратно (см. численные методы).
5. Фракталы и дробная размерность
Странные аттракторы фрактальны, поэтому нужен способ мерить размерность у множеств, у которых её «нет» в обычном смысле.
Определение (box-counting размерность). Покроем множество A сеткой с ячейкой ε и обозначим N(ε) число непустых ячеек. Тогда
D_box = lim(ε→0) [ log N(ε) / log(1/ε) ]
Интуиция через привычные случаи: у отрезка N(ε) ~ 1/ε (размерность 1), у квадрата N(ε) ~ 1/ε² (размерность 2). Определение просто читает показатель степени. Ничто не запрещает ему быть дробным.
Канторово множество (то самое из статьи про теорию множеств): на шаге k имеем N = 2^k отрезков длины 3^-k. Подставляем:
D_box = log(2^k) / log(3^k) = log 2 / log 3 = 0.6309
Другие каноничные значения: кривая Коха log4/log3 = 1.2619, треугольник Серпинского log3/log2 = 1.5850, граница множества Мандельброта — ровно 2 (теорема Шишикуры, 1998; при этом её площадь равна нулю).
Посчитаем размерность аттрактора Лоренца честным перебором клеток:
def box_dimension(pts, ns=(4, 8, 16, 32, 64, 128, 256)):
lo = pts.min(axis=0)
L = (pts.max(axis=0) - lo).max()
counts = []
for n in ns:
idx = np.clip(np.floor((pts - lo) / L * n).astype(np.int64), 0, n - 1)
key = (idx[:, 0] * n + idx[:, 1]) * n + idx[:, 2] # хеш ячейки
counts.append(len(np.unique(key)))
slope = np.polyfit(np.log(ns), np.log(counts), 1)[0]
return slope, counts
dt = 0.002
s = np.array([1.0, 1.0, 1.0])
for _ in range(20_000):
s = rk4(s, dt)
pts = np.empty((400_000, 3))
for i in range(400_000):
s = rk4(s, dt)
pts[i] = s
slope, counts = box_dimension(pts)
print(counts) # [27, 108, 399, 1498, 5652, 20534, 67069]
print(round(slope, 3)) # 1.886
Сложность: O(|ns| * N log N) по времени (из-за unique), O(N) по памяти.
И тут важный урок, а не победная реляция. Получилось 1.886 против теоретических 2.062. Это не баг кода — это фундаментальное свойство метода: при 400 тысячах точек мелкие ячейки пусты просто потому, что точек не хватило, и наклон занижается. Увеличив выборку до 3 миллионов, я получил 1.94 — всё ещё не 2.06. Требуемое число точек растёт как ε^(-D), то есть экспоненциально по числу декад разрешения. Практический вывод: box-counting на реальных данных почти всегда занижает размерность, и любую публикацию с «фрактальной размерностью финансового ряда 1.42» надо читать с этим в уме. Для оценок по данным используют корреляционную размерность (о ней ниже) — она заметно экономнее по числу точек.
Множества Жюлиа и Мандельброта
Та же итерация, но на комплексной плоскости: z_{n+1} = z_n² + c.
- Фиксируем
c, спрашиваем «для какихz_0орбита ограничена» — получаем множество Жюлиа. - Фиксируем
z_0 = 0, спрашиваем «для какихcорбита ограничена» — получаем множество Мандельброта.
Критерий выхода тривиален: если |z| > 2, орбита гарантированно уходит на бесконечность.
MAXIT = 100
def escape(c, maxit=MAXIT):
"""Число итераций до выхода за |z| = 2; maxit => точка считается принадлежащей множеству."""
z = 0j
for n in range(maxit):
z = z * z + c
if abs(z) > 2.0:
return n
return maxit
chars = " .:-=+*#%@"
for row in range(24):
y = 1.2 - 2.4 * row / 23
line = ""
for col in range(78):
x = -2.2 + 3.0 * col / 77
n = escape(complex(x, y))
line += "#" if n == MAXIT else chars[min(len(chars) - 1, n // 4)]
print(line)
Связь с динамикой прямая: множество Мандельброта — это карта того, при каких c квадратичное отображение имеет притягивающий цикл. Его «луковицы» пронумерованы периодами циклов, а вдоль вещественной оси оно превращается в тот самый каскад удвоений логистического отображения (замена z = x - r/2, c = r/2 - r²/4 связывает их точно).
6. Хаос против плавающей точки
Теперь неприятная часть, которую полезно осознать до того, как вы напишете симуляцию.
Ассоциативность больше не спасает
def A(x, r=3.9): return r * x * (1 - x)
def B(x, r=3.9): return r * x - r * x * x # алгебраически то же самое
a = b = 0.5
for n in range(1, 81):
a, b = A(a), B(b)
if n in (10, 30, 50, 70, 80):
print(n, f"{a:.15f}", f"{b:.15f}", f"{abs(a-b):.1e}")
10 0.104009713267468 0.104009713267468 8.3e-17
30 0.972843439510898 0.972843439509923 9.7e-13
50 0.241355112407020 0.241355226199985 1.1e-07
70 0.356440495162445 0.361728691997257 5.3e-03
80 0.931944264689895 0.733744226164328 2.0e-01
Две алгебраически тождественные формулы, одинаковые входные данные, один и тот же процессор — и через 80 итераций расхождение в первом знаке. Разница в один ULP на первом шаге усилилась на 16 порядков. То же самое произойдёт при смене -O2 на -O3, при включении FMA, при смене порядка редукции на GPU, при переезде с x86 на ARM. Подробности про то, почему a*(1-a) != a - a*a в машинной арифметике, — в статье про численные методы.
Практическое следствие для тех, кто гоняет симуляции или обучение: bit-exact воспроизводимость хаотической системы требует фиксации всего — версии BLAS, флагов компилятора, порядка суммирования, детерминированных ядер CUDA. Иначе «тот же самый прогон» даст другой результат, и вы будете полдня искать несуществующий баг.
Почему тогда симуляциям вообще можно верить
Спасает лемма о слежении (shadowing lemma). Для гиперболических систем она утверждает: любая приближённая траектория с локальной ошибкой не больше δ находится в ε-окрестности какой-то настоящей траектории системы (с другим начальным условием). Ваш численный прогон — это не траектория из x_0, но это честная траектория из какого-то x_0' рядом.
Отсюда правильная эпистемология численного счёта в хаосе:
- Не верьте конкретной траектории после нескольких ляпуновских времён.
- Верьте статистике по аттрактору: его форме, размерности, показателям Ляпунова, инвариантной мере, средним значениям наблюдаемых.
Это тот же переход, что и в теории вероятностей: индивидуальная реализация непредсказуема, распределение — вполне. Погоду на 3 июня не предскажут никогда; средняя температура июня предсказывается прекрасно. Прогноз погоды и климатическая модель — разные задачи, и вторая устойчива к хаосу первой.
Оговорка честности ради: Лоренц не гиперболичен строго, и вопрос о применимости леммы к реальным моделям тонкий (см. Hammel, Yorke, Grebogi, «Do numerical orbits of chaotic dynamical processes represent true orbits?», J. Complexity, 1987). Но численные результаты по статистикам воспроизводятся на разных схемах и разных шагах — это лучшее эмпирическое подтверждение, которое есть.
7. Где это реально встречается в программировании
Метастабильные отказы: ретрай-шторм как бифуркация
Самое ценное приложение теории динамических систем к инфраструктуре. Смоделируем сервис, у которого за порогом ёмкости появляется отрицательная обратная связь по полезной работе: перегруженный сервер тратит ресурсы на запросы, чьи клиенты уже отвалились по таймауту.
C = 100.0 # ёмкость (RPS)
BETA = 0.9 # сколько ретраев порождает один отказ
def served(L):
"""До ёмкости обслуживаем всё; за ней goodput падает — работа делается впустую."""
return L if L <= C else C * C / L
def step(L, lam):
"""L — суммарный поток (новые + ретраи), lam — поток от пользователей."""
return lam + BETA * (L - served(L))
def settle(L0, lam, n=500):
L = L0
for _ in range(n):
L = step(L, lam)
return L
print("lam из L=80 из L=150 из L=400")
for lam in (60, 80, 95, 100, 105):
print(f"{lam:3d} {settle(80., lam):9.1f} {settle(150., lam):10.1f} {settle(400., lam):10.1f}")
lam из L=80 из L=150 из L=400
60 60.0 60.0 305.9
80 80.0 664.6 664.6
95 95.0 843.3 843.3
100 100.0 900.0 900.0
105 955.8 955.8 955.8
Читайте внимательно строку lam = 80. Утилизация 80%, всё прекрасно, L* = 80, goodput 80. Но если систему однократно толкнуть выше порога — деплой, GC-пауза, сетевой блип, — она сваливается во второе устойчивое состояние L* = 664.6, где goodput равен 15 вместо 80. И остаётся там навсегда, хотя пользовательская нагрузка не изменилась и триггер давно прошёл.
Это не «перегрузка». Это бистабильность: у системы два аттрактора, и вы застряли в плохом. Именно это в литературе называется metastable failure — и это ровно то, почему инцидент не заканчивается сам после устранения причины.
Порог опрокидывания при lam = 80 находится численно и равен L ≈ 135. А критическую нагрузку, ниже которой плохого аттрактора не существует вообще, можно выписать аналитически. Неподвижная точка на верхней ветви:
L = λ + β(L - C²/L)
(1 - β)L² - λL + βC² = 0
Вещественные корни есть при λ² ≥ 4β(1-β)C², то есть
λ_крит = 2C * sqrt(β(1-β))
| β (ретраев на отказ) | λ_крит при C = 100 |
|---|---|
| 0.5 | 100 (= 100% ёмкости) |
| 0.7 | 91.7 |
| 0.9 | 60.0 |
| 0.95 | 43.6 |
| 0.99 | 19.9 |
Вот это и есть настоящий ответ на вопрос «до какой утилизации можно грузить сервис». Не «до 80%, потому что так принято», а: при агрессивных ретраях (β → 1) метастабильное состояние существует уже при 20% утилизации. Каждый процент, на который вы уменьшаете β — ретрай-бюджеты, circuit breaker, отказ от ретраев на не-идемпотентных вызовах, дедлайны, распространяемые вниз по стеку, — двигает порог вверх квадратично.
которых уже никто не ждёт S--xU: ещё больше таймаутов U->>LB: ещё ретраи: 300, затем 660 rps end Note over S,DB: новая устойчивая точка L* = 664,
goodput 15 — GC-паузы давно нет,
система не выходит сама rect rgb(110,140,110) Note over LB: выход только принудительный:
сброс очередей, load shedding,
отключение ретраев, дренаж трафика end
Отсюда весь набор проверенных практик, и каждая из них имеет точный смысл в терминах динамики:
- Экспоненциальный backoff с jitter уменьшает эффективный
βи одновременно разрушает синхронизацию клиентов (без jitter клиенты фазово синхронизируются — это классическая синхронизация связанных осцилляторов, и она превращает размазанную нагрузку в периодические пики). Разбор с измерениями: AWS Architecture Blog, «Exponential Backoff And Jitter». - Ретрай-бюджеты (не более X% ретраев от общего трафика) — это жёсткая верхняя граница на
β, то есть прямое управление положением бифуркации. - Load shedding сдвигает функцию
served(L), устраняя её падающий участок: если перегруженный сервис быстро отвечает 429 вместо того, чтобы делать бесполезную работу,servedстановится монотонной и верхний аттрактор просто исчезает. - Дедлайны, распространяемые по стеку (deadline propagation) не дают серверу работать над безнадёжными запросами — тот же эффект.
Читать дальше: Bronson et al., «Metastable Failures in Distributed Systems», HotOS 2021, Huang et al., «Metastable Failures in the Wild», OSDI 2022, и глава про каскадные отказы в Google SRE Book.
Автоскейлинг: бифуркация Хопфа в вашем HPA
Автоскейлер — управляющая обратная связь с запаздыванием: он видит метрику с задержкой (окно усреднения + время старта пода). Простейшая модель x_{n+1} = x_n + k(target - x_{n-d}), где d — лаг. Линеаризация даёт классический результат теории управления: при росте коэффициента k пара комплексно-сопряжённых собственных значений выходит за единичную окружность, неподвижная точка теряет устойчивость и рождается предельный цикл. Это бифуркация Андронова–Хопфа, и на дашборде она выглядит как ровная синусоида числа реплик.
Лечится ровно тем, чем лечится любая колебательная обратная связь: уменьшить k (порог срабатывания), уменьшить лаг d (более быстрые метрики, прогретые образы), добавить гистерезис и cooldown (разные пороги на scale-up и scale-down), добавить демпфирование (stabilizationWindowSeconds в Kubernetes HPA). Все четыре ручки — про сдвиг собственных значений внутрь единичного круга.
Обучение нейросетей: край хаоса
Прямой проход глубокой сети — это итерация отображения h_{l+1} = φ(W_l h_l + b_l). Значит, у него есть показатель Ляпунова, зависящий от дисперсии инициализации весов:
λ < 0(упорядоченная фаза): сигналы схлопываются, все входы дают почти одинаковый выход, градиенты затухают;λ > 0(хаотическая фаза): близкие входы разъезжаются, градиенты взрываются;λ ≈ 0(«край хаоса»): корреляции распространяются на максимальную глубину — и именно тут глубокие сети обучаемы.
Это не метафора, а количественная теория: Poole et al., «Exponential expressivity in deep neural networks through transient chaos» и Schoenholz et al., «Deep Information Propagation» вычисляют «глубину распространения корреляций» ξ, которая расходится на границе фаз. Инициализации вроде He/Xavier — это в точности рецепты посадки сети на λ ≈ 0. А «взрыв градиентов» в RNN, разобранный у Pascanu et al. в «On the difficulty of training recurrent neural networks», — это положительный показатель Ляпунова рекуррентного отображения; gradient clipping — грубый, но работающий способ обрезать экспоненту.
Генераторы псевдослучайных чисел и криптография
Хаотические отображения выглядят соблазнительно в роли ГПСЧ: детерминированы, дают «шум», быстро считаются. Не делайте так. Причины конкретные:
- В плавающей точке орбита живёт в конечном множестве, и период может оказаться катастрофически коротким и зависящим от платформы.
- Динамика реконструируется по короткому отрезку выхода (см. следующий раздел — методы восстановления работают именно потому, что система низкоразмерна).
- Восстановление параметра и состояния по наблюдениям — стандартная задача, решаемая методами нелинейной идентификации.
Разбор десятков сломанных «хаотических шифров»: Alvarez & Li, «Some basic cryptographic requirements for chaos-based cryptosystems». Используйте ChaCha20 и /dev/urandom (man 4 random, man 2 getrandom), а хаос оставьте для моделирования.
Исключение — отображение кота Арнольда (x, y) → (2x + y, x + y) mod 1, которое используют в перемешивании пикселей. Его матрица [[2,1],[1,1]] имеет det = 1 (площадь сохраняется) и собственные значения (3 ± √5)/2 ≈ 2.618 и 0.382, откуда λ = ln 2.618 ≈ 0.962. Это красивый пример: отображение линейно, но взятие mod 1 делает его отображением тора, и вот там уже полноценный хаос. Обратите внимание на подвох: как перестановка конечной сетки N×N оно строго периодично (для N = 256 период равен 192), так что «шифрование» тривиально обращается повторным применением.
Прочее из практики
- Фрактальное сжатие изображений (системы итерированных функций, Барнсли) — реально работавшая технология, проигравшая вейвлетам по скорости кодирования, но подарившая индустрии понимание самоподобия в естественных изображениях.
- Процедурная генерация: фрактальный шум (fBm), diamond-square для ландшафтов, L-системы для растительности — всё это про самоподобие в разных масштабах.
- Клеточные автоматы: Rule 30 Вольфрама годами служил генератором случайных чисел в Mathematica. Простое локальное правило, хаотическая глобальная динамика — тот же сюжет, что у логистического отображения, но на решётке.
- Хаос-инжиниринг (Chaos Monkey и родственники) к теории хаоса отношения не имеет — совпадение названий. Хотя, если честно, метастабильные отказы выше показывают, что связь могла бы быть теснее, чем кажется: chaos engineering как раз ищет те самые «толчки за порог», которые переводят систему в плохой аттрактор.
8. Как распознать хаос в реальных данных
Допустим, у вас есть скалярный временной ряд: латентность, длина очереди, курс. Хочется понять, это низкоразмерный детерминированный процесс или просто шум.
Теорема Такенса (1981) даёт основание для попытки. Если система имеет d-мерный аттрактор, то отображение x(t) → (x(t), x(t-τ), x(t-2τ), ..., x(t-(m-1)τ)) при m > 2d является вложением: восстановленный аттрактор диффеоморфен настоящему. Из одной наблюдаемой координаты восстанавливается геометрия всей системы. Это утверждение, которое при первом знакомстве кажется невозможным.
Корреляционная размерность (Грассбергер–Прокаччиа, 1983) — практичный способ измерить размерность вложенного аттрактора:
C(r) = (2 / (N(N-1))) * #{ пары (i,j), i<j : |y_i - y_j| < r }
D_2 = наклон log C(r) по log r на «плато»
Диагностика простая: у детерминированной системы D_2 насыщается с ростом размерности вложения m, у шума — растёт линейно, потому что шум заполняет любое пространство, в которое его вложили.
import numpy as np
rng = np.random.default_rng(0)
def embed(x, m, tau):
"""Вложение с задержкой по Такенсу."""
n = len(x) - (m - 1) * tau
return np.stack([x[i * tau: i * tau + n] for i in range(m)], axis=1)
def corr_dim(Y, nsamp=3000):
idx = rng.choice(len(Y), nsamp, replace=False)
P = Y[idx]
D = np.sqrt(((P[:, None, :] - P[None, :, :]) ** 2).sum(-1))
d = D[np.triu_indices(nsamp, 1)]
rs = np.exp(np.linspace(np.log(0.5), np.log(8), 12))
Cs = np.array([(d < r).mean() for r in rs])
return np.polyfit(np.log(rs)[2:9], np.log(Cs)[2:9], 1)[0]
# xs — координата x аттрактора Лоренца, 20000 точек с dt = 0.01
for m in (1, 2, 3, 4, 5):
print("Лоренц, m =", m, " D2 =", round(corr_dim(embed(xs, m, 17)), 3))
noise = rng.normal(size=len(xs))
for m in (2, 3, 4, 5):
print("шум, m =", m, " D2 =", round(corr_dim(embed(noise, m, 17)), 3))
Лоренц, m = 1 D2 = 0.979
Лоренц, m = 2 D2 = 1.689
Лоренц, m = 3 D2 = 1.910
Лоренц, m = 4 D2 = 2.001
Лоренц, m = 5 D2 = 2.020 <- насыщение около 2.05
шум, m = 2 D2 = 1.239
шум, m = 3 D2 = 1.989
шум, m = 4 D2 = 2.832
шум, m = 5 D2 = 3.701 <- растёт вместе с m
Сложность: O(nsamp² * m) по времени и O(nsamp²) по памяти — квадратично, поэтому и берут подвыборку.
Как этим не обмануться. Литература 1990-х завалена статьями, «обнаружившими» низкоразмерный хаос в котировках, ЭЭГ и трафике; большинство не воспроизвелось. Дисциплина такова:
- Метод суррогатных данных. Постройте суррогаты — ряды с тем же спектром мощности и той же амплитудной гистограммой, но со случайными фазами. Если ваша статистика (
D_2,λ₁, ошибка нелинейного прогноза) на суррогатах не отличается от исходного ряда — вы нашли не хаос, а автокорреляцию. По сути это тест гипотезы из статистики, просто с нестандартной нулевой моделью. - Проверьте длину ряда. Для честной оценки
D_2 = Dнужно порядка10^Dточек, а лучше больше. «Размерность 5.6 по 500 наблюдениям» — арифметически невозможное утверждение. - Нестационарность маскируется под хаос. Дрейф среднего даёт те же симптомы. Проверяйте на скользящих окнах.
- Спросите себя, сколько степеней свободы у источника. Лоренц низкоразмерен, потому что это три уравнения. Продакшн-латентность — это тысячи слабо связанных процессов; ожидать от неё аттрактора размерности 2.5 нет никаких оснований. Чаще всего правильный ответ — «это не хаос, это шум с автокорреляцией и тяжёлым хвостом», и моделировать надо статистически.
Инструменты: TISEAN (канонический пакет, к нему же прилагается книга Kantz & Schreiber), nolds для Python.
Управление хаосом
Раз хаотический аттрактор пронизан плотной сетью неустойчивых периодических орбит, их можно стабилизировать — причём малыми воздействиями, потому что чувствительность работает и в вашу пользу.
- OGY (Ott, Grebogi, Yorke, PRL 1990): дождаться, когда траектория подойдёт близко к нужной неустойчивой орбите, и микроскопически подтолкнуть параметр так, чтобы попасть на её устойчивое многообразие. Стабилизация ценой воздействий порядка 0.1%.
- Задержанная обратная связь Пирагаса (1992): добавить к управлению член
K(x(t) - x(t - T)), гдеT— период целевой орбиты. На самой орбите член обращается в ноль, то есть управление «бесплатно» в установившемся режиме. Инженерно куда удобнее OGY. - Синхронизация хаоса (Pecora & Carroll, 1990): две связанные хаотические системы могут идти в такт — контринтуитивный факт, лежащий в основе схем скрытой связи (по большей части взломанных, см. выше).
9. Типичные заблуждения
«Хаос — это случайность». Нет. Хаос полностью детерминирован: x_{n+1} = f(x_n) без единого случайного бита. Он лишь выглядит случайным, потому что усиливает неизвестные вам младшие разряды начального условия. Различаются они операционально: у хаоса низкоразмерный аттрактор (D_2 насыщается), у шума — нет.
«Эффект бабочки значит, что любая мелочь имеет большие последствия». Нет. Он значит, что в хаотической системе малое возмущение растёт экспоненциально до масштаба аттрактора — и только. Взмах крыла не «вызывает ураган»: он меняет, какая из уже возможных погод реализуется. Статистика (климат) при этом не меняется вовсе.
«Хаос значит, что предсказать ничего нельзя». Нельзя предсказать траекторию за горизонтом (1/λ)ln(Δ/δ₀). Инвариантная мера, аттрактор, средние, размерность, отклик на изменение параметров — предсказываются отлично. Прогноз погоды на 3 дня работает; климатические модели работают; прогноз погоды на 3 месяца — нет.
«Хаос возможен в любой нелинейной системе». Нелинейность необходима, но не достаточна. Для автономного непрерывного потока на плоскости теорема Пуанкаре–Бендиксона запрещает хаос: ограниченная траектория обязана прийти к точке равновесия или предельному циклу. Нужно n ≥ 3 (Лоренц — ровно 3). Для дискретных отображений хватает одного измерения (логистическое). Для неавтономных систем время добавляет размерность — поэтому возбуждаемый маятник хаотичен «в двух измерениях».
«Линейные системы не бывают хаотическими». В R^n — верно: решения линейных систем это суммы экспонент и синусов, они либо ограничены без разбегания, либо разбегаются неограниченно. Но отображение кота Арнольда линейно и хаотично — потому что живёт на торе, а взятие mod 1 нелинейно. Формулировка должна включать пространство.
«Странный аттрактор — просто красивое название для сложного аттрактора». «Странный» — точный термин про геометрию: нецелая размерность, канторова структура поперёк слоёв. «Хаотический» — про динамику: λ₁ > 0. Обычно они идут вместе, но существуют странные нехаотические аттракторы (у квазипериодически возбуждаемых систем) — фрактальные, но с λ₁ ≤ 0.
«Возьму double, и точности хватит». 53 бита мантиссы при λ = ln 2 заканчиваются за 53 итерации. Экспонента съедает любую разрядность за линейное время. Переход на float128 покупает вам не порядок, а +11 итераций.
«Мой продовый график выглядит хаотично, значит это хаос». Почти наверняка нет. Прогоните тест на суррогатных данных, посчитайте насыщение D_2, посмотрите на число степеней свободы источника. В девяти случаях из десяти это шум, автокорреляция или нестационарность.
Мини-итог
- Динамическая система — пространство состояний плюс правило эволюции с полугрупповым свойством. Каскад (итерация отображения) или поток (векторное поле).
- Устойчивость неподвижной точки читается по спектру якобиана:
|λ| < 1для каскадов,Re λ < 0для потоков. Граница спектра — место рождения бифуркаций. - Логистическое отображение
x → r x(1-x)проходит весь путь от вымирания до хаоса через каскад удвоений с универсальной константой Фейгенбаумаδ = 4.6692, одинаковой для всего класса отображений с квадратичным максимумом. - Хаос по Девани = транзитивность + плотные периодические точки (чувствительность отсюда следует). Количественная мера — показатель Ляпунова
λ₁ > 0; горизонт предсказуемости(1/λ)ln(Δ/δ₀)растёт лишь логарифмически по точности. - Механизм всегда один: растяжение (разбегание соседей) плюс складывание (удержание в ограниченной области). Отсюда фрактальная слоистость странных аттракторов.
- Странный аттрактор Лоренца:
div F = -13.667(нулевой объём) приλ₁ = 0.906(разбегание) иD_KY = 2.062. Сумма показателей Ляпунова обязана равняться дивергенции — бесплатная проверка численного счёта. - Box-counting размерность на реальных данных систематически занижает результат; для оценок по временным рядам берут корреляционную размерность и обязательно проверяют суррогатами.
- В хаосе доверять надо не траектории, а статистике по аттрактору. Плавающая точка ломает конкретный прогон (перестановка
r*x*(1-x)наr*x - r*x*xдаёт расхождение в первом знаке за 80 итераций), но лемма о слежении спасает статистики. - Инженерная отдача: метастабильные отказы — это бистабильность, и критическая нагрузка
λ_крит = 2C√(β(1-β))количественно объясняет, зачем нужны ретрай-бюджеты, jitter, load shedding и дедлайны; колебания автоскейлера — бифуркация Хопфа от запаздывающей обратной связи; обучаемость глубоких сетей — посадка на край хаосаλ ≈ 0.
Источники
Книги
- Steven Strogatz, «Nonlinear Dynamics and Chaos», 2-е изд., CRC Press 2015 — лучшая первая книга по теме; видеокурс автора выложен в открытый доступ.
- Edward Ott, «Chaos in Dynamical Systems», 2-е изд., Cambridge University Press 2002 — стандартный аспирантский уровень, подробно про размерности и управление хаосом.
- Robert Devaney, «An Introduction to Chaotic Dynamical Systems», 3-е изд., CRC Press 2021 — источник определения хаоса.
- Hirsch, Smale, Devaney, «Differential Equations, Dynamical Systems, and an Introduction to Chaos», 3-е изд., Academic Press 2012.
- Kenneth Falconer, «Fractal Geometry: Mathematical Foundations and Applications», 3-е изд., Wiley 2014 — строгая теория размерностей.
- Kantz & Schreiber, «Nonlinear Time Series Analysis», 2-е изд., Cambridge 2004 — как честно работать с реальными данными; к книге прилагается пакет TISEAN.
- Benoit Mandelbrot, «The Fractal Geometry of Nature», W. H. Freeman 1982.
- James Gleick, «Chaos: Making a New Science», Viking 1987 — научно-популярная история области; отличное чтение до и после формального курса.
Статьи
- E. N. Lorenz, «Deterministic Nonperiodic Flow», J. Atmos. Sci. 20 (1963) — https://doi.org/10.1175/1520-0469(1963)020%3C0130:DNF%3E2.0.CO;2
- R. May, «Simple mathematical models with very complicated dynamics», Nature 261 (1976) — https://doi.org/10.1038/261459a0
- T.-Y. Li, J. Yorke, «Period Three Implies Chaos», Amer. Math. Monthly 82 (1975) — https://doi.org/10.2307/2318254
- M. Feigenbaum, «Quantitative universality for a class of nonlinear transformations», J. Stat. Phys. 19 (1978) — https://doi.org/10.1007/BF01020332
- Banks, Brooks, Cairns, Davis, Stacey, «On Devaney’s Definition of Chaos», Amer. Math. Monthly 99 (1992) — https://doi.org/10.2307/2324899
- P. Grassberger, I. Procaccia, «Measuring the strangeness of strange attractors», Physica D 9 (1983) — https://doi.org/10.1016/0167-2789(83)90298-1
- F. Takens, «Detecting strange attractors in turbulence», Lecture Notes in Math. 898 (1981) — https://doi.org/10.1007/BFb0091924
- W. Tucker, «A Rigorous ODE Solver and Smale’s 14th Problem», Found. Comput. Math. 2 (2002) — https://doi.org/10.1007/s002080010018
- Ott, Grebogi, Yorke, «Controlling chaos», Phys. Rev. Lett. 64 (1990) — https://doi.org/10.1103/PhysRevLett.64.1196
- K. Pyragas, «Continuous control of chaos by self-controlling feedback», Phys. Lett. A 170 (1992) — https://doi.org/10.1016/0375-9601(92)90745-8
- Pecora & Carroll, «Synchronization in chaotic systems», Phys. Rev. Lett. 64 (1990) — https://doi.org/10.1103/PhysRevLett.64.821
- Hammel, Yorke, Grebogi, «Do numerical orbits of chaotic dynamical processes represent true orbits?», J. Complexity 3 (1987) — https://doi.org/10.1016/0885-064X(87)90024-0
- Laskar & Gastineau, «Existence of collisional trajectories of Mercury, Mars and Venus with the Earth», Nature 459 (2009) — https://doi.org/10.1038/nature08096
Инженерные источники
- Bronson, Aghayev, Charapko, Zhu, «Metastable Failures in Distributed Systems», HotOS 2021 — https://sigops.org/s/conferences/hotos/2021/papers/hotos21-s11-bronson.pdf
- Huang et al., «Metastable Failures in the Wild», OSDI 2022 — https://www.usenix.org/conference/osdi22/presentation/huang-lexiang
- Marc Brooker, «Exponential Backoff And Jitter», AWS Architecture Blog — https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
- Google SRE Book, «Addressing Cascading Failures» — https://sre.google/sre-book/addressing-cascading-failures/
- Poole et al., «Exponential expressivity in deep neural networks through transient chaos» — https://arxiv.org/abs/1606.05340
- Schoenholz et al., «Deep Information Propagation» — https://arxiv.org/abs/1611.01232
- Pascanu, Mikolov, Bengio, «On the difficulty of training recurrent neural networks» — https://arxiv.org/abs/1211.5063
- Alvarez & Li, «Some basic cryptographic requirements for chaos-based cryptosystems» — https://arxiv.org/abs/nlin/0311039
- Документация:
scipy.integrate.solve_ivp— https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html ; TISEAN — https://www.pks.mpg.de/tisean/ ; nolds — https://github.com/CSchoel/nolds ; Kubernetes HPA (behavior.scaleDown.stabilizationWindowSeconds) — https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/
Что дальше
Это была последняя статья трека «Математика для программиста». Мы начали с пустого множества и правил вывода, прошли через линейную алгебру, абстрактную алгебру и теорию категорий, выяснили границы вычислимого и практически считаемого, научились рассуждать о неопределённости и не терять точность в машинной арифметике — и закончили системами, которые детерминированы, но непредсказуемы. Если хочется перечитать карту целиком — она в обзоре трека.
Куда двигаться дальше, зависит от того, что вам ближе:
- Применять математику к машинам. Треки «Машинное обучение» и «Нейронные сети» на портале продолжают ровно ту линию, которая шла через собственные значения и SVD, вероятность и раздел про край хаоса в этой статье.
- Применять её к системам. Треки «Операционные системы», «Инженерия данных» и «Архитектурные паттерны» — там метастабильные отказы, очереди и обратные связи из этой статьи становятся ежедневной работой. Формальная сторона надёжности растёт из теории графов и теории игр.
- Применять её к коду. Языковые треки — Go, Elixir, TypeScript, C# — плюс «Алгоритмы», «Структуры данных», «Парадигмы программирования» и «Паттерны проектирования», где абстракции из статьи о теории категорий в программировании превращаются в конкретные типы и интерфейсы.
Общая карта всех треков портала и рекомендуемые маршруты — в дорожной карте.