Возьмите функцию на комплексной плоскости, у которой производная существует всюду, кроме отдельных изолированных точек.
Что необходимо о ней знать, чтобы восстановить её целиком?
Ответ поразительный: достаточно списка точек, где функция не дифференцируема, и «силы» каждой поломки. Формулу написал шведский математик Гёста Миттаг-Леффлер, и она — одна из самых красивых в комплексном анализе.
В статье далее — электростатика на комплексной плоскости, спины у классических мультиполей, контрчлены как в квантовой теории поля, вывод в две строчки, самая дорогая ошибка Пуанкаре и разбор легенды о том, почему не бывает Нобелевской премии по математике. И картинки на matplotlib, разумеется, — с кодом.
Статья для тех, кто хочет по-настоящему понять суть основ комплексного анализа!
Почему такое возможно только на комплексной плоскости
Чтобы понять, откуда берётся эта магия, нужно на минуту вспомнить, чем комплексные функции отличаются от обычных.
Когда мы требуем от функции комплексной переменной, чтобы у неё существовала производная, мы требуем гораздо больше, чем в школьном анализе. К точке на прямой можно подойти только слева или справа. К точке на плоскости — с любой стороны, по любой траектории, по спирали, если хочется. И предел разностного отношения обязан получаться один и тот же во всех случаях. Это условие формализуется в виде условий Коши–Римана: если , то
Функции, которые их выдерживают, называются голоморфными, и ведут они себя подобно стеклу. Вещественную гладкую функцию можно локально пошевелить — остальной график этого не заметит. С голоморфной так нельзя: тронешь её на крошечном кусочке — и она вся целиком обязана перестроиться. Есть даже теорема (её называют теоремой единственности):
если две голоморфные функции совпали на последовательности точек, сходящейся внутри области, они совпадают всюду.
Именно эта жёсткость и делает возможным всё, что будет дальше.
Ландшафт с иглами
Теорема работает с особым классом функций — мероморфными. Представьте бескрайний холмистый ландшафт: перепады, впадины, плавные склоны, всё гладко и предсказуемо. А теперь вообразите, что в нескольких изолированных точках из этого ландшафта торчат бесконечно высокие иглы — тонкие, уходящие в небо шпили.

Модуль гамма-функции Иглы — полюса в точках
; вершины срезаны, на самом деле они уходят в бесконечность. Справа — рост
вдоль вещественной оси.
Это и есть мероморфная функция. Всюду, кроме отдельных точек, она голоморфна, то есть максимально приличная. А в этих точках — полюсах — обращается в бесконечность.
Примеров вокруг сколько угодно. Простейший — : одна игла в нуле. Тангенс: иглы во всех точках
, где косинус обнуляется. Функция
: иглы в целых кратных
.
Гамма-функция: иглы в нуле и во всех отрицательных целых точках. Любая рациональная дробь: иглы там, где знаменатель занулился.
Ключевое слово — «изолированные». Полюса не сливаются в стену, не образуют сплошной линии обрыва. У каждого есть личное пространство: вокруг него можно нарисовать окружность, внутри которой он один. Поэтому полюса можно перенумеровать:
Уже кое-что: список точек, где функция ломается.
На комплексную функцию, кстати, можно смотреть не только как на рельеф модуля. Есть способ увидеть её целиком — фазовый портрет: цветом кодируется аргумент , яркостью — модуль. Полюса и нули на такой картинке выглядят как радужные вертушки:

Фазовый портрет. Полюса в целых точках, нули в полуцелых. Вдали от вещественной оси цвет постоянный — функция стремится к константе
.
Вычеты: заряды на плоскости
Список положений игл — это ещё не всё. Иглы бывают разные: одна взлетает вверх стремительно, другая помедленнее, третья закручивается по-своему. Нужна характеристика каждой.
Такая характеристика есть — называется вычетом.
Формально: около полюса функцию раскладывают в ряд Лорана — как ряд Тейлора, только с добавкой отрицательных степеней. Вычет — это коэффициент при , то есть при самой слабой из «плохих» степеней. Для простого полюса его можно достать одним движением:
У вычета есть очень прозрачный физический смысл. Давайте рассматривать мероморфную функцию как электрическое поле на плоскости, а полюса — как точечные заряды.
Тогда вычет — это величина заряда. И знаменитая теорема о вычетах говорит буквально то же, что теорема Гаусса в электростатике: интеграл по замкнутому контуру равен сумме зарядов внутри, умноженной на . Что творится снаружи — совершенно не важно.
Итого, у нас есть: положения зарядов на плоскости , величины зарядов
и одно опорное значение
. Вопрос: хватит ли этого, чтобы восстановить поле всюду?
Собственно теорема (в простейшем случае)
Если все полюса простые, а функция не растёт слишком буйно на бесконечности (проще говоря, при
), то
Каждый полюс вносит в общую сумму своё слагаемое. Вся функция — целиком, в любой точке плоскости — собрана из описаний её собственных катастроф плюс одна константа.
Строго:
пусть
мероморфна в
, ноль — не полюс, все полюса
простые и упорядочены по неубыванию модуля,
— вычеты в них. Пусть существует последовательность радиусов
(окружности
не проходят через полюса), такая что
Тогда ряд выше сходится равномерно на компактах (не содержащих полюсов), если суммировать в порядке нумерации.
Условие на рост можно ослаблять: если ограничен сам на окружностях, поправки
вообще не нужны; если
растёт как
, приходится вычитать больше членов тейлоровского разложения — по
на каждый полюс.
А если игла толще?
В формулировке спрятана оговорка: все полюса простые. Что это значит и что делать, если нет?
Простой полюс означает, что функция уходит в бесконечность «со скоростью» , не быстрее. У
полюс уже второго порядка, у
— пятого. В ряде Лорана у такой функции не одно отрицательное слагаемое, а несколько:
Этот кусок называют главной частью — это полное описание того, как именно функция взрывается в данной точке.
Для простого полюса главная часть состоит из одного слагаемого, и вычета достаточно: одно число исчерпывает особенность. Для кратного нужен весь набор коэффициентов.
Теорема от этого не ломается — просто вместо в сумму подставляется главная часть целиком. Идея остаётся прежней: собери описания всех катастроф, сложи их, добавь константу — получишь функцию.
А как это в электростатике?
Оказывается, у толстых игл тоже есть электростатические аналоги!
Полюс порядка — это точечный мультиполь. Двойной полюс — диполь, тройной — квадруполь, полюс порядка
—
-поль.

Линии поля для
,
и
: заряд, диполь, квадруполь. Чем выше порядок полюса, тем больше лепестков и тем быстрее поле спадает с расстоянием.
Теоретическая справка: двумерная электростатика
Всё это — двумерная электростатика: потенциал точечного заряда , а не
. Мультиполи раскладываются по
, а не по сферическим гармоникам, и их поля падают как
.
Двумерная электростатика — это модель полей заряженных нитей в обычном трёхмерном пространстве, а ещё удобный способ моделирования двумерной гидродинамики.
Важная тонкость: аналитичен здесь не потенциал, а поле.
Если — комплексный потенциал, то
Голоморфность поля буквально означает вакуумные уравнения Максвелла:
и
— это и есть условия Коши–Римана, записанные для компонент поля.
Точечный заряд даёт , то есть поле
:
простой полюс с вычетом, пропорциональным заряду.
Теперь диполь — два заряда на малом расстоянии
(комплексное
сразу задаёт и величину, и направление):
У потенциала появился простой полюс, а у поля — двойной: , где
.
Сближая два противоположных диполя, получаете квадруполь с , и так далее.
Значит, главная часть
— это ровно мультипольное разложение особенности:
— заряд,
— дипольный момент,
— квадрупольный, и так далее.
Как в классической физике неожиданно возникает спин
Легко заметить, что при повороте картинки на угол коэффициент
умножается на
. Получается, что заряд — скаляр (не меняется вовсе), дипольный момент — вектор (набегает фаза
), квадрупольный момент — объект спина 2.
Полюс порядка преобразуется как объект спина
.
Таким образом, в самой обычной классической физике мы внезапно получаем конструкции, которые обычно считают характерными исключительно для релятивистской квантовой механики. Спин — это просто закон преобразования при поворотах.
Самая хитрая деталь: зачем там дополнительное слагаемое
Дальше начинается самое тонкое место. Что это за странная добавка в скобках? Она не зависит от
, то есть это просто константа. Зачем она вообще?
Затем, что полюсов может быть бесконечно много, и без неё всё разваливается.
Возьмём для примера функцию с полюсами во всех целых точках и вычетами, равными единице. Если наивно сложить вклады, получится по всем целым
. А это, по сути, гармонический ряд: слагаемые убывают как
, и сумма расходится.
Формула, которая ничего не даёт, — плохая формула.
Теперь добавим поправку.
Вместо берём
.
Приводим к общему знаменателю: получается .
При больших это ведёт себя как
, а ряд
сходится — та самая эйлеровская сумма. Ряд стал послушным, формула заработала.

Один и тот же ряд в точке Без поправки частичные суммы уходят в
как
. С поправкой
— выходят на конечный предел (для любопытных: он равен
, проверяется через дигамма-функцию).
Что мы, собственно, сделали? Вычли из каждого слагаемого его первый член разложения Тейлора в нуле. Микроскопическая правка, не затрагивающая сути дела около полюса, но радикально улучшающая поведение суммы вдали от него.
Приём этот выходит далеко за пределы комплексного анализа. Так поступают, когда суммируют расходящиеся ряды в теории чисел. То же самое делают физики, из бесконечных выражений квантовой теории поля вычитая контрчлены и получая конечные, проверяемые на опыте числа.
И каждый раз логика одна: аккуратно подобрать вычитаемое, которое не портит физику, но спасает сходимость. Теорема Миттаг-Леффлера — едва ли не самый прозрачный и самый широко используемый на практике пример этой идеи во всей математике.
Откуда странная добавка взялась с точки зрения физика
по всем целым — это цепочка одинаковых зарядов, и ряд расходится.
Видно, почему: каждый далёкий заряд создаёт около нуля почти однородное поле $\sim 1/n$, и эти вклады убывают слишком медленно. Поправка вычитает у каждого заряда ровно то поле, которое он создаёт в начале координат, — то есть мы меряем поле не абсолютно, а как разность с полем в нуле.
Однородная добавка ничего не говорит об источниках, так что мы ничего не портим; остаётся градиент , и сумма сходится.
Ответ — , и вдали от цепочки поле выходит на постоянную величину
по разные стороны — в точности как у равномерно заряженной прямой.
А вот — цепочка диполей, и здесь никакие поправки не нужны: суммарный заряд нулевой, поле само падает как
.
Ответ — , и вдали от цепочки оно затухает экспоненциально.

Слева: цепочка единичных зарядов () — вдали линии поля выпрямляются, как у заряженной прямой. Справа: цепочка диполей (
) — поле заперто около оси и вдали экспоненциально исчезает.
То есть контрчлены Миттаг-Леффлера — это нейтрализующий фон.
Физики узнают этот приём мгновенно: точно так же устроено суммирование по Эвальду для кулоновских решёток и вычисление постоянных Маделунга в кристаллах — заряды считают на компенсирующем фоне, иначе суммы расходятся.
Как это доказывают
Доказательство короткое и очень изящное, поэтому расскажу идею.
Сначала мы берём не саму функцию , а её слегка модифицированную версию:
Здесь — та точка, в которой мы хотим узнать значение, а
— переменная, по которой будем интегрировать.
Заметьте фокус: мы своими руками подложили в конструкцию два дополнительных полюса, в точках и
. Именно они потом вытащат наружу
и
.

Контур доказательства: окружность раздувается и заглатывает полюса один за другим.
Дальше берём большую окружность радиуса , охватывающую точку
и первые
полюсов функции, и считаем по ней контурный интеграл — двумя способами.
Способ первый, изнутри: по теореме о вычетах интеграл равен сумме вычетов всех полюсов внутри. Их три сорта: наш подложенный в нуле даёт , подложенный в точке
даёт
, а настоящие полюса функции дают сумму по всем
.
Способ второй, снаружи: оценим интеграл сверху. Длина окружности растёт пропорционально . А подынтегральное выражение убывает быстрее: в знаменателе два множителя, каждый порядка
, и есть условие
. Итог: при
интеграл стремится к нулю.
Приравниваем. Слева ноль, справа — комбинация из и суммы по полюсам. Разрешаем относительно
), пользуемся тождеством
и — вот она, наша формула!
Мне кажется, здесь и прячется главная красота метода: мы ничего не конструировали вручную.
Вычислим что-нибудь и увидим мощь формулы
Функция мероморфна, её полюса — все целые точки, а вычет в каждой равен единице. Применяем формулу и после сворачивания слагаемых с
и
получаем:
Теперь разложим обе части в ряд около нуля.
Слева котангенс даёт, справа каждое слагаемое даёт
Сравниваем коэффициенты при :
то есть
Знаменитая базельская задача, над которой математики ломали головы почти столетие — её поставил Пьетро Менголи около 1650 года, а решил Эйлер только в 1735-м, — выпадает из теоремы Миттаг-Леффлера как простое тривиальное следствие. Если пойти дальше и сравнить коэффициенты при, получите сумму обратных четвёртых степеней:
. И так далее — вся лестница значений
.
Проверим на тангенсе
Возьмём функцию, которую все видели в школе. У тангенса полюса стоят в точках, вычет в каждом равен
, а в нуле
. Подставляем в формулу, объединяем полюса, симметричные относительно нуля, и получаем:
Проверить можно прямо на коленке. При маленьких тангенс почти равен самому
.
Справа при маленьких каждое слагаемое приближённо равно
. Сумма обязана дать
, а это значит, что
— сумма обратных квадратов нечётных чисел. Тождество известное, но полюбуйтесь путём, которым мы к нему пришли: мы просто выписали, где ломается тангенс и с какой силой, — а сумма ряда выпала сама.

Численная проверка: обе суммы честно сходятся к обещанным значениям.
Теорема на самом деле ещё сильнее
Теорема Миттаг-Леффлера утверждает вещь ещё более радикальную — она не ограничивается полезной формулой для вычислений.
Она утверждает: задайте любую последовательность точек, уходящую на бесконечность, и назначьте каждой точке любую главную часть, какую вздумается, — и найдётся мероморфная функция ровно с этими особенностями и никакими другими.
У этой теоремы есть родная сестра — теорема Вейерштрасса о произведениях, которая делает то же самое для нулей: любой наперёд заданный набор нулей реализуется бесконечным произведением. Полюса и нули, разложение в сумму и разложение в произведение — два дуальных сюжета.
А в XX веке из этих сюжетов выросли задачи Кузена, теория пучков и когомологии: почти вся современная комплексная геометрия начинается именно здесь. На современном языке классическая теорема Миттаг-Леффлера — это утверждение:
первая когомология плоскости с коэффициентами в пучке голоморфных функций тривиальна. Одна строчка — но чтобы её прочитать, человечеству понадобилось семьдесят лет.
Где это работает
Теория управления.
Инженеры-автоматчики описывают линейную систему передаточной функцией — и работают именно с её полюсами и вычетами. Список полюсов сразу говорит, устойчива система или пойдёт вразнос (все полюса в левой полуплоскости — устойчива), а разложение на простейшие дроби превращает страшную дробь в сумму понятных экспонент. Обратное преобразование Лапласа — по сути, применение этой же идеи.
Физика резонансов.
У матрицы рассеяния полюс — это частица или связанное состояние; вещественная часть даёт энергию, мнимая — время жизни. Функции Грина в электродинамике и квантовой механике разбираются по полюсам ровно так же.
Гидро- и аэродинамика.
Легко заметить, что вычет может быть комплексным числом, а электрический заряд — число действительное. Как быть? Оказывается, и это описывается физикой — гидродинамикой. В ней комплексный вычет — это источник плюс вихрь: вещественная часть — расход, мнимая — циркуляция. А циркуляция — это подъёмная сила: теорема Жуковского выражает её ровно через тот самый вычет комплексной скорости. Кратные полюса там тоже в деле — для описания обтекания тел.
Суммирование рядов.
Математики и физики часто используют теорему как мощную машину: подобрал функцию с нужными вычетами — и сумма посчиталась. Мы только что видели два примера.
Немного истории: как теорема появлялась
Как это часто бывает, у теоремы был предшественник, опередивший всех на полтора века, — Эйлер. Разложение котангенса в сумму простейших дробей и синуса в бесконечное произведение он выписал ещё в 1730–40-е годы, попутно решив базельскую задачу.
По сути это частные случаи будущих теорем Миттаг-Леффлера и Вейерштрасса — только без каких-либо обоснований сходимости. Эйлер суммировал ряды так же естественно, как дышал, и вопросами законности не мучился.
Строгая история началась в Берлине. В 1875 году молодой швед Гёста Миттаг-Леффлер приехал учиться к Карлу Вейерштрассу — по совету Эрмита, который встретил его в Париже словами, вошедшими в математический фольклор:
«Вы ошиблись, сударь, вам следовало слушать курс Вейерштрасса в Берлине. Это наш общий учитель».
Вейерштрасс как раз опубликовал (1876) свою теорему о представлении целых функций произведениями по их нулям.
Миттаг-Леффлер понял, что у теоремы учителя должна быть двойственная пара — про полюса, — и начал публиковать первые версии своей теоремы в 1876–1877 годах, ещё работая в Хельсинки. Окончательная общая форма появилась в 1884 году в программной статье «Sur la représentation analytique des fonctions monogènes uniformes d'une variable indépendante» — 79 страниц в четвёртом томе Acta Mathematica. К тому моменту теорема умела задавать особенности не только на последовательностях, уходящих в бесконечность, но и на куда более диких множествах — и здесь Миттаг-Леффлер опирался на свежайшую теорию множеств Кантора, которую он одним из первых воспринял всерьёз: печатал в Acta французские переводы канторовских работ, когда дома, в Германии, Кантора травил Кронекер. (Справедливости ради: в 1885-м тот же Миттаг-Леффлер отговорил Кантора публиковать статью о порядковых типах — «она опередила время лет на сто», — и обидчивый Кантор больше в Acta не печатался никогда.)
Годом позже, в 1885-м, Карл Рунге упростил доказательство — и из этого упрощения родилась знаменитая теорема Рунге о приближении рациональными функциями. Так один результат потянул за собой целую цепочку.
Человек, который построил математическую Европу
Сама по себе теорема — не единственное и, возможно, даже не главное достижение Миттаг-Леффлера (1846–1927). Он был редким типом учёного-строителя, и вот несколько сюжетов из его жизни, каждый из которых тянет на отдельную статью.
Acta Mathematica.
В 1882 году, заручившись поддержкой короля Оскара II, Миттаг-Леффлер основал журнал Acta Mathematica — и задумал его как мост между французской и немецкой математикой, которые после войны 1870–71 годов друг друга в упор не видели. Журнал мгновенно стал одним из главных в мире — и остаётся им до сих пор.
Ковалевская.
Миттаг-Леффлер добился, чтобы кафедру в только что созданной Стокгольмской высшей школе получила Софья Ковалевская — его соученица по семинару Вейерштрасса. В 1884 году она стала профессором (первая в новейшей истории женщина — профессор математики), в 1889-м получила пожизненную профессуру, была редактором Acta и в 1888 году взяла премию Бордена Парижской академии за знаменитый «волчок Ковалевской».
Без упрямства Миттаг-Леффлера, годами продавливавшего это назначение через скандализированных коллег, ничего этого не было бы.
Премия короля Оскара II и самая продуктивная ошибка в истории математики.
В 1885 году Acta объявила международный конкурс к 60-летию короля; в жюри — Вейерштрасс, Эрмит и Миттаг-Леффлер. Среди задач — устойчивость Солнечной системы. Победил Анри Пуанкаре с мемуаром о задаче трёх тел. И вот когда премия уже была вручена, а том Acta отпечатан, редактор Эдвард Фрагмен задал Пуанкаре несколько уточняющих вопросов — и Пуанкаре нашёл у себя ошибку. Исправляя её, он обнаружил гомоклинические переплетения траекторий — то, что мы сегодня называем детерминированным хаосом. Миттаг-Леффлер изъял отпечатанный тираж, Пуанкаре оплатил перепечатку из своего кармана: 3585 крон при премии в 2500. Ошибка, принёсшая автору чистый убыток и родившая теорию хаоса.
Институт.
По завещанию Миттаг-Леффлер оставил Королевской шведской академии наук свою виллу в Юрсхольме под Стокгольмом вместе с одной из лучших математических библиотек мира. Institut Mittag-Leffler считается старейшим математическим исследовательским институтом на свете и работает до сих пор: каждый семестр туда съезжаются математики со всей планеты — жить в доме основателя и доказывать теоремы.
Легенда о Нобелевской премии
Говорят, что Нобелевской премии по математике нет, потому что Нобель не поделил с Миттаг-Леффлером женщину.
Легенда красивая и полностью выдуманная.
В 1985 году два шведских математика — Ларс Гординг и Ларс Хёрмандер (тот самый, филдсовский лауреат) — разобрали её в короткой заметке в The Mathematical Intelligencer. Аргументы убийственные: Нобель никогда не был женат; Нобель уехал из Швеции в 1865 году, когда Миттаг-Леффлер был ещё студентом, и всерьёз они, судя по всему, вообще не пересекались; никаких свидетельств вражды не существует. Скучная правда, скорее всего, в том, что Нобель завещал премии за «наибольшую пользу человечеству» в практических областях — и чистая математика просто не входила в его картину мира.
Математики, впрочем, в обиде не остались: с 1936 года есть Филдсовская медаль, а с 2003-го — Абелевская премия, по статусу и деньгам вполне нобелевская.
Что почитать и посмотреть
Учебники (по нарастанию весёлости):
Б. В. Шабат, «Введение в комплексный анализ», т. 1 — строгая классика, ряды Миттаг-Леффлера и произведения Вейерштрасса.
М. А. Лаврентьев, Б. В. Шабат, «Методы теории функций комплексного переменного» — вся физика из этой статьи: двумерная электростатика, гидродинамика, крыло Жуковского.
А. И. Маркушевич, «Теория аналитических функций» — энциклопедично и с историческими комментариями.
L. Ahlfors, Complex Analysis — мировой стандарт.
T. Needham, Visual Complex Analysis — та самая книга, где комплексный анализ показывают руками и картинками.
E. Wegert, Visual Complex Functions — всё о фазовых портретах (как второй рисунок этой статьи).
Первоисточники и история:
G. Mittag-Leffler, «Sur la représentation analytique des fonctions monogènes uniformes d'une variable indépendante», Acta Mathematica 4 (1884), 1–79 — та самая статья; старые тома Acta лежат в открытом доступе на Project Euclid.
L. E. Turner, «The Mittag-Leffler Theorem: The origin, evolution, and reception of a mathematical result, 1876–1884», Historia Mathematica 40 (2013) — подробнейшая история теоремы, включая роль Кантора.
Биография Миттаг-Леффлера в архиве MacTutor — включая цитату Эрмита.
Institut Mittag-Leffler — тот самый дом в Юрсхольме.
J. Barrow-Green, Poincaré and the Three Body Problem (AMS/LMS, 1997) — детектив про премию Оскара II, ошибку и рождение хаоса, по архивам института.
L. Gårding, L. Hörmander, «Why is there no Nobel Prize in mathematics?», The Mathematical Intelligencer 7:3 (1985), 73–74 — разбор легенды из первых рук; популярный пересказ есть у Snopes.
Статьи в Википедии: русская и английская (в английской есть современное доказательство через теорему Рунге).
Базельская задача у Эйлера — работа «De summis serierum reciprocarum» (1740, в каталоге Энестрёма — E41); легко находится в The Euler Archive.
Если после прочтения вам захотелось взять свою любимую мероморфную функцию, выписать её полюса и посмотреть, какая сумма выпадет, — статья удалась. Делитесь результатами в комментариях: за, полученный новым способом, я поставлю плюс к комментарию и лайк аккаунту (если еще не залайкал).
Код для всех иллюстраций в этой статье.
Библиотеки: numpy, scipy, matplotlib. Скрипт целиком, каждая функция — одна картинка.
# -*- coding: utf-8 -*- """ Иллюстрации к статье о теореме Миттаг-Леффлера. Зависимости: numpy, scipy, matplotlib. """ import os import numpy as np import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib.colors import hsv_to_rgb from scipy.special import gamma as cgamma OUT = "/home/claude/figs" os.makedirs(OUT, exist_ok=True) plt.rcParams.update({ "font.family": "DejaVu Sans", "font.size": 12, "axes.titlesize": 13, "figure.facecolor": "white", "axes.facecolor": "white", "savefig.bbox": "tight", }) # ---------------------------------------------------------------------- # 1. Ландшафт с иглами: |Gamma(z)| # ---------------------------------------------------------------------- def fig_gamma(): x = np.linspace(-4.6, 4.6, 640) y = np.linspace(-2.4, 2.4, 420) X, Y = np.meshgrid(x, y) G = np.abs(cgamma(X + 1j * Y)) G = np.clip(G, 0, 6.0) # версия для статьи (белый фон, оси) fig = plt.figure(figsize=(10.5, 6.0)) ax = fig.add_subplot(111, projection="3d") ax.plot_surface(X, Y, G, cmap="magma", rcount=220, ccount=300, linewidth=0, antialiased=True) ax.set_xlabel("Re z"); ax.set_ylabel("Im z"); ax.set_zlabel("|Γ(z)|") ax.set_title("Ландшафт с иглами: |Γ(z)|. Полюса — в точках 0, −1, −2, −3, …") ax.view_init(elev=38, azim=-64) ax.set_zlim(0, 6) fig.savefig(f"{OUT}/01_gamma_landscape.png", dpi=160) plt.close(fig) # тёмная обложка (КДПВ) без осей, пропорции ~780x440 fig = plt.figure(figsize=(7.8, 4.4), facecolor="#0e0e16") ax = fig.add_subplot(111, projection="3d", facecolor="#0e0e16") ax.plot_surface(X, Y, G, cmap="inferno", rcount=220, ccount=300, linewidth=0, antialiased=True) ax.set_axis_off() ax.view_init(elev=40, azim=-64) ax.set_zlim(0, 6) fig.subplots_adjust(left=-0.25, right=1.25, top=1.35, bottom=-0.35) fig.savefig(f"{OUT}/00_cover_gamma.png", dpi=200, facecolor="#0e0e16", bbox_inches=None) plt.close(fig) # ---------------------------------------------------------------------- # 2. Фазовый портрет pi*ctg(pi z) # ---------------------------------------------------------------------- def fig_phase(): x = np.linspace(-3.23, 3.23, 1500) y = np.linspace(-1.58, 1.58, 780) X, Y = np.meshgrid(x, y) Z = X + 1j * Y with np.errstate(all="ignore"): W = np.pi / np.tan(np.pi * Z) H = (np.angle(W) + np.pi) / (2 * np.pi) # hue = фаза logm = np.log(np.abs(W) + 1e-300) frac = (logm / np.log(1.9)) % 1.0 # линии уровня модуля V = 0.62 + 0.38 * frac S = np.full_like(V, 0.92) rgb = hsv_to_rgb(np.dstack([H, S, V])) fig, ax = plt.subplots(figsize=(10.5, 5.6)) ax.imshow(rgb, origin="lower", extent=[x[0], x[-1], y[0], y[-1]], interpolation="bilinear", aspect="equal") ax.scatter(np.arange(-3, 4), np.zeros(7), s=42, facecolor="white", edgecolor="black", zorder=5, label="полюса (целые числа)") ax.scatter(np.arange(-3, 3) + 0.5, np.zeros(6), s=42, marker="s", facecolor="black", edgecolor="white", zorder=5, label="нули (полуцелые)") ax.set_xlabel("Re z"); ax.set_ylabel("Im z") ax.set_title("Фазовый портрет π·ctg(πz): цвет — аргумент, " "линии — уровни модуля") ax.legend(loc="upper right", framealpha=0.9, fontsize=10) fig.savefig(f"{OUT}/02_cot_phase_portrait.png", dpi=160) plt.close(fig) # ---------------------------------------------------------------------- # 3. Контрчлены: расходимость против сходимости # ---------------------------------------------------------------------- def fig_counterterms(): z = 0.5 Nmax = 200_000 n = np.arange(1, Nmax + 1, dtype=float) A = np.cumsum(1.0 / (z - n)) # без поправки B = np.cumsum(1.0 / (z - n) + 1.0 / n) # с поправкой +1/n idx = np.unique(np.logspace(0, np.log10(Nmax), 1200).astype(int)) - 1 fig, ax = plt.subplots(figsize=(10.5, 5.4)) ax.semilogx(n[idx], A[idx], color="#c0392b", lw=2.2, label=r"$\sum_{n\leq N} \frac{1}{z-n}$ — без поправки") ax.semilogx(n[idx], B[idx], color="#1a7a4a", lw=2.2, label=r"$\sum_{n\leq N}\left(\frac{1}{z-n}+\frac{1}{n}\right)$ — с контрчленом") ax.semilogx(n[idx], -np.log(n[idx]) - 1.9635, "--", color="#888888", lw=1.4, label=r"$-\ln N + \mathrm{const}$") ax.axhline(-2 * np.log(2), color="#1a7a4a", ls=":", lw=1.4) ax.annotate(r"предел $= -2\ln 2 \approx -1.386$", xy=(3e3, -2 * np.log(2)), xytext=(2.2e2, -0.55), arrowprops=dict(arrowstyle="->", color="#1a7a4a"), color="#1a7a4a", fontsize=12) ax.annotate("уходит в $-\\infty$\nкак гармонический ряд", xy=(6e4, A[idx][-1] + 0.35), xytext=(2.5e3, -8.6), arrowprops=dict(arrowstyle="->", color="#c0392b"), color="#c0392b", fontsize=12) ax.set_xlabel("число слагаемых N") ax.set_ylabel("частичная сумма (z = 1/2)") ax.set_title("Зачем в формуле поправка +1/zⱼ: одна и та же сумма " "без контрчлена и с ним") ax.grid(alpha=0.3) ax.legend(loc="lower left", fontsize=11) fig.savefig(f"{OUT}/03_counterterms.png", dpi=160) plt.close(fig) # ---------------------------------------------------------------------- # Вспомогательное: линии поля для комплексного f = Ex - i*Ey # ---------------------------------------------------------------------- def stream_of(ax, f, lim=1.5, n=420, rmask=0.09, density=1.5, poles=((0, 0),)): x = np.linspace(-lim, lim, n) y = np.linspace(-lim, lim, n) X, Y = np.meshgrid(x, y) Z = X + 1j * Y with np.errstate(all="ignore"): F = f(Z) Ex, Ey = np.real(F), -np.imag(F) mask = np.zeros_like(X, dtype=bool) for (px, py) in poles: mask |= (X - px) ** 2 + (Y - py) ** 2 < rmask ** 2 speed = np.hypot(Ex, Ey) Exm = np.ma.array(Ex, mask=mask) Eym = np.ma.array(Ey, mask=mask) col = np.ma.array(np.log10(speed + 1e-12), mask=mask) ax.streamplot(X, Y, Exm, Eym, color=col, cmap="magma", density=density, linewidth=1.0, arrowsize=1.1) ax.set_xlim(-lim, lim); ax.set_ylim(-lim, lim) ax.set_aspect("equal") ax.set_xticks([]); ax.set_yticks([]) def fig_multipoles(): fig, axes = plt.subplots(1, 3, figsize=(12.6, 4.5)) cases = [ (lambda z: 1.0 / z, "заряд: f ~ 1/z\n(спин 0 — скаляр)"), (lambda z: 1.0 / z ** 2, "диполь: f ~ 1/z²\n(спин 1 — вектор)"), (lambda z: 1.0 / z ** 3, "квадруполь: f ~ 1/z³\n(спин 2)"), ] for ax, (f, title) in zip(axes, cases): stream_of(ax, f, lim=1.4, rmask=0.10, density=1.35) ax.plot(0, 0, "o", ms=9, mfc="white", mec="black", zorder=6) ax.set_title(title, fontsize=12.5) fig.suptitle("Плоские мультиполи = кратные полюса: линии поля " "E_x − iE_y = f(z)", y=1.02, fontsize=14) fig.savefig(f"{OUT}/04_multipoles.png", dpi=160) plt.close(fig) # ---------------------------------------------------------------------- # 5. Цепочка зарядов против цепочки диполей # ---------------------------------------------------------------------- def fig_chains(): fig, axes = plt.subplots(1, 2, figsize=(12.6, 4.9)) # (а) цепочка единичных зарядов: f = pi*ctg(pi z) ax = axes[0] lim_x, lim_y, n = 2.35, 1.7, 460 x = np.linspace(-lim_x, lim_x, n) y = np.linspace(-lim_y, lim_y, n) X, Y = np.meshgrid(x, y) Z = X + 1j * Y with np.errstate(all="ignore"): F = np.pi / np.tan(np.pi * Z) Ex, Ey = np.real(F), -np.imag(F) mask = np.zeros_like(X, dtype=bool) for p in range(-2, 3): mask |= (X - p) ** 2 + Y ** 2 < 0.085 ** 2 col = np.ma.array(np.log10(np.hypot(Ex, Ey) + 1e-12), mask=mask) ax.streamplot(X, Y, np.ma.array(Ex, mask=mask), np.ma.array(Ey, mask=mask), color=col, cmap="magma", density=1.5, linewidth=1.0, arrowsize=1.1) ax.plot(range(-2, 3), [0] * 5, "o", ms=8, mfc="white", mec="black", zorder=6) ax.set_title("Σ 1/(z−n): цепочка зарядов.\nВдали поле однородно — " "как у заряженной прямой", fontsize=12) ax.set_xlim(-lim_x, lim_x); ax.set_ylim(-lim_y, lim_y) ax.set_aspect("equal"); ax.set_xticks([]); ax.set_yticks([]) # (б) цепочка диполей: f = pi^2 / sin^2(pi z) ax = axes[1] with np.errstate(all="ignore"): F = (np.pi / np.sin(np.pi * Z)) ** 2 Ex, Ey = np.real(F), -np.imag(F) col = np.ma.array(np.log10(np.hypot(Ex, Ey) + 1e-12), mask=mask) ax.streamplot(X, Y, np.ma.array(Ex, mask=mask), np.ma.array(Ey, mask=mask), color=col, cmap="magma", density=1.5, linewidth=1.0, arrowsize=1.1) ax.plot(range(-2, 3), [0] * 5, "o", ms=8, mfc="white", mec="black", zorder=6) ax.set_title("Σ 1/(z−n)²: цепочка диполей.\nВдали поле затухает " "экспоненциально", fontsize=12) ax.set_xlim(-lim_x, lim_x); ax.set_ylim(-lim_y, lim_y) ax.set_aspect("equal"); ax.set_xticks([]); ax.set_yticks([]) fig.savefig(f"{OUT}/05_chains_field.png", dpi=160) plt.close(fig) # ---------------------------------------------------------------------- # 6. Контур доказательства # ---------------------------------------------------------------------- def fig_contour(): fig, ax = plt.subplots(figsize=(8.6, 7.2)) R = 3.0 t = np.linspace(0, 2 * np.pi, 400) ax.plot(R * np.cos(t), R * np.sin(t), color="#2c3e7a", lw=2.2) # стрелка направления обхода ax.annotate("", xy=(R * np.cos(1.62), R * np.sin(1.62)), xytext=(R * np.cos(1.52), R * np.sin(1.52)), arrowprops=dict(arrowstyle="-|>", color="#2c3e7a", lw=2)) ax.text(0.15, R + 0.18, "|w| = R → ∞", color="#2c3e7a", fontsize=13) # радиус ax.plot([0, R * np.cos(-0.42)], [0, R * np.sin(-0.42)], color="#999999", lw=1, ls="--") ax.text(1.45, -0.83, "R", color="#777777", fontsize=13) # подложенные полюса ax.plot(0, 0, "s", ms=10, color="#1a6fb5", zorder=5) ax.annotate("w = 0 → даёт −f(0)/z", xy=(0, 0), xytext=(-2.9, 0.42), fontsize=12, color="#1a6fb5", arrowprops=dict(arrowstyle="->", color="#1a6fb5")) zx, zy = 1.05, 0.85 ax.plot(zx, zy, "o", ms=10, color="#1a7a4a", zorder=5) ax.annotate("w = z → даёт f(z)/z", xy=(zx, zy), xytext=(0.35, 2.2), fontsize=12, color="#1a7a4a", arrowprops=dict(arrowstyle="->", color="#1a7a4a")) # полюса самой функции poles = [(-1.7, 0.95), (0.75, -1.45), (-0.95, -0.7), (2.05, -0.35), (-2.2, -1.5)] for i, (px, py) in enumerate(poles, 1): ax.plot(px, py, "x", ms=11, mew=2.6, color="#c0392b", zorder=5) ax.text(px + 0.10, py + 0.10, f"$z_{i}$", color="#c0392b", fontsize=12) ax.text(-2.75, -2.35, "полюса f → дают Σ bⱼ·(…)", color="#c0392b", fontsize=12) # полюс за контуром ax.plot(3.45, 1.6, "x", ms=11, mew=2.6, color="#e0a5a0", zorder=5) ax.text(2.62, 1.95, "подождёт\nбольшего R", color="#b08681", fontsize=10.5) ax.axhline(0, color="#cccccc", lw=0.8) ax.axvline(0, color="#cccccc", lw=0.8) ax.set_xlim(-4.2, 4.2); ax.set_ylim(-3.4, 3.6) ax.set_aspect("equal") ax.set_xticks([]); ax.set_yticks([]) ax.set_title(r"Идея доказательства: $\oint_{|w|=R}\frac{f(w)}{w\,(w-z)}\,dw$" " считаем двумя способами", fontsize=13) fig.savefig(f"{OUT}/06_proof_contour.png", dpi=160) plt.close(fig) # ---------------------------------------------------------------------- # 7. Базельские частичные суммы # ---------------------------------------------------------------------- def fig_basel(): N = 30_000 n = np.arange(1, N + 1, dtype=float) S_all = np.cumsum(1.0 / n ** 2) odd = np.arange(1, N, 2, dtype=float) S_odd = np.cumsum(1.0 / odd ** 2) idx = np.unique(np.logspace(0, np.log10(N), 900).astype(int)) - 1 iodd = np.unique(np.logspace(0, np.log10(len(odd)), 900).astype(int)) - 1 fig, ax = plt.subplots(figsize=(10.5, 5.2)) ax.semilogx(n[idx], S_all[idx], color="#1a6fb5", lw=2.2, label=r"$\sum 1/n^2$ — из котангенса") ax.semilogx(odd[iodd], S_odd[iodd], color="#b06c00", lw=2.2, label=r"$\sum 1/(2k+1)^2$ — из тангенса") ax.axhline(np.pi ** 2 / 6, color="#1a6fb5", ls="--", lw=1.3) ax.axhline(np.pi ** 2 / 8, color="#b06c00", ls="--", lw=1.3) ax.text(1.25, np.pi ** 2 / 6 + 0.03, r"$\pi^2/6 \approx 1.6449$", color="#1a6fb5", fontsize=13) ax.text(1.25, np.pi ** 2 / 8 + 0.03, r"$\pi^2/8 \approx 1.2337$", color="#b06c00", fontsize=13) ax.set_xlabel("N"); ax.set_ylabel("частичная сумма") ax.set_title("Две суммы, которые «выпадают» из теоремы Миттаг-Леффлера") ax.grid(alpha=0.3) ax.legend(loc="lower right", fontsize=11.5) ax.set_ylim(0.95, 1.75) fig.savefig(f"{OUT}/07_basel_sums.png", dpi=160) plt.close(fig) if __name__ == "__main__": fig_gamma(); print("01 gamma ok") fig_phase(); print("02 phase ok") fig_counterterms();print("03 counterterms ok") fig_multipoles(); print("04 multipoles ok") fig_chains(); print("05 chains ok") fig_contour(); print("06 contour ok") fig_basel(); print("07 basel ok") print("done ->", OUT)

