Русский
Русский
English
Статистика
Реклама

Математика

Ultimate Guide по карьере в AI как выбрать специальность, прокачаться и найти классную работу

26.09.2020 12:23:55 | Автор: admin


3 августа в наших соцсетях выступал Сергей Ширкин, специалист по ML и искусственному интеллекту.

Сергей занимался автоматизацией финансовых технологий и базами данных в Сбербанке и Росбанке, построением финансовых моделей на основе машинного обучения и аналитической деятельностью в компании Equifax. Прогнозирует телесмотрение с применением методов искусственного интеллекта в Dentsu Aegis Network Russia. Приглашённый преподаватель ВШЭ (магистерская программа Коммуникации, основанные на данных).

Также Сергей исследует квантовые вычисления в приложении к ИИ и машинному обучению. Он стоит у истоков факультетов Искусственного интеллекта, Аналитики Big Data и Data Engineering онлайн-университета Geek University, на которых работает деканом и преподавателем.

Делимся с вами расшифровкой эфира и записью.

***

Меня зовут Сергей Ширкин, сегодня мы поговорим об искусственном интеллекте. Обсудим начальные пути как попасть в искусственный интеллект, как обучиться необходимым предметам, какие курсы пройти, какую литературу читать, как начать карьеру. Также про различные направления.

Сегодняшние темы могут быть интересны не только новичкам, но и опытным программистам например, как перейти из сферы программирования в сферу машинного обучения, искусственного интеллекта, нейронных сетей. В зависимости от того, в какой технологии человек занимается и какие языки изучает, практичный переход в эту сферу может проходить по-разному. Специальностей в ИИ очень много.

Порекомендуйте материалы по самостоятельному погружению в ИИ?


Если вы совсем новичок, то лучше начать с изучения Python. Быстрый способ для этого, как я видел на примере других новичков это сайт PythonTutor.ru. Там надо изучить теорию и порешать задачи хотя бы процентов 70. Задачи могут показаться сложными, если вы совсем не программировали до этого.

Следующий шаг язык запросов SQL, и здесь поможет сайт SQL-EX.ru: там есть упражнения по SQL. Они организованы по этапам: обучающий этап, этап для получения рейтинга можно занять определенное место в рейтинге. Здесь вы научитесь работать с базами данных. Параллельно там есть обучающие материалы от автора Моисеенко, и их довольно легко изучить.

Потом потребуется изучить само машинное обучение. Различные алгоритмы, начиная с линейной регрессии, логистической регрессии, вплоть до градиентного бустинга. Здесь очень много материалов. Потом можно перейти к нейронным сетям для компьютерного зрения, для NLP; вы изучите сверточные, рекуррентные нейронные сети, и самые современные трансформеры, Берт и т.д.

Расскажу про развитие ИИ. Если посмотреть на историю этого развития до 2010, то она достаточно скудна: были, конечно, некоторые великие свершения в ИИ и в смежных областях в больших данных, например, и были готовы многие математические алгоритмы. Но для ИИ было недостаточно вычислительной мощности и данных. Начиная с 2010 года скорее, с 2012 пошел бурный рост ИИ. В 2012 году на одном из соревнований нейросеть победила классические алгоритмы машинного зрения и научилась распознавать около 1000 классов изображений.

После этого достижения появился большой отрыв от конкурентов, применявших классическое компьютерное зрение, и началось развитие искусственных нейронных сетей. Появились различные архитектуры сверточных сетей, и в компьютерном зрении случился прорыв. Раньше считалось, что для нейронной сети различить изображение кошки и собаки это очень сложная вещь, но в 2012 году нейросети научились распознавать, классифицировать изображения гораздо быстрее и точнее человека.

Сейчас компьютерное зрение достигло больших успехов. Параллельно развивается обработка естественного языка NLP. С появлением модели GPT-3, которые создала компания OpenAI пару месяцев назад, нейросеть научилась генерировать текст (а также музыку и другие последовательности). Это один из важных шагов в NLP скорее всего, в этом десятилетии она достигнет расцвета. Появятся чат-боты, которые смогут полноценно поддерживать диалог с человеком.

SQL и Python немного. После курсов по data science, без опыта, можно сразу устроиться data scientist, или сначала надо поработать аналитиком БД?


Сейчас попасть в data science сложнее, чем 5 лет назад. Тогда можно было принять участие в каком-нибудь конкурсе на Kaggle и занять место не обязательно самое первое, например, в первых 10% в каком-нибудь интересном соревновании, не обучающего уровня. После этого можно было уже ходить по компаниям, отвечать на несложные вопросы по машинному обучению, и такого человека могли взять. Специалистов было мало.

Сейчас все гораздо сложнее, поэтому иногда не получается сразу после того, как вы изучили машинное обучение и математику, попасть на вакансию своей мечты специалиста по ИИ или data scientist.

Хороший путь сначала поработать с данными, аналитиком баз данных или аналитиком данных. Факт в том, что вы должны научиться предварительно обрабатывать, чистить данные, применять статистику. Это могут быть технологии БД, Python в том числе. Когда вы наберетесь опыта, появится у вас бэкграунд, то вы сможете, используя свои знания по библиотекам data science в Python Pandas, NumPy, SKLearn, устроиться на вакансию, связанную с ИИ или data science.

В чем отличия специалистов от ИИ от data scientists?


Нужен ли C++ специалисту по ИИ? Что посоветуете изучить для того, чтобы стать спецом по компьютерному зрению?

Сейчас в вакансиях западных компаний появилось разделение: помимо data scientist есть отдельные вакансии для специалистов по ИИ. Раньше подразумевалось, что data scientist это человек, который занимается и анализом табличных данных, и задачами компьютерного зрения, и задачами NLP. Также существовала вакансия аналитика данных она оплачивалась меньше, хотя и была также довольно престижной; такой человек должен был анализировать данные, но не слишком углубляться в ИИ, связанный с речью, текстом и изображениями, работая в основном с табличными данными. Затем произошло смешение вакансий: в Долине всех аналитиков данных стали называть data scientist, включая и тех, кто работает только с табличными данными, и тех, кто работает с NLP и компьютерным зрением. И одновременно, чуть позже, начали выделять отдельно специалиста по ИИ. В российских компаниях обычно такого разделениям нет, хотя иногда бывают специализированные вакансии например, инженер NLP/компьютерного зрения. Data scientistу желательно понемногу уметь все.

Насчет С++: самый основной это Python. То есть, если вы работаете специалистом по ИИ, вы должны использовать TensorFLow, Keras или PyTorch он сейчас на первом месте. Но если вы пишете более низкоуровневые программы например, если вакансия связана с робомобилями, то часто будет требоваться код на C++. Python не всегда быстро работает. Библиотеки для машинного обучения обычно написаны на C++, но иногда нужно всю программу написать на C++: помимо самих моделей может работать логика (if-else и т.д.), и на С++ это работает быстрее. Конечно, сразу приходить на такую вакансию сложно, и лучше сперва поработать на такой, где будет достаточно Python например, там, где идет аналитика соцсетей с анализом изображений, без потребности в быстрой обработке.

Для того, чтобы стать спецом, надо научиться работать с библиотеками для нейронных сетей, изучить библиотеку OpenCV для Python она также есть для C++. Так вы получите инструментарий. Еще желательно уметь работать с библиотекой NumPy, понимать саму математику анализа изображений то есть, разбираться в линейной алгебре и матанализе, также знать архитектуру нейронных сетей. И так далее.

Почему на собеседованиях в ML задают вопросы о том, как разруливать конфликты в hash-таблице?


Почему это маркер при приеме на работу, если это можно загуглить по ходу?

Не в каждой вакансии это спрашивают. Если вы идете на аналитику табличных данных, то вряд ли у вас это спросят. Точно спросят, если вы претендуете на место ML-инженера: то есть, вы не просто создаете модели ML, вы их еще и внедряете, и вам надо знать алгоритмы и структуры данных. А если вы разрабатываете что-то наподобие робомобиля, то тем более: там придется писать код высокого и низкого уровня, и это знание обязательно. А иногда такое знание требуется и в анализе табличных данных допустим, вы пишете модуль для этого на C++.
Если вы пока не готовы на такие вакансии претендовать, можно пройти побольше собеседований. Допустим, если вы пойдете устраиваться data scientistом в банк, то там подобных вопросов будет меньше.

Пишу на Python 10 лет, но без высшего образования. Насколько сложно входить в сферу ИИ без вышмата?


Высшая математика нужна. Придется пройти курсы или изучить литературу, и это будет долгий процесс. Вам будет нужна подготовка по линейной алгебре, математическому анализу, теории вероятности и математической статистике. Обычной школьной программы явно не хватит для того, чтобы заниматься ИИ; конечно, программы бывают разные в некоторых школах и в 10-м классе проходятся темы из ВУЗов, но такое редко бывает.

Pandas, SKLearn, Catboost, Seaborn, результаты в тренировочных соревнованиях Kaggle 3% и 13%. Нужно ли погружаться в DL, или уже можно искать работу?


По библиотекам уже все хорошо; у вас уже есть Pandas библиотека для работы с табличными данными, и SKLearn модели машинного обучения, и Catboost градиентный бустинг, и Seaborn для визуализации. Результаты 3% и 13% значит, если это не учебные соревнования, то с такими результатами у вас уже должна быть какая-то медаль.

Deep Learning не всегда нужен. Вы можете уже пробовать искать работу, я думаю. Но, если вам нужна именно работа с DL, то нужно еще поучить нейронные сети.

Какой базовый набор книг нужно прочесть?


Я собираюсь показать свои книги в конце стрима. Я выбрал базовый набор, ничего особенно продвинутого.

Насколько сейчас востребованы эти профессии? Будет ли много вакансий через 2 года?


Если вспомнить 2015-16 годы тогда, например, на Headhunter было не больше 5-10 вакансий data scientist. То есть, был практически пустой рынок. Конечно, потом было переименование аналитиков в data scientist, но этого тоже было немного.

Сейчас одномоментно требуется несколько сотен, если смотреть по тому же сайту. Говорят, есть вакансии, которых там нет. Например, на ODS OpenDataScience если посмотреть, есть отдельный раздел вакансий. В целом, пока вакансии не кончаются я думаю, через 2 года их будет только больше. Не только крупные компании этим занимаются: есть стартапы, мелкие компании; data scientistы сейчас требуются и в госучреждениях например, в разных муниципальных департаментах, в налоговой службе и так далее.

В какой отрасли ИИ наиболее востребован?


Самое простое применение ИИ, где его явное применение может автоматизировать большое количество умственной работы специалистов это финансовая сфера. Есть огромное количество банков. Каждому из них нужно, например, оценивать кредитоспособность заемщиков то есть, определять по различных признакам, стоит ли выдавать кредит, не переоценивает ли человек свои силы и сможет ли вернуть кредит. Это самое очевидное использование ИИ.

Потом маркетинг, построение рекламных кампаний: то есть, когда нужно предсказывать, посмотрит ли человек рекламу (в Интернете, на ТВ и т.д.). Это тоже развитое направление, его невозможно не автоматизировать с помощью ИИ. Плюс, сейчас развивается роботизация: есть не только промышленные, но и бытовые роботы робопылесосы и другие принадлежности для дома, которые тоже кто-то разрабатывает. Или различные приложения для смартфона в общем, отраслей много, начиная от промышленности, медицины, ритейла, финансов, маркетинга и заканчивая развлечениями. Например, в играх тоже можно применить ИИ.

Сто ценится больше при приеме на работу в data science: знание математики, понимание работы конкретных алгоритмов, опыт работы?


За плечами техническая магистратура и год работы аналитиком данных в консалтинге.

У вас хороший бэкграунд технический вуз, год работы аналитиком данных. Если вы уже изучили технологии и умеете программировать, то попасть в data science легко. Если вы работали а анализе БД и знаете SQL это большой плюс, а если прибавить программирование и машинное обучение это очень хороший набор.

Расскажу о том, как я строю модели машинного обучения на работе. Компания, в которой я работаю это Dentsu Aegis, очень известная компания, особенно среди тех, кто работает в маркетинге. Это коммуникационная группа, входящая в топ-5 в мире; ее штаб находится в Токио, есть офисы в 145 странах. Российский филиал Dentsu Aegis Network Russia. В России работает 25 лет и выступает пионером медиа-инноваций.

Я расскажу про тот участок, за который отвечаю как data scientist. Это как раз то применение, о котором я рассказывал как о наиболее очевидном в практическом применении. ИИ в маркетинге помогает автоматизировать многие задачи специалистов, и одна из них прогнозирование просмотра различных видов контента различными целевыми аудиториями. Я расскажу подробнее про одну из своих непосредственных задач прогнозирование телесмотрения.

Аудиторий может быть несколько сотен, и для того, чтобы их прогнозировать вручную, требовалась бы работа десятков специалистов. Это непомерно. Очень большое количество данных до миллиардов строк в таблицах. Надо заботиться не только о том, чтобы построить модель машинного обучения, но и о том, чтобы она быстро работала. Для такой работа надо хорошо знать реляционные и нереляционные БД, работать с Linux, иметь навыки devops и вообще разбираться в архитектуре приложения, в IT-инфраструктуре компании, хорошо знать Python, возможно C++.
Когда мы строим прогноз телепросмотров, мы применяем современные методы машинного обучения. Для табличных данных это градиентный бустинг и случайный лес. Если анализируется текст, мы применяем нейронные сети; помимо них тематическое моделирование, TF-IDF и другие распространенные методы NLP.

Мы применяем градиентный бустинг, потому что, если мы прогнозируем с использованием табличных данных, то по работе с такими данными градиентный бустинг опережает все известные алгоритмы. В Kaggle, начиная с 2018 года, все основные достижения в соревнованиях с использованием табличных данных достигались именно с помощью градиентного бустинга. Большинство кегглеров тогда перешло на XGBoost это была первая известная библиотека для градиентного бустинга, а позже многие освоили LightGBM от Microsoft или CatBoost от Yandex. Для задачи прогноза просмотров телепередач также хорошо подходит применение временных рядов, но такие методы не всегда хорошо работают периодически появляются неожиданные события, на которые нужно вовремя реагировать или предвосхищать. Иногда встречаются большие аномальные периоды от нескольких дней до месяцев: например, ЧМ по футболу в 2018 году сильно повлиял на просмотры. Карантин тоже стал аномальным периодом: люди начали проводить больше времени дома и больше смотреть ТВ. Это тоже надо как-то учитывать, предвосхищать. Вообще, этот период это своеобразный вызов для машинного обучения и ИИ, потому что нужно постоянно осуществлять мониторинг моделей и контролировать их, чтобы они работали корректно. Помимо аномальных периодов на прогноз оказывают влияние праздники, погодные условия, изменения трендов в просмотрах конкретных передач и каналов. В итоге модели получаются достаточно сложными, потому что надо учесть все возможные варианты, учесть или предвосхитить аномалии и отклонения.

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

Один из самых объемных по времени этапов работы с данными это feature engineering: обработка или создание признаков. Для этой части data science нужен большой опыт: заранее известных рецептов либо нет, либо они слишком просты, и способы подготовки признаков приходится придумывать на ходу.

Иногда в данных встречаются курьезы: допустим, зритель включает ТВ вечером и засыпает. Получается так, как будто он всю ночь смотрел передачи. Это один из примеров шума в данных вроде бы данные точные, а вроде бы и нет, и это нужно учиться угадывать, хотя это и сложно. Кроме того, ночью обычно показывается очень мало рекламы.

Когда мы строим модель, надо не просто сделать так, чтобы она работала, но и обеспечить тестирование и мониторинг. Для этого нам нужны метрики. Так как у нас задача регрессии, наш набор метрик будет отличаться от набора для классификации, например. Это среднеквадратичная ошибка и коэффициент детерминации они все очень важны. Также есть метрики, которое приходится создавать самостоятельно для решения определенной бизнес-задачи например, задачи оптимизации расходов на рекламную кампанию. В таком случае нам надо предсказывать не только телевизионный рейтинг, но и охват рекламной кампании; у нас работает не только машинное обучение, но и сложные статистические и эконометрические методы. Это тот случай, когда знаний машинного обучения оказывается недостаточно: здесь нужен матанализ, линейная алгебра, методы математической оптимизации. В отличие от обычных задач машинного обучения регрессии, классификации, кластеризации здесь приходится придумывать собственные методы, и одного программирования не хватит.

Хочу упомянуть программу Высшей школы экономики Коммуникации, основанные на данных. Приходилось по ходу работы помогать студентам на этой программе, они занимаются маркетингом и предметами, связанными с машинным обучением. Собственно, для чего машинное обучение и data science маркетологу? Раньше не предполагалось, что специалист такой области будет программировать и делать сложные модели, но сейчас это навык, который дает преимущества на рынке труда. Если специалист, дополнительно к своей профессии, овладел data science, то он получает возможность либо поменять работу и стать data scientistом, либо продолжить развиваться в своей предметной сфере, но с большими конкурентными преимуществами. Знаток машинного обучения сможет делать более точные прогнозы, но для этого потребуется многое изучить.

Стоит ли обратить внимание на курс Data Science МФТИ/Yandex, или, возможно, посмотреть в сторону Udacity?


Я так понимаю, вы имеете ввиду курс от МФТИ/Yandex на Coursera. Udacity это отдельная площадка для обучения; там не только data science, хотя для ИИ и data science и предназначена довольно большая часть курсов. Я рекомендую не зацикливаться на одном ресурсе, а попробовать пройти несколько курсов. Курсы не совпадают на 100%, вы всегда можете найти что-то новое, чего раньше не знали. Также новый курс можно использовать для повторения. Например, курсы на GeekBrains на наших факультетах ИИ, data engineering и аналитики big data. Так как я являюсь их деканом и преподавателем, я могу более подробно рассказать о них.

Курсы объединяются в факультеты например, на факультете искусственного интеллекта есть 17 курсов, плюс 8 дополнительных. Почти в каждом курсе есть практическая работа как финальный проект. Таким образом, у обучающегося на нем специалиста появляется практика. Я рекомендую не просто изучать теорию, а делать проекты: хорошие практические умения приблизят вас к прохождению собеседований и началу карьеры.

Я сам обучался некоторое время назад на Udacity проходил курс по робомобилям, очень длинный, запланировано было 9 месяцев, но курс длился около года. Действительно узнал много нового, впечатления от платформы положительные. Но, конечно, все курсы там преподаются на английском.

Как учесть аномалии по временным рядам и можно ли их вырезать?


Неприятный процесс. Для этого нет готового рецепта нужно огромное количество тестов. Точнее, есть готовые модели, но они рассчитаны только на обнаружение аномалий в прошлом, а их нужно не только обнаруживать, но и предвосхищать.

Для таких случаев есть различные наработки, но их приходится создавать самостоятельно. Самое важное определить, что будет в будущем: например, увеличение телесмотрения по определенным каналам и передачам. Когда проходит время, эти данные попадают в прошлое к тренировочным данным, и их нужно обработать правильным образом.

Если даже в будущем не будет аномалий, прошлые аномалии могут повлиять на ваш прогноз. Здесь есть много методов; самый простой удаление аномальных данных, но, если их много, это может привести к выпадению целого периода временного ряда из рассмотрения, поэтому такой способ не всегда подходит.

Каким образом устроиться на работу без подтвержденного опыта?


Хороший опыт это ваши проекты. То есть, если вы не просто учите теорию, а сразу делаете проект желательно, под руководством ментора (человека с опытом в data science и ИИ) то вы знаете, что вы делаете. Вы не просто умеете применять теорию, или применять модель к данным, найденным в интернете, а решать практические задачи. При работе над такими проектами вы получаете знания, которые нельзя получить из книг и курсов, и помощь ментора здесь неоценима.

Поговорим о книгах я приготовил небольшую стопку.

Если вы работаете в data science, то, скорее всего, вам придется работать в среде Linux. При этом вы не будете администратором то есть, слишком глубокие знания вам не понадобятся но уверенное знание этой платформы для простых административных задач (планирование запуска скриптов или распоряжение ресурсами ОС) будет обязательно. Здесь поможет книга LINUX карманный справочник Скотта Граннемана. Его можно изучить за пару дней.

По теории вероятностей я бы порекомендовал книгу Г. Г. Битнера Теория вероятностей в ней есть и теория, и задачи. Теория вероятностей пригодится вам и на собеседовании, и в работе.
Любому, кто работает в IT, требуется минимальный набор знаний и навыков. Соответственно, книга Теоретический минимум по Computer Science все, что нужно знать программисту и разработчику (Фило Владстон Феррейра) это ликбез по computer science.

Если вы будете погружаться в программирование и низкоуровневые разработки, то вам будут нужны алгоритмы. В книге Алгоритмы для начинающих теория и практика для разработчика Паноса Луридаса даются алгоритмы без привязки к конкретному языку. Есть более объемная книга для C++ Алгоритмы на C++ Роберта Седжвика; она полезна, если вы хотите исключить какие-то высокоуровневые операции, которые есть в Python, и создавать алгоритмы с нуля.

Если вы хотите получить общее представление о верхнеуровневой работе специалиста по data science, то вам подойдет книга Работа с данными в любой сфере как выйти на новый уровень, используя аналитику Кирилла Еременко. Здесь нет программирования. Но, если вы уже специалист, она пригодится вам только в том случае, если вы еще не работали с данными.
Далее: Data Science. Наука о данных с нуля Джоэля Граса тоже полезная книга. От того же издания Практическая статистика для специалистов Data Science. 50 важнейших понятий Питера Брюса и Эндрю Брюса. Здесь тоже можно изучить статистику.

Если вы будете работать с данными в Python и использовать библиотеку Pandas, то вам обязательно нужна Python и анализ данных Уэса Маккини автора самой библиотеки Pandas.
По машинному обучению я рекомендую две книги: Машинное обучение Петера Флаха и Python и машинное обучение Себастьяна Рашки.

Для глубокого обучения есть книга Глубокое обучение на Python Франсуа Шолле, там можно изучить нейронные сети для задач NLP и компьютерного зрения. Конкретно по NLP есть Прикладной анализ текстовых данных на Python Бенджамин Бенгфорд, Ребекка Белбро и Тони Охеда.

Если хотите изучить TensorFlow для глубокого обучения есть одноименная книга Бхарата Рамсундара и Реза Босаг Заде.

Также есть книга, в которой просто и понятно объясняются принципы работы нейронных сетей книга Эндрю Траска Грокаем глубокое обучение. Есть также Грокаем алгоритмы там хорошо объясняются алгоритмы, которые могут пригодиться на собеседовании и на практике.

Что вы спрашиваете на собеседованиях?


Есть небольшой сборник вопросов. Есть вопросы по классическому машинному обучению специалист, который устраивается в сферу data science и ИИ, должен знать, как работают классические модели машинного обучения: линейная, логистическая регрессия, градиентный спуск, регуляризация L1-L2. Нужно, чтобы человек рассказал про принцип работы деревьев решений, про критерий информативности для задач классификации и регрессии; чтобы человек знал, как работает случайный лес, градиентный бустинг. Очень хорошо, если он знает отличия моделей градиентного бустинга Catboost, LightGBM, XGBoost то есть, чем отличаются эти библиотеки, как в них реализован градиентный бустинг. Также нужно, чтобы человек владел библиотеками для машинного обучения Pandas, NumPy, SKLearn. Если специалисту нужно будет работать с нейронными сетями, с компьютерным зрением, с NLP, то будут вопросы по этим темам.
Вопросов может быть очень много. Если человек хорошо отвечает, то интересно бывает расспросить его о каких-то его проектах если человек что-то сделал, у собеседующего сразу появляется много вопросов, связанных именно с проектами. Если у вас на GitHub есть личные проекты, или учебные проекты с курсов будет очень хорошо, если вы сумеете подробно рассказать про технологии и алгоритмы, которые вы применяли.

Помимо этого, на собеседовании можно спрашивать разные базовые вопросы. Обычно, если человек хорошо отвечает на них скорее всего, хороший специалист. Конечно, важно, чтобы он сумел выполнить тестовое задание. Теория это одно, а вот то, как человек сможет решить практическую задачу, запрограммировать ее, какой он напишет код это тоже важно. Если человек знает всю теорию, но присылает код, в котором не используется OP, когда это нужно значит, он не знает, как правильно применять теорию. Кроме того, конечно, сам код должен быть читабельным и комментированным.

Я хотел еще рассказать про квантовые вычисления, квантовое машинное обучение это еще одна моя область интересов, но сегодня уже не успею.

Что должно быть написано в резюме, чтобы получить приглашение на собеседование?


Резюме это ответственный момент. Во-первых, оно не должно быть раздутым по объему: в нем должен быть только релевантный опыт. Если вы работали по специальности, не связанной с IT это не нужно. Перечислите свои достижения вкратце, проекты, пройденные курсы, релевантные для вакансии. Вписывайте то, что показывает вас как специалиста, который способен выполнять эту работу. И, конечно, резюме должно быть читабельно.


Что было ранее


  1. Илона Папава, Senior Software Engineer в Facebook как попасть на стажировку, получить оффер и все о работе в компании
  2. Борис Янгель, ML-инженер Яндекса как не пополнить ряды стремных специалистов, если ты Data Scientist
  3. Александр Калошин, СEO LastBackend как запустить стартап, выйти на рынок Китая и получить 15 млн инвестиций.
  4. Наталья Теплухина, Vue.js core team member, GoogleDevExpret как пройти собеседование в GitLab, попасть в команду разработчиков Vue и стать Staff-engineer.
  5. Ашот Оганесян, основатель и технический директор компании DeviceLock кто ворует и зарабатывает на ваших персональных данных.
  6. Сания Галимова, маркетолог RUVDS как жить и работать с психиатрическим диагнозом. Часть 1. Часть 2.
  7. Илья Кашлаков, руководитель фронтенд-отдела Яндекс.Денег как стать тимлидом фронтендеров и как жить после этого.
  8. Влада Рау, Senior Digital Analyst в McKinsey Digital Labs как попасть на стажировку в Google, уйти в консалтинг и переехать в Лондон.
  9. Ричард Левелорд Грей, создатель игр Duke Nukem 3D, SiN, Blood про личную жизнь, любимые игры и о Москве.
  10. Вячеслав Дреер, гейм-дизайнер и продюсер игр с 12-летним стажем про игры, их жизненный цикл и монетизацию
  11. Андрей, технический директор GameAcademy как видеоигры помогают прокачивать реальные навыки и найти работу мечты.
  12. Александр Высоцкий, ведущий PHP-разработчик Badoo как создаются Highload проекты на PHP в Badoo.
  13. Андрей Евсюков, заместитель CTO в Delivery Club про найм 50 синьоров за 43 дня и о том, как оптимизировать фреймворк найма
  14. Джон Ромеро, создатель игр Doom, Quake и Wolfenstein 3D байки о том, как создавался DOOM
  15. Паша Жовнер, создатель тамагочи для хакеров Flipper Zero о своем проекте и другой деятельности
  16. Татьяна Ландо, лингвист-аналитик в Google как научить Google-ассистента человеческому поведению
  17. Путь от джуна до исполнительного директора в Сбербанке. Интервью с Алексеем Левановым
  18. Как Data Science продает вам рекламу? Интервью с инженером Unity



Подробнее..

Исправление кратных ошибок при кодировании сообщений

04.09.2020 20:08:16 | Автор: admin

В информационных системах обмен сообщениями в сетях связи или вычислительных сопровождается возмущающими воздействиями среды или нарушителя, что приводит к появлению искажений сигналов и к ошибкам в символах при цифровой передаче. Борьбу с этим явлением ведут, используя корректирующие коды. Ранее я описывал код Хемминга, и показал как исправляется одиночная ошибка в кодовом слове. Естественно возник вопрос и о ситуациях с большим количеством ошибок. Сегодня рассмотрим случай двух ошибок в кодовом слове (кратную ошибку). С одной стороны, все в теории более менее просто и понятно, но с другой совершенно не очевидно. Изложение материала выполнено на основе работ Э. Берлекемпа.


Теоретические положения


Идея использования организованной избыточности в сообщениях привела Р. Хемминга к построению корректирующего кода описанного здесь. Линейный корректирующий (n, k)-код характеризуется проверочной (mn) матрицей H. Требования к матрице просты: число строк совпадает с числом проверочных символов, ее столбцы должны быть отличны от нулевого и все различны. Более того, значения столбцов описывают номера позиций, занимаемых в кодовом слове символами слова, являющимися элементами конечного поля.

Часто для установления ошибочности переданного слова декодер использует вычисление синдрома, вычисляемого для этого слова. Синдром равен сумме столбцов этой матрицы, умноженных на компоненты вектора ошибки. Если в H имеется m строк и код позволяет исправлять одиночные ошибки, то длина блока (кодового слова) не превышает $ n2^m-1 $. Важна также выполнимость требуемой удаленности кодовых слов друг от друга.

Коды Хемминга достигают этой границы. Каждая позиция кодового слова кода Хемминга может быть занумерована двоичным вектором, совпадающим с соответствующим столбцом матрицы H. При этом синдром будет совпадать непосредственно с номером позиции, в которой произошла ошибка (если она только одна) или с двоичной суммой номеров (если ошибок несколько).
Идея векторной нумерации весьма плодотворна. Далее будем полагать $ n2^m-1 $ и, что i-я позиция слова занумерована числом i.

Нумерацию в двоичном виде,(т.е. такое представление) называют локатором позиции. Допустим, что требуется исправлять все двойные и одиночные ошибки. Видимо, для этого потребуется большая избыточность кода, т.е. матрица H должна иметь больше строк (вдвое большое число). Поэтому будем формировать матрицу H с 2m строками и с $ n2^m-1 $ столбцами, и эти столбцы разумно выбирать различными. В качестве первых m строк будем брать прежнюю матрицу кода Хемминга. Это базисные векторы-слова пространства слов.

Пример 1. Пусть m = 5 и n = 31. Желательно было бы получить (n, k)-код, исправляющий двойные ошибки, с проверочной матрицей Н в виде:

Для обозначенных в матрице функций fj() желательно иметь функцию, которая отображала бы множество 5-мерных векторов в себя. Последние 5 строк матрицы будут задавать код Хемминга тогда и только тогда, когда функция f является биекцией (перестановкой).

Если первые 5 строк и последние 5 строк в отдельности позволят исправлять одиночные ошибки, то возможно совместно они позволят исправлять две ошибки.
Мы должны научиться складывать, вычитать, умножать и делить двоичные 5-ти мерные векторы, представлять их многочленами степени не выше 4-ой, чтобы находить требуемую функцию fj().

Пример 2 .
00000 0
00010 1
00011 х

$11011 х^4 +х^3+х+1$
Сумма и разность таких многочленов соответствует сумме и разности векторов:
0 0 = 0, 0 1 = 1, 1 0 = 1, 1 1 = 0, знаки имеют в двоичном случае совпадающий смысл. Не так с умножением, показатель степени результата умножения может превысить 4.

Пример 3 .
$(х^3+х+1)х^4+х^3+х+1)=х^7+х^6+х^5+3х^4+2х^3+х^2+2х+1=$
$=х^7+х^6+х^5+х^4+х^2+1$.

Необходим метод понижения степеней больше 4.
Он называется (редукцией) построением вычетов по модулю неприводимого многочлена M(x) степени 5; метод состоит в переходе от многочленов произведений к их остаткам от деления на $M(x)=x^5+x^2 + 1$


Так что
$х^7+х^6+х^5+х^4+х^2+1 =(х^2+х + 1)(х^5 +х^2 +1) +х^3 +х^2 +х$ или $х^7+х^6+х^5+х^4+х^2+1 (х^3+х^2 + x)mod(х^5 +х^2 +1).$

Символ читается сравнимо с.
В общем виде A(x) a(x)mod M(x)
Тогда и только тогда, когда существует такой многочлен C(x), что
A(x)= M(x)C(x) +a(x) коэффициенты многочленов приводятся по модулю два:
A(x) a(x)mod(2,M(x)).

Важные свойства сравнений


Если а(x) А(x)mod M(x) и b(x) B(x)mod M(x), то
а(x) b(x) А(x) B(x)mod M(x) и
а(x)b(x) А(x)B(x)mod M(x).

Более того, если степени многочленов а(х) и А(х) меньше степени М(х), то из формулы
а(x) А(x)mod(2,M(x)) следует, что а(x) = А(x).
Различных классов вычетов существует 2 в степени degM(x) т.е. столько, сколько существует различных многочленов степени, меньшей m, т.е. сколько может быть различных остатков при делении. С делением еще больше сложностей.

Алгоритм деления


Для чисел.
Для данных a и M существуют однозначно определенные числа q и A, такие, что а =qM + A, 0 A M,
Для многочленов с коэффициентами из данного поля.
Для данных a(x) и M(x) существуют однозначно определенные многочлены q(x) и A(x), такие, что a(x) = q(x)M(x) + A(x), degA(x) <deg M(x).
Возможность деления многочленов обеспечивается алгоритмом Евклида.

Для чисел пример расширенного НОД описан здесь
Для заданных a и b существуют такие числа A и B, что aA +bB = (a,b), где (a,b) НОД чисел a и b.
Для многочленов с коэффициентами из данного поля.
Для заданных a(x) и b(x) существуют такие многочлены A(x) и B(x), что
a(x)A(x) + b(x)B(x) = (a(x), b(x)),
где (a(x), b(x)) нормированный общий делитель a(x) и b(x) наибольшей степени.
Если а(х) и М(х) имеют общий делитель d(x) 1, то деление на a(x) по mod M(x) не всегда возможно.

Очевидно, что деление на a(x) эквивалентно умножению на A(x).
Так как если (a(x), b(x))= 1 =НОД, то согласно алгоритму Евклида, существуют такие A(x) и B(x), что a(x)A(x) + b(x)B(x) = 1, так, что a(x)A(x) 1mod b(x). Проверка того, что двоичный многочлен является неприводимым над полем GF ($2^5$), выполняется непосредственным делением на всевозможные делители со степенями, меньшими, чем deg M(x).

Пример 4. $M(x) = x^5+x^2 +1$ делим на х и на (х + 1)
на линейные делители. Результат деления не нуль. Делим на квадратные делители $х^2 , х^2 +х=х(х +1),х^2 +1, х^2 +2х +1= (х +1)^2, х^2 + х + 1.$. Они выдают остатки, не равные нулю. Делителей степени 3 не существует, так как их произведение дает степень 6.
Таким образом, многочлены можно складывать, вычитать, умножать и делить по модулю $M(x) = x^5+x^2 +1$.

Переходим к поиску функции для проверочной матрицы H, задающей код с исправлением двойных ошибок с блоковой длиной 31 и скоростью 21/31; 31-21=10 =2t проверочных символов = 10. Такая функция должна иметь своими корнями номера ошибочных позиций в кодовом слове, т.е. при подстановке в эту функцию номеров позиций, обращает ее в нуль.

Поиск функции


Предположим, что 1 и 2 номера искаженных символов (позиций) слова. Используя двоичную запись чисел 1 и 2 можно представить эти номера в виде классов вычетов по модулю M(x) т.е. установить соответствие i (i)(x) двоичные многочлены степени < 5.

Первые 5 проверочных условий определяют 1 + 2; второе множество проверочных уравнений должны определять f(1) + f(2).
Декодер должен определить 1 и 2 по заданной системе:

Какой же должна быть функция f(x)?

Простейшая функция это умножение на константу f() (х)modM(x).
Но тогда 2 = 1, т.е. уравнения системы зависимы. Новая пятерка проверочных условий декодеру не даст ничего нового.
Аналогично и функция f() = + не изменяет ситуацию, так как 2 = 1.
Пробуем степенные функции: сначала возьмем $f() = ^2$. При этом


Эти уравнения также зависимы, так как

$$display$$1^2 =(1 + 2)^2=1^2 + 212 + 2^2 = 1^2 + 2^2 =2$$display$$


Таким образом, второе уравнение является квадратом первого.
Пробуем $f() = ^3$. Уравнения декодера меняют вид:
Откуда $inline$2 =1^3 + 2^3=(1 + 2)(1^2 -12+ 2^2)=1(12-1^2).$inline$

Так что при 10 имеем

Значит, 1 и 2 удовлетворяют уравнению

Таким образом, если произошло точно две ошибки, то их локаторы удовлетворяют этому уравнению.

Так как в поле двоичных многочленов по модулю M(x) данное уравнение имеет точно 2 корня, то декодер всегда сможет найти два нужных локатора.
Если произошла только одна ошибка, то 1=1 и $1^2 = 2$. Следовательно, в этом случае единственная ошибка удовлетворяет уравнению + 1 = 0 или 1+ 1-1= 0.

Наконец, декодер всегда производит декодирование, если ошибок не произошло, то в этом случае 1 + 2 = 0 .
Более удобно (на практике) оперировать не непосредственно с многочленом, корнями которого являются локаторы ошибок, а с многочленом, корни которого взаимны к локаторам; т.е. являются к ним мультипликативным обратными величинами.
Ясно, что при не более чем двух ошибках декодер может определить номера ошибок. Если же искажаются три или более символов, то произойдет ошибка декодирования или отказ от декодирования.
Таким образом, функция $f(x) = x^3$ подходит для построения нижних пяти строк проверочной матрицы Н двоичного кода с длиной кодовых слов 31 и 10-ю проверочными символами, исправляющего все двойные ошибки.
Первые пять проверок задают сумму номеров ошибок (S1); вторые пять проверок задают сумму кубов номеров ошибок (S3).
Процедура декодирования состоит из трех основных шагов:
1. каждое полученное кодовое слово проверяется и вычисляются S1 и S3;
2. находится многочлен локаторов ошибок от (z);
3. вычисляются взаимные величины для корней (z) и изменяются символы в соответствующих позициях полученного слова.
Подробнее..

Самописная криптуха. Vulnerable by design

08.09.2020 12:21:09 | Автор: admin

Автор: Иннокентий Сенновский (rumata888)


Как заинтересовать студента скучным предметом? Придать учебе форму игры. Довольно давно кто-то придумал такую игру в области безопасности Capture the Flag, или CTF. Так ленивым студентам было интереснее изучать, как реверсить программы, куда лучше вставить кавычки и почему проприетарное шифрование это как с разбегу прыгнуть на грабли.


Студенты выросли, и теперь в этих играх участвуют и взрослые прожженные специалисты с детьми и ипотекой. Они многое успели повидать, поэтому сделать таск для CTF, чтобы старики не ныли, не самая простая задача.


А переборщишь с хардкором и взвоют команды, у которых это непрофильная предметная область или первый серьезный CTF.


В статье я расскажу, как наша команда нашла баланс между хм, нечто новенькое и это какая-то жесть, разрабатывая таск по крипте для финала CTFZone в этом году.


Финальный scoreboard CTFZone 2020


Финальный scoreboard CTFZone 2020


Содержание


  1. Необязательное введение: объясняем CTF за 2 минуты
  2. Как мы поняли, что нам нужна крипта
  3. Как мы выбрали стек
  4. Как мы нашли идею для задания
    4.1. А. Что такое Гамильтоновость графов
    4.2. Б. Как идентифицировать себя с помощью Гамильтонова цикла
  5. Как мы построили задание
    5.1. Протокол: вид сверху
    5.2. Протокол: внутренности
    5.3. Последняя уязвимость
  6. Как мы разрабатывали таск
  7. Как мы тестировали таск
  8. Как мы боролись с читерством
  9. Заключение

Необязательное введение: объясняем CTF за 2 минуты


Для тех, кто не знаком с CTF, это соревнования среди специалистов по практической кибербезопасности. Их проводят профильные организации, CTF-команды и университеты.


Различают два вида турниров: jeopardy и attack-defense.


Турниры jeopardy проходят онлайн. Он устроены по формату американской телевикторины Jeopardy!, в российской версии известной как Своя игра. Участникам предлагают разнообразные задания, за выполнение которых начисляют очки. Чем сложнее задание, тем дороже правильный ответ. Выигрывает команда, у которой больше всего очков.


Турниры attack-defense (AD) немного сложнее. Участники соревнуются в предоставленной организатором инфраструктуре, поэтому такие соревнования обычно проводят офлайн например на конференциях. Часто в этом формате проходят финалы: для участия в attack-defense командам нужно попасть в топ-10 или топ-20 по итогам отборочного турнира jeopardy.


На старте соревнования AD команды получают vulnboxes виртуальные машины или хосты с уязвимыми сервисами, которые необходимо защищать. У всех команд vulnboxes одинаковые. Задача участников защитить свои хосты, при этом сохранить их доступность для сервера проверки (checker). То есть нельзя обеспечить безопасность хоста, просто закрыв к нему доступ.


Одновременно нужно атаковать хосты соперников, чтобы захватить флаги уникальные идентификаторы, которые сервер проверки размещает на хостах на каждом раунде. Как правило, раунд длится 5 минут. За захват флага команды получают очки. Если vulnbox не доступен для сервера проверки, очки отнимаются.


Итак, перед каждой командой стоят следующие цели:


  • поддержать функционирование vulnbox;
  • устранять ошибки, чтобы защитить его отатак;
  • атаковать хосты соперников, чтобы захватить флаги.

Если вы когда-либо принимали участие в CTF, то наверняка помните, что задания разделяются на несколько категорий:


  • web,
  • pwn,
  • misc,
  • PPC,
  • forensic,
  • reverse,
  • crypto (то есть криптография, а не криптовалюта).

Бывают и миксы, и задания вне категорий, но вообще этот список скорей описывает задания для турниров jeopardy. У некоторых тасков нет интерактивного компонента, и для AD они плохо подходят. В категории Криптография тоже есть свои нюансы, из-за которых мы долго сомневались, включать ли крипту в финал нашего соревнования CTFZone.


Как мы поняли, что нам нужна крипта


Планируя финал CTFZone, мы обсуждали, есть ли смысл разрабатывать криптографический таск для турнира AD.


Дело в том, что ошибки на каждом уязвимом хосте в рамках AD, как правило, имеют разный уровень сложности. Простые ошибки закладывают, чтобы их смогли обнаружить более слабые команды и порадоваться маленькой победе. Сильным командам такие ошибки позволяют быстрее захватить флаги соперников, и это придает состязанию динамичности.


К сожалению, в случае с криптографией уже на первой ошибке команды могут застрять. Придумывать новые простые криптографические уязвимости довольно сложно. В результате приходится использовать широко известные уязвимости, например повторное использование nonce в ECDSA, которые опытные команды обнаруживают сходу, а новички не могут найти вовсе.


Если исключить простые ошибки, можно сделать еще хуже. Как-то я участвовал в AD-состязании со сложными и интересными криптографическими заданиями, где простых тасков не было совсем. Но проблема была в том, что в каждом раунде захватить флаг удавалось только одной команде: кража по сути стирала флаг. Такая схема разочарует атакующих, которые потратили время на продумывание атаки, а в результате ничего не получили.


Иными словами, криптографические задания довольно часто разочаровывают либо организаторов (потому что команды недостаточно углубляются в задание), либо участников. Поэтому у нас были большие сомнения, стоит ли вообще делать крипту.


Однако вскоре стало понятно, что без криптографического компонента у нас окажется слишком много веба, а в CTF важно разнообразие. Так что выбора не осталось нужно было делать таск.


Таск должен был быть сложным, но одновременно под силу новичкам, но чтобы решение стоило потраченных усилий. Элементарно, не правда ли? :)


Как мы выбрали стек


Как правило, найти и устранить ошибки при выполнении криптозаданий достаточно просто, потому что они чаще всего написаны на скриптовых языках типа Python. Читаешь код, замечаешь ошибку и устраняешь ее.


Мы подумали, что для квалификационного этапа DEF CON это несерьезно, поэтому для нашего стека решили использовать Python + C (звучит убийственно, знаю). Большая часть функциональности была реализована на языке C, тогда как Python обеспечивал удобную обертку для манипуляций с сокетами, а также функциональность сервера.


После того, как мы определились со стеком, надо было понять, какие системы использовать и какие ошибки предусмотреть в этих системах.


Как мы нашли идею для задания


Мы не хотели, чтобы в задании было много уязвимостей, известных любому участнику CTF, поэтому решили остановиться на менее стандартном решении Zero Knowledge Proofs of Knowledge (ZKPoK), то есть протоколе доказательства с нулевым разглашением. Идея заключается в том, чтобы доказать, что вам известна какая-либо секретная информация, не раскрывая эту информацию. Было решено использовать ZKPoK в качестве схемы идентификации: если сервер проверки сможет что-либо доказать, он получает флаг. В основу нашей схемы была положена гамильтоновость графов.


А. Что такое Гамильтоновость графов


Оговорка. Я не математик и не криптограф. Нижеприведенные утверждения могут быть неверными с математической точки зрения и не учитывать все детали. Моя цель рассказать о концепции, не перегружая читателя излишними подробностями.


Итак, допустим, есть некий граф. Цикл, который проходит через каждую вершину этого графа ровно по одному разу, называется Гамильтоновым.


Найти Гамильтонов цикл для графа сложная задача. С другой стороны, создать граф с Гамильтоновым циклом проще простого. Я покажу, как это сделать.




Но сначала немного вводной информации.


Мы решили представить наши графы с помощью матриц смежности. Это квадратные матрицы, в которых две вершины, соединенные ребром, обозначаются единицей в соответствующей ячейке, и нулем, если они не соединяются. Например, имеется 4 вершины: A, B, C, D. A соединена с B, C соединена с D.


Матрица будет выглядеть следующим образом:
Матрица


В зависимости от того, какие задачи вы решаете с помощью графа, диагональ можно заполнить нулями или единицами.


Итак, если вы создаете граф с известным Гамильтоновым циклом, первым делом нужно перемешать массив вершин: например, ABCD BADC. Из этого массива вы определяете циклический порядок следования вершин (контур): BADCB.




Далее вы отображаете эту зависимость при помощи матрицы. На первом шаге все ячейки в матрице заполнены нулями. Затем для каждого направленного ребра графа вы заменяете текущее значение в ячейке на единицу. Ряд этой ячейки определяется индексом начальной вершины этого ребра, а столбец индексом конечной вершины. Получаем следующее:


Матрица


В результате получаем матрицу, у которой в каждом столбце и в каждом ряду по одной единице. Теперь у нас есть Гамильтонов цикл.


На основе полученного цикла мы легко можем создать граф, добавив произвольные ребра. Нам удобнее будет использовать симметричные графы, поэтому для ребра добавляем симметричное ему, если оно еще не существует.


Один из возможных результатов таких действий это следующая матрица смежности:


Матрица смежности


Как видно, каждому ребру (x, y) соответствует (y, x), и я добавил ребро между B и D.


Б. Как идентифицировать себя с помощью Гамильтонова цикла


Схема простая:


  • каким-нибудь другим методом публикуем граф, который содержит Гамильтонов цикл. Он в открытом доступе и виден всем;
  • подключаемся к серверу команды и доказываем, что нам известен цикл, не раскрывая сам цикл.

Часть с доказательством проходит в три этапа, не считая подготовительного, при этом мы принимаем роль Доказывающего (Prover), а сервер выполняет роль Проверяющего (Verifier).


0. Подготовительный этап. Мы переставляем вершины графа в случайном порядке, например: ABCDA. Используя эту перестановку, мы получаем новый граф, который будет изоморфным (т. е. все отношения между элементами сохранятся) по отношению к первому. Это достигается за счет представления перестановки в виде графа и выполнения нескольких операций по умножению и транспонированию (симметричному отображению элементов относительно диагонали) матриц.


Умножение и транспонирование матриц


Далее мы вычисляем Гамильтонов цикл на новом графе, применяя такие же перестановки к матрице инцидентности этого цикла. В качестве результата мы получаем новый граф с Гамильтоновым циклом тоже изоморфный по отношению к исходному.


1. Этап обязательства. Получив новый граф, мы отправляем на сервер так называемое обязательство (commitment). В нашем случае обязательство содержит граф, созданный путем перестановки вершин, и два закрытых контейнера: первый содержит созданную нами перестановку, а второй цикл, полученный в результате перестановки. Проверяющий получает оба контейнера в запечатанном виде, поэтому он не может заглянуть внутрь, но Доказывающий может раскрыть их содержимое в любое время.


Обязательства должны обладать двумя важными свойствами:


  1. скрывать данные. У Проверяющего не должно быть возможности открыть контейнеры без помощи Доказывающего;
  2. носить обязательный характер. Если Доказывающий принял на себя обязательство, у него не должно быть возможности изменить содержимое контейнеров. У него есть только один выбор: оставить крышку открытой или закрытой.

2. Этап вызова. После того как Доказывающий направил свои обязательства, Проверяющий произвольно выбирает бит вызова (challenge) $b \in \{0,1\}$ и отправляет Доказывающему. В ответ на это Доказывающий направляет информацию, в которой открывается или первый (если $b=0$) или второй (если $b=1$) контейнер обязательства.


Случайный бит вызова нужен, чтобы Доказывающий не знал заранее, какой контейнер придется открыть иначе он легко обманет Проверяющего.


3. Этап проверки. Теперь, в зависимости от выбранного бита вызова, Проверяющий может узнать либо перестановку, при помощи которой Доказывающий создал новый граф, либо Гамильтонов цикл на новом графе. То есть Доказывающий убеждает Проверяющего либо в том, что он знает цикл на новом графе, либо в том, что новый граф изоморфен опубликованному.


Вся прелесть состоит в том, что предоставить доказательства для обоих утверждений может только честный Доказывающий, которому действительно известен Гамильтонов цикл на оригинальном графе. Без этих знаний можно лишь пытаться обмануть Проверяющего, угадав, какой бит вызова он сгенерирует.


Последнее можно легко доказать путем моделирования. Представим, что мы как Доказывающий можем обратить часть этапов в протоколе и изменить обязательство уже после получения бита вызова $b$. Зная $b$, мы всегда можем обмануть Проверяющего: если $b=0$, сгенерировать случайную перестановку и изоморфный граф на ее основе, а если $b=1$, сгенерировать граф с новым гамильтоновым циклом. Далее мы принимаем обязательства в отношении первого или второго согласно известному $b$, Проверяющий выпускает такой же бит вызова еще раз и проверяет открытое обязательство. Так как в этом случае доказательства были сгенерированы не на основе гамильтонового цикла на изначальном графе, Проверяющий не может выжать какую-либо информацию из доказательства (в случае с реальным Доказывающим, то есть не в ходе моделирования).


Как вы, наверное, поняли, всегда есть 50-процентная вероятность, что злонамеренный Доказывающий успешно проведет атаку, не имея информации об исходном цикле. Если это выполнить только один раз, о безопасности не будет и речи. Но такая последовательность и не предполагает разового выполнения. Цель состоит в том, чтобы Проверяющий и Доказывающий проделали все этапы несколько раз. При одном повторе вероятность обмана составит 25%, при двух уже 12,5% и т. д. Всегда можно задать количество повторов, которые вписываются в ваши границы риска.


Вот такую систему мы решили внедрить в качестве схемы идентификации для сервера проверки.


P.S. Если вы мало что поняли из сказанного выше или хотите больше узнать о Zero Knowledge, почитайте блог д-ра Мэтью Грина Zero Knowledge Proofs: An illustrated primer. Он объясняет концепцию куда понятнее меня.


Как мы построили задание


Примечание. Далее по тексту сервер команды = Проверяющий, сервер проверки = Доказывающий, атакующая команда = злонамеренный Доказывающий.


Протокол: вид сверху


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


  • граф;
  • флаг для соответствующего раунда;
  • 16-байтовую произвольную строку RANDOMR, полученную с сервера команды;
  • RSA-подпись всех вышеуказанных величин.

Величина RANDOMR была необходима, чтобы команда не могла повторно использовать пакет с валидной подписью для построения такого же графа на серверах других команд.


Кстати, здесь и была заложена первая ошибка: флаг таким способом нельзя было украсть, но можно было произвести DoS-атаку на команду противника. Мы выбрали схему подписи PKCS#1 v1.5. Она в целом не является уязвимой, за исключением некоторых ее реализаций, которые могут быть уязвимы к атаке Блейхенбахера на подпись с экспонентой 3 (Bleichenbacher's e=3 signature attack). Чтобы создать возможности для проведения такой атаки, мы выбрали значение 3 для открытой экспоненты открытого ключа и реализовали уязвимый алгоритм снятия дополнения (очевидно, что SAFE_VERSION macro не был определен):


 uint8_t* badPKCSUnpadHash(uint8_t* pDecryptedSignature, uint32_t dsSize){    uint32_t i;    if (dsSize<MIN_PKCS_SIG_SIZE) return NULL;    if ((pDecryptedSignature[0]!=0)||(pDecryptedSignature[1]!=1))return NULL;    i=2;    while ((i<dsSize) && (pDecryptedSignature[i]==0xff)) i=i+1;    if (i==2 || i>=dsSize) return NULL;    if (pDecryptedSignature[i]!=0) return NULL;    i=i+1;    if ((i>=dsSize)||((dsSize-i)<SHA256_SIZE)) return NULL;    #ifdef SAFE_VERSION    //Check that there are no bytes left, apart from hash itself    //(We presume that the caller did not truncate the signature afteк exponentiation    // and the dsSize is the equal to modulus size in bytes    if ((dsSize-i)!=SHA256_SIZE) return NULL;    #endif    return pDecryptedSignature+i;}

После настройки графа сервер проверки каждые 30 секунд делал следующее:


  • подключался к серверу каждой команды;
  • запрашивал число проверок, которые хочет провести сервер команды;
  • доказывал, что ему точно известен Гамильтонов цикл.

Предполагалось, что в случае верного доказательства сервер команды отдает флаг.


Примечание. Чтобы сократить количество циклов в раунде, мы предусмотрели одновременные, а не последовательные доказательства. Однако стоит принять во внимание, что в реальных условиях это может быть проблематичным, так как такие изменения могут нарушать математические доказательства протоколов.


Протокол: внутренности


Мы рассмотрели общий протокол, а теперь обратимся к уязвимостям, которые мы предусмотрели.


Наш языковой стек Python + C. Мы создали библиотеку C, содержащую 95% функциональности приложения. Затем мы создали классы поддержки на Python для Доказывающего и Проверяющего. Они включали указатели на соответствующие структуры и обертывали вызовы библиотеки. (Кстати, будьте внимательны при работе с void_p в ctypes. В 64-битных системах он может быть урезан до 32 бит при передаче в качестве аргумента в функцию).


Основной скрипт сервера команды на Python содержал инициализацию Проверяющего:


verifier=Verifier(4,4,7)

Аргументы были следующие:


  1. Желательное количество вершин в графе.
  2. Количество одновременных доказательств.
  3. Выбранные алгоритмы обязательств.

Разберем их по порядку.


Число вершин в графе. Как можно догадаться, четырех вершин, как в примерах выше, совсем не достаточно для безопасной конфигурации: все возможные циклы можно с легкостью перебрать.


Что касается максимально разрешенного количества вершин, то мы остановились на 256. Не стали увеличивать его по двум причинам:


  • Во-первых, чтобы установить изоморфизм графов с таким параметром, требовалось 2 часа на обычном ноутбуке (по крайней мере, так показала наша проверка). Вероятность того, что атакующие команды найдут цикл быстрее чем за 5 минут, представлялась при нашей модели злоумышленника как минимум оптимистичной.
  • Во-вторых, зависимость между размером доказательства в байтах и количеством вершин является квадратичной, а нагружать сеть особого желания не было.

Количество одновременных доказательств. Как и в случае с вершинами, четырех параллельных доказательств недостаточно, чтобы говорить о какой-то безопасности. Вероятность правильного угадывания битов доказательства составляет $\frac{1}{16}$. Можно пробовать несколько раз и после ряда попыток все получится.


Максимальное значение, поддерживаемое функцией инициализации, составляло 64, что означало вероятность читерства $\frac{1}{2^{64}}$. При наших времени раунда и мощностях злоумышленника практически никаких шансов.


Итак, вторая ошибка уязвимая конфигурация количества вершин и числа доказательств.


Алгоритмы обязательств. Этот параметр по сути является битовой маской, 3 возведенных бита которой означают, что на сервере команды доступны все три реализованных алгоритма обязательства:


  • обязательства, в основе которых лежат "хеши" CRC32;
  • обязательства, в основе которых лежат хеши SHA-256;
  • обязательства, в основе которых лежит шифрование при помощи AES-128 в режиме CBC.

Правильными значениями для данной битовой маски будут $1-7$, так что командам необходимо выбрать хотя бы один алгоритм. Сервер проверки выбирает их в следующей последовательности: CRC32, SHA-256, AES. Так, если доступны CRC32 и AES, приоритетным будет CRC32.


Прежде чем углубиться в алгоритмы обязательства, давайте вкратце остановимся на реализованном протоколе доказательства:


  1. Доказывающий запрашивает конфигурацию доказательства у Проверяющего (количество доказательств и поддерживаемые алгоритмы).
  2. Проверяющий в ответ направляет конфигурацию.
  3. Доказывающий создает доказательства, обязательства из доказательств и отправляет 1. Доказывающему обязательства по количеству доказательств proof_count.
  4. Проверяющий направляет Доказывающему вызов (случайные биты в количестве proof_count).
  5. В ответ Доказывающий направляет информацию, которая раскрывает обязательства в соответствии с вызовом.
  6. Проверяющий проверяет корректность доказательств и, если все верно, возвращает флаг.
  7. Доказывающий на этом останавливается либо начинает заново с шага 3.

И вот тут есть хитрая деталь. Доказывающий может попытаться начать с любого шага доказательства в любой момент (например, запросить вызов до получения обязательства). В большинстве случаев Проверяющий выдаст ошибку. Однако у всех команд по умолчанию был активирован режим симуляции (simulation mode), и это было третьей ошибкой. Соответствующий бит конфигурации настраивался во время инициализации Проверяющего внутри скомпилированной библиотеки. Для его поиска и замены командам необходимо было произвести обратную разработку скомпилированного бинарного файла. В режиме симуляции, после сохранения обязательства и получения вызова от Проверяющего, Доказывающий мог снова направить обязательство, уже основанное на известном вызове. Это полностью нарушало протокол, но исправить это было очень легко. К сожалению, насколько нам известно, эту ошибку никто не обнаружил


Теперь вернемся к алгоритмам обязательств. Они работают следующим образом.


В случае с обязательствами CRC32 и SHA-256 перестановка, графы с перестановленными вершинами и перестановленные циклические матрицы упаковываются. Упаковывание состоит в том, что размер квадратной матрицы размещается в пределах одного двухбайтного слова (uint16_t), а битовый поток плотно упаковывается в байты таким образом, чтобы каждый байт содержал 8 бит из ячеек исходного представления матрицы. После того, как матрицы упакованы, к каждому упакованному массиву применяется выбранная хеш-функция. В итоге на одно доказательство приходится три хеша:


$Hash(Pack(permutation)) | Hash(Pack(permuted\_graph)) | Hash(Pack(permuted\_cycle))$


Данные хеши далее направляются Проверяющему в качестве обязательства. Как только Проверяющий отправляет бит вызова $b$, в ответ ему направляются исходные упакованные матрицы либо для перестановки и графа с перестановленными вершинами, либо для графа с перестановленными вершинами и перестановочным циклом. Проверяющий может проверить, что хеши соответствуют полученным значениям, а значения удовлетворяют условиям обязательства. Если этап успешно завершается, Проверяющий удостоверяется, что матрица отвечает требованиям к доказательству.


Если это обязательство AES, Доказывающий создает два ключа: $K_1$ и $K_2$. Он упаковывает матрицы тем же способом, как и в случае с другими обязательствами, но затем он шифрует упакованную перестановку при помощи $K_1$ и упакованный перестановочный цикл при помощи $K_2$. Он отправляет оба шифротекста и упакованный граф с перестановленными вершинами в виде открытого текста:


$Enc(Pack(permutation),K_1) | Enc(Pack(permuted\_cycle),K_2) | Pack(permuted\_graph)$


После получения бита вызова $b$ от Проверяющего Доказывающий направляет ему $K_b$. Проверяющий дешифрует соответствующий зашифрованный текст и проверяет достоверность доказательства.


Как видите, обязательство, основанное на AES, успешно скрывает доказательство и не дает Доказывающему себя нарушить (невозможно получить перестановку и перестановленный цикл без ключей, и Доказывающий не может найти другой ключ, который бы позволил ему обмануть Проверяющего). Обязательство, основанное на SHA-256, также носит обязательный характер с точки зрения расчетов (нужно обнаружить коллизию), а так как мы по факту направляем не сами матрицы в каком-либо виде или форме, они действительно удовлетворяют свойству скрытия.


CRC32, однако, не в полной мере удовлетворяет условиям скрытия, и это четвертая ошибка. Для поиска противоречия можно подобрать CRC32 с ориентировочным уровнем сложности $2^{32}$. Но его можно обнаружить еще быстрее при помощи подхода Meet-in-the-Middle (Встреча посередине), ориентировочный уровень сложности которого составляет $2^{17}$.
Обновление: Как мне подсказали, можно еще быстрее. Пример здесь. Но узнать про MitM всё равно полезно, т.к. этот метод можно применить и в других местах.


Атака Meet-in-the-Middle может быть использована, потому что можно обратить раунды CRC32. Допустим, у нас есть


$y=CRC32(x_0)$


и надо найти некое $x_1$ таким образом, чтобы


$CRC32(x_1)=y$


Давайте зафиксируем длину искомого $x_1$, пусть она будет 6 байтов (позже это нам пригодится). CRC32 состоит из трех фаз:


  1. Инициализация $Init$.
  2. Цикл прохода по байтам сообщения $t_{i+1}=Round(t_i,b_i)$, где ($t_i$ внутреннее состояние, а $b_i$ байт).
  3. Постпроцессинг $Finish$.

Итак, для 6 байтов:


$CRC32_6(x)=Finish(Round(Round(Round(Round(Round(Round(Init()\\,b_1),b_2),b_3),b_4),b_5),b_6))$


или произведение функций от $t$:
Произведение функций от t
Теперь можно разбить $CRC32_6$ на две части:


$CRC32_6=CRC32_{FH}CRC32_{SH}$


где


$CRC32_{FH}=InitRound_{b_1}Round_{b_2}Round_{b_3}$


$CRC32_{SH}=Round_{b_4}Round_{b_5}Round_{b_6}Finish$


Мы разделили CRC32 пополам. Более того, функции $Round_{b_i}$ и $Finish$ являются обратимыми, что делает $CRC32_{SH}$ тоже обратимой:


$CRC32_{SH\_INV}=CRC32^{1}_{SH}$


Теперь вместо подбора $CRC32_6$ мы:


  1. Рассчитываем около $2^{17}$ значений $CRC32_{FH}$ с различными $b_1b_2b_3$ и помещаем эти значения в хеш-таблицу, чтобы можно было посмотреть результаты $b_1b_2b_3$ для каждого значения за константное время.
  2. Рассчитываем значения $CRC32_{SH\_INV}(y)$ для различных $b_4b_5b_6$. После расчета каждого значения проверяем, отражено ли оно в хеш-таблице. Как только мы найдем одно из них, прекращаем процесс. Шанс найти одно значение при каждой попытке находится в диапазоне $\frac{1}{2^{16}}-\frac{1}{2^{15}}$.
  3. Получаем значение: $t=CRC32_{FH}(b_1,b_2,b_3)=CRC32_{SH\_INV}(y,b_6,b_5,b_4)$, и это означает, что $y=CRC32(b_1b_2b_3b_4b_5b_6)$, и мы обнаружили коллизию.

Я уже слышу: Все это, конечно, замечательно, но наши данные не должны быть случайными это должна быть одна из тех матриц, и она должна пройти всяческие проверки, так что все это напрасно. Все было бы именно так если бы мы не написали распаковывание матриц таким образом, чтобы оно позволяло добавлять мусорные байты. Вспомните, как мы упаковываем матрицы:


$SIZE (2\space bytes)\space |\space PACKED\_DATA$


Очень важно, что при проверке HASHES Проверяющий хеширует весь этот объект но, когда матрицы распаковываются, алгоритм принимает только биты $size^2$ из $packed\_data$, и все лишнее отбрасывается. Итак, мы можем добавить еще 6 байтов:


$ SIZE (2\space bytes)\space |\space PACKED\_DATA \space|\space e_1\space|\space e_2\space|\space e_3\space|\space e_4\space|\space e_5\space|\space e_6$


И произвести такую же атаку, но теперь $CRC32_{FH}$ будет обрабатывать


$ SIZE (2\space bytes) \space |\space PACKED\_DATA \space|\space e_1\space|\space e_2\space|\space e_3$


тогда как $CRC32_{SH\_INV}$ будет обрабатывать


$e_4\space|\space e_5\space|\space e_6$


После обнаружения противоречий злонамеренный Доказывающий может передать результаты проверки Проверяющего и получить флаг.


Последняя уязвимость


Мы рассказали о работе всей системы и рассмотрели 4 запланированных уязвимости. Однако была еще и пятая ошибка. Биты вызова, предоставляемые Доказывающим, были из небезопасного источника ГПСЧ (PRNG или генератор псевдослучайных чисел).


Обычно в таких случаях выбор падает на небезопасный примитив языка C rand, но нам хотелось чего-то более интересного. В последние годы было опубликовано несколько исследований о Legendre PRF, поэтому мы решили реализовать его в качестве ГПСЧ вместо избитого Вихря Мерсенна.


Идея, положенная в основу этого ГПСЧ, это поле Галуа $GF(p)$, то есть фактор-модулей простого числа $p$. Каждый элемент этого поля либо является квадратичным вычетом, либо нет. Таким образом, в поле $\frac{(p-1)}{2}$ квадратичных вычетов (если исключить 0) и столько же неквадратичных вычетов.


Если вы случайным образом выбираете какой-либо элемент этого поля, за исключением элемента $0$, вероятность того, что он представляет собой квадратичный вычет, будет равна $\frac{1}{2}$. Довольно легко проверить, является ли данный элемент (исключая нулевой $r0$) квадратичным вычетом или нет. Для этого необходимо вычислить его символ Лежандра. Если для элемента $r$ выполняется равенство $r^\frac{p1}{2}=1\space mod \space p$, тогда он является квадратичным вычетом. Так как вероятность для каждого элемента $r$ представлять собой квадратичный вычет составляет 50%, а квадратичные вычеты соседствуют с неквадратичными в случайном порядке, то можно создать искомый ГПСЧ.


Мы инициализируем ГПСЧ с произвольным элементом $a\in GF(p)$. Алгоритм в Python будет выглядеть следующим образом:


def LegendrePRNG(a,p):    if a==0:        a+=1    while True:        next_bit=pow(a,(p-1)//2,p)        if next_bit==1:            yield 1        else:            yield 0        a=(a+1)%p        if a==0:            a+=1

Мы специально выбрали 32-битный $p$, чтобы команды смогли его использовать в атаке Meet-in-the-Middle. Идея заключается в том, чтобы получить $2^{16}+31$ бит из ГПСЧ, направляя постоянные запросы на вызовы. Как только вы их получите, можно конвертировать этот битовый поток в $2^{16}$ 32-битовых целых чисел, преобразуя каждую последовательность из 32 бит внутри потока в целое число. Неважно, какой порядок использовать big-endian или little-endian, главное использовать один и тот же везде.


Поместите эти целые числа в словарь в виде ключей, а значения будут означать их позиции в битовом потоке. Теперь инициализируйте свой собственный экземпляр Legendre PRNG. Выберите $a=1$ и сгенерируйте 32 псевдослучайных бита при помощи ГПСЧ. Преобразуйте эти биты в целое число и проверьте, есть ли оно в словаре (предполагается, что алгоритмическая сложность поиска будет приближаться к константе).


Если его там нет, измените инициализацию на $a=1+2^{16}$ и повторите. Увеличивайте $a$ шагом $2^{16}$ до тех пор, пока не найдете совпадение. Когда вы его обнаружите, то узнаете, каким было значение внутреннего состояния ГПСЧ Проверяющего несколько шагов назад. Обновите $a$ так, чтобы оно соответствовало текущему внутреннему состоянию ГПСЧ и вы успешно клонировали ГПСЧ Проверяющего. Если вы заранее знаете, какие будут вызовы, то обмануть Проверяющего не составит труда.


Возможно, вы подумали, что при нескольких одновременных атаках могут возникнуть проблемы. Мы предвидели это и спроектировали сервис так, что отдельные подключения порождают независимые состояния ГПСЧ. Таким образом, команды не могли влиять на подключения друг друга.


Итак, в сервисе присутствуют следующие уязвимости:


  1. Атака через подделку подписи Bleichenbachers e=3.
  2. Небезопасные параметры Проверяющего.
  3. Включенный режим симуляции.
  4. Обязательство CRC32.
  5. Небезопасный ГПСЧ.

Как мы разрабатывали таск


Писать задание было довольно весело, так как некоторые задачи пришлось решать нестандартно.


Например, на ранних этапах мы решили, что не будем добавлять никаких зависимостей, за исключением стандартных библиотек. Из-за этого нам пришлось использовать Linux Usermode Kernel CryptoAPI для хеширования и шифрования.


Поскольку мы писали библиотеку матриц с нуля, то вскоре столкнулись с проблемами с быстродействием: умножение матриц занимало слишком много времени. Мы не использовали SIMD, и это накладывало ограничения на скорость работы. К счастью, мы быстро заметили, что на самом деле умножение матриц в общем виде нам не нужно: у нас был особый случай. Нам требовалось умножение только для вырожденного случая расчета матриц графов и циклов с переставленными вершинами.


Допустим, имеется граф или циклическая матрица $M$ и матрица перестановки $P$. Чтобы найти перестановленную матрицу $M_p$, нужно произвести следующий расчет: $M_p=P^TMP$. Матрицы перестановки это особый случай. В каждом ряду и каждом столбце матрицы перестановки встречается только одна ячейка со значением $1$, во всех остальных ячейках значение равно $0$. То же самое относится к транспонированной матрице перестановки. Давайте умножим такую матрицу $P$ на $M$, $R=PM$.


Как известно, ряды матрицы умножаются на столбцы. Следовательно, зная, что в каждом ряду и в каждом столбце значение $1$ встречается только один раз, мы можем:


  1. Взять первую строку $P$.
  2. Искать положение $1$, скажем, это будет $j$.
  3. Взять $j$-й ряд $M$ и скопировать в первый ряд $R$.
  4. Повторить эти действия с другими рядами.

Давайте попробуем на примере:


Пример


Сначала мы ищем в первой строке матрицы $P'$ (той, что слева) первую и единственную единицу.


Матрица


Единица находится на второй позиции, считая с нуля. Поэтому берем вторую строку $M'$ и копируем в первую строку итоговой матрицы $R$.


Матрица


Повторяем то же самое со второй строкой $P'$. Ищем единицу.


Матрица


Она на первой позиции, поэтому берем первую строку $M'$ и кладем на место второй строки в итоговой матрице.


Матрица


Так можно легко получить итоговую матрицу.


Итоговая матрица


Поскольку мы знаем, что в каждом ряду значение $1$ встречается только один раз, нам также известно, что только $j$-е значение каждого столбца будет влиять на полученную в результате ячейку. Кроме того, используя memcpy для копирования, мы ускоряем умножение с помощью SIMD, так как memcpy задействует его для более быстрого копирования. Этот метод позволил значительно ускорить вычисления.


Финальная версия сервиса была встроена в докер-контейнер в два этапа. Сначала библиотека Zero Knowledge собиралась отдельно, затем скомпилированная библиотека копировалась в финальную версию контейнера вместе с оберткой на Python, серверным скриптом и открытым ключом сервера проверки в формате PEM. Все символы, за исключением импорта и экспорта, были удалены из библиотеки, чтобы командам пришлось реверсить основную функциональность.


Как мы тестировали таск


Как вы догадываетесь, задание нужно было тщательно протестировать.


Сначала мы запустили непрерывное нагрузочное тестирование серверов команды и проверки на 24 часа, убрав интервалы между запросами, при этом сервер проверки направлял сервису несколько сотен запросов в секунду. Так мы выявили сложную для обнаружения ошибку в использовании CryptoAPI: я забыл закрыть один из сокетов AF_ALG, в результате чего у сервиса быстро закончились доступные дескрипторы файлов.


Мы также протестировали каждую задуманную уязвимость и обнаружили пару проблем в реализации, из-за которых эти уязвимости было невозможно эксплуатировать. В результате нам пришлось вносить изменения в некоторые части приложения за несколько часов до начала соревнования.


Кроме того, мы попросили коллег посмотреть задание, чтобы убедиться в отсутствии непредвиденных ошибок в логике.


Но больше всего мы опасались, что останутся случайные ошибки в скомпилированных бинарных файлах, которые можно было бы использовать в атаках на удаленное исполнение. Нам не хотелось, чтобы без нашего ведома появилось еще одно задание на pwn :)


Вот что мы сделали, чтобы снизить этот риск:


  • Во-первых, мы полностью воссоздали взаимодействие между Доказывающим и Проверяющим на языке C со всеми обязательствами, с наименьшим и наибольшим возможным количеством вершин и доказательств. Тест создавался с использованием ASAN (Address Sanitizer), с помощью которого мы нашли несколько утечек.
  • Во-вторых, мы выписали все интерфейсы, которые будут принимать входные данные из недоверенных источников, будь то на стороне Проверяющего или Доказывающего. Для каждого из этих интерфейсов мы сохранили входные значения, получаемые при взаимодействии сервера проверки с сервером команды. Затем мы написали обвязки под Libfuzzer для каждой точки входа, включая всю необходимую инициализацию и очистку.
    Однако невозможно было так просто подготовить нашу библиотеку к фаззингу: при данном виде тестирования все должно быть по максимуму детерминировано. Поэтому мы заменили получение значений из /dev/urandom вызовами к randrand, а также заменили инициализацию ГПСЧ (не Legendre PRF, а основного) на srand(0). Также было очевидно, что в ходе фаззинга нельзя получить правильный хеш или шифротекст с корректными матрицами. Поэтому мы выключили все хеш-проверки и заменили AES-шифрование простым копированием данных для фаззинга.
    В общей сложности фаззинг длился несколько дней, и за это время мы обнаружили множество ошибок. Но после этого мы чувствовали себя немного уверенней, когда отдавали нашу библиотеку на анализ соревнующимся командам.

Итак, мы сделали все от нас зависящее, чтобы протестировать устойчивость сервиса к непредвиденным атакам. Единственное, что оставалось, предусмотреть меры против читерства: участникам должно быть невыгодно прибегать к заурядным средствам защиты, которые позволили бы устранить ряд уязвимостей легче, чем это было задумано.


Как мы боролись с читерством


Для эксплуатации Legendre PRNG нужно отправлять множество запросов Доказывающему. Нам не хотелось, чтобы командные серверы обрывали соединения в случае нескольких последовательных запросов на получение вызова. Чтобы команды отказались от реализации таких простых и скучных способов митигации, мы разнообразили функциональность сервера проверки.


В начале каждого раунда сервер проверки размещал флаг и матрицу для соответствующего раунда на сервере команды и сразу же запускал экземпляр протокола Proof of Knowledge, чтобы получить флаг обратно. После этого сервер проверки вызывался каждые тридцать секунд для подтверждения, что флаг не поврежден, а поведение сервера не отклоняется от нормы. Для этого мы предусмотрели, чтобы сервер проверки выбирал одну из трех стратегий при каждом вызове:


  1. Простое доказательство. Сервер проверки просто доказывает, что знает цикл. Если что-то идет неправильно, то у команды отнимаются очки SLA (соглашения об уровне сервиса).
  2. Сервер проверки создает валидные обязательства, но перед отправкой на сервер команды повреждает их случайным образом, чтобы Доказывающий счел полученное обязательство ошибочным. Он повторяет цикл неверных доказательств в течение нескольких секунд. Затем он реализует правильное взаимодействие и получает флаг. Если в какой-то момент от сервера команды придет неожиданный ответ или сервер оборвет соединение, то команда теряет очки SLA.
  3. Сервер проверки инициализирует протокол и направляет валидное обязательство. Далее он дает команду Проверяющему на отправку вызова в цикле на протяжении пары секунд. По истечении этого времени он выбирает последний вызов, создает пакет, который раскрывает обязательство, и направляет его на сервер команды. Если сервер отдает флаг, значит, все сделано правильно. За любые отклонения от ожиданий сервера проверки отнимаются очки SLA.

У одной из команд (Bushwhackers) показатель SLA составил только 65%, потому что они провалили вторую стратегию. Кстати, все причины неудач выводились на scoreboard, чтобы команды могли сделать вывод об ошибочности определенных стратегий защиты.


Заключение


Когда я начал писать статью, я не думал, что она настоооооолько затянется. Но мне не хотелось упустить ни одной важной детали.


Составлять задание было увлекательно, и мне приятно осознавать, что оно нас ни разу не подвело за все время соревнований. Как и ожидалось, командам не удалось найти все уязвимости, но они проявили себя весьма достойно. Надеюсь, что решать наше задание участникам понравилось.


Если у вас будет желание, можно собрать и запустить наш таск самостоятельно по ссылке https://github.com/bi-zone/CTFZone-2020-Finals-LittleKnowledge. Мы выложили его в открытый доступ, чтобы любой мог попробовать его решить. Там есть подробные комментарии по всем функциям внутри библиотеки, так что вы можете пойти путем команд (без информации о внутренностях библиотеки) либо посмотреть исходный код. Также доступна примитивная реализация сервера проверки. Учитывая все усилия, надеемся, что этот труд не пропадет. Возможно, кто-нибудь возьмет наш материал на вооружение при подготовке к CTF.


Спасибо, что прочитали до конца, и удачи!

Подробнее..

Код Рида-Соломона

10.09.2020 12:19:55 | Автор: admin

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

Так, например, для определенного Рида-Соломона кода (РС-кода) необходимо установить:
длину n кодового слова (блока);
количество k информационных и N-k проверочных символов;
неприводимый многочлен р(х), задающий конечное поле GF(2r);
примитивный элемент конечного поля;
порождающий многочлен g(x);
параметр j кода;
используемое перемежение;
последовательность передачи кодовых слов или символов в канал и еще некоторые другие.

Здесь в работе рассматривается несколько другая частная задача моделирование собственно РС-кода, являющаяся центральной основной частью названной выше задачи анализа кода.

Описание РС-кода и его характеристик


Для удобства и лучшего уяснения сущности устройства РС-кода и процесса кодирования вначале приведем основные понятия и термины (элементы) кода.
Рида Соломона коды (РС-код) можно интерпретировать как недвоичные коды БЧХ (Боуза Чоудхури Хоквингема), значения кодовых символов которых взяты из поля GF(2r), т. е. r информационных символов отображаются отдельным элементом поля. Коды Рида Соломона это линейные недвоичные систематические циклические коды, символы которых представляют собой r-битовые последовательности, где r целое положительное число, большее 1.

Коды Рида Соломона (n, k) определены на r-битовых символах при всех n и k, для которых:
0 < k < n < 2r + 2, где
k число информационных символов, подлежащих кодированию,
n число кодовых символов в кодируемом блоке.

Для большинства (n, k)-кодов Рида Соломона; (n, k) = (2r1, 2r12t), где
t количество ошибочных символов, которые может исправить код, а
nk = 2t число контрольных символов.

Код Рида Соломона обладает наибольшим минимальным расстоянием (числом символов, которыми отличаются последовательности), возможным для линейного кода. Для кодов Рида Соломона минимальное расстояние определяется следующим образом: dmin = nk +1.

Определение. РС-кодом над полем GF(q=рm), с длиной блока n = qm-1, исправляющим t ошибок, является множество всех кодовых слов u(n) над GF(q), для которых 2t последовательных компонентов спектра с номерами $m_0,m_0 +1,...,m_0+2t-1$ равны 0.

Тот факт, что 2t последовательных степеней корни порождающего многочлена g(x) или что спектр содержит 2t последовательных нулевых компонентов, является важным свойством кода, позволяющим исправлять t ошибок.

Информационный многочлен Q. Задает текст сообщения, которое делится на блоки (слова) постоянной длины и оцифровывается. Это то, что подлежит передаче в системе связи.
Порождающий многочлен g(x) РС-кода многочлен, который преобразует информационные многочлены (сообщения) в кодовые слова путем перемножения Qg(x)= С =u(n) над GF(q).

Проверочный многочлен h(x) позволяет устанавливать наличие искаженных символов в слове.
Синдромный многочлен S(z). Многочлен, содержащий компоненты соответствующие ошибочным позициям. Вычисляется для каждого принятого декодером слова.
Многочлен ошибок E. Многочлен с длиной равной кодовому слову, с нулевыми значениями во всех позициях, кроме тех, что содержат искажения символов кодового слова.

Многочлен локаторов ошибок (z) обеспечивает нахождение корней, указывающих позиции ошибок в словах, принятых приемной стороной канала связи (декодером). Корни его могут быть найдены методом проб и ошибок, т.е. путем подстановки по очереди всех элементов поля, пока (z) не станет равным нулю.
Многочлен значений ошибок (z)(z)S(z) (modz2t) сравним по модулю z2t с произведением многочлена локаторов ошибок на синдромный многочлен.

Неприводимый многочлен поля р(x). Конечные поля существуют не при любом числе элементов, а только в случае, если число элементов является простым числом р или степенью q=рm простого числа. В первом случае поле называется простым (его элементы-вычеты чисел по модулю простого числа р), во втором-расширением соответствующего простого поля (его q элементов-многочленов степени m-1 и менее это вычеты многочленов по модулю неприводимого над простым полем многочлена р(x) степени m)

Примитивный многочлен. Если корнем неприводимого многочлена поля является примитивный элемент , то р(x) называют неприводимым примитивным многочленом.
В ходе изложения действий с РС-кодом нам потребуется неоднократно обращение к полю Галуа, поэтому сразу здесь поместим рабочую таблицу с элементами этого поля при разных представлениях элементов (десятичным числом, двоичным вектором, многочленом, степенью примитивного элемента).

Таблица П Характеристики элементов конечного поля расширения GF(24), неприводимый многочлен p(x) = x4+x+1, примитивный элемент =0010= 210


Пример 1. Над конечным полем GF(24), задан неприводимый многочлен поля p(x) = x4 + x + 1, примитивный элемент =2, и задан (n, k)- код Рида-Соломона (РС-код). Кодовое расстояние этого кода равно d = n k + 1 = 7. Такой код может исправлять до трёх ошибок в блоке (кодовом слове) сообщения.

Порождающий многочлен g(z) кода имеет степень m =n-k = 15-9 = 6 (его корнями являются 6 элементов поля GF(24) в десятичном представлении, а именно элементы 2, 3, 4, 5, 6, 7) и определяется соотношением, т.е. многочленом от z с коэффициентами (элементами) из GF(24) в десятичном представлении при i = 1(1)6. В рассматриваемом РС-коде 29 = 512 кодовых слов.

Кодирование сообщений РС-кодом


В таблице П эти корни имеют и степенное представление $^1=2, ^2=3,...,^6=7$.
.
Здесь z- абстрактная переменная, а -примитивный элемент поля, через его степени выражены все (16) элементы поля. Многочленное представление элементов поля использует переменную х.
Вычисление порождающего многочлена g(x)=АВ РС-кода выполним частями (по три скобки):

Векторное представление (через коэффициенты g(z) элементами поля в десятичном представлении) порождающего многочлена имеет вид
g(z) = G<7>= (1, 11, 15, 5, 7, 10, 7).

После формирования порождающего многочлена РС-кода, ориентированного на обнаружение и исправление ошибок, задается сообщение. Сообщение представляется в цифровом виде (например, ASCII- кодом), от которого переходят к многочленному или векторному представлению.

Информационный вектор (слово сообщения) имеет k компонентов из (n, k). В примере k = 9, вектор получается 9-компонентный, все компоненты это элементы поля GF(24) в десятичном представлении Q<9> = (11, 13, 9, 6, 7, 15, 14, 12, 10).

Из этого вектора формируется кодовое слово u<15> вектор с 15 компонентами. Кодовые слова, как и сами коды, бывают систематическими и несистематическими. Несистематическое кодовое слово получают умножением информационного вектора Q на вектор, соответствующий порождающему многочлену
.

После преобразований получаем несистематическое кодовое слово (вектор) в виде
Qg = <11, 15, 3, 9, 6, 14, 7, 5, 12, 15, 14, 3, 3, 7, 1>.
При систематическом кодировании сообщение (информационный вектор) представляют многочленом Q(z) в форме Q(z)=q(z)g(z) + R(z), где степень degR(z)<m = 6. После этого к вектору Q справа приписывается остаток R (всё в десятичном виде). Это делается так.

Многочлен Q сдвигают в сторону старших разрядов на величину m = n k, что достигается путём умножения Q(z) на Zn k (в примере Zn k = Z 6) и выполняют после сдвига деление Q(z)Zn k на g(z). В результате находят остаток от деления R(z). Все операции выполняют над полем GF(24)
(11, 13, 9, 6, 7, 15, 14, 12, 10, 0, 0, 0, 0, 0, 0) =
=(1, 11, 15, 5, 7, 10, 7)(11, 15, 9, 10,12,10,10,10, 3) + (1, 2, 3, 7, 13, 9) = GS + R.

Остаток при делении многочленов вычисляется обычным способом (уголком см.здесь Пример 6). Деление выполняется по образцу: Пусть Q = 26, g(z) = 7 тогда 26 = 73 +R(z), R(z)=26 -73 =26-21 = 5. Вычисление остатка R(z) от деления многочленов. Приписываем к вектору Q справа остаток R.

Получаем u<15> кодовое слово в систематическом виде. Этот вид явно содержит информационное сообщение в k старших разрядах кодового слова
u<15> = (11,13,9,6,7,15,14,12,10; 1, 2, 3, 7, 13, 9).
Разряды вектора нумеруются справа налево от 0(1)14. Шесть младших разрядов справа являются проверочными.

Декодирование кодов Рида-Соломона


После получения блока декодер обрабатывает каждый блок (кодовое слово) и исправляет ошибки, которые возникли во время передачи или хранения. Декодер делит полученный многочлен на порождающий многочлен кода РС. Если остаток равен нулю, то ошибок не обнаружено, в противном случае имеют место ошибки.

Типичный РС-декодер выполняет пять этапов в цикле декодирования, а именно:
1. Вычисление синдромного многочлена (его коэффициентов ), обнаруживаются ошибки.
2. Решается ключевое уравнение Падэ вычисление значений ошибок и их позиций соответствующих местоположений.
3. Реализуется процедура Ченя нахождение корней многочлена локатора ошибок.
4. Используется алгоритм Форни для вычисления значения ошибки.
5. Вносятся корректирующие поправки в искаженные кодовые слова;
завершается цикл извлечением сообщения из кодовых слов (снятие кода).

Вычисление синдрома.
Генерация синдрома из принятого кодового слова является первым этапом процесса
декодирования. Здесь вычисляются синдромы и определяется, есть ли ошибки в полученном кодовом слове или нет

Декодирование кодовых слов РС кода может быть организовано разными способами. К классическим способам относится декодирование с привлечением алгоритмов, работающих во временной или в частотной области, которые используют вычисление синдрома, либо не используют. Не углубляясь в теорию этого вопроса, остановим свой выбор на декодировании с вычислением синдромов кодовых слов во временной области.

Обнаружение искажений.


Синдромный $S = (S_v,S_{v+1},...,S_{m+v-1})$, где $v{0, 1}$ вектор последовательно определяется для каждого из полученных декодером на его входе кодовых слов. При нулевых значениях компонентов вектора синдрома $Sj = 0, j=v,v+1,...,m + v - 1$, декодер считает, что в принятом слове ошибки нет. Если же хотя бы для одного $j1,Sj0$, то декодер делает вывод о наличии ошибок в кодовом векторе и приступает к их выявлению, что является 1-м шагом работы декодера.

Вычисление синдромного многочлена
Умножение на приемной стороне кодового слова С на проверочную матрицу Н может давать в результате два исхода:
синдромный вектор S=0, что соответствует отсутствию ошибок в векторе C;
синдромный вектор S0, что означает наличие ошибок (одной или более) в компонентах вектора C.

Интерес представляет второй случай.
Кодовый вектор с ошибками представлен в виде C(E) =C + E, E вектор ошибок. Тогда $(C +E)H^t = CH^t + EH^t = 0 + EH^t = S$
Компоненты Sj синдрома определяются либо соотношением суммирования
для n = q-1 и j = 1(1)m = n-k, либо схемой Горнера:
$inline$S_j = C_0 +^j(C_1 +^j(C_2 +...+^j(C_{n-2} +^jC_{n-1})...))$inline$

Пример 2. Пусть вектор ошибок имеет вид Е =<0 0 0 0 12 0 0 0 0 0 0 8 0 0 0>. Он искажает в кодовом векторе символы на 3-й и 10-й позициях. Значения ошибок соответственно 8 и 12 эти значения также являются элементами поля GF(24) и заданы в десятичном (табл. П) представлении. В векторе Е нумерация позиций от младших справа налево, начиная с 0(1)14.

Сформируем теперь кодовый вектор с двумя ошибками в 3-ем разряде и в 10-ом со значениями 8 и 12 соответственно. Это осуществляется суммированием в поле GF(24) по правилам арифметики этого поля. Суммирование элементов поля с нулем не изменяет их значения. Ненулевые значения (элементы поля) суммируются после преобразования их к многочленному представлению, как обычно суммируются многочлены, но коэффициенты при неизвестной приводятся по mod 2.

После получения результата суммирования они вновь преобразуются к десятичному представлению, пройдя предварительно через степенное представление

Ниже показано вычисление искажённых ошибками значений в 10 и 3 позициях кодового слова:
$inline$(7+12) ^6+^11 =x^3 +x^2 +x^3 +x^2 +x^1 =^1 = 2,$inline$
$inline$(3 + 8) ^2+ ^7 =x^2 +x^3 +x^1 + 1 =^{12}=13.$inline$

Декодер вычисления выполняет по общей формуле для компонентов Sj, j=1(1)m. Здесь (в модели) используем соотношение $S=EH^t$, так как E задаём (моделируем) в программе сами, то ненулевые слагаемые получаются только при i = 3 и i = 10.


Специально ниже покажем вычисления по этой формуле в развернутом виде.

Проверочная матрица РС кода


Как только сформулирован порождающий многочлен кода, появляется возможность построения проверочной матрицы для кодовых слов, а также определение количества исправляемых ошибок (см.здесь, декодер ). Построим вспомогательную матрицу [715], из которой могут быть получены две разные проверочные матрицы: первые шесть строк одна и последние шесть строк другая.

Сама матрица формируется специальным образом. Первые две строки очевидны, третья строка и все последующие получены вычитанием из предыдущей (второй) строки отрезка чисел натурального ряда 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 по mod 15. При возникновении нулевого значения оно заменяется числом 15, отрицательные вычеты преобразуются в положительные.


Каждая матрица соответствует своему порождающему многочлену для систематического и несистематического кодирования.

Определение коэффициентов синдромного многочлена


Далее будем определять коэффициенты синдромного многочлена при j=1(1)6.
Относительно кодового слова с длиной $n<q=p^r$, поступающего на вход декодера мы допускаем, что оно искажено ошибками.

Относительно вектора ошибок для его выявления необходимо знать следующее:
количество искаженных позиций у кодового слова
$vv_{max}=0.5m$;
номера (положение) искаженных позиций в кодовом слове $ _i: _i=0(1)n-1$;
значения (величины) искажений $inline$e_; e_GF(2^4)$inline$.
Как вычисляется и используется далее синдромный вектор (многочлен) S? Его роль при декодировании кодовых слов очень значительна. Покажем это с иллюстрацией на числовом примере.

Пример 3. (Вычисление компонентов синдромного вектора $S_{<6>}$ )

то в итоге имеем $S_{<6>}=(S_1,S_2,S_3,S_4,S_5,S_6)$ =<8,13,7,13,15,15>.

Для дальнейшего рассмотрения введем новые понятия. Величину $x_i = ^{_i}$будем называть локатором ошибок, здесь искаженный символ кодового слова на позиции $_i$, примитивный элемент поля GF(24).
Множество локаторов ошибок конкретного кодового слова рассматривается далее как коэффициенты многочлена локаторов ошибок (z), корнями $z_i$ которого являются значения $x_i ^{-1}$, обратные локаторам.

При этом выражения $(1-zx_i)=0 $обращаются в нуль.
$inline$(z) = (1-zx_1)(1-zx_2)...(1-zx_v) =_vz^v +_{v-1}z^{v-1} +...+_1z +_0$inline$
всегда свободный член уравнения всегда свободный член уравнения $_0 =1$.
Степень многочлена локаторов ошибок равна v количеству ошибок и не превышает величины $vv_{max}=0.5m$.

Все искаженные символы находятся на разных позициях слова, следовательно, среди локаторов $x_i = ^i$,, не может быть повторяющихся элементов поля, а многочлен (z)=0 не имеет кратных корней.
Величины ошибок для удобства записи переобозначим символом $Y_i = e_i$. Для коэффициентов синдромного многочлена ранее рассматривались нелинейные уравнения. В нашем случае v=1 начало отсчета компонентов синдрома.


где $y_i,x_i$ неизвестные величины, а $S_j$ известные, вычисляемые на первом этапе декодирования, параметры (компоненты синдромного вектора).
Методы решения подобных систем нелинейных уравнений неизвестны, но решения отыскивают, используя ухищрения (обходные пути). Выполняется переход к Ганкелевой (теплицевой) системе линейных уравнений относительно коэффициентов $_i$ многочлена локаторов.

Преобразование к системе линейных уравнений
В уравнение $_i$ многочлена локаторов ошибок подставляется значение его корней $z =x_i^{-1}$. При этом многочлен обращается в нуль. Образуется тождество, обе части которого умножаем на $y_ix_i^{j+v}$, получаем:
$inline$y_i(_vx_i^{j}+_{v-1}x_i^{j+1}+...+_1x_i^{j+v-1}+x_i^{j+v})=0,1iv, 1jv$inline$.

Таких равенств получаем $vv =v^2$.
Суммируем эти равенства по всем $ i, 1iv$, при которых эти равенства выполняются. Так как многочлен (z) имеет v корней $x_i^{-1}$, раскроем скобки и перенесем коэффициенты $_i$ за знак суммы:


В этом равенстве согласно системе нелинейных уравнений, приведенной
ранее, каждая сумма равна одному из компонентов вектора синдрома. Отсюда заключает, что относительно коэффициентов $_v, _{v-1},...,_1$ можно выписать систему уже линейных уравнений.


Знаки при вычислениях над двоичным полем опускаются, так как со-ответствуют +. Полученная система линейных уравнений является ганкелевой и ей соответствует матрица с размерами $v(v+1)$ бит.

Эта матрица не вырождена, если число ошибок в кодовом слове C(E) строго равно $v , v 0.5(n-k)$, т.е. способность помехоустойчивости данного кода не нарушилась.

Решение системы линейных уравнений


Полученная система линейных уравнений в качестве неизвестных содержит коэффициенты $_i =1(1)t$ многочлена локаторов ошибок для кодового слова C(E). Известными считаются вычисленные ранее компоненты синдромного вектора $S_j, j=1(1)m$. Здесь t количество ошибок в слове, m количество проверочных позиций в слове.
Существуют разные методы решения сформированной системы.

Отметим, что матрица (ганкелева) не вырождена для размерностей, ограниченных количеством допустимым в отдельном слове (меньшем 0.5m) ошибок. При этом система уравнений однозначно разрешается, а задача может быть сведена просто к обращению ганкелевой матрицы. Желательно было бы снять ограничение на размерность матриц, т.е. над бесконечным полем.

Над бесконечными полями известны методы решения ганкелевой системы линейных уравнений:
итеративный метод Тренча Берлекэмпа Месси (ТБМ-метод); (1)
прямой детерминированный Питерсона Горенштейна Цирлера; (ПГЦ метод); (2)
метод Сугиямы, использующий алгоритм Евклида для нахождения НОД (С-метод).(3)
Не рассматривая других методов, остановим свой выбор на ТБМ-методе. Мотивировка выбора следующая.

Метод (ПГЦ) прост и хорош, но для малого количества исправляемых ошибок, С-метод сложен для реализации на ЭВМ и ограниченно опубликован (освещен) в источниках, хотя С-метод как и ТБМ-метод по известному многочлену синдромов S(z) обеспечивает решение уравнения Падэ над полем Галуа. Это уравнение сформировано для многочлена локаторов ошибок (z) и многочлена (z), в теории кодирования называется ключевым уравнением Падэ:
$S(z)(z) = (z)(mod z^m)$.

Решением ключевого уравнения является совокупность $x_i^{-1}$ корней многочлена (z), и соответственно локаторов $x_i =^{_i}$, т.е. позиции ошибок. Значения (величины) ошибок $e_i$ определяются из формулы Форни в виде

где $_z^{'}(^{-i})$ и $ (^{-i})$ значения многочленов (z) и (z) в точке $z =^{-i}$, обратной корню многочлена (z);
i позиция ошибки;$_z^{'}(z)$ формальная производная многочлена (z) по z;

Формальная производная многочлена в конечном поле


Имеются различия и сходство для производной по переменной в поле вещественных чисел и формальной производной в конечном поле. Рассмотрим многочлен

$a_i$ это элементы поля, i = 1(1)n.
Элементы поля. Задан код над вещественным полем GF(24). Производная по z имеет вид: .
В бесконечном вещественном поле операции умножить на n и суммировать n раз совпадают. Для конечных полей производная определяется иначе.
Производная по аналогии определяется соотношением:

где ((i)) = 1+1+...+1, (i) раз, суммируемых по правилам конечного поля: знак + обозначает операцию суммировать столько-то раз, т.е. элемент $a_2z$ повторить 2 раза, элемент $a_3z^2$ повторить 3 раза, элемент $a_nz^{n-1}$ повторить n раз.

Ясно, что эта операция не совпадает с операции умножения в конечном поле. В частности, в полях GF(2r) сумма четного числа одинаковых слагаемых берется по mod2 и обнуляется, а нечетного равна самому слагаемому без изменений. Следовательно, в поле GF(2r) производная получает вид

вторая и старшие четные производные в этом поле равны нулю.

Из алгебры известно, если многочлен имеет кратные корни (кратность р ), то производная многочлена будет иметь этот же корень, но с кратностью р-1. Если р = 1, то f(z) и f '(z) не имеет общего корня. Следовательно, если многочлен и его производная имеют общий делитель, то существует кратный корень. Все корни производной f '(z) эти корни кратные в f(z).

Метод решения ключевого уравнения


ТМБ (Тренча-Берлекэмпа-Месси) метод решения ключевого уравнения.
Итеративный алгоритм обеспечивает определение многочленов (z) и (z), и решение уравнения Падэ (ключевого).

Исходные данные: коэффициенты многочлена $S_1,S_2,...,S_n$ степени n-1.
Цель. Определение в явном (аналитическом) виде многочленов (z) и (z).
В алгоритме используются обозначения: j номер шага, $v_j$ степень многочлена, $inline$_j(z) =_{ji}z^i +_{ji-1}z^{i-1}+...+_{j1}z+_{j0}$inline$ разложение многочлена по степеням $z, k_j, L_j, _j(z)$ и $ _j(z)$ промежуточные переменные и функции на j-м шаге алгоритма;

Начальные условия необходимо задавать, так как здесь используется
рекурсия.
Начальные условия:


Пример 4. Выполнение итеративного алгоритма для вектора
S=(8,13,7,13,15,15). Определяются многочлены $(z) =_n(z)$ и $(z) = _n(z)$.
Таблица 2 Расчет многочленов локаторов ошибок




Итак $inline$_j^(z)=14z^2+13z+1$inline$, $inline$_j^(z)$inline$=7z+8.
Многочлен локаторов ошибок (z) над полем GF(24) с неприводимым многочленом p(x) = x4 + x + 1 имеет корни
$z_1 = ^{-i_1} = 13 = 4^{-1}$ и $z_2 =^{-i_2} = 6 = 11^{-1}$, в этом легко убедиться непосредственной проверкой, т.е. $inline$i_1= 3, i_2 =10, 13 = ^{12}, 1 =^{12}^{3}$inline$ и $^{12} =^{-3}=>13=4^{-1}$. Подстановка корней в
$inline$(z=13)=14(13)^2+1313+1=^{13}(^{12})^2+(^{12})^2+^0= ^{37}+^{24} +^{0}$inline$=
=$^{7}+^{9}+^0 =x^3+x+1=0(mod2)$;
$inline$(z = 6)=14(6)^2+136+1 = ^{13}(^{5})^2+(^{5})^2+^{0}$inline$=
=$^{8}+^{2} +^{0} = x^2 +1+x^2 +1 = 0(mod2)$.

Взяв формальную производную от (z), получаем _2(z) =214+13 =13, так как 14z берется в сумме 2 раза и по mod 2 обращается в нуль.
С использованием формулы Форни найдем выражения для расчета величин ошибок $e_i$.


Подстановкой значений i = 3 и i = 10 позиций в последнее выражение
находим
$е_3 = 10^{15-3}+11 =^{6}+^{10}$= =$x^3+x^2+x^2+x+1=x^3+x+1=^{7}=>8$;
$inline$е_{10} = 10^{15-10}+11 =^{9}^{5}+^{10}=^{14}+^{10}$inline$= =$x^3+x^2+x=^{11}=>12$.

Архитектура построения программного комплекса


Для построения программного комплекса предлагается использовать следующее архитектурное решение. Программный комплекс реализуется в виде приложения с графическим интерфейсом пользователя.
Исходными данными для программного комплекса является цифровой поток информации, выгруженной с помощью дампа из файла. Для удобства анализа и наглядности работы комплекса предполагается использование .txt файлов.
Загруженный цифровой поток представляется в виде массивов данных, в ходе работы комплекса над которыми применяются различные вычислительные действия.
На каждом этапе работы комплекса предоставляется возможность наглядного представления промежуточных результатов работы.
Результаты работы программного комплекса представляются в виде числовых данных, отображающихся в таблицах.
Сохранение промежуточных и окончательных результатов анализа производится в файлы.

Схема функционирования программного комплекса

Работа с комплекса начинается с загрузки цифрового потока с помощью дампа из файла. После загрузки пользователю предоставляется возможность визуального представления двоичного содержимого файла и его текстового содержимого.
В рамках данного интерфейса должны реализовываться следующие функциональные задачи:
Загрузка исходного сообщения;
Преобразование сообщения в дамп;
Кодирование сообщения;
Моделирование перехваченного сообщения
Построение спектров полученных кодовых слов с целью анализа их визуального представления;
Вывод на экран параметров кода.

Описание работы программного комплекса

При запуске исполняемого файла программы на экране появляется окно представленное на рисунке 2, в котором отображён основной интерфейс программы.
На вход программы подается файл, который нужно передать по каналу связи. Для его передачи по реальным каналам связи требуется кодирование добавление к нему проверочных символов, необходимых для однозначного декодирования слова на источнике-получателе. Для начала работы комплекса необходимо с помощью кнопки Загрузить файл выбрать нужный текстовый файл. Его содержимое будет отображено в нижнем поле главного окна программы.
Двоичное представление сообщения будет представлено в соответствующем поле, двоичное представление информационных слов в поле Двоичное представление информационных слов.

Число бит исходного сообщения и общее число слов в нем отображаются в полях Количество бит в передаваемом сообщении и Количество слов в передаваемом сообщении.
Сформированные информационные и кодовые слова отображаются в таблицах в правой части основного окна программы.
Окно программы с промежуточными результатами представлено на рисунке 3.


Рисунок 3 Промежуточное представление результатов работы
программного комплекса

Рисунок 4. Результаты загрузки файла сообщения

Рисунок 5. Результаты кодирования файла

Рисунок 6. Вывод сообщения с внесенными в него ошибками.

Рисунок 7. Вывод результатов декодирования и сообщения с внесенными в него ошибками

Рисунок 8. Вывод декодированного сообщения.

Заключение


АНБ США является главным оператором глобальной системы перехвата Эшелон. Эшелон располагает разветвлённой инфраструктурой, включающей в себя станции наземного слежения, расположенные по всему миру. Отслеживаются практически все мировые информационные потоки.

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

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

Список используемой литературы


1. Блейхут Р. Теория и практика кодов, контролирующих ошибки. М.: Мир, 1986. 576 с.
2. Мак-Вильямс Ф. Дж, Слоэн Н. Дж. А. Теория кодов, исправляющих ошибки. М.: Связь, 1979. 744 с.
3. Берлекэмп Э. Алгебраическая теория кодирования. М.: Мир, 1971. 478 с.
4. Габидулин Э.М., Афанасьев В.Б. Кодирование в радиоэлектронике. М.: Радио и связь, 1986. 176 с., ил.
5. Вернер М. Основы кодирования. Учебник для ВУЗов. М.: Техносфера, 2004. 288 с.
6. Трифонов П.В. Адаптивное кодирование в многочастотных системах. Диссертация на соискание ученой степени кандидата технических наук. СПб: Санкт-Петербургский государственный политехнический университет, 2005. 147 с.
7. Фомичев С. М., Абилов А.В. Обзор математических моделей каналов связи и их применение в телекоммуникационных системах. Ижевск: Ижевский государственный технический университет, 2001. 60 с.
8. Касами Т., Токура Н., Ивадари Е., Инагаки Я. Теория кодирования. М.: Мир, 1978. 576 с.
9. Муттер В. М. Основы помехоустойчивой телепередачи информации. Л.: Энергоатомиздат. Ленинградское отделение, 1990. 288 с.
10. Ваулин А. Е., Смирнов С.И. Моделирование помехозащищенного канала передачи сообщения в системе связи/Сборник алгоритмов и программ типовых задач. Вып.26. под редакцией ктн доц. И.А. Кудряшова . СПб.: ВКА им А.Ф. Можайского, 2007. стр. 121-130.
11. Карпушев С.И Конспект лекций по алгебре (часть 2. Абстрактная
алгебра). ВИКУ им. А. Ф. Можайского, 2002. 97 с.
12. Зайцев И. Е. Методика определения параметров помехоустойчивого каскадного кодирования. Л.: ВИКИ, 1987 120 с.
Подробнее..

Принятие решений. Пример

21.09.2020 14:20:44 | Автор: admin

Прочие статьи цикла
Принятие решений
Принятие решений. Пример

Продолжая тему скажу, что посмотрел публикации других авторов Хабра по этой теме. Интерес к проблемам имеется, но в теорию влезать никто не желает. Действуют как пионеры первооткрыватели. Это было бы прекрасно, при получении ими новых результатов, достижений, но к этому никто и не стремится.

А на деле получается хуже, чем уже известно, не учитывается масса факторов, результаты теории применяются там, где она это делать не рекомендует и вообще не очень серьезно все выглядит, хотя Хабр, как надо понимать к этому и не стремится. Читатели не могут и не должны играть роль фильтра.

Исходное множество альтернатив их измерение и оценивание


Известно, что проблемы поиска решения возникают только в многоальтернативных ситуациях выбора. Для рассмотрения ситуации принятия решения, формулирования конкретной задачи принятия решения (ЗПР), выбора метода ее решения необходимо располагать некоторой исходной информацией об альтернативах, отношением предпочтения.

Покажем в подразделе, какие существуют способы ее получения. Альтернативы обладают многими свойствами (признаками), оказывающими влияние на решение. Например, показатели свойств объектов (вес, объем, твердость, температура и др.) могут быть количественными или качественными.

Пусть некоторое свойство множества альтернатив из выражается числом, т. е. существует отображение : Е1, где Е1 множество действительных чисел. Тогда такое свойство характеризуют показателем, а число z = (x) называют значением (оценкой) альтернативы по показателю.

Для проведения оценивания альтернатив необходимо произвести измерение показателей.

Определение. Под измерением показателя некоторого свойства понимается приписывание числовых значений отдельным уровням этого показателя в определенных единицах. При этом важным является выбор единицы измерения.

Так, например, если объем некоторой части емкости сначала измеряется в кубометрах, а затем в литрах, то сущность показателя при этом не изменится; изменится лишь число единиц измерения. Такие показатели свойств допускают масштабирование, умножение или деление значения показателя свойства на постоянную величину.

С другой стороны, имеются свойства, показатели которых не допускают подобного манипулирования с их значениями. Степень нагрева тел характеризуется температурой и измеряется в градусах. Значение этого показателя +10С и 15С не позволяют судить о том, во сколько раз больше нагрето тело, имеющее температуру +10С, чем тело с температурой 15С

Из этих примеров можно (и важно) сделать вывод о том, что показатели объем и температура относятся к различным типам свойств, над значениями z = (x) которых допустимы или недопустимы определенные преобразования f(z)=f((x)).

Именно, множество допустимых преобразований f(z) и принимают за основу определения типа шкалы, в которой выполняется измерение показателя некоторого признака (свойства). Выполняя то или иное измерение показателя признака, который выделен исследователем, мы приходим к задаче определения типа шкалы, в которой измерение должно проводиться.

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

Определение. Шкалой измерений называется принятая по соглашению последовательность значений одноименных величин различного размера.
Рассмотрим подробнее основные типы шкал.



1. Номинальные шкалы. Номинальные шкалы используются тогда, когда исследователь имеет дело с объектами, описываемыми некоторыми признаками. В зависимости от наличия у данного объекта некоторого значения признака или отсутствия такового объект относят к тому или иному классу.

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

Шкала в этом случае образована названиями всех профессий. Очевидно, на этой шкале нуль не обозначен, хотя отсутствие профессии у субъекта допускает его отнесение именно к категории людей без профессии. Названия профессий никак на этой шкале не упорядочены, хотя часто из соображений удобства их располагают при перечислении по алфавиту.

Из этих cоображений такая шкала называется шкалой наименований.
Допустимые преобразования значений в этой шкале это все взаимно однозначные функции: f(x) f(y) <=> x y.

2. Порядковые шкалы. Если изучаемый признак, например, твердость материала, по-разному проявляется у объектов и имеет значения, которые нельзя конкретно измерить, но можно однозначно судить о сравнительной интенсивности его проявления для любых двух объектов, то говорят, что значение признака измеряется в порядковой шкале. Классическим примером такого признака является твердость минералов. Начало отсчета 0 на шкале не определено.

Значение признака определяется следующим образом. Более твердый минерал рассматриваемой пары оставляет царапину на другом. Все минералы по значениям этого признака могут быть упорядочены так: первый наименее твердый, второй более твердый он оставляет царапину только на первом, третий оставляет царапину на первых двух и т. д.

Отличие порядковой шкалы от номинальной в том, что значения признака удается упорядочить, в то время как значения на номинальной шкале даже упорядочить не удается. Неудобство порядковой шкалы состоит в том, что она не пропорциональная.

Ответить на вопрос насколько или во сколько раз один минерал тверже другого не удается. Допустимые преобразования порядковой шкалы состоят из всех монотонно возрастающих функций, обладающих свойством: x y => f(x) f(y).

3. Шкала интервалов (интервальная). отличаются от шкал порядка тем, что для описываемых ими свойств имеют смысл не только соотношения эквивалентности и порядка, но и суммирования интервалов (разностей) между различными количественными проявлениями свойств. Характерный пример шкала интервалов времени.

Интервалы времени (например, периоды работы, периоды учебы) можно складывать и вычитать, но складывать даты каких-либо событий бессмысленно.

Другой пример, шкала длин (расстояний) пространственных интервалов определяется путем совмещения нуля линейки с одной точкой, а отсчет делается у другой точки. К этому типу шкал относятся и шкалы температур по Цельсию, Фаренгейту, Реомюру.

В этих шкалах допустимы линейные преобразования, (x y)/(z -v); x y; в них применимы процедуры для отыскания математического ожидания, стандартного отклонения, коэффициента асимметрии и смещенных моментов.

4. Шкала разностей(балльная) Шкалы разностей отличаются от шкал порядка тем, что по шкале интервалов можно уже судить не только о том, что размер больше другого, но и на сколько больше в сущности это та же абсолютная шкала, но ее значения сдвинуты на некоторую величину относительно абсолютных значений (x y) < (z -v); x y;

5. Шкала отношений. Шкала, для которой множество допустимых преобразований состоит всех преобразований подобия, называется шкалой отношений. На этой шкале фиксируется начало отсчета и для нее допустимо изменение масштаба измерений.

Пусть в этой шкале выполняется измерение длины предмета. При этом можно переходить от измерения в метрах к измерению в сантиметрах, уменьшая единицу измерения в 100 раз. Очевидно, что в этом случае отношение длин L(A) и L(B) двух предметов А и В, измеренных в одинаковых единицах, не изменится при изменении единиц измерения.

Значения показателя признака, измеренные в этой шкале позволяют
отвечать на вопрос во сколько раз интенсивнее признак проявляется у одного предмета, чем у другого. С этой целью необходимо рассмотреть отношение значений L(A)/L(B) = k.

Если отношение больше единицы (k >1), значение показателя признака у первого объекта А в k раз больше чем у В, если k < 1, то значение показателя признака у объекта В в 1/ k раз больше чем у А. Допустимым преобразованием показателя является умножение на целое положительное число и только оно.

6. Абсолютная шкала. Наиболее простой из всех шкал является шкала, допускающая только одно преобразование f(x) = x. Этой ситуации соответствует единственный способ измерения показателя свойства объекта, а именно простой пересчет предметов.

Такая шкала получила название абсолютной шкалы. Когда мы регистрируем объект х, то ничего другое кроме этого объекта нас не интересует. Абсолютная шкала может рассматриваться как частная реализация других некоторых шкал.

Задача принятия решения. Получение матрицы отношений


Перечислим возможные постановки ЗПР, к ним относятся:

  • линейное упорядочение альтернатив (верхняя в цепочке лучшая);
  • выделение лучшей альтернативы;
  • выделение подмножества (неупорядоченного) лучших альтернатив;
  • выделение упорядоченного подмножества лучших альтернатив;
  • частичное упорядочение альтернатив;
  • упорядоченное (частично упорядоченное) разбиение альтернатив;
  • неупорядоченное разбиение альтернатив (классификация).

На основе анализа измерений показателей свойств альтернатив в различных шкалах результаты измерений могут быть представлены различными способами [1, 5].

1. Классификационная таблица. Таблица получается при проведении измерений в номинальных шкалах и представляет собой таблицу, строками которой являются: наименование объекта, а столбцами наименования классов $X_1, X_2, X_3,$ и т. д. В столбцах класс 1, класс 2 и т. д. ставится 1, если объект принадлежит данному классу и 0 если нет (табл. Классы объектов).

В таблице классов объекты $x_1 ,x_2 X_1, x_3 X_2, x_4 X_3.$

2. Матрица отношения предпочтений. Получается при проведения измерения в порядковых шкалах. Выявить предпочтения на множестве объектов значит указать множество всех тех пар объектов (х, у) из этого множества для которых объект х предпочтительнее (например, тверже) объекта у. Матрица отношения предпочтений получается следующим образом. (см.здесь, рис 2.15)

Строится $А_{[nn]}^p$ квадратная матрица. Ее i-я строка соответствует i-му элементу $x_i$ множества , а j-й столбец элементу $x_j$. На пересечении i-той строки и j-го столбца ставится единица, если объект $x_i$ предпочтительнее объекта $x_j$, нуль, если объект $x_j$ предпочтительнее объекта $x_i$, 1/2, если объекты $x_i$ и $x_j$ безразличны, и ничего не ставится если объекты несравнимы $x_i$ и $x_j$ нельзя сравнить.

Пример такого отношения предпочтений представлен матрицей ниже


3. Таблица показателей. Получается при проведении измерения в шкале отношений. Выбираются свойства показателей, которых будут измеряться. Производится измерение показателей этих свойств, и результаты измерений записываются в таблицу.

В столбцах $p_1, p_2, p_3, p_4$ таблицы Отношение предпочтения размещены значения показателей свойств, по которым оцениваются объекты $x_1,x_2,x_3,x_4,x_5,x_6$ и $x_7$.

После получения результатов измерений в данных формах представления, нам необходимо произвести отображение результатов в форму отношений, так как мы будем решать ЗПР, опираясь на хорошо разработанный аппарат теории бинарных отношений.

Отображение таблицы предпочтений в матрицу бинарного отношения осуществляется следующим образом:


Из матрицы отношения предпочтения $А_{[44]}^p$ для 4-х альтернатив представленной в табл. Отношения предпочтения получится матрица $А_{[44]}^p$, которая выгляди следующим образом:


Отображение матрицы показателей в матрицу отношения предпочтения осуществляется следующим образом: $a_{i,j}=1$, если:
1)число показателей, по которым объект $x_i$ предпочтительнее объекта $x_j$ больше, чем число показателей, по которым объект $x_j$ предпочтительнее объекта $x_i$;

2) для объекта $x_i$ ни один из показателей не принимает наименьшего из возможных значений.

3) из условия 1 следует, что те показатели, по которым объект $x_i$ не хуже объекта $x_j$, составляют большинство среди всех рассматриваемых показателей.

Однако при выполнении этого условия может оказаться, что по тем показателям, по которым объект $x_i$ хуже объекта $x_j$, разница значительна; чтобы уменьшить число таких случаев при отдаче предпочтения в пользу х, вводится условие 2.

Методы решения задачи принятия решения


Пусть после получения исходных данных мы располагаем отношением R на множестве альтернатив $= {(x_1, ...,x_n)}$. И стоит задача принятия решения. Основной метод линейное упорядочение (ранжирование) альтернатив, т. е. выстраивание альтернатив в цепочку по убыванию их ценности, пригодности, важности и тому подобное, от самой хорошей до самой плохой.

Отношение R может быть:

  1. полным нетранзитивным отношением;
  2. отношением частичного порядка;
  3. линейным порядком.

Только в случае линейности отношения R, структура предпочтений отвечает поставленной задаче. В этом случае ранжирование альтернатив из множества получается непосредственно, путем построения линейной диаграммы упорядоченного множества. На диаграмме альтернатива $x_i$, будет находиться строго выше альтернативы $x_j$, если она более предпочтительна.

Решение поставленной задачи для полных и транзитивных отношений осуществляется с помощью методов (алгоритма) ранжирования альтернатив, а для частичных порядков с помощью алгоритма линейного доупорядочения. Эти алгоритмы будут рассмотрены в следующих пунктах ниже.

Ранжирование альтернатив. Пусть отношение [, R] полное и нетранзитивное. Свойство полноты говорит о том, что все альтернативы $ = {(x_1, ...,x_n)}$ из множества сравнимы между собой. Наличие нетранзитивности возможно только в том случае, если граф G[, R] предпочтения содержит контуры.

Необходимо преобразовать структуру графа отношения так, чтобы были устранены логические противоречия в форме контуров. Если предположить что в отношении R имеется контур $x_1, x_2, ,x_k, x_1,$ то при ранжировании альтернатив $x_1$ должна быть расположена выше $x_2, x_3, ,x_k, x_1,$, что приводит к противоречию.
Введем следующее утверждение [1,5].

Пусть В' и В" два произвольных контура графа вида G[, R], тогда если некоторый элемент $x_i$ B' доминирует элемент $x_j$ B'', то и любой элемент $x_1$ B' доминирует любой элемент $x_k$ B''.

Это предложение предоставляет возможность разбиения множества R на m подмножеств $B_1, B_2, ,B_m$, таких что $inline$B_i B_j=, i,j [1, m]; UB_i =.$inline$
Итак, задача ранжирования альтернатив множества распадается на два этапа:
1) выделение контуров графа, т.е. разбиение множества на подмножества $B_1, B_2, ,B_m$ и групповое упорядочение этих подмножеств;
2) ранжирование элементов контуров, выделенных на первом этапе.

Алгоритм выделения контуров графа

Для нахождения контуров графа существует простой алгоритм [1]. Пусть $А_{[nn]}$ матрица смежности графа G[, R], а $Е_{[nn]}$единичная матрица. Образуем $Е_{[nn]}+ А_{[nn]}$, $(Е_{[nn]}+ А_{[nn]})^2$, $(Е_{[nn]}+ А_{[nn]})^3$, последовательность степеней матриц, элементы которых выражают количество путей длины не более 1, 2, 3 Для некоторого значения m n получим следующее равенство (установившуюся матрицу):
$inline$(Е_{[nn]}+ А_{[nn]})^m=(Е_{[nn]}+А_{[nn]})^{m+1}$inline$.

Из теории графов известно [10], что каждой системе всех одинаковых строк установившейся матрицы соответствует подмножество вершин графа, лежащих в одном контуре. Группируя соответствующие вершины в классы, получим разбиение исходного множества на подмножества $B_1, B_2, ,B_m$.

Очевидно, что среди этих подмножеств можно найти такое подмножество $B_im$, что элементы этого подмножества не будут доминироваться никакими элементами из других подмножеств. Это подмножество будем считать наилучшим, и оно займет первое место в ранжировке по убыванию предпочтительности.

Затем найдем среди оставшихся подмножеств по такому же принципу лучшее подмножество и поставим его на второе место. Эту процедуру будем
продолжать до тех пор, пока все подмножества не займут свои места в ранжировке.

Пусть на множестве задано отношение предпочтения R матрицей $А_{[66]}$.


Граф отношения R изображен на рис. Г.


Рис. Г. Граф нетранзитивного отношения R

Для осуществления первого этапа ранжирования элементов множества необходимо выделить контуры графа G[, R]. Это делается возведением матрицы смежности графа в последовательные степени, пока не выполнится совпадение матриц.

Получаем $(Е_{[66]}+ А_{[66]})$, $(Е_{[66]}+ А_{[66]})^2$, $(Е_{[66]}+ А_{[66]})^3$.
Далее последовательно вычисляем возрастающие степени матрицы, суммируя их с единичной матрицей соответствующей размерности до тех пор пока матрица не перестанет изменяться:


Так как $inline$(Е_{[66]}+ А_{[66]})^2 =(Е_{[66]}+ А_{[66]})^3$inline$, можно сделать вывод, что $inline$(Е_{[66]}+ А_{[66]})^2 = (Е_{[66]}+ А_{[66]})^k$inline$, при k 2. Из анализа матрицы $(Е_{[66]}+ А_{[66]})^2$ следует, что строки, соответствующие элементам $x_1,x_4,x_6$ совпадают, это говорит о принадлежности этих элементов одному контуру графа G[, R].

Элементы $x_1,x_4,x_6$ образуют множество $B_1 =(x_1,x_4,x_6)$. Другой контур образован элементами $x_2,x_3,x_5$, которые входят в множество $B_2 =(x_2,x_3,x_5)$.

Таким образом, мы провели разбиение множества на m=2 класса $B_1, B_2$. Проведем групповое упорядочение этих подмножеств. Для этого необходимо найти какой-нибудь элемент $x_i B_1$, который доминирует элемент $x_j B_2$.

Это будет означать превосходство подмножества $B_1$ над $B_2$. В нашем примере $x_1 B_1$ доминирует над $x_2 B_2$. Следовательно, подмножество $B_1$ доминирует над $B_2$. Графическое представление доминирования в разбиении изображено на рис. КК.


Рис. КК. Ранжирование контуров, выделенных на первом этапе

Алгоритм ранжирования элементов контуров. Возможно, ли упорядочить элементы отношения, лежащие в одном контуре, эквивалентны ли они друг другу или между ними имеют место достаточно тонкие различия, позволяющие их различать? Оказывается такая возможность, как правило, существует [1].

Обозначим через $ B_{h_{[nn]}}$ матрицу смежности h-го контура. Введем понятие $p^i(k)$-силы порядка k элемента i, значение которой вычисляется как сумма элементов i-й строки в матрице $ B_{h_{[nn]}}^k$ (1).

Пусть $ b_{h_{[i,j]}}^k$ элемент, стоящий в i-й строке и j-м столбце матрицы, тогда


Под относительной силой k-го порядка элемента i понимают дробь

При неограниченном возрастании k (k ), число $_i(k)$ стремится к некоторому пределу , который мы в дальнейшем будем называть силой элемента i. Вектор $П_{[n]} = (_1,...,_n)$ называется предельным вектором.

Вследствие теоремы Перрона-Фробениуса [1] предел всегда существует. Собственный нормированный вектор матрицы смежности контура совпадет с ее предельным вектором. Следовательно, вектор $П_{[n]} = (_1,...,_n)$ (2)

может быть найден без вычислений интегрированных сил $p^i(k)$, путем решения системы линейных уравнений
$B_{h_{[nn]}} П_{[n]} = П_{[n]}$, (3)
где наибольший неотрицательный действительный корень характеристического уравнения
$det(Е_{[nn]}- B_{h_{[nn]}}) = 0$ (4)
Необходимо отметить, что нормированный собственный вектор неотрицательной неразложимой матрицы не меняется при умножении матрицы на число s > 0, а также при суммировании ее с матрицей вида sE.

Затем элементы контура упорядочиваются по уменьшению значений соответствующих компонентов вектора $П_{[n]}$, т.е. элемент i доминирует элемент j тогда, когда $_i >_j$.

Осуществим ранжировку элементов множества $B_1 =(x_1,x_4,x_6)$. Построим матрицу предпочтений для этого множества


Вектор интегрированных сил 1-го порядка для элементов $(x_1, x_4, x_6)$ выглядит (1,1,2), вектор относительный сил П(k) = (1/4,1/4,2/4).
Ранжирование элементов $(x_1, x_4, x_6)$по силе 1-го порядка представлено на рис. Р.


Рис. Р. Ранжирование элементов

Найдем векторы, характеризующие силы 2-го, 3-го, 4-го и 5-го порядков.


Графическое представление ранжирования изображено на рис. П.



Рис.Ц Цепь

Осуществив аналогичным образом ранжирование элементов множества В2, получим результаты представленные на рис. П справа.

В результате совмещения ранжировки элементов множества В1 и В2 переходим к окончательному упорядочению элементов множества (рис. Ц).

Линейное доупорядочение строгих частичных порядков


Пусть отношение R (рис. А ниже по тексту), полученное в результате агрегации индивидуальных суждений экспертов является отношением частичного порядка, на множестве . В этом случае упорядоченное множество. Построение линейного упорядочения альтернатив представляет собой получение глобальных оценок их возможностей в порядковой шкале.

В силу каких-либо причин, некоторые эксперты не могут сравнить определенные пары альтернатив по предпочтительности. В этом случае агрегированное отношение R на множестве не будет линейным порядком. Очевидно, это приводит к задаче линейного доупорядочения альтернатив из . Такое доупорядочение часто возможно многими способами.

Наличие нескольких линейных упорядочений для частичного порядка указывает на то, что имеющегося в структуре внутреннего порядка недостаточно для единственного линейного упорядочения. Таким образом, возникает необходимость решения задачи линейного доупорядочения частичных порядков. Пусть R частичный порядок.

Теорема (Шпильрайн [5, 10]). Всякий порядок R на множестве можно продолжить до линейного порядка на этом множестве.

Следствие из теоремы Шпильрайна: всякое линейное доупорядочение подмножества $_i $ может быть продолжено до линейного доупорядочения всего упорядоченного множества .

Если X подмножество в , состоящее из несравнимых альтернатив, то всякое линейное упорядочение X может быть продолжено до линейного упорядочения всего множества . При этом порядок R выражается через линейные порядки $R_i$.

В силу теоремы Шпильрайна, на множестве существует нумерация $x_1, x_2,...,x_n $ элементов этого множества. Нумерация n-элементного упорядоченного множества , с заданным на нем отношением порядка R, есть взаимно-однозначное отображение множества в себя, т.е. в {1, 2, ..., n}, при котором большему относительно порядка элементу соответствует больший номер [5]. Далее под ранжированием элементов будем понимать любое линейное доупорядочение этого порядка. Отметим, что нумерация упорядоченного множества представляет собой его измерение.

В общем случае, задача поиска линейных доупорядочений сводится к нахождению всех допустимых нумераций исходного частичного упорядоченного множества. Можно выписать все перестановки элементов, из , которых будет n! и для каждой проверить условие, что большему элементу соответствует больший номер. Однако, такой метод отыскания всех доупорядочений очень трудоемкий и не эффективный.

Для упорядоченного множества , с заданным на нем порядком R, элемент х' множества называется максимальным, если не существует строго большего его элемента х, т.е. если x>x' не выполняется ни для какого x . Элемент x'' называется наибольшим элементом упорядоченного множества [, R], если он больше любого другого элемента х, т.е. для всякого x , x''>x [5].

Если в упорядоченном множестве имеется наибольший элемент, то он является максимальным элементом. Если в упорядоченном множестве имеется единственный максимальный элемент, то он будет наибольшим элементом. В частично упорядоченном множестве допускается наличие нескольких максимальных элементов.

При всякой нумерации n-элементного множества , номер N приписывается максимальному элементу. Все нумерации множества можно получить, если известны все нумерации всех подмножеств, получающихся из удалением одного из таких максимальных элементов. К каждому подмножеству применим тот же прием [7]. Рассмотрим алгоритм построения всех нумераций упорядоченного множества [, R].

1. Строится вспомогательный граф [, R] упорядоченного множества [, R], вершины которого удовлетворяют условиям:

а) являются подмножествами ;

б) для любых двух подмножеств X, Y верно: (X,Y R) тогда, когда
подмножество Y может быть получено из подмножества X удалением одного из его максимальных элементов (рис. А и АА).


2. Для каждого одноэлементного подмножества множества R выписываем его единственную нумерацию. Для получения всех нумераций подмножества X необходимо перебрать все смежные с ним подмножества и для каждого такого подмножества продолжить все его нумерации. В итоге будут получены все нумерации множества , т.е. все линейные продолжения порядка R.

Задача заключается в нахождении всех линейных доупорядочений частичного порядка, диаграмма которого представлена на рис. A. В этом отношении нет информации, например, о том, доминирует ли альтернатива $x_1 $альтернативу $х_2$ или наоборот, а также аналогично для пар $(х_3, х_6);(х_4, х_5)$. Это и означает, что А частичное упорядочение. Достроить до линейного упорядочения существует много (22) вариантов, которые желательно привести к единственному. Это возможно с привлечением дополнительной информации об альтернативах, получаемой при детальном изучении ситуации.

1. Строим вспомогательный граф [, R], начинаем с множества $(x_1, x_2, x_3,x_4, x_5, x_6)$ и снизу. Цифра около дуги графа указывает удалением какого максимального элемента получено подмножество, в которое эта стрелка направлена (рис. AA).

2. Формируем табл. AAA для нахождения всех нумераций подмножеств, являющихся вершинами графа [, R]. Заполнение таблицы выполняется построчно сверху вниз. Каждая строка есть нумерация подмножества, записанного в левой колонке таблицы (табл. AAA).

3. При составлении нумерации подмножества X, состоящего из k элементов надо переписать все записанные ранее (для предыдущего подмножества) нумерации подмножеств Y R(x) и присвоить номер тому элементу, который дополняет Y до X.


В последнем (нижнем) блоке (табл. AAA) находятся все нумерации линейных доупорядочений множества . Графическое представление этих доупорядочений изображено на рис. AAA.


Рис. AAA. Графическое представление доупорядочений

Необходимо отметить, что всего линейных порядков на множестве из 6 элементов 6! или 720, а линейных доупорядочений множества с отношением, заданным графом, изображенным на рис. AA, всего 22. Это также достаточно много для принятия решения.
Существуют ли возможности для сокращения количества таких вариантов? Да существуют.
Для уменьшения числа линейных доупорядочений, необходимо воспользоваться дополнительной информацией.

Дополнительная информация


Пусть [, R] исходное отношение, тогда дополнительную информацию можно представить в виде отношения на множестве , где условие (x,y) , т.е. (x>y) интерпретируется как сообщение о том, что объект х доминирует объект у;
отношение можно рассматривать тогда, как множество подобных сообщений информации о доминировании, заданной в виде бинарного отношения , возможно два случая при использовании дополнительной информации:

  1. граф отношения R содержит контуры;
  2. граф отношения R не содержит контуров.

В первом случае линейное упорядочение множества с заданным на нем отношением R производится с помощью алгоритма ранжирования,
рассмотренного ранее.

Во втором случае линейное упорядочение множества с заданным на нем отношением R производится с помощью алгоритма линейного
доупорядочения, рассмотренного выше. Необходимо отметить, что отношение R, не содержащее контуров, может быть нетранзитивным и вследствие этого не являться частичным порядком.

Для успешного выхода из этой ситуации необходимо, чтобы дополнительная информация и исходное отношение R были заданы в виде диаграмм Хассе, т.е. без явного указания транзитивных связей. Ценность дополнительной информации будет определяться тем, во сколько раз уменьшиться число линейных доупорядочений при ее использовании.

Например, если поступит информация о том, что $x_2$ доминирует $x_4$, т.е. $x_2>x_4$, то число линейных доупорядочений сократиться с 22 до 19, а если поступит информация: $x_1>x_2$, то число линейных доупорядочений сократиться в два раза. Таким образом, возникает вопрос: какая информация будет наиболее ценна, или при добавлении, какого отношения число доупорядочений уменьшится в наибольшей степени?

Для решения этой задачи для всех пар элементов $(x_i, x_j)$ множества , которые не входят в отношение R, в нижнем блоке табл. ААА, необходимо вычислить $n_{ij}$ сколько раз номер элемента $x_i$ больше номера элемента $x_j$, т.е. элемент $x_i$ стоит выше элемента $x_j$ и $n_{ji}$ сколько раз элемент $x_j$ стоит выше элемента $x_i$.
Степень ценности дополнительной информации об отношении в этой паре, будет тем выше, чем меньше разность $|n_{ij} - n_{ji}|$. Большее из чисел $n_{ij}, n_{ji}$ будет равняться числу доупорядочений множества [, R]. Для рассматриваемого примера получим сводку характеристик графического представления доупорядочений


Из анализа таблицы следует, что наиболее полезной будет являться
информация об отношении в парах $(x_1, x_2)$ и $(x_4, x_5)$. Получение дополнительной информации об отношении в любой из этих пар приводит к сокращению числа линейных доупорядочений в два раза.

Заключение


Формулирование и решение ЗПР возможно только в ситуации множества альтернатив и выбора лучшей из них. Если выбора нет, то следуй путем на котором оказался.
В основе решений, принимаемых ЛПР, лежит его предпочтение, которое описывается отношением предпочтения. Наличие отношения позволяет построить математическую модель для проведения исследований. Неопределенность в предпочтениях устраняется использованием дополнительной информации, которая не является экспертной.

Уделено внимание рассмотрению измерений и оценкам значений показателей свойств объектов. Приведены примеры многообразных шкал, которые часто игнорируются.
Перечислены возможные постановки ЗПР и информация необходимая для их решения.

На конкретном числовом примере показано применение алгебраических методов решения ЗПР, без привлечения статистических выборок и эмпирических методов обработки.
В основе метода лежит результат теоремы о возможности продолжения частичного порядка до линейного (совершенного).

Список использованной литературы


1. Берж К. Теория графов и ее применение. М.: ИЛ, 1962. 320 с.
2. Ваулин А. Е. Дискретная математика в задачах компьютерной безопасности. Ч. I. СПб.: ВКА имени А. Ф. Можайского, 2015. 219 с.
3. Ваулин А. Е. Дискретная математика в задачах компьютерной безопасности.Ч. II. СПб.: ВКА имени А. Ф. Можайского, 2017. 151 с.
4. Ваулин А. Е. Методы исследования информационных вычислительных комплексов. Вып. 2. Л.: ВИКИ им. А. Ф. Можайского, 1984. 129 с.
5. Ваулин А. Е. Методология и методы анализа информационных вычислительных комплексов. Вып.1. Л.: ВИКИ им. А. Ф. Можайского, 1981. 117 с.
6. Ваулин А. Е. Методы цифровой обработки данных: дискретные ортогональные преобразования. СПб.: ВИККИ им. А. Ф. Можайского, 1993. 106 с.
7. Кузьмин В. Б. Построение групповых решений в пространствах четких и нечетких бинарных отношений. М.: Наука,1982. 168 с.
8. Макаров И. М. и др.Теория выбора и принятия решений. М.: Физматлит, 1982. 328 с.52.
9. Розен В.В. Цель оптимальность решение. М.: Радио и связь,1982.169 с.
10. Szpilraijn E Sur Textension de l'ordre partiel. Fundam. math.,1930, vol.16,pp.386-389.
Подробнее..

Динамическая балансировка нагрузки в pull-схеме

01.09.2020 22:17:56 | Автор: admin
В прошлой новости про принципы работы коллекторов логов PostgreSQL я упомянул, что одним из недостатков pull-модели является необходимость динамической балансировки нагрузки. Но если делать ее аккуратно, то недостаток превращается в достоинство, а система в целом становится гораздо более устойчивой к изменениям потока данных.


Давайте посмотрим, какие решения есть у этой задачи.

Распределение объектов по мощности


Чтобы не углубляться в неинтересные абстракции, будем рассматривать на примере конкретной задачи мониторинга. Соотнести предлагаемые методики на свои конкретные задачи, уверен, вы сможете самостоятельно.

Равномощные объекты мониторинга


В качестве примера можно привести наши коллекторы метрик для Zabbix, которые исторически имеют с коллекторами логов PostgreSQL общую архитектуру.

И правда, каждый объект мониторинга (хост) генерирует для zabbix практически стабильно один и тот набор метрик с одной и той же частотой все время:


Как видно на графике, разница между min-max значениями количества генерируемых метрик не превышает 15%. Поэтому мы можем считать все объекты равными в одинаковых попугаях.

Сильный дисбаланс между объектами


В отличие от предыдущей модели, для коллекторов логов наблюдаемые хосты совсем не являются однородными.

Например, база рабочего один хост может генерировать в лог миллион планов за сутки, другой десятки тысяч, а какой-то и вовсе единицы. Да и сами эти планы по объему и сложности и по распределению во времени суток сильно отличаются. Так и получается, что нагрузку сильно качает, в разы:


Ну, а раз нагрузка может меняться настолько сильно, то надо учиться ей управлять

Координатор


Сразу понимаем, что нам явно понадобится масштабирование системы коллекторов, поскольку один отдельный узел со всей нагрузкой когда-то точно перестанет справляться. А для этого нам потребуется координатор тот, кто будет управлять всем зоопарком.

Получается примерно такая схема:


Каждый worker свою нагрузку в попугаях и в процентах CPU периодически сбрасывает master'у, те коллектору. А он, на основании этих данных, может выдать команду типа новый хост посадить на ненагруженный worker#4 или hostA надо пересадить на worker#3.

Тут еще надо помнить, что, в отличие от объектов мониторинга, сами коллекторы имеют вовсе не равную мощность например, на одном у вас может оказаться 8 ядер CPU, а на другом только 4, да еще и меньшей частоты. И если нагрузить их задачами поровну, то второй начнет затыкаться, а первый простаивать. Отсюда и вытекают

Задачи координатора


По сути, задача всего одна обеспечивать максимально равномерное распределение всей нагрузки (в %cpu) по всем доступным worker'ам. Если мы сможем решить ее идеально, то и равномерность распределения %cpu-нагрузки по коллекторам получим автоматом.

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

Динамическая балансировка


Простую задачу (zabbix) мы можем решить достаточно банально:

  • вычисляем относительную мощность каждого коллектора в задачах
  • делим все задачи между ними пропорционально
  • между worker'ами распределяем равномерно



Но что делать в случае сильно неравных объектов, как для коллектора логов?..

Оценка равномерности


Выше мы все время употребляли термин "максимально равномерное распределение", а как вообще можно формально сравнить два распределения, какое из них равномернее?

Для оценки равномерности в математике давно существует такая вещь как среднеквадратичное отклонение. Кому лениво вчитываться:
S[X] = sqrt( sum[ ( x - avg[X] ) ^ 2 of X ] / count[X] )

Поскольку количество worker'ов на каждом из коллекторов у нас тоже может отличаться, то нормировать разброс по нагрузке надо не только между ними, но и между коллекторами в целом.

То есть распределение нагрузки по worker'ам двух коллекторов [ (10%, 10%, 10%, 10%, 10%, 10%) ; (20%) ] это тоже не очень хорошо, поскольку на первом получается 10%, а на втором 20%, что как бы вдвое больше в относительных величинах.

Поэтому введем единую метрику-расстояние для общей оценки равномерности:
d([%wrk], [%col]) = sqrt( S[%wrk] ^ 2 + S[%col] ^ 2 )
То есть величины среднеквадратичного отклонения для наборов величин нагрузки по всем worker'ам и по всем коллекторам воспринимаем как координаты вектора, длину которого будем стараться минимизировать.

Моделирование


Если бы объектов у нас было немного, то мы могли бы полным перебором разложить их между worker'ами так, чтобы метрика оказалась минимальной. Но объектов у нас тысячи, поэтому такой способ не подойдет. Зато мы знаем, что коллектор умеет перемещать объект с одного worker'а на другой давайте этот вариант и смоделируем, используя метод градиентного спуска.

Понятно, что идеальный минимум метрики мы так можем и не найти, но локальный точно. Да и сама нагрузка может изменяться во времени настолько сильно, что искать за бесконечное время идеал абсолютно незачем.

То есть нам осталось всего лишь определить, какой объект и на какой worker эффективнее всего переместить. И сделаем это банальным переборным моделированием:

  • для каждой пары (целевой host, worker) моделируем перенос нагрузки
  • нагрузку от host внутри исходного worker'а считаем пропорционально попугаям
    В нашем случае за попугая оказалось вполне разумно взять объем получаемого потока логов в байтах.
  • относительную мощность между коллекторами считаем пропорциональной суммарным попугаям
  • вычисляем метрику d для перенесенного состояния

Выстраиваем все пары по возрастанию метрики. В идеале, нам всегда стоит реализовать перенос именно первой пары, как дающий минимальную целевую метрику. К сожалению, в реальности сам процесс переноса стоит ресурсов, поэтому не стоит запускать его для одного и того же объекта чаще определенного интервала охлаждения.

В этом случае мы можем взять вторую, третью, по рангу пару лишь бы целевая метрика уменьшалась относительно текущего значения.

Если же уменьшать некуда вот он локальный минимум!

Пример на картинке:


Запускать итерации до упора при этом вовсе не обязательно. Например, можно делать усредненный анализ нагрузки на интервале 1 мин, и по его завершению делать единственный перенос.

Микро-оптимизации


Понятно, что алгоритм со сложностью T(целей) x W(процессов) это не очень хорошо. Но в нем стоит не забыть применить некоторые более-менее очевидные оптимизации, которые его могут ускорить в разы.

Нулевые попугаи


Если на замеренном интервале объект/задача/хост сгенерировал нагрузку 0 штук, то его не то что перемещать куда-то его даже рассматривать и анализировать не надо.

Самоперенос


При генерации пар нет необходимости оценивать эффективность переноса объекта на тот же самый worker, где он и так находится. Все-таки уже будет T x (W - 1) мелочь, а приятно!

Неразличимая нагрузка


Поскольку мы моделируем все-таки перенос именно нагрузки, а объект всего лишь инструмент, то пробовать переносить одинаковый %cpu нет смысла значения метрик останутся точно те же, хоть и для другого распределения объектов.

То есть достаточно оценить единственную модель для кортежа (wrkSrc, wrkDst, %cpu). Ну, а одинаковыми вы можете считать, например, значения %cpu, совпадающие до 1 знака после запятой.

Пример реализации на JavaScript
var col = {  'c1' : {    'wrk' : {      'w1' : {        'hst' : {          'h1' : 5        , 'h2' : 1        , 'h3' : 1        }      , 'cpu' : 80.0      }    , 'w2' : {        'hst' : {          'h4' : 1        , 'h5' : 1        , 'h6' : 1        }      , 'cpu' : 20.0      }    }  }, 'c2' : {    'wrk' : {      'w1' : {        'hst' : {          'h7' : 1        , 'h8' : 2        }      , 'cpu' : 100.0      }    , 'w2' : {        'hst' : {          'h9' : 1        , 'hA' : 1        , 'hB' : 1        }      , 'cpu' : 50.0      }    }  }};// вычисляем опорные метрики и нормализуем по "мощности"let $iv = (obj, fn) => Object.values(obj).forEach(fn);let $mv = (obj, fn) => Object.values(obj).map(fn);// initial reparsefor (const [cid, c] of Object.entries(col)) {  $iv(c.wrk, w => {    w.hst = Object.keys(w.hst).reduce((rv, hid) => {      if (typeof w.hst[hid] == 'object') {        rv[hid] = w.hst[hid];        return rv;      }      // нулевые значения ничего не решают, поэтому сразу отбрасываем      if (w.hst[hid]) {        rv[hid] = {'qty' : w.hst[hid]};      }      return rv;    }, {});  });  c.wrk = Object.keys(c.wrk).reduce((rv, wid) => {    // ID воркеров должны быть глобально-уникальны    rv[cid + ':' + wid] = c.wrk[wid];    return rv;  }, {});}// среднеквадратичное отклонениеlet S = col => {  let wsum = 0    , wavg = 0    , wqty = 0    , csum = 0    , cavg = 0    , cqty = 0;  $iv(col, c => {    $iv(c.wrk, w => {      wsum += w.cpu;      wqty++;    });    csum += c.cpu;    cqty++;  });  wavg = wsum/wqty;  wsum = 0;  cavg = csum/cqty;  csum = 0;  $iv(col, c => {    $iv(c.wrk, w => {      wsum += (w.cpu - wavg) ** 2;    });    csum += (c.cpu - cavg) ** 2;  });  return [Math.sqrt(wsum/wqty), Math.sqrt(csum/cqty)];};// метрика-расстояниеlet distS = S => Math.sqrt(S[0] ** 2 + S[1] ** 2);// выбираем оптимальный перенос и моделируем егоlet iterReOrder = col => {  let qty = 0    , max = 0;  $iv(col, c => {    c.qty = 0;    c.cpu = 0;    $iv(c.wrk, w => {      w.qty = 0;      $iv(w.hst, h => {        w.qty += h.qty;      });      w.max = w.qty * (100/w.cpu);      c.qty += w.qty;      c.cpu += w.cpu;    });    c.cpu = c.cpu/Object.keys(c.wrk).length;    c.max = c.qty * (100/c.cpu);    qty += c.qty;    max += c.max;  });  $iv(col, c => {    c.nrm = c.max/max;    $iv(c.wrk, w => {      $iv(w.hst, h => {        h.cpu = h.qty/w.qty * w.cpu;        h.nrm = h.cpu * c.nrm;      });    });  });  // "текущее" среднеквадратичное отклонение  console.log(S(col), distS(S(col)));  // формируем набор хостов и воркеров  let wrk = {};  let hst = {};  for (const [cid, c] of Object.entries(col)) {    for (const [wid, w] of Object.entries(c.wrk)) {      wrk[wid] = {        wid      , cid      , 'wrk' : w      , 'col' : c      };      for (const [hid, h] of Object.entries(w.hst)) {        hst[hid] = {          hid        , wid        , cid        , 'hst' : h        , 'wrk' : w        , 'col' : c        };      }    }  }  // реализация переноса нагрузки на целевой worker  let move = (col, hid, wid) => {    let w = wrk[wid]      , h = hst[hid];    let wsrc = col[h.cid].wrk[h.wid]      , wdst = col[w.cid].wrk[w.wid];    wsrc.cpu -= h.hst.cpu;    wsrc.qty -= h.hst.qty;    wdst.qty += h.hst.qty;    // перенос на другой коллектор с "процентованием" нагрузки на CPU    if (h.cid != w.cid) {      let csrc = col[h.cid]        , cdst = col[w.cid];      csrc.qty -= h.hst.qty;      csrc.cpu -= h.hst.cpu/Object.keys(csrc.wrk).length;      wsrc.hst[hid].cpu = h.hst.cpu * csrc.nrm/cdst.nrm;      cdst.qty += h.hst.qty;      cdst.cpu += h.hst.cpu/Object.keys(cdst.wrk).length;    }    wdst.cpu += wsrc.hst[hid].cpu;    wdst.hst[hid] = wsrc.hst[hid];    delete wsrc.hst[hid];  };  // моделирование и оценка переноса для пары (host, worker)  let moveCheck = (orig, hid, wid) => {    let w = wrk[wid]      , h = hst[hid];    // тот же воркер - ничего не делаем    if (h.wid == w.wid) {      return;    }    let col = JSON.parse(JSON.stringify(orig));    move(col, hid, wid);    return S(col);  };  // хэш уже проверенных переносов (hsrc,hdst,%cpu)  let checked = {};  // перебираем все возможные пары (какой хост -> на какой воркер)  let moveRanker = col => {    let currS = S(col);    let order = [];    for (hid in hst) {      for (wid in wrk) {        // нет смысла пробовать повторно перемещать одну и ту же (с точностью до 0.1%) "мощность" между одной парой воркеров        let widsrc = hst[hid].wid;        let idx = widsrc + '|' + wid + '|' + hst[hid].hst.cpu.toFixed(1);        if (idx in checked) {          continue;        }                let _S = moveCheck(col, hid, wid);        if (_S === undefined) {          _S = currS;        }        checked[idx] = {          hid        , wid        , S : _S        };        order.push(checked[idx]);      }    }    order.sort((x, y) => distS(x.S) - distS(y.S));    return order;  };  let currS = S(col);  let order = moveRanker(col);  let opt = order[0];  console.log('best move', opt);  // реализуем перенос  if (distS(opt.S) < distS(currS)) {    console.log('move!', opt.hid, opt.wid);    move(col, opt.hid, opt.wid);    console.log('after move', JSON.parse(JSON.stringify(col)));    return true;  }  else {    console.log('none!');  }  return false;};// пока есть что-куда переноситьwhile(iterReOrder(col));

В результате, нагрузка по нашим коллекторам распределяется практически одинаково в каждый момент времени, оперативно нивелируя возникающие пики:

Подробнее..

Математика верстальщику не нужна, или Временные функции и траектории для покадровых 2D анимаций на сайтах

07.09.2020 14:15:25 | Автор: admin


Математика верстальщику не нужна!, говорили они. Арифметики за 2 класс школы хватит!, говорили они. Верстальщик не программист, так что нечего себе голову забивать точными науками!, чего только не услышишь на просторах интернета на тему нужности тех или иных знаний при разработке сайтов. И на самом деле в большинстве случаев человеку, который делает интерфейсы, и правда хватает умения складывать числа. Что-то более сложное встречается редко и обычно уже есть готовый алгоритм где-то в недрах NPM. Но сайты понятие растяжимое, и иногда все же нужно включить голову, и разобраться в каком-то вопросе. И один из таких вопросов это траектории в 2D анимациях.


Наблюдая за людьми, которые осваивают JS, и, в частности, покадровые анимации в вебе, я заметил, что у многих возникают сложности, когда нужно сделать движение какого-то объекта на странице по определенной траектории. И, если эта траектория не нарисована заранее заботливым дизайнером в виде path в SVG-картинке, а формулируется какими-то общими словами и ссылками на референсы из сети, или, что еще хуже, должна генерироваться на лету, то задача приводит их в полный ступор. По всей видимости все упирается в тотальное непонимание того, как получить кривую той или иной формы в рамках JS. Об этом мы сегодня и поговорим в формате своеобразной лекции о временных функциях для анимаций в самых разных их проявлениях.


Мы постараемся избежать излишней теоретизации, отдавая предпочтение картинкам и объяснению всего на пальцах, с акцентом на практическое использование в вопросах 2D анимаций на сайтах. Так что местами формулировки могут быть не совсем точными с точки зрения математики или не совсем полными, цель этой статьи дать общее представление о том, что бывает, и откуда можно начать в случае чего.


Немного определений


Школьная программа имеет свойство выветриваться из готовы после покидания этой самой школы, но все же такое понятие, как декартова система координат должно остаться. В работе с HTML и CSS мы постоянно к ней обращаемся. Две перпендикулярных оси, которые обычно обозначаются буквами X и Y, одинаковый масштаб по обеим осям это именно она. Декартова система координат это частный случай прямоугольной системы координат, у которой масштаб по осям может быть разным. Такие системы координат в вебе тоже встречаются, правда чаще в вопросах, связанных с канвасом и WebGL. Вопросы переходов между системами координат в общем случае мы пока отложим, так как это не будет нужно для решения текущих задач. Сейчас нам важно научиться делать кривые определенной формы.


Второе понятие, которое нам пригодится это график функции. В нашем случае функция это зависимость чего-то одного от чего-то другого. Дали что-то на вход получили что-то на выходе. Давая функции на вход разные значения получаем разные значения на выходе. А может и не разные. А может она вообще определена только для определенного набора значений. Всякое может быть. В любом случае такие наборы значений можно отобразить в виде графиков в той или иной системе координат на наш вкус.


Вы конечно спросите, каким образом связаны графики и анимации? Представьте себе канвас. Вы рисуете на нем точку. И начинаете ее двигать, в каждом кадре очищая канвас и рисуя ее в новом месте. Или еще лучше, если это будет кружочек, имитирующий мячик. Он будет подпрыгивать, отскакивая от пола. Представьте это. А теперь представьте, что получится, если между кадрами не очищать канвас? Там будет оставаться след от мячика, причем он будет не совсем случайным, а в виде кривой линии, напоминующей много парабол, соединенных между собой. Так вот, нам нужно решить обратную задачу сначала сделать эту кривую линию, график некоторой функции, а потом уже вдоль нее двигать мячик, постепенно меняя параметры функции и получая каждый раз новое положение для него. А этим мячиком уже может быть что угодно HTML-элемент, что-то в SVG-картинке, что-то на канвасе не важно.


I. Простые функции вида y = f(x) и коэффициенты


Начнем мы с простых функций. Из школьной тригонометрии вы должны помнить такие слова, как синус, косинус, тангенс, котангенс, и.т.д. В примерах будет часто использоваться синус, но все идеи и приемы будут универсальными их можно использовать с чем угодно и как угодно.


Давайте посмотрим на график синуса:


(x) => {    return Math.sin(x);}

Примеры функций-зависимостей в виде кода будут на условном JS.



Здесь у нас две оси, X и Y, и Y зависит от X. Если мы будем менять X от 0 до 10 (условно), то Y будет плавать туда-сюда, туда-сюда, туда-сюда Если нужно сделать покачивание чего-то на странице это то, что нужно. Мы можем использовать requestAnimationFrame и в каждом кадре слегка увеличивать x, использовать нашу пока еще простую функцию для вычисления y, и таким образом каждый раз получать новые координаты для какого-то объекта. И, разумеется, никто не мешает оставить объект на одном месте по оси X, а менять только его положение по Y здесь уже все зависит от задачи.


Но тут есть нюанс обычно мы мыслим в пикселях, десятках и сотнях пикселей, а здесь координаты будут слишком маленькими. Нужно будет весь этот график подвинуть куда-то по экрану, или растянуть, или еще что-то с ним сделать. Будет очень полезно разобраться с тем, как те или иные коэффициенты влияют на конечный результат, чтобы потом добавлять их на уровне интуиции.


Первым делом будут множители:


(x) => {    return 2 * Math.sin(x);}(x) => {    return 0.5 * Math.sin(5 * x);}

Они будут растягивать или сжимать наш график.



Умножая параметр (x в наших примерах) на какой-то коэффициент, мы получаем растяжение или сжатие по оси X. Умножая всю функцию на коэффициент получаем растяжение или сжатие по оси Y. Здесь все просто.


Добавим еще коэффициентов, только на этот раз не в виде множителей, а в виде слагаемых:


(x) => {    return Math.sin(x) + 1;}(x) => {    return Math.sin(x + 1);}

Что же там получилось?



Очевидно график смещается. Добавляем коэффициент к параметру x получаем смещение по X, добавляем коэффициент ко всей функции получаем смещение по Y. Тоже, ничего сложного.


Эти же соображения действуют для всех подобных функций, не только для синуса.

Здесь у нас останется еще вопрос поворота всего графика. Для этого нам понадобится матрица поворота, но тема матриц и преобразований координат достойна отдельной статьи, так что сейчас мы лишь упомянем это ключевое слово и пойдем дальше.


Что будет, если сложить две синусоиды с разными коэффициентами? Давайте посмотрим!


(x) => {    return Math.sin(x) + 0.2 * Math.sin(5 * x);}

Получается что-то такое:



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


Но синусом мир не ограничивается. Никто не мешает поскладывать и другие функции. И поумножать тоже. И поделить. Для примера возьмем косинус и степенную функцию:


(x) => {    return 2 * Math.cos(3 * x) / Math.pow(x + 1, 2);}


Получилась штука, которую во фронтенде ассоциируют со словом bounce (зеленая на графике). Заготовка для анимаций, построенная подобным образом, часто используется и в CSS-библиотеках, и встречается во всех более-менее популярных JS библиотеках для анимаций.


Можно подумать, что зеленая кривая здесь отображает график колебания пружины, но это не совсем так. Просто очень похоже на нее. Настолько похоже, что пользователь скорее всего не заметит разницы.

Но к вопросам физики мы еще вернемся.


Промежуточные итоги


Прежде, чем идти дальше, отметим самое важное, что нужно запомнить:


  • Графики функций вида y = f(x) можно использовать как траектории для анимаций.
  • Коэффициенты позволяют подвинуть траекторию в нужное место и растянуть до нужного размера.
  • Иногда можно сымитировать физический процесс с достаточной точностью путем сочетания простых функций.

II. Полярная система координат


Переход из одной системы координат в другую часто может сделать что-то сложное чем-то простым, либо напротив что-то очень простое превратить во что-то, ломающее мозг. Полярная система координат не очень часто встречается в работе верстальщика, так что у многих возникают сложности с ее пониманием. И действительно, мы привыкли задавать положение чего-то в виде координат (X, Y), а здесь нужно брать угол (направление) и радиус (расстояние) до точки. Это очень непривычно.


В целом связь между координатами (X, Y) и (R, ) простая, но от этого само преобразование не становится очевидным:


const x = r * Math.cos(p);const y = r * Math.sin(p);

Здесь и дальше мы будем использовать букву p вместо стандартной , чтобы примеры кода не наполнялись unicode-символами.


На пальцах разница между полярной и прямоугольной системами координат примерно такая же, как между выражением идите отсюда 5 километров на северо-восток, а там увидите и вот вам GPS-координаты по широте и долготе.

На самом деле самый толковый способ понять, как все это работает это посмотреть на практике, чем мы и займемся.


Возьмем простую функцию:


(p) => {    return p / 5;}

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



Но линейные зависимости это слишком скучно. Вернем нашу синусоиду на место и нарисуем ее одновременно в декартовой системе координат, используя как зависимость Y от X, и в полярной, как зависимость расстояния от угла.



Стоп. Что? Именно такая мысль должна здесь вас посетить. Почему синусоида стала круглой? Может даже показаться, что здесь что-то нечисто, что рисовалка графиков сломалась, но нет. Все так и должно быть. И на самом деле это небольшое наблюдение дает широкий простор для творчества.


Давайте произведем все те же манипуляции с коэффициентами, что мы делали с функциями в самом начале, и посмотрим, как они влияют на графики в полярной системе координат.


(p) => {    return Math.sin(5 * p);}(p) => {    return 2 * Math.sin(5 * p + 1);}


Цветочки! Конечно, что же еще здесь могло получиться?


Очень полезно воспользоваться одной из множества рисовалок графиков в сети и поэкспериментировать с разными функциями и коэфффициентами самостоятельно. Личный опыт в таких вопросах всегда дает лучшее понимание происходящего, чем отдельно взятые примеры от других людей.

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


Можно также заметить, что количество лепестков связано с коэффициентом при угле, так что очень легко делать цветочки с разным количеством лепестков.

Продолжим:


(p) => {    return Math.sin(p) + 0.2 * Math.sin(2 * p);}(p) => {    return 2 * Math.sin(p) + 0.2 * Math.sin(10 * p);}(p) => {    return Math.sin(3 * p - Math.PI / 2) * Math.sin(4 * p);}


Человечек! Начинает казаться, что комбинации графиков можно использовать как замену для пятен Роршаха.


Продолжая комбинировать функции мы получим своеобразные кляксы:


(p) => {    return (Math.sin(p) + Math.sin(p * 2) + 2) / 4;}(p) => {    return (Math.sin(p) + Math.sin(p * 3) + 2) / 2;}(p) => {    return Math.sin(p) + Math.sin(p * 4) + 2;}


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


Промежуточные итоги


Повторим некоторые моменты:


  • Полярная система координат помогает строить сложные абстрактные траектории вокруг одной точки.
  • Полезно экспериментировать с комбинациями функций и коэффициентов, чтобы при необходимости получить определенный характер движения, примерно знать, из чего можно сформировать нужную траекторию.

III. Параметрическое представление функций


Мы начали с использования функций вида y = f(x). Но мы также можем сделать x зависящим от чего-то еще. Это приведет к заданию координат в виде системы:


x = f1(t)y = f2(t)

В нашем случае это может быть очень кстати. Дело в том, что все предыдущие примеры были очень предсказуемыми, ограниченными. Мы не могли синусоиду развернуть, полетать туда-сюда или еще что-то такое сделать. Каждому x соответствовал один y. Ну или, в терминах полярной системы координат каждому углу соответствовал один радиус. С добавлением нового параметра мы снимаем эти ограничения.


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

На самом деле мы можем представить все предыдущие примеры так, что была зависимость:


x = ty = f(t)

Или, в терминах кода, как вариант:


x: (t) => {    return t;},y: (t) => {    return (t / 2) - 1;}

Наши простые прямые выглядели бы как-то так:



Это было бы усложнением примеров на ровном месте, т.к. параметр t не играл бы никакой практической роли, мы меняли координату x напрямую, но дальше мы будем его везде подразумевать.


Сейчас мы не будем подробно останавливаться на этом вопросе, но отметим, что некоторые стандартные геометрические фигуры можно легко получить именно используя параметрическое представление:



Полезно об этом помнить.


Что будет, если мы возьмем для разнообразия косинус, и модуль от него? Получится что-то похожее на траекторию полета подпрыгивающего резинового мячика. Но реальный мячик будет подпрыгивать все меньше и меньше. Мы можем попытаться выразить изменения в движении по каждой из координат (X, Y) в виде отдельных функций, используя опыт, полученный ранее из экспериментов с ними. Вариантов там будет бесконечно много, но допустим наугад получилось что-то такое:


x: (t) => {    return 5 * t / (t + 1);},y: (t) => {    return 2 * Math.abs(Math.cos(5 * t)) / (t*t + 1);}

Что будет выглядеть как-то так:



Мы попытались угадать комбинации простейших функций, которые бы дали в результате что-то похожее на подпрыгивание мячика. Но насколько это на самом деле похоже на него? Можем ли мы вообще вот так взять и угадать траекторию такого сложного с точки зрения физики движения?


На самом деле вот так по графику мы не сможем оценить субъективную реалистичность движения, его нужно будет посмотреть в динамике. Но я вам скажу по секрету, что это не будет похоже на настоящее подпрыгивание. Ну то есть если мы запустим точку летать по этой траектории, то формально это подойдет под описание подпрыгивающего объекта, но где-то глубоко в подсознании будет казаться, что что-то здесь не так, что движение неестественное. Да, оно похоже на то, что нужно, но не совсем. Все же физические законы и наша попытка угадать их это не одно и то же. Отсюда можно сделать важное замечание:


Если у вас в проекте нужны достаточно реалистичные движения не гадайте, а возьмите готовые формулы из учебника физики.

Давайте попробуем это сделать на примере все этого же мячика.


Немного физики


Как только мы откроем учебник, нас встретят формулы для скорости и положения тела в зависимости от времени. Ну и также в них есть сопуствующие коэффициенты вроде ускорения свободного падения.


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


x: (t) => {    return t;},y: (() => {    let y0 = 2;     // Изначальное положение по Y    let v0 = 0;     // Изначальная скорость    let localT = 0; // Локальное время полета от последнего отскока    let g = 10;     // Ускорение свободного падения    let k = 0.8;    // Условный коэффициент для не совсем упругого отскока    return (t) => {        let y = y0 + v0 * localT - g * localT * localT / 2;        if (y <= 0) {            y = 0;            y0 = 0;            v0 = (-v0 + g * localT) * k;            localT = 0;        }        localT += 0.005;        return y;    };})()

Но зато результат будет и более реалистичным, и более практичным в плане адаптации его под разные условия использования.



Удобство здесь вещь не самая бесполезная. Обратите внимание, как меняется график, когда мы меняем коэффициенты. Пусть ускорение свободного падения станет равным 1.62, как на Луне. Что произойдет? Траектория сохранила характер, но условный мячик теперь дольше висит в воздухе после каждого подпрыгивания. Или поменяем коэффициент, условно определяющий насколько упругое отталкивание происходит и у нас уже не резиновый мячик, а деревянный карандаш. Стандартные формулы дают предсказуемое поведение при изменении отдельных коэффициентов. Если бы мы имели чисто на глазок угаданную закономерность, было бы очень сложно ее адаптировать таким же образом, особенно при редактировании кода, написанного несколько лет назад.


Такая проблема часто встречается в разных экспериментальных примерах с CodePen, когда оригинальный автор угадал что-то под свою задачу, сильно похожую на нашу, и мы попадаем в ловушку, говоря да там работы на пару минут, сейчас все будет двигаться как нужно, а потом, спустя час, решаем, что переписать это решение проще, чем доработать. Будьте внимательны!

В качестве упражнения вы можете попробовать смоделировать аналогичное падение мячика, но уже по наклонной поверхности, это может быть хорошей практикой по этой теме.



Подобные относительно сложные с точки зрения физики процессы, связанные с кинематикой, магнетизмом или моделированием жидкостей редко встречаются в обычной верстке. Здесь, конечно, речь идет о дизайнерских сайтах с генеративной графикой или какими-то интерактивными физическими моделями. Но тем не менее, если вы стремитесь развиваться в своей области это может быть неплохой темой для ознакомления на пару вечеров. Специалистом по моделированию физических процессов вы не станете, но кругозор в вопросах анимирования расширится, а это очень здорово. Когда встанет задача сделать что-то с этими темами связанное, вы уже будете знать, куда смотреть.


Промежуточные итоги


На последних примерах мы увидели следующее:


  • Время в анимациях можно использовать в качестве параметра в параметрическом представлении функций. Да и в целом привязывать анимацию ко времени удобно.
  • При использовании параметрического представления функций у нас нет ограничений вида одному X соответствует один Y. Можно усложнить траектории.
  • Параметрическое представление подталкивает к моделированию физических процессов, но, если вам нужна реалистичность и универсальность имеет смысл запастить справочником по физике и брать готовые формулы оттуда.

IV. Кривые Безье


С кривыми Безье сталкивался, наверное, каждый верстальщик. Мы их постоянно используем в CSS. Но на самом деле то, что мы там используем это очень частный случай их применения. Мы же посмотрим на все это в более общем случае.


Если пойти на википедию и почитать о том, что же такое кривая Безье, то мы увидим страшные формулы с большим знаком суммы, который так и отдает высшей матиматикой и всех пугает. Но можно это все сформулировать на пальцах.


У нас есть набор точек. Две. Три. Пять. Десять. Сколько угодно. Для этих точек по определенной закономерности строятся выражения, которые мы потом складываем в одно и получаем супер-мега-параметрическую функцию, где координаты этих точек играют роль коэффициентов.

В зависимости от количества точек кривая будет иметь соответствующее название. Кубическая кривая безье будет иметь 4 точки, а наш параметр t (в контексте анимаций время) будет максимум в третьей степени поэтому она и кубическая. Если будет больше точек, то степени будут увеличиваться. Это все приведет к увеличению нагрузки на процессор, снижению производительности в большинстве случаев, и широкое практическое применение имеют только кубические кривые Безье. В CSS используются именно они. А еще они используются в SVG для создания path. Но об этом чуть дальше.


Здесь стоит сказать, что чем больше точек, тем более сложную кривую можно построить. Из двух точек получится просто прямая. Из трех дуга (возможно ассимметричная). Из четырех загогулька в виде буквы S, либо дуга с двойным перегибом. Из пяти загогулька с еще одним перегибом, и.т.д.


Но вернемся к четырем точкам, пропустим все расчеты и сразу перейдем к нужной нам форме двум функциям для X и Y от t:


x: (t) => {    return ((1-t)*(1-t)*(1-t)*x0) + (3*(1-t)*(1-t)*t*x1) + (3*(1-t)*t*t*x2) + (t*t*t*x3);},y: (t) => {    return ((1-t)*(1-t)*(1-t)*y0) + (3*(1-t)*(1-t)*t*y1) + (3*(1-t)*t*t*y2) + (t*t*t*y3);}

Здесь (x0, y0) координаты первой точки, (x1, y1) второй и.т.д. Они, как мы уже отметили, выполняют роль коэффициентов.


Давайте построим график, взяв первую точку в координатах (0, 0), а последнюю, четвертую, в (1, 1). Для удобства первая и вторая, а также третья и четвертая точки будут соединяться отрезками, чтобы было понятно, в каком порядке они идут.



На графике можно заметить, что меняя параметр t от 0 до 1 мы получаем знакомую кривую, вписанную в квадрат, которую мы привыкли видеть в CSS. Это она и есть.


Вы можете спросить, а зачем так все усложнять? Та кривая на картинке это же почти график корня x, неужели нельзя ее получить более простым способом?


Все дело в том, что это очень частный случай. Мы не обязаны фиксировать точки в координатах (0, 0) или (1, 1). Мы можем брать вообще любые точки. Что, собственно, и сделаем:



Как вы можете видеть, получаемые траектории плавные, могут самопересекаться, и, что самое главное прогнозируемые. Немного экспериментов и вы будете четко понимать, куда пойдет кривая, если подвинуть ту или иную точку. Это очень удобно.


Вы можете также обратить внимание, что график начинает идти точками. Это особенность работы конкретной программы, но это не баг, а фича. Расстояние во времени (параметре t в нашем случае) между точками одинаковое. Эти графики строятся по принципу мячика, оставляющего след, о котором мы говорили в самом начале. Этот пунктир показывает, где анимация будет идти быстрее, где скорость движения увеличится. В поворотах скорость меньше, на относительно прямых участках больше. Это добавляет определенную физику в движение, делает его более разнообразным, что часто бывает очень кстати.


Такого рода кривые хорошо использовать при анимациях появления или улетания объектов.

Но что-то мы совсем забыли о синусе. Почему бы его не добавить к имеющимся выражениям?



Ооо Небольшой шум в виде синуса делает траекторию менее предсказуемой, но с другой стороны она сохраняет общее направление и стала даже более интересной. Полезно это запомнить.


Последовательности кривых Безье


В примере с мячиком мы уже столкнулись с сочетанием нескольких функций. Движение идет сначала по одной, потом по другой и.т.д. Мы можем сочетать все, что угодно, но наверное самые интереные и полезные результаты будут, если начать комбинировать кривые Безье. В SVG именно последовательности этих кривых используются для того, чтобы делать path сложной формы. Там, конечно, они разбавляются еще прямыми, получается более запутанная структура, но общий смысл мы постараемся воспроизвести.


У кривых Безье есть одна особенность, которая нам в этом поможет. Часть кривой, соответствующая значениям параметра от 0 до 1 начинается в первой точке и заканчивается в четверной (мы все еще говорим про кубические кривые). Этот факт позволяет ограничить траекторию, и мы будем точно знать, где она начнется, и где закончится.


А дальше самое интересное: если угол наклона отрезка между третьей и четвертой точками одной кривой и первой и второй точками у второй кривой будет одинаковым, то они сольются в одну. Там не будет стыка, острого угла, резкого перехода. А если их длина будет совпадать, то это сыграет нам на руку, нормализовав разницу в скорости движения, если мы будем использовать время как параметр в наших функциях.


Такое сочетание лучше посмотреть на картинке:



Здесь мы видим 4 кривых Безье, которые слились в одну. Если убрать вспомогательные точки и отрезки, то никто и не догадается, что это не одна кривая, а несколько.


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

Пусть точки, где кривые соединяются, будут называться опорными. Что будет, если эти опорные точки будут стоять на месте, а соседние с ними мы начнем двигать, симметрично относительно друг друга (если одна движется вверх вторая вниз, одна влево другая вправо)?


У нас получится набор кривых, схожих по направлению, но как бы слегка колеблющихся:



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


Промежуточные итоги


Давайте обобщим сказанное.


  • Кривые Безье это очень мощный инструмент, позволяющий делать сложные, но предсказуемые траектории для абстрактных анимаций.
  • Любые кривые можно соединить в более сложную траекторию, если проявить некоторую аккуратность.

Заключение


Не так страшен черт, как его малюют. Генерирование траекторий определенной формы для движений в покадровых анимациях кажется сложной задачей, но имея общие представления о том, что можно делать с графиками функций, она становится вполне доступной. Дальше останется только вопрос оптимизаций, но он индивидуален для каждого проекта, да и редко на самом деле подобные функции становятся узким местом в производительности. Надеюсь, что данный набор иллюстраций поможет начинающим разработчикам составить представление о том, что можно делать, и где примерно искать ответы на свои вопросы, если встанет вопрос о сложных движениях в 2D анимациях.

Подробнее..

Математика верстальщику не нужна 2 Матрицы, базовые трансформации, построение 3D и фильтры для картинок

22.09.2020 12:22:19 | Автор: admin


В прошлый раз мы говорили о графиках и траекториях для покадровых анимаций, а сегодня речь пойдет про матрицы. Мы разберемся с тем, как строятся базовые трансформации в CSS, SVG и WebGL, построим отображение 3D-мира на экране своими руками, попутно проводя параллель с таким инструментом, как Three.js, а также поэкспериментируем с фильтрами для фотографий и разберемся, что же за магия такая лежит в их основе.

Напомню, что в этой серии статей мы знакомимся с разными штуками из области математики, которые пугают верстальщиков, но могут быть полезны при решении рабочих задач. Мы стараемся избежать излишней теоретизации, отдавая предпочтение картинкам и объяснению на пальцах, с акцентом на практическое применение во фронтенде. В связи с этим формулировки местами могут быть не совсем точными с точки зрения математики, или не совсем полными. Цель этой статьи дать общее представление о том, что бывает, и откуда можно начать в случае чего.

Скрипты для генерирования картинок в стиле этого цикла статей находятся на GitHub, так что если если вы хотите себе сообразить такие же вы знаете что делать.

Немного определений


Матрица в математике это такая абстракция, можно сказать, что это тип данных в каком-то смысле, и записываетя она в виде прямоугольной таблицы. Количество столбцов и строк может быть любым, но в вебе мы почти всегда имеем дело с квадратными матрицами 2x2, 3x3, 4x4, и 5x5.

Также нам понадобится такое определение, как вектор. Думаю из школьной геометрии вы можете вспомнить определение, связанное со словами длина и направление, но вообще в математике вектором может называться очень много чего. В частности мы будем говорить о векторе, как об упорядоченном наборе значений. Например координаты вида (x, y) или (x, y, z), или цвет в формате (r, g, b) или (h, s, l, a) и.т.д. В зависимости от того, сколько элементов вошло в такой набор мы будем говорить о векторе той или иной размерности: если два элемента двумерный, три трехмерный и.т.д. Также, в рамках рассматриваемых тем, может быть удобно иногда думать о векторе, как о матрице размера 1x2, 1x3, 1x4 и.т.д. Технически можно было бы ограничиться только термином матрица, но мы все же будем использовать слово вектор, чтобы отделить эти два понятия друг от друга, хотя бы в логическом смысле.

Для матриц, равно как и для векторов, определены разные операции, которые с ними можно делать. В частности умножение. Мы постоянно их умножаем между собой. Сам алгоритм умножения не очень сложный, хотя может показаться немного запутанным:

function multiplyMatrices(a, b) {    const m = new Array(a.length);    for (let row = 0; row < a.length; row++) {        m[row] = new Array(b[0].length);        for (let column = 0; column < b[0].length; column++) {            m[row][column] = 0;            for (let i = 0; i < a[0].length; i++) {                m[row][column] += a[row][i] * b[i][column];            }        }    }    return m;}

Но для нас на самом деле не так важно постоянно помнить принцип его работы при решении бытовых задач. Здесь мы упоминаем его скорее для полноты картины, чтобы внести контекст в дальнейшие примеры.

При работе со сложными сущностями в математике очень полезно абстрагироваться. Как здесь мы будем часто говорить про умножение, но не будем уделять внимание тому, какие именно арифметические операции в каком порядке там происходят. Мы знаем, что умножение определено и этого достаточно для работы.

Мы будем использовать только квадратные матрицы в очень частном наборе задач, так что будет достаточно набора простых правил:

  • Умножать можно только матрицы одной размерности.
  • Умножаем матрицу на матрицу получаем матрицу.
  • Можно умножить матрицу на вектор получим вектор.
  • Порядок умножения важен.



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

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



И последнее, что нам понадобится, это пример, показанный на картинке выше. Это как бы частный случай умножения матрицы на вектор, когда у матрицы последняя строка представляет собой кусочек единичной матрицы, а у вектора последний элемент тоже равен 1.

В этом примере мы используем буквы (x, y), и как вы уже можете догадаться, дальше речь пойдет о координатах в 2D. Но зачем добавлять третью координату и оставлять ее единицей? спросите вы. Все дело в удобстве, или даже лучше сказать, что в универсальности. Мы очень часто добавляем +1 координату для упрощения расчетов, и работа с 2D идет с матрицами 3x3, работа с 3D с матрицами 4x4, а работа с 4D, например с цветами в формате (r, g, b, a) идет с матрицами 5x5. На первый взгляд это кажется безумной идеей, но дальше мы увидим, насколько это унифицирует все операции. Если вы захотите более подробно разобраться в этой теме, то можете загуглить выражение однородные координаты.

Но довольно теории, перейдем к практике.

I. Базовые трансформации в компьютерной графике


Давайте возьмем выражения из упомянутого примера и посмотрим на них как есть, вне контекста матриц:

newX = a*x + b*y + cnewY = d*x + e*y + f

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

newX = 1*x + 0*y + 0 = xnewY = 0*x + 1*y + 0 = y

Здесь ничего не меняется новые координаты (x, y) идентичны старым. Если подставить эти коэффициенты в матрицу и внимательно присмотреться, то увидим, что получится единичная матрица.

А что получится, если взять другие коэффициенты? Например вот такие:

newX = 1*x + 0*y + A = x + AnewY = 0*x + 1*y + 0 = y

Мы получим смещение по оси X. Впрочем, что еще здесь могло получиться? Если для вас это не очевидно, то лучше вернуться к первой части, где мы говорили про графики и коэффициенты.

Меняя эти 6 коэффициентов a, b, c, d, e, f и наблюдая за изменениями x и y, рано или поздно мы придем к четырем их комбинациям, которые кажутся полезными и удобными для практического применения. Запишем их сразу в форме матриц, возвращаясь к изначальному примеру:



Названия этих матриц говорят сами за себя. При умножении этих матриц на вектора с координатами каких-то точек, объектов на сцене и.т.д. мы получаем новые координаты для них. Причем оперируем мы интуитивно понятными трансформациями перемещение, масштабирование, поворот и наклон, а коэффициенты определяют выраженность той или иной трансформации по соответствующим осям.

Очень часто удобно воспринимать матрицы именно как трансформации для чего-либо, например для координат. Это еще раз к слову об абстракциях.

Трансформации можно складывать. В терминах матриц мы будем использовать операцию умножения, что может немного путать, но это издержки разговорного языка. Если нам нужно какой-то объект сместить в сторону и увеличить, то мы можем взять матрицу для смещения, матрицу для масштабирования, и перемножить их. Результат будет представлять из себя матрицу, которая дает и смещение, и масштабирование одновременно. Останется только каждую точку нашего объекта подвергнуть трансформации с ее помощью.



Базовые трансформации в CSS


Но это все слова. Давайте посмотрим, как это выглядит в реальном фронтенде. В CSS у нас (внезапно) есть функция matrix. Выглядит она в контексте кода как-то так:

.example {    transform: matrix(1, 0, 0, 1, 0, 0);}

Многих новичков, которые впервые видят ее, накрывает вопрос почему здесь шесть параметров? Это странно. Было бы 4 или 16 еще куда не шло, но 6? Что они делают?

Но на самом деле все просто. Эти шесть параметров те самые коэффициенты, из которых мы только что собирали матрицы для базовых трансформаций. Только их почему-то расставили в другом порядке:



Также в CSS есть функция matrix3d для того, чтобы задавать с помощью матрицы трансформацию в 3D. Там уже есть 16 параметров, ровно чтобы сделать матрицу 4x4 (не забываем, что мы добавляем +1 размерность).

Матрицы для базовых 3D трансформаций строятся так же, как и для 2D, только нужно больше коэффициентов, чтобы расставлять их не по двум координатам, а по трем. Но принципы те же.

Естественно, каждый раз городить матрицу и следить за корректной расстановкой коэффициентов при работе с простыми трансформациями в CSS было бы странно. Мы, программисты, обычно все же стараемся упростить себе жизнь. Так что у нас в CSS появились краткие функции для создания отдельных трансформаций translateX, translateY, scaleX и.т.д. Обычно мы используем именно их, но важно понимать, что внутри они создают эти же самые матрицы, о которых мы говорили, просто скрывая от нас этот процесс за еще одним слоем абстракции.

Эти же самые трансформации translate, rotate, scale и skew, а также универсальная функция matrix для задания трансформаций, присутствуют и в SVG. Синтаксис немного другой, но суть такая же. При работе с трехмерной графикой, например с WebGL, мы тоже будем прибегать к этим же трансформациям. Но об этом чуть позже, сейчас важно понять, что они есть везде, и работают везде по одному и тому же принципу.

Промежуточные итоги


Обобщим вышесказанное:

  • Матрицы могут быть использованы в качестве трансформаций для векторов, в частности для координат каких-то объектов на странице.
  • Почти всегда мы оперируем квадратными матрицами и добавляем +1 размерность для упрощения и унификации вычислений.
  • Есть 4 базовых трансформации translate, rotate, scale и skew. Они используются везде от CSS до WebGL и везде работают схожим образом.

II. Построение 3D сцены своими руками


Логичным развитием темы про трансформации координат будет построение 3D сцены и отображение ее на экране. В той или иной форме эта задача обычно есть во всех курсах по компьютерной графике, но вот в курсах по фронтенду ее обычно нет. Мы посмотрим может быть немного упрощенный, но тем не менее полноценный вариант того, как можно сделать камеру с разными углами обзора, какие операции нужны, чтобы посчитать координаты всех объектов на экране и построить картинку, а также проведем параллели с Three.js самым популярным инструментом для работы с WebGL.

Здесь должен возникнуть резонный вопрос зачееем? Зачем учиться делать все руками, если есть готовый инструмент? Ответ кроется в вопросах производительности. Вероятно вы бывали на сайтах с конкурсами вроде Awwwards, CSS Design Awards, FWA и им подобных. Вспомните, насколько производительные сайты принимают участие в этих конкурсах? Да там почти все тормозят, лагают при загрузке и заставляют ноутбук гудеть как самолет! Да, конечно, основная причина обычно это сложные шейдеры или слишком большое количество манипуляций с DOM, но вторая невероятное количество скриптов. Это катастрофическим образом влияет на загрузку подобных сайтов. Обычно все происходит так: нужно сделать что-то на WebGL берут какой-нибудь 3D движок (+500КБ) и немного плагинов для него (+500КБ); нужно сделать падение объекта или разлетание чего-то берут физический движок (+1МБ, а то и больше); нужно обновить какие-то данные на странице ну так добавляют какой-нибудь SPA-фреймворк с десятком плагинов (+500КБ) и.т.д. И таким образом набирается несколько мегабайтов скриптов, которые мало того, что нужно загрузить клиенту (и это еще вдобавок к большим картинкам), так еще и браузер с ними что-то будет делать после загрузки они же не просто так к нему прилетают. Причем, в 99% случаев, пока скрипты не отработают, пользователь не увидит всей той красоты, которую ему бы нужно с самого начала показывать.

Народная примета гласит, что каждые 666КБ скриптов в продакшене увеличивают время загрузки страницы на время, достаточное, чтобы пользователь отправил разработчика сайта на следующий круг ада. Three.js в минимальной комплектации весит 628КБ...

При этом часто задачи просто не требуют подключения сложных инструментов. Например чтобы показать пару плоскостей с текстурами в WebGL и добавить пару шейдеров, чтобы картинки расходились волнами, не нужен весь Three.js. А чтобы сделать падение какого-то объекта не нужен полноценный физический движок. Да, он скорее всего ускорит работу, особенно если вы с ним знакомы, но вы заплатите за это временем пользователей. Тут каждый решает сам для себя, что ему выгоднее.

Цепочка преобразований координат


На самом деле суть преобразований координат для построения 3D-сцены у себя на экране довольно простая, но мы все же разберем ее по шагам, т.к. скорее всего для многих верстальщиков этот процесс будет чем-то новым.

Так вот. Допустим дизайнер нарисовал 3D-модель. Пусть это будет куб (в примерах будем использовать максимально простые построения, чтобы не усложнять иллюстрации на ровном месте):



Что эта модель из себя представляет? По сути это набор точек в какой-то системе координат и набор взаимосвязей между ними, чтобы можно было определить, между какими из точек должны находиться плоскости. Вопрос плоскостей в контексте WebGL будет лежать на плечах самого браузера, а для нас важны координаты. Нужно понять, как именно их преобразовать.

У модели, как мы и сказали, есть система координат. Но обычно мы хотим иметь много моделей, хотим сделать сцену с ними. Сцена, наш 3D-мир, будет иметь свою, глобальную систему координат. Если мы просто будем интерпретировать координаты модели как глобальные координаты то наша модель будет находиться как бы в центре мира. Иными словами, ничего не поменяется. Но мы хотим добавить много моделей в разные места нашего мира, примерно так:



Что же делать? Нужно преобразовать координаты каждой отдельной модели в глобальные координаты. Расположение модели в пространстве задается смещениями, поворотами и масштабированием мы уже видели эти базовые трансформации. Сейчас нам нужно для каждой модели построить матрицу трансформаций, которая будет хранить в себе как раз эту информацию о том, где находится модель относительно мира, и как повернута.

Например для кубиков будут примерно такие матрицы:

// Центральный кубик в центре мира.// Единичная матрица ничего не меняет и координаты просто интерпретируются как глобальные.const modelMatrix1 = [    [1, 0, 0, 0],    [0, 1, 0, 0],    [0, 0, 1, 0],    [0, 0, 0, 1]];// Кубик, смещенный по глобальной оси X.const modelMatrix2 = [    [1, 0, 0, 1.5],    [0, 1, 0, 0  ],    [0, 0, 1, 0  ],    [0, 0, 0, 1  ]];// Кубик, смещенный по глобальной оси X в другую сторону.const modelMatrix3 = [    [1, 0, 0, -1.5],    [0, 1, 0, 0   ],    [0, 0, 1, 0   ],    [0, 0, 0, 1   ]];

Дальше мы будем действовать примерно следующим образом:

для каждой точки модели {    глобальные координаты точки = [ матрица этой модели ] * локальные кординаты}

Соответственно для каждой модели нужна своя матрица.

Аналогичным образом можно делать цепочку из каких-то объектов. Если птица должна махать крыльями, то будет уместно координаты точек крыла сначала перевести в координаты птицы, а потом уже в глобальные мировые координаты. Это будет куда проще угадывания траектории крыла сразу в глобальных координатах. Но это так, к слову.


Дальше нужно определиться с тем, с какой стороны мы будем смотреть на мир. Нужна камера.



Камера это такая абстракция, как бы имитация физической камеры. У нее есть координаты и какие-то углы наклона, задающие ее расположение в глобальных координатах. Наша задача преобразовать набор теперь уже глобальных координат в систему координат камеры. Принцип тот же, что и в предыдущем примере:

для каждой точки в мире {    координаты точки для камеры = [ матрица камеры ] * глобальные кординаты}

Если у вас сложная сцена, то будет иметь смысл произвести некоторые оптимизации и не считать ничего для точек, которые точно не попадут в поле зрения камеры. Не забывайте о производительности!

Давайте посмотрим на сцену с того места, где стоит наша условная камера:



Сейчас, преобразовав все точки в систему координат камеры, мы можем просто выбросить ось Z, а оси X и Y интерпретировать как горизонтальную и вертикальную. Если нарисовать все точки моделей на экране, то получится картинка, как в примере никакой перспективы и сложно понять, какая часть сцены на самом деле попадает в кадр. Камера как бы бесконечного размера во все стороны. Мы можем как-то все подогнать, чтобы то, что нужно, влезло на экран, но было бы неплохо иметь универсальный способ определения того, какая часть сцены попадет в поле зрения камеры, а какая нет.

У физических камер мы можем говорить о такой штуке, как угол обзора. Почему бы и здесь его не добавить?

Для этого нам понадобится еще одна матрица матрица проекции. Вообще строить ее можно по-разному. В зависимости от того, что взято за изначальные параметры, получится немного разный вид этой самой матрицы, но суть будет одна и та же. Мы возьмем следующий, немного упрощенный вариант:

// Пусть угол обзора будет 90 градусовconst s = 1 / (Math.tan(90 * Math.PI / 360));const n = 0.001;const f = 10;const projectionMatrix  = [    [s, 0, 0,          0],    [0, s, 0,          0],    [0, 0, -(f)/(f-n), -f*n/(f-n)],    [0, 0, -1,         0]];

Матрица проекции, так или иначе, содержит в себе три параметра это угол обзора, а также минимальное и максимальное расстояние до точек, с которыми идет работа. Выражаться это может по-разному, использоваться тоже по-разному, но эти параметры будут в этой метрице в любом случае.

Понимаю, что ни разу не очевидно, почему матрица выглядит именно так, но для ее выведения с пояснениями понадобится формул на 2-3 страницы. Это еще раз возвращает нас к мысли, что абстрагироваться полезно мы можем оперировать более общим результатом, не вдаваясь в мелкие детали там, где это не нужно для решения конкретной задачи.

Теперь, произведя уже знакомые преобразования:

для каждой точки в системе координат камеры {    координаты точки = [ матрица проекции ] * координаты в камере}

Получим в нашем поле зрения именно то, что и ожидаем. Увеличивая угол видим больше всего по сторонам, уменьшая угол видим только то, что ближе к направлению, куда камера направлена. PROFIT!



Но на самом деле нет. Мы забыли про перспективу. Бесперспективная картинка нужна мало где, так что нужно ее как-то добавить. И здесь, внезапно, нам не нужны матрицы. Задача выглядит очень сложной, но решается банальным делением координат X и Y на W для каждой точки:



* Здесь мы сместили камеру в сторону, и добавили параллельные линии на полу, чтобы было понятнее, где эта самая перспектива появляется.

Подбирая коэффициенты на свой вкус получим разные варианты перспективы. В каком-то смысле коэффициенты здесь определяют тип объектива, насколько он плющит окружающее пространство.

Теперь мы имеем полноценную картинку. Можно брать координаты X и Y для каждой точки и рисовать ее на экране любым удобным вам способом.

Вообще этого достаточно, чтобы построить сцену, но в реальных проектах вы также можете столкнуться с дополнительной трансформацией, связанной с масштабированием, в самом конце. Идея в том, что после проекции мы получаем координаты (x, y) в пределах 1, нормализованные координаты, а потом уже умножаем их на размер экрана или канваса, получая координаты для отображения на экране. Этот дополнительный шаг выносит размер канваса из всех вычислений, оставляя его только в самом конце. Иногда это удобно.

Здесь у вас, вероятно заболела голова от количества информации, так что притормозим и повторим еще раз все трансформации в одном месте:



Если эти трансформации объединить в одну, то получится паровозик.

Как это выглядит в Three.js?


Теперь, когда мы понимаем, откуда этот паровозик взялся, посмотрим на пример вершинного шейдера по умолчанию в Three.js, который ничего не делает:

void main() {    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}

или в более полном варианте:

void main() {    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);}

Ничего он вам не напоминает? Да, это именно этот паровозик. И под ничего не делает мы подразумеваем, что он как раз делает всю работу по пересчету координат, на основе заботливо переданных со стороны Three.js матриц. Но никто не мешает эти матрицы сделать своими руками, не так ли?

Типы камер в компьютерной графике и Three.js


Тема типов камер не касается непосредственно матриц, но все же уделим ей пару минут, раз уж все равно зашла речь про Three.js, а то порой у людей каша в голове по этому поводу.

Камера это абстракция. Она помогает нам мыслить в 3D мире таким же образом, как мы мыслим в мире реальном. Как мы уже сказали, у камеры есть положение в пространстве, направление, куда она смотрит, и угол обзора. Все это задается с помощью двух матриц и, возможно, дополнительного деления координат для создания перспекивы.

В компьютерной графике мы имеем два вида камер это с перспективой и без перспективы. Это два принципиально разных типа камер в техническом плане, требующих разных действий для получения изображения. И все. Больше ничего нет. Все остальное это их комбинации, какие-то более сложные абстракции. Например в Three.js есть стерео-камера это не какой-то особый тип камеры в техническом плане, а просто абстракция две камеры, немного разнесенные в пространстве и расположенные под углом:



Для каждой половины экрана мы берем свою камеру и получается стерео-картинка. А CubeCamera это 6 обычных камер, расположенных с разных сторон от какой-то точки, не более того.

Что дальше?


Следующий шаг, после получения координат объектов, это определение того, какие объекты будут видны, а какие скроются за другими. В контексте WebGL это браузер сделает сам. Ну и останутся еще сопутствующие задачи вроде наложения текстур на них, расчет освещения по нормалям, тени, постобработка картинки, и.т.д. Но самую важную и сложную для понимания часть мы уже сделали. Это очень здорово. На самом деле многие генеративные штуки не нуждаются в этих самых текстурах и освещении, так что очень может быть, что полученных сейчас знаний будет достаточно для работы с ними.

Кстати, отбрасывание тени от какого-то объекта на плоскость есть ни что иное, как проекция этого объекта на эту самую плоскость под определенным углом с последующим смешением цветов. Процесс по своей сути очень похож на работу камеры, но там добавляется еще угол между плоскостью проекции и направлением взгляда.

Про текстуры и эффекты для картинок на WebGL, в том числе без библиотек, мы говорили в предыдущих статьях уже не раз. Вы можете обратиться к ним, если вам эта тема интересна. Таким образом, объединив все эти знания воедино, мы сможем строить полноценные красочные 3D-штуки своими руками.

Мы действительно можем строить полноценные 3D-сцены своими руками. Но все же здесь важно соблюдать баланс для простой генеративной графики или каких-то эффектов для картинок это будет хорошей идеей. Для сложных сцен может быть проще все же взять готовый инструмент, потому что свой по объему будет приближаться к тому же Three.js и мы ничего не выиграем при избавлении от него. А если вам нужно естественное освещение, мех, стекло, множественные отражения или еще что-то в этом духе, то из соображений производительности может иметь смысл отказаться от самой идеи рендерить что-то в реальном времени и заранее заготовить картинки с видами на сцену с разных сторон и уже их показывать пользователю. Мыслите шире, не зацикливайтесь на определенном подходе к решению задач.

Промежуточные итоги


Сейчас самое время обобщить вышесказанное, чтобы в голове появилось место для следующего варианта применения матриц.

Итак:

  • Можно построить 3D мир и рассчитать координаты объектов на экране своими руками, используя паровозик из матриц.
  • В 3D мире мы оперируем такой абстракцией, как камера. Она имеет расмоложение, направление и угол обзора. Все это задается с помощью все тех же матриц. И есть два базовых вида камер с перспективой и без перспективы.
  • В контексте WebGL ручное построение картинки на экране или физические расчеты часто позволяют выбросить тяжелые зависимости и ускорить загрузку страниц. Но важно соблюдать баланс между своими скриптами, готовыми инструментами, а также альтернативными вариантами решения задач, уделяя внимание не только своему удобству, но и вопросам скорости загрузки и конечной производительности, в том числе на телефонах.

III. Фильтры для изображений


Напоследок мы рассмотрим такую область применения матриц, как фильтры для изображений. Если мы рассмотрим цвет в формате RGBA как вектор, то можно предположить, что здесь можно применить трансформацию, аналогичную той, что мы использовали с координатами:



И применить это для картинки по очевидному принципу:

для каждого пикселя картинки {    новый цвет пикселя = [ фильтр ] * старый цвет пикселя}

Если в качестве матрицы будет выступать единичная матрица ничего не изменится, это мы уже знаем. А что будет, если применить фильтры, схожие с трансформациями translate и scale?



Оу. Получились фильтры яркости и контраста. Занятно.

При экспериментах с такими фильтрами всегда следует помнить о коррекция значений, чтобы изображение не засвечивалось. Если вы что-то умножаете на большое число скорее всего нужно где-то что-то вычесть или поделить. Что и было показано в предыдущем примере.

А как сделать черно-белое изображение из цветного? Первое, что может прийти в голову это сложить значения каналов RGB, поделить на 3, и использовать полученное значение для всех трех каналов. В формате матрицы это будет выглядеть как-то так:



И хотя у нас действительно получилось черно-белое изображение, его все еще можно улучшить. Наш глаз на самом деле по-разному воспринимает светлость разных цветов. И для того, чтобы как-то это передать при обесцвечивании, мы делаем разные коэффициенты для каждого канала RGB в этой матрице.

В примере ниже будут представлены общепринятые значения для этих коэффициентов, но никто не мешает с ними поиграть. В сумме эти коэффициенты должны давать 1, но в зависимости от их пропорций мы получим немного разные черно-белые изображения. Это может в какой-то степени имитировать разную цветопередачу при работе с пленочными фотоаппаратами.

А если мы еще и домножим главную диагональ немного, то получится универсальный фильтр насыщенности:



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

Вообще играться с фильтрами можно долго, получая самые разные результаты:



* Использованные в этом примере матрицы можно посмотреть на GitHub, если они вдруг вам понадобились. Для вставки в статью их объем будет избыточным.

Но давайте все же уделим немного внимания тому, где это собственно применить. Понятно, что сама идея замены цвета для каждого пикселя наводит на мысли о шейдерах для обработки фото, либо для постобработки какой-то 3D-сцены, но может быть это еще где-то есть во фронтенде?

Фильтры в CSS


В CSS у нас есть свойство filter. И там, в частности, есть вот такие варианты фильтров, связанных с цветами:

  • brightness (мы его сделали)
  • contrast (сделали)
  • invert (то же, что и контраст, только коэффициенты по главной диагонали с другим знаком)
  • saturate (сделали)
  • grayscale (как уже отметили, это частный случай saturate)
  • sepia (очень размытое понятие, разные варианты сепии получаются игрой с коэффициентами, где мы так или иначе уменьшаем присутствие синего цвета)

И эти фильтры принимают на вход коэфициенты, которые потом в том или ином виде подставляются в те матрицы, которые мы сделали ранее. Теперь мы знаем, как эта магия устроена изнутри. И теперь понятно, как эти фильтры комбинируются в недрах интерпретатора CSS, ведь здесь все строится по тому же принципу, что и с координатами: умножаем матрицы складываем эффекты. Правда пользовательской функции matrix в этом свойстве в CSS не предусмотрено. Но зато она есть в SVG!

Фильтры-матрицы в SVG


В рамках SVG у нас есть такая штука, как feColorMatrix, которая применяется при создании фильтров для изображений. И здесь у нас уже есть полная свобода можем сделать матрицу на свой вкус. Синтаксис там примерно такой:

<filter id=my-color-filter>    <feColorMatrix in=SourceGraphics        type=matrix,        matrix=1 0 0 0 0                0 1 0 0 0                0 0 1 0 0                0 0 0 1 0                0 0 0 0 1    /></filter>

А еще SVG фильтры можно применить к обычным DOM-элементам в рамках CSS, там для этого есть специальная функция url Но я вам этого не говорил!

На самом деле SVG-фильтры в рамках CSS до сих пор не поддерживаются всеми браузерами (не показываю пальцем на IE), но ходят слухи, что Edge окончательно переезжает на хромиумный движок, и старые версии лишатся поддержки в обозримом будущем, так что пора эту технологию осваивать, с ней можно много всякого интересного делать.

А что еще бывает?


Помимо эффектов для картинок, построенных по принципу трансформаций, есть разные штуки построенные на смещениях пикселей, смешениях их цветов и других манипуляциях, где матрица может быть удачным форматом хранения данных, по которым эта самая манипуляция должна происходить.

Kernel matrix


В частности во фронтенде мы встречаем такую штуку, как kernel matrix, и связанными с ней эффектами. Суть проста есть квадратная матрица, обычно 3x3 или 5x5, хотя может быть и больше, а в ней хранятся коэффициенты. В центре матрицы для текущего пикселя, вокруг центра для соседних пикселей. Если матрица 5x5 то появляется еще один слой вокруг центра для пикселей, находящихся через один от текущего. Если 7x7 то еще один слой и.т.д. Иными словами мы рассматриваем матрицу как такое двумерное поле, на котором можно расставить коэффициенты по своему усмотрению, уже без привязки к каким-то уравнениям. А трактоваться они будут следующим образом:

для каждого пикселя картинки {    новый цвет пикселя =        сумма цветов соседних пикселей, умноженных на их коэффициенты из матрицы}

Чистый канвас не очень подходит для таких задач, а вот шейдеры очень даже. Но нетрудно догадаться, что чем больше матрица, тем больше соседних пикселей мы будем использовать. Если матрица 3x3 мы будем складывать 9 цветов, если 5x5 25, если 7x7 49 и.т.д. Больше операций больше нагрузка на процессор или видеокарту. Это неизбежно повлияет на производительность страницы в целом.

По возможности используйте маленькие матрицы для подобных эффектов, если вам нужно их накладывать куда-то в реальном времени.


В рамках SVG у нас есть специальный тег feConvolveMatrix, который сделан как раз для создания подобных эффектов:

<filter id=my-image-filter>    <feConvolveMatrix        kernelMatrix=0 0 0                      0 1 0                      0 0 0    /></filter>

Здесь мы сделали простейший фильтр для картинки, который не делает ничего новый цвет для каждого пикселя будет равен текущему, умноженному на 1, а значения для цветов соседних пикселей умножатся на 0.

Обратите внимание, что разные браузеры рендерят SVG по-разному, и цветопередача тоже может плавать в очень широких пределах. Порой разница просто катастрофическая. Так что всегда тестируйте свои SVG фильтры или используйте canvas это более предсказуемая штука в нашем контексте.

Если мы начнем расставлять числа по слоям, от большего у меньшему, то получится blur:



Чем больше матрица, чем больше соседних пикселей мы затрагиваем, тем сильнее мылится картинка. Главное здесь не забывать нормализовать значения, а то изображение просто засветится.

Теперь, зная как работает blur, мы можем понять, почему активное его использование на странице в рамках CSS или SVG ведет к тормозам для каждого пикселя браузер проделывает кучу расчетов.

Если начать экспериментировать со сменой знаков у коэффициентов и расставлять их в разные паттерны, то получатся эффекты sharpen, edge detection и некоторые другие. Попробуйте поиграть с ними самостоятельно. Это может быть полезно.



Таким образом можно делать разные эффекты для фотографий, или даже видео, в реальном времени, и делать их зависящими от каких-то действия пользователя. Здесь уже все зависит от вашей фантазии.

Промежуточные итоги


Обобщим сказанное в этой части:

  • Матрицы могут использоваться не только для трансформаций, связанных с координатами, но и для создания цветовых фильтров. Делается все по тому же принципу.
  • Матрицы могут использоваться как удобное 2D-хранилище для каких-то данных, в том числе разных коэффициентов для визуальных эффектов.

Заключение


Если немного абстрагироваться от запутанных алгоритмов, то матрицы станут вполне доступным инструментом для решения практических задач. С их помощью можно своими руками рассчитывать геометрические трансформации, в том числе в рамках CSS и SVG, строить 3D сцены, а также делать всевозможные фильтры для фотографий или для постобработки изображений в рамках WebGL. Все эти темы обычно выходят за рамки классического фронтенда и больше связаны с компьютерной графикой в целом, но, даже если вы не будете решать эти задачи непосредственно, знание принципов их решения позволит лучше понимать, как устроены некоторые ваши инструменты. Это лишним никогда не будет.

Надеюсь, что эта статья помогла вам разобраться в теме практического применения матриц во фронтенде, ну или по крайней мере дала основу, от которой можно оттолкнуться при своем дальнейшем развитии. Если вы считаете, что какие-то еще темы, связанные с математикой или физикой, достойны такого же обзора в контексте верстки, то пишите свои мысли в комментариях, возможно в одной из следующих статей речь пойдет и о них.
Подробнее..

Все есть бит

04.09.2020 20:08:16 | Автор: admin
Бог это вечная и бесконечная истина, не имеющая ценности и смысла.
Барух Бенедикт Спиноза

Сегодня я хочу рассказать вам о самой смелой и красивой гипотезе в современной теоретической физике. Многие ученые относятся к ней крайне скептически, некоторые называют ее откровенно шизофреническим бредом, а другие находят крайне интересной. Давайте же пустимся в путешествие, которое может навсегда изменить ваше представление о Вселенной.



В поисках теории всего


Начиная с середины 20-ого века самой сложной и перспективной задачей теоретической физики является поиск так называемой теории всего, которая объединит в себе общую теорию относительности и квантовую механику, тем самым дав точное объяснение всем наблюдаемым физическим явлениям. На роль такой теории претендуют многочисленные теории струн, теория квантовой петлевой гравитации и многие другие. Но мы будем говорить не о них. Мы сделаем шаг еще дальше.

Профессор MIT Макс Тегмарк в своей книге Наша математическая Вселенная призывает нас задуматься о самом удивительном свойстве всех существующих физических теорий, которое обычно люди считают само собой разумеющимся все наши физические теории описываются математикой.

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

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

Так что же будет с теорией относительности, квантовой механикой или пресловутой теорией всего, если мы выкинем из них всю словесную шелуху, вроде слов квант, пространство, свет. Там останутся только формулы, и ничего больше. И в этом месте рассуждений Макс Тегмарк задает интереснейший вопрос: что может полностью описываться чистой математикой? И он дает на него единственно разумный ответ. Чистой математикой может быть описана лишь сама чистая математика. Таким образом Тегмарк приходит к самой поразительной из возможных гипотез: вся наша Вселенная это математическая структура.

Все из бита


Макс Тегмарк не был первым, кто пришел к такой идее. Задолго до него эту идею выдвигал знаменитый американский физик, научный руководитель Ричарда Фейнмана, Хью Эверетта и Кипа Торна, а также автор терминов черная дыра и кротовая нора Джон Уилер.

В своей статье it from bit Джон Уилер задумывался над тем фактом, что все свойства элементарных частиц вроде массы, заряда, спина, цвета, странности и красоты не имеют никакого собственного смысла, а лишь проявляются при взаимодействиях с другими частицами. Таким образом, все эти свойства являются по сути битом информации в некоторой математической структуре. Уилер писал:
Все сущее каждая частица, каждое силовое поле, даже сам пространственно-временной континуум получают свою функцию, свой смысл и, в конечном счёте, самое своё существование даже если в каких-то ситуациях не напрямую из ответов, извлекаемых нами с помощью физических приборов, на вопросы, предполагающие ответ да или нет, из бинарных альтернатив, из битов. Всё из бита символизирует идею, что всякий предмет и событие физического мира имеет в своей основе в большинстве случаев в весьма глубокой основе нематериальный источник и объяснение; то, что мы называем реальностью, вырастает в конечном счёте из постановки да-нет-вопросов и регистрации ответов на них при помощи аппаратуры

Чтобы вы лучше поняли, что имел в виду Джон Уилер, я приведу вам в пример картинку из книги Макса Тегмарка о том, как отношения между точками пространства (ребра куба) можно представить в виде матрицы битов:



Сами вершины этого куба, обозначенные индексом от 1 до 8, не несут никакого смысла, а вот матрица отношений между ними (ребер куба) уже обладает некоторыми уникальными свойствами: например, вращательной симметрией. Наша Вселенная, конечно же, устроена на порядки сложнее куба, но в ее основе лежат те же самые принципы. Поняв это, мы можем двигаться дальше.

Инфляционная модель Вселенной и фракталы


Если мы все-таки живем в математической модели, то в какой?

Давайте посмотрим на нашу Вселенную: она состоит из множества скоплений миллиардов галактик, галактики состоят из миллиардов звезд, у многих звезд есть несколько планет, а у многих планет есть некоторое количество спутников. Более того, согласно гипотезе вечной инфляции, являющейся объяснением и расширением инфляционной модели развития вселенной, в отдаленном от нас пространстве ежесекундно происходят миллионы больших взрывов, порождающих свои пузыри Вселенных.

Но вернемся к нашему миру: все скопления, галактики, звезды и планеты, в какой бы части Вселенной они не находились, очень похожи между собой, но все же уникальны. Какая математическая структура обладает такими свойствами? Это фрактал.



Фрактал порождается простейшей рекуррентной формулой, но развивается в красивейшую циклическую картину, каждый маленький кусочек которой одновременно и уникален, и похож на общую структуру.

Асимметрия времени и вычисление рекурсивной функции


И как раз фрактальная структура нашей Вселенной открывает нам глаза на самую главную загадку современной физики время. Идет ли время только вперед? Линейно ли оно?

Современная физика говорит о существовании так называемой асимметрий времени или стрел времени. Первая стрела времени психологическая: мы помним прошлое, но не будущее. Эта ассиметрия является частным случаем более общей второй стрелы времени причинно-следственной. Причины порождают следствия, но не наоборот. С другой стороны это может быть лишь частью нашего восприятия и при обратном ходе времени мы бы приняли причины за следствия, а следствия за причины. Но существуют третья абсолютно объективная асимметрия времени, также называемая вторым законом термодинамики энтропия в замкнутой системе со временем всегда растет. То есть, при обратном ходе времени она бы падала.

Как это можно объяснить? Одним из первых объяснение, согласующееся с гипотезой математической Вселенной, дал немецкий пионер компьютеростроения и автор первого языка программирования высокого уровня Конрад Цузе. Он предположил, что наша Вселенная является не статичной математической моделью, а постоянно вычисляющийся чистой рекурсивной функцией. На вход такой функции поступает результат вычисления предыдущей итерации. Каждый тик такой функции является планковским временем, а проще говоря мгновением. Такая гипотеза очень хорошо объясняет все стрелы времени. Результат вычисления такой функции зависит от ее входа будущее зависит от прошлого, но не наоборот. Со временем количество информации в такой системе будет расти, а значит будет расти и энтропия. И главное, эта гипотеза очень хорошо согласуется с фрактальностью нашей Вселенной, ведь фрактал результат вычисления рекуррентной функции.

Таким образом, мы можем дать определение времени таким образом: время это процесс вычисления чистой рекурсивной функции расчета развития нашей Вселенной.

Вы можете возразить, что наша Вселенная недетерминирована и при коллапсе волновой функции Шредингера результат выхода кванта из суперпозиции непредсказуем. Но согласно многомировой интерпретации квантовой механики Эверетта в момент коллапса волновой функции наша Вселенная просто разделяется на две параллельных реальности, в одной из которых суперпозиция переходит в одно состояние, а в другой в противоположное.

Также стоит учесть, что это время не то же самое, что описывается в общей теории относительности Эйнштейна. Это абсолютное время тики процессора вычисляющего нашу Вселенную.

Матрица и антропный принцип


Но если вся наша Вселенная это вычислительная машина, то как определить, что мы живем не в Матрице? С одной стороны это недоказуемо и неопровергаемо. С другой стороны, если мы живем в Матрице и крутимся на компе у какого-то программиста из реальной вселенной, то его вселенная тоже будет подчинятся законам математики и тоже может оказаться Матрицей второго уровня, которая существует в реальном мире. Этот ряд можно продолжать до бесконечности и ни в одном уровне Матрицы не будет возможности доказать, существует или нет реальный мир более высокого уровня.

В любом случае, у Макса Тегмарка есть более красивое объяснение математичности нашей Вселенной. Для начала зададимся вопросом: почему мы живем именно в такой математической структуре, а не в какой-то другой? Тегмарк находит ответ на этот вопрос в антропном принципе: все непротиворечивые математические структуры существуют, но лишь в немногих из них может зародится такая тонко настроенная Вселенная, которая позволяет существовать нейронным сетям, способным осознать причинно-следственные связи.

Заключение



У гипотезы математической вычислимой Вселенной существуют интересные последствия: герои книг, фильмов, историй и даже ваш выдуманный друг столь же реальны, как и вы сами, так как точно так же являются математическими структурами, придуманными другой математической структурой внутри громадной математической структуры. Это заставляет задуматься над самим значением слова реальность.

Для более глубокого ознакомления с данной темой я рекомендую книгу Макса Тегмарка "Наша математическая Вселенная" и статью в википедии про цифровую физику.
Подробнее..

Перевод Простое и строгое доказательство 2610 измерений в теории струн

14.09.2020 08:09:56 | Автор: admin


вы нигде не найдете.


По крайней мере, у меня не получилось сделать его таковым. Требование определенного и большого числа пространственно-временных измерений (26 для более простой бозонной теории струн и 10 для более сложных суперструн) это один из наиболее неправильно понимаемых аспектов, который, собственно, является основным источником негативных чувств к данной теории. Придется очень постараться, чтобы объяснить происхождение этих странных чисел неспециалистам.


Строго говоря, действительно магическим числом является не сама D-мерность пространства-времени, а D2 измерений, поперечных струне, в которых она может колебаться (минус один для измерения времени и минус один для измерения, продольного струне). Другими словами, 1D-струна образует в пространстве-времени 2D-поверхность, называемую мировым слоем. Магическое число это число оставшихся направлений, доступных для струны. Так и выходит D-2.



Эта картинка была плохо нарисована по крайней мере тысячу раз, так что пусть будет еще одной плохой картинкой больше


24-поистине мистическое число. Джон Баэз дает фантастический отчет о том, почему его любимое число именно 24 которое является одним из драгоценных камней разбросанных по математике. Некоторые из них выглядят абсолютно ничем иным, как чистой нумерологией:


$ 1^2 + 2^2 + 3^2 + \ldots + 23^2 + 24^2 = 70^2 $


и это работает только для 24, за исключением 0 и 1, конечно. (Если вы любите сложные математические головоломки, попробуйте доказать это. А я не буду). И уж совсем невероятно, как это забавное тождество связано с неожиданно сложной и увлекательной (чудовищно вздорной) математикой и теорией струн (которая действует как клей для чудовищного вздора). Немаловажное значение имеет рождение 24/2 из ряда:


$$display$$ 1 + 2 + 3 + 4 + \ldots "=" -\frac{1}{12} $$display$$


Собственно, именно поэтому в бозонной теории струн D-2=24. В суперструнах эквивалентным безумием будет:


$$display$$ 1 - 2 + 3 - 4 + \ldots "=" \frac{1}{4} $$display$$


что дает D-2=8. В конце концов, это звучит как случайные несвязанные факты (и факты в кавычках), и хотя каждый из них может быть легко объяснен непрофессионалу, я на самом деле не объясняю, какова должна быть связь с числом измерений в теории струн. Я кажусь сумасшедшим не потому, что говорю что-то неправильное, а потому, что эти вещи непоследовательны и бессвязны. Проблема в том, что соединительная ткань слишком сложна с технической точки зрения, и с математической, и с физической, и с психологической, чтобы я мог ее просто объяснить.


Как с наименьшими возможными усилиями убедить когото, что D-2 = 24? Это намного проще, если этот кто-то принимает сумасшедшее уравнение 1+2+3+... = -1/12; но это явно не удовлетворительно строго. Даже если это можно понять, например, с помощью -регуляризации, то есть с регуляризацией теплового ядра + аналитической перенормировкой (и на этот счет уже есть много материала), то это все равно будет неудовлетворительно, поскольку нет никакой причины, по которой все эти манипуляции должны иметь какое-либо отношение к физике. Возможно ли прийти к правильным результатам без этого сумасшедшего уравнения; то есть, по сути, не сталкиваясь с какой-либо нерегулярностью для регуляризации? Да, конечно. Но как именно кратко и элементарно такое проделать?


Я обнаружил, что доказательства D=26 можно примерно классифицировать как:


  1. Квантование светового конуса
    • с сумасшедшим уравнением. Требуется немного знаний о поляризациях массивных / безмассовых векторных бозонов.
    • без сумасшедшего уравнения. Требуется, по существу, изучить все о квантовании струн / алгебре Вирасоро.
  2. Конформная теория поля
    • без сумасшедшего уравнения. Требуется знание конформных теорий поля и конформных аномалий.
  3. Модулярная инвариантность
    • с сумасшедшим уравнением. Требует довольно элементарной квантовой механики и математики.
    • без сумасшедшего уравнения. Требуется элементарная КМ, но с базовым нудным комплексным анализом.

Доказательство 1.1, я недавно привел здесь. Это довольно просто, но вы должны доверять сумасшедшему уравнению. Другое доказательство с сумасшедшим уравнением 3.1, находится в слайдах Баэза.


Доказательства, свободные от безумия, любопытным образом образуют нечестивую Троицу. Каждое из них требует знания только из одной вершины треугольника, который образуют:


  • Теория струн
  • Теоретическая физика
  • Математика

Доказательство 1.2 является наиболее распространенным во введении в теорию струн, так как вы уже изучаете необходимую физику струн в любом случае, и это математически не пугает. Доказательство 2.1 предназначено для более углубленного изучения и дает очень четкую физическую интерпретацию критического числа измерений, поскольку оно непосредственно связано с отменой конформной аномалии. Доказательство 3.2 я никогда раньше не видел оно кажется идеальным для людей, которые знают что-то из базового комплексного анализа (теорема вычетов и все такое) и некоторые азы квантовой механики, что не требует почти никаких знаний о физике струн.


Я очень старался выжать простое доказательство, которое является "строгим" (не использует сумасшедшее уравнение), но у меня ничего не вышло. Я пришел к убеждению, что три вершины треугольника следует понимать как перевод на разные языки одного и того же концептуального ядра, и это ядро нередуцируемо. Так что если два звена в цепи доведены до минимальной сложности, то третьему приходится все это съедать.


Все, что у меня вышло это следующая реализация доказательства 3.2. Оно отодвинет теоретическую и струнную физику на задний план и сосредоточит внимание на математике, то есть будет максимально строгим с математической стороны (хотя иногда будут замахи и на физику). Доказательство будет довольно длинным, но я нахожу его удовлетворительным. Мы соберем 24 по частям (как 2, умноженное на 3, умноженное на 4). Считайте это письмом с извинениями за освобождение 1+2+3+...=-1/12 в дикую природу.


Пруф


Мы уже говорили о мировой поверхности, которую струна образует в пространстве-времени. На этой поверхности мы можем установить систему координат $(\sigma^1,\sigma^2)$. Очевидно, что, хотя наблюдаемые могут быть записаны в терминах этих координат, они должны быть инвариантны при изменениях координат $(\sigma^1,\sigma^2) \rightarrow (\sigma^{1}\prime,\sigma^2 \prime)$. В конце концов, координаты изначально произвольны. Простым примером (локального) изменения координат является растяжение/масштабирование:


$ (\sigma^1, \sigma^2) \rightarrow (\lambda \sigma^1, \lambda\sigma^2) $


Итак, мы понимаем, что наблюдаемые в нашей теории должны быть инвариантны при таких масштабированиях. Это легко сделать классически, но становится очень нетривиальным, когда проблема переходит в удел квантовой механики. Эта масштабная симметрия является частью так называемой конформной симметрии мировой поверхности теории струн.


Представьте себе ситуацию, в которой струна делает петлю во времени, оставляя торовидный след в пространстве-времени. Конечно, множество возможных форм торов это все равно правильный выбор. Мы хотим вычислить вероятность того, что этот процесс произойдет с определенной формой тора, или лучше, квантовой амплитудой, квадрат модуля которой является вероятностью. Основной момент квантовой механики состоит в том, что мы можем вычислить амплитуду, суммируя $exp(iE_it)$ по всем возможным состояниям i, каждое из которых имеет энергию $E_i$, через время t. таким образом,


$ Z = \sum_i e^{i E_i t} $


это общая амплитуда. Z должна удовлетворять нашим симметриям.


Важным моментом является то, что составная система AB из двух невзаимодействующих подсистем A и B имеет вид $Z_{AB} = Z_A Z_B$. Благодаря этому хорошему свойству мы можем сначала сосредоточиться на вычислении Z струны, колеблющейся в одном поперечном измерении, а затем возвести ее в степени D-2, чтобы заставить ее колебаться в поперечных измерениях D-2.



Подобно реальным вибрирующим струнам, струны теории струн имеют гармоники, которые являются целыми кратными фундаментальной.


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


$ E_n = \omega \left( \frac{1}{2} + n \right) $


Так ведь? Таким образом, Z одного квантового гармонического осциллятора будет


$ Z_{QHO} = \sum_{n=0}^\infty e^{i E_n t} = e^{\frac{i}{2}\omega t} \sum_{n=0}^\infty e^{i n \omega t} = \frac{e^{\frac{i}{2} \omega t}}{1 - e^{i\omega t}} $


Геометрический ряд, который я только что суммировал, кажется, имеет отношение |r| = 1, что означает, что он на самом деле не сходится. Давайте сорвем повязку прямо сейчас: такие вот Z, определенные наивно как у нас, почти никогда не сходятся в лоренцевой (то есть пространственно-временной) теории. Часто мы, физики, говорим, что они осциллирующие, потому что мы суммируем кучу множителей exp(ix), а затем махаем руками, дескать, они каким-то волшебным образом отменяются, но это дешевая ложь мы просто имеем в виду, что они не сходятся. Это справедливо не только в теории струн, но и для всей квантовой теории поля, или для стандартной квантовой механики, или даже для низкочастотного квантового гармонического осциллятора.


И что дальше? Правильней будет сделать время комплексной переменной. Придавая ему мнимую часть, мы заставим Z сходиться. Мало того: когда вы вычисляете из этого непосредственно наблюдаемые величины, а затем возвращаете t обратно к реальной оси, вы восстанавливаете разумные ответы (которые соответствуют эксперименту, когда это возможно). Итак, это не трюк; этот рецепт наше определение того, что значит иметь квантовую теорию в пространстве-времени (не волнуйтесь, предположим, что t является сложным и в верхней полуплоскости Im t > 0.).


Чтобы вернуться в нужное русло, мы предположили, что одна струна в одном измерении имеет бесконечные моды колебаний, которые явно имеют частоты, кратные фундаментальной. На самом деле =1,2,3,... и поэтому Z для нашей 1D-струны является произведением


$ Z_{1,L} = \prod_{k=1}^\infty \frac{e^{\frac{i}{2}k t}}{1- e^{ikt}} = e^{\frac{i}{2} (1+2+3+\ldots)t } \left(\prod_k 1- e^{ikt}\right)^{-1} $


и со всего размаху бухаемся на дно. Безумие 1+2+3+ ... вновь настигло нас. Если бы мы были слабы духом, мы бы поддались и заменили его 1+2+3+...-1/12, и получили бы правильный ответ, пропустив почти всю математику в этой статье. Но мы здесь не для этого, мы здесь для того, чтобы выдавить 24 без использования колдовства. Поэтому давайте продолжим.


Почему расходящаяся сумма вообще появилась в этом показателе? Если вы проследите наши расчеты назад, то увидите, как оно вылазит из нулевых энергий КГО $ E_0 = \frac{\omega}{2} $. Однако энергии нулевого уровня произвольны, и это был просто полезный традиционный выбор. В конечном счете существует двусмысленность в построении квантового ГО из классического ГО, называемая упорядочивающей двусмысленностью, поскольку вам нужно преобразовать коммутирующие переменные p, q в некоммутирующие операторы $\hat p, \hat q$, и нет никакого предпочтительного квантования таких вещей, как pq. Нужно квантование $\hat p, \hat q$? Или $\hat p, \hat q$? Разницей будет константа произвол в энергии нулевой точки. Таким образом, наш самый консервативный заклад заключается в том, что нулевые колебания здесь фактически суммируются до конечного, но неизвестного значения:


$ Z_{1,L}^{-1} = e^{-irt} \; \prod_k 1 - e^{ikt} $


где r неизвестное вещественное число. Обратите внимание, что безумный расчет дал бы магическое значение r = 1/24. Итак, потребуется парочка трюков:


  • Я собираюсь показать, используя симметрию, что 1 / r это число поперечных измерений.
  • Я собираюсь показать, используя другую симметрию, что r = 1/24 в любом случае, даже без 1+2+3+...=-1/12

Чтобы начать говорить об этих симметриях, мне нужно связать мою переменную t с формой тора. Связь заключается в том, что тор строится путем склеивания одноцветных ребер в этом параллелограмме:



где t представляется в виде точки на комплексной плоскости. (Сторону 2 выбрали чтоб частоты =1,2,3,... были целыми числами). Или для чистоты нотации примем =t/2 (+ простое масштабирование, которое, я напоминаю, является симметрией):



Заметим, что при приближении к вещественной оси тор вырождается. Это имеет физический смысл: может ли струна действительно замкнуться в себе в реальном времени? Это было бы путешествие во времени. Такое может произойти только во мнимом времени (по крайней мере, в верхней полуплоскости).


При определении $q:=e^{i2\pi\tau}$, наша $Z_{1, L}$ становится


$ Z_{1,L}^{-1}(\tau) = q^r \; \prod_k (1-q^k) $


Трюк первый


Прежде всего, если мы сдвинем +1, то получим разные параллелограммы



но они образуют один и тот же тор после склеивания (проверьте это!). Если форма на самом деле не меняется, то и физические величины не должны меняться.


Однако при этом преобразовании наша амплитуда действительно преобразуется:


$ Z_{1,L}^{-1} \rightarrow e^{2\pi i r} \, Z_{1,L}^{-1} $


Это не имеет смысла пока мы не вспомним, что это только для одного измерения. Для поперечных размеров D2 полная амплитуда равна $Z^{D-2}_{1, L}$, и она останется инвариантной, если


$ r(D-2) = 1 $


это уравнение определяет критическое измерение теории струн. Если мы найдем целое значение 1 / r, то теория будет иметь смысл только в D = 1/r+2 пространственно-временных измерениях.


Теперь доказать, что r = 1/24, будет не так просто. Чтож, попробуем.


Второй трюк


Другая симметрия, которую мы будем рассматривать, это -1/. Полученные торы не идентичны, но они похожи это масштабированные версии друг друга. Вы можете проверить, что исходная сторона 1 совпадает с новой стороной -1 / , а исходная сторона совпадает с новой стороной 1. Как мы уже говорили, масштабирование мирового пласта должно быть симметрией.


Таким образом, полная амплитуда должна быть инвариантной; однако мы не должны ожидать этого от $Z^{D-2}_{1, L}$. А? Разве $Z^{D-2}_{1, L}$ не является полной амплитудой? Нет, я солгал, чтобы защитить вас от суровой правды. Истина (часть ее) состоит в том, что колебания на струне всегда происходят парами. Существует множество возможных характеристик этой дихотомии: левые и правые движущие силы, голоморфные и антиголоморфные, синус и косинус


Важно то, что для нашего $Z_{1, L}$ (L теперь понимается как левый) также будет парный $Z_{1, R}$ для сестринских колебаний. К счастью, я просто махну рукой, что $Z_{1, R} = Z_{1, L}^*$ так, чтобы общая амплитуда была произведением $|Z^{D-2}_{1, L}|^2$.


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


Какова амплитуда вероятности того, что квантовая частица в 1D останется в одном и том же месте через определенное время? Намек состоит в том, что для чисто мнимых времен уравнение Шредингера является уравнением теплопроводности. И бесконечно концентрированное пятнышко тепла эволюционирует в соответствии с уравнением теплопроводности в 1D, распространяясь в гауссовский пик, пик которого уменьшается как $(time)^{-1/2}$. Таким образом, позвольте мне предположить, что амплитуда для центра масс примерно такова


$ (\Im \tau)^{-1/2} $


Если это так, то, общая амплитуда должна быть примерно такой


$ Z \sim (\Im \tau)^{-\frac{D-2}{2}} \, | Z_{1,L} |^{2(D-2)} $


и для 1/:


$ \Im \tau \rightarrow \frac{1}{ | \tau |^2} \Im \tau $


поэтому, если бы $Z_{1, L}$ была трансформирована как-то так


$ Z_{1,L} \rightarrow \tau^{-1/2} Z_{1,L} $


наша общая амплитуда была бы инвариантной, и тогда теория имела бы смысл. Теперь я собираюсь доказать, что это может произойти только в том случае, если r=1/24.


Колдовство


Давайте сделаем некоторые предварительные переобозначения


$ Z_{1,L}^{-1} (\tau) = q^r P(\tau)\,,\quad P(\tau) := \prod_{\ell=1}^\infty (1-q^\ell) $


Тогда, наиболее удобным вариантом представления произведения P() будет:


$-\log P(\tau) = - \sum_{\ell=1}^\infty \log(1-q^\ell) = \sum_{l,k = 1}^\infty \frac{1}{k} q^{k\ell} = \sum_{k = 1} \frac{1}{k} \frac{q^k}{1-q^k} = \sum_{k=1} \frac{1}{k} \frac{1}{q^{-k} -1} $


Я использовал разложение Тейлора для -log(1-x) и геометрический ряд. Если вы не уверены, вы можете более тщательно проверить, разумны ли эти шаги (включая своп суммы) для Im >0.


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


$ f(w) = \cot w \cot \frac{w}{\tau} $


а затем, вводя еще один реальный параметр $\nu$ (наш завершающий ход будет заключаться в том, чтобы устремить его в бесконечность), мы строим комбинацию


$ g(w) = \frac{f(\nu w)}{w} $


Давайте посчитаем полюса g. Быстрый осмотр показывает, что существует набор простых полюсов при w=nkv и еще один при w=nktv, для k=1,2,3,...; плюс тройной полюс при w=0. Вычеты легко вычисляются следующим образом:


$ \operatorname{Res}_{\pm\frac{\pi k}{\nu}} g = \frac{1}{\pi k}\cot \left( \frac{\pi k}{\tau} \right)\\ \operatorname{Res}_{\pm\frac{\pi k \tau}{\nu}} g = \frac{1}{\pi k}\cot \left( \pi k \tau \right)\\ \operatorname{Res}_{0} g = - \frac{1}{3} (\tau + \tau^{-1}) $


Если вычет в тройной точке (полюс третьего порядка) не кажется очевидным, вспомним, что разложение Тейлора котангенса в нуле начинается $\cot s \sim \frac 1 s - \frac s 3$.



График g (w), для ( = i), ( = 1). Белые пятнышки это полюса, и порядок таков, сколько раз цвета повторяются вокруг них.


Теперь должно быть ясно, что g нам для применения теоремы вычетов. Рассмотрим траекторию бегущую против часовой стрелки вокруг параллелограмма с вершинами 1,,1,.


Теорема вычетов гласит:


$ \frac{1}{2\pi i} \int_\gamma \frac{f(\nu w)}{w} d\omega = \sum_{p \in \operatorname{poles}} \operatorname{Res}_p g $


Сумма на самом деле пробегает по полюсам, которые находятся внутри параллелограмма, но мы скоро увидим, что это не то, о чем нам следует беспокоиться.


Теперь я хотел бы использовать это уравнение, чтобы доказать тождество, которое нам действительно нужно. Однако я докажу это только для на мнимой оси, потому что это проще, но на самом деле это будет верно для всех Im >0. Поскольку обе части уравнения, которое я выведу, голоморфны в над верхней полуплоскостью, их согласия на прямой достаточно, чтобы доказать, что они всегда равны. Короче говоря, предположим, что пока чисто мнимое, но в конце мы можем просто отбросить это предположение.


Заделаем же интеграл на параллелограмме, который теперь ромб. Для $g(w)=f(\nu w)w^{1}$, кажется, первообразную сходу не отгадаешь. Итак, возьмем предел . Нетрудно заметить, что f(vw) сходится к константе (1,-1,1,-1) соответственно на четырех отрезках (если вы ее не видите, запишите ее с помощью комплексных экспонент). Таким образом, в пределе интеграл равен


$ \left( \int_1^\tau - \int_\tau^{-1} + \int_{-1}^{-\tau} - \int_{-\tau}^{1} \right) \frac{dw}{w} $


Легкотня! Интеграл от 1/w это log w, так что позвольте мне просто приписать его и ААА! Контур обхода! Нам нужно выбрать обход для логарифма и убедиться, что он не перепрыгивает через берег разреза. Мы можем использовать симметрию чтоб переписать


$ 2\left( \int_1^\tau + \int_1^{-\tau} \right) \frac{dw}{w} $


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


$ 4 \log \left(\frac{\tau}{i} \right) $


Славненько. Теперь о вычетах. Если , то все полюса сжимаются и приближаются к началу координат; таким образом, в пределе все они находятся внутри ромба, а сумма идет по всем полюсам. Таким образом, сумма равна


$ -\frac{1}{3} (\tau + \tau^{-1}) + 2 \sum_{k=1}^\infty \left( \frac{1}{\pi k} \cot(\frac{\pi k}{\tau}) + \frac{1}{\pi k} \cot(\pi k \tau) \right) $


на случай, если кто не помнит


$ \cot s = \frac{ e^{is} + e^{-is} }{e^{is} - e^{-is} } = \frac{1 + e^{-2is}}{1 - e^{-2is}} = -1 + \frac{2}{1-e^{-2is}} $


и наша сумма вычетов:


$ -\frac{1}{3} (\tau + \tau^{-1}) + \frac{4}\pi \sum_{k=1}^\infty \frac{1}{k} \left( \frac{1}{1-e^{-\frac{2\pi i k}{\tau}} } - \frac{1}{ 1- e^{-2 \pi i k \tau} } \right) \\ =-\frac{1}{3} (\tau + \tau^{-1}) + \frac{2}{\pi} \left(\log P(-1/\tau) - \log P(\tau) \right) $


Наконец-то мы возвращаемся на Землю. Это начинает выглядеть как теорема о нашем произведении P(). Теперь, когда у нас есть обе части равенства, давайте воспользуемся теоремой вычетов.


$ \frac{1}{2} \log\left(\frac{\tau}{i} \right) = - \frac{\pi i}{12} (\tau + \tau^{-1}) - \left(\log P(\tau) - \log(P(-1/\tau) \right) $


А вот и наше магическое число! По крайней мере, половина. Это все еще выглядит как тарабарщина, хотя давайте для пущей наглядности перейдем к экспонентам:


$ e^{\frac{2\pi i / \tau}{24}} P(-1/\tau) = \sqrt{\tau/i} \; e^{\frac{2 \pi i \tau}{24}} P(\tau) $


Казалось бы, ничего особенного, но комбинация


$ \eta(\tau) := e^{\frac{2\pi i \tau}{24}} P(\tau) = q^{\frac{1}{24}} \prod_{k=1}^\infty (1-q^k) $


как мы недавно доказали, красиво трансформируется при преобразовании -1/:


$ \eta(-1/\tau) = \sqrt{\frac{\tau}{i}} \eta(\tau) $


И это все! Именно это мы и искали! Чтоб преобразовать $Z^{-1}_{1, L}(\tau)$ во что надо, замечаем, что оно должно быть самим (), и поэтому r=1/24, и поэтому, наконец


$ D = 26 $


Выводы


Из-за философии, лежащей в основе всего этого поста, чтобы использовать математику как можно более элементарно и чтобы она была самодостаточной, мы ускорили то, что на самом деле является невероятно увлекательной (и, конечно же, гораздо более элегантной) математикой. () это, конечно, функция Дедекинда, и мы доказали ее свойства преобразования в модулярной группе; точнее, что модулярный дискриминант $\Delta(\tau):=(2\pi)^{12}\eta^{24}$ является модулярной формой веса 12. Теория струн тесно связана с этой областью математики; на самом деле я надеюсь, что для тех, кто уже знает этот материал, это послужило беглым взглядом на то, что струны даже имеют отношение к модульным формам. В любом случае, я не думаю, что могу судить предмет, который я на самом деле едва знаю по-верхам, так что тут попримолкну.


Иногда всплывает интересный вопрос: если это так неправильно, то почему 1+2+3+...=-1/12, или -регуляризация / тепловое ядро / суммирование Абеля или как бы вы это ни называли, дают малой кровью тот же правильный результат, что и более строгие пути?


У меня нет ни малейшего представления, почему оно так выходит.


Инстинктивно я бы пробормотал что-нибудь о физическом резоне или аналитичности, но, честно говоря, это просто чистое безумие. Это определенно не совпадение, потому что тот же трюк работает и для суперструн. На самом деле я даже не уверен, какова точная взаимосвязь между тремя различными классами доказательств, они выглядят как совершенно разные рассуждения. В квантовании светового конуса при первом чтении вы даже не понимаете, как масштабная / конформная инвариантность вписывается она очень глубоко зарыта. И если вы случайно знаете какую-то конформную теорию поля, посмотрите, как это доказательство строит 24. Они звучат совсем не как переводы одного и того же, хотя должны.


Удивительно, как много информации о содержании теории несут доказательства D=26 (или D=10), и как много вы уже узнаете о струнах, просто пытаясь объяснить то, что по существу является самым основным фактом теории. Это наглядный пример того, что в теории струн все сходится, ничего не бросается туда просто так, все существенно.


Дополнение


Возможно, существует более быстрое доказательство, если принять теорему Эйлера о пятиугольных числах


$ P(\tau) = \prod_{k=1}^\infty (1-q^n) = \sum_{k=-\infty}^{\infty} (-1)^k q^{k(3k-1)/2} $


Если вы выделите квадрат в экспоненте вы можете переписать


$ P(\tau) = q^{-1/24} \sum_{k=-\infty}^{\infty} (-1)^k q^{\frac{3}{2}(k-\frac{1}{6})^2} $


таким образом


$ \eta(\tau) = q^{1/24} P(\tau) = \sum_{k=-\infty}^{\infty} (-1)^k q^{\frac{3}{2}(k-\frac{1}{6})^2} $


С в таком виде уже реально доказать $\eta(1/\tau)=\sqrt{\tau/i}\eta(\tau)$ используя суммирование Пуассона.


Тем не менее, обращение к теореме Эйлера определенно похоже на мошенничество, поэтому я не пошел по этому пути.

Подробнее..

Перевод Насколько близко компьютеры подошли к автоматическому построению математических рассуждений?

16.09.2020 14:04:44 | Автор: admin

Инструментарий искусственного интеллекта определяет форму автоматических доказывателей теорем нового поколения, а вместе с этим и взаимоотношения математики и машин




Говорят, что в 1970-е годы ныне почивший математик Пол Джозеф Коэн, единственный лауреат Филдсовской премии за работы по математической логике, сделал огульное предсказание, которое до сих пор продолжает как восторгать, так и раздражать математиков: когда-нибудь в будущем математиков заменят компьютеры. Коэн, известный дерзостью методов в работе с теорией множеств, предсказал, что можно автоматизировать всю математику, включая и написание доказательств.

Доказательство это пошаговая логическая аргументация, подтверждающая истинность гипотезы или математического предположения. После появления доказательства гипотеза становится теоремой. Оно как подтверждает правильность утверждения, так и объясняет, почему оно верно. Но доказательство штука странная. Оно абстрактно, и не привязано к материальному опыту. Они представляют собой результат безумного контакта между вымышленным, не физическим миром, и существами, появившимися в результате биологической эволюции, сказал когнитивист Саймон Дедео из университета Карнеги-Меллона, изучающий математическую определённость через анализ структур доказательств. К этому нас эволюция не готовила.

Компьютеры хорошо подходят для объёмных вычислений, но доказательствам требуется нечто другое. Гипотезы возникают на основе индуктивных рассуждений особой интуиции, связанной с интересной задачей а доказательств обычно следуют дедуктивной, пошаговой логике. Им часто требуется сложное творческое мышление, а также тяжёлая работа по заполнению пустот, и машины с такой комбинацией навыков не справляются.

Компьютеризованные доказыватели теорем можно разбить на две категории. Автоматические доказыватели теорем (automated theorem provers, ATP) обычно используют методы прямого перебора, перемалывая огромные кучи цифр. Интерактивные доказыватели теорем (interactive theorem provers, ITP) служат ассистентами для человека, и умеют проверять точность аргументов, а также искать ошибки в существующих доказательствах. Однако даже если объединить две эти стратегии (как делают более современные доказыватели), автоматической рассуждающей системы из них не выйдет.


Когнитивист Саймон Дедео из университета Карнеги-Меллона помог продемонстрировать, что люди и машины создают математические доказательства похожим образом

Кроме того, эти инструменты мало кто приветствует, и большинство математиков не используют их и не одобряют. Для математиков это противоречивая тема, сказал Дедео. Большинству из них эта идея не нравится.

Одна из открытых трудных проблем в этой области вопрос о том, какую часть процесса создания доказательства можно автоматизировать. Сможет ли система сгенерировать интересную гипотезу и доказать её так, чтобы это было понятно людям? Набор недавних прорывов, достигнутых лабораториями по всему миру, предлагает способы ответить на этот вопрос при помощи искусственного интеллекта (ИИ). Джозеф Урбан из Чешского института информатики, робототехники и кибернетики в Праге, изучает различные подходы, использующие машинное обучение для увеличения эффективности существующих доказывателей. В июле его группа показала набор оригинальных гипотез и доказательств, созданных и подтверждённых машинами. В июне группа из Google Research под руководством Кристиана Сзегеди опубликовала результаты попыток использовать сильные стороны систем обработки естественного языка, чтобы сделать компьютерные доказательства более похожими по структуре и объяснениям на человеческие.

Некоторые математики считают доказыватели теорем инструментами, способными кардинально изменить обучение студентов написанию доказательств. Другие говорят, что заставлять компьютеры писать доказательства для передовой математики не нужно, а вероятно, и невозможно. Однако система, способная предсказать полезную гипотезу и доказать новую теорему, сможет достичь чего-то нового некоего машинного варианта понимания, сказал Сзегеди. А это говорит о возможности автоматических рассуждений.

Полезные машины


Математики, логики и философы давно уже спорят о том, какая часть создания доказательств является человеческой по своей природе, и дебаты о механизации математики продолжаются и сегодня особенно в тех местах, где информатика объединяется с чистой математикой.

Для специалистов по информатике доказыватели теорем не являются чем-то противоречивым. Они дают чёткий способ подтвердить работоспособность программы, а аргументы об интуиции и творческом начале менее важны, чем поиск эффективных способов решения задач. К примеру, Адам Члипала, специалист по информатике из Массачусетского технологического института, разработал инструменты для доказывания теорем, генерирующие криптографические алгоритмы, оберегающие транзакции в интернете при том, что обычно такие алгоритмы придумывают люди. Код его группы уже используется в большей части коммуникаций браузера Google Chrome.


Эмили Риел из Университета Джонса Хопкинса использует доказыватели теорем для обучения студентов и компьютерных ассистентов.

Можно взять любое математическое утверждение и закодировать его при помощи одного инструмента, а потом объединить все аргументы, и получить доказательство безопасности, сказал Члипала.

В математике доказыватели теорем помогли выдать сложные и перегруженные вычислениями доказательства, на которые иначе ушли бы тысячи математических человеко-лет. Ярким примером служит гипотеза Кеплера о плотнейшей упаковке шаров в трёхмерном пространстве (исторически это были апельсины или пушечные ядра). В 1998 году Томас Хейлс со своим студентом Сэмом Фергюсоном завершили это доказательство при помощи различных компьютеризованных математических технологий. Результат получился таким громоздким доказательство заняло 3 ГБ что 12 математиков несколько лет анализировали его, прежде чем объявить, что на 99% уверены в его истинности.

Гипотеза Кеплера не единственная знаменитая задача, решённая машинами. С теоремой о четырёх красках, утверждающая, что для закраски любой двумерной карты, при которой не будет никаких двух соприкасающихся участков одинакового цвета, всегда хватит четырёх красок, разобрались в 1977 году при помощи компьютерной программы, обработавшей пятицветные карты, и показавшей, что всех их можно превратить в четырёхцветные. В 2016 году трое математиков использовали компьютерную программу, чтобы доказать долго существовавшую булеву проблема пифагоровых троек, однако первая версия доказательства получилась размером в 200 ТБ. Если у вас достаточно быстрый канал в интернет, вы сможете скачать его недели за три.

Смешанные чувства


Такие примеры часто преподносятся как успехи, но они тоже добавляют свою толику в споры. Компьютерный код, более 40 лет назад доказавший теорему о четырёх красках, невозможно было проверить человеку. Математики с тех пор спорят, считать ли это доказательством, или нет, сказал математик Майкл Харрис из Колумбийского университета.


Многие математики вместе с Майклом Харрисом из Колумбийского университета сомневаются в необходимости создавать компьютеризированных доказывателей теорем и в том, что последние сделают математиков ненужными

Ещё одно недовольство математиков связано с тем, что если те хотят использовать доказыватели теорем, сначала им нужно научиться программировать, а потом придумать, как выразить свою задачу на понятном компьютеру языке а всё это отвлекает от занятий математикой. К тому времени, как я переформулирую вопрос в виде, подходящем для этой технологии, я эту задачу решу и сам, сказал Харрис.

Многие просто не видят необходимости в использовании решателей теорем. У них своя система, карандаш и бумага, и она работает, сказал Кевин Баззард, математик из Имперского колледжа в Лондоне, три года назад поменявший направление исследований от чистой математики к доказывателям теорем и формальным доказательствам. Компьютеры проводят для нас потрясающие вычисления, однако они никогда не решали сложную задачу самостоятельно, сказал он. И пока такого не случится, математики на всё это не купятся.

Но Баззард и другие считают, что, возможно, им всё-таки стоит присмотреться к технологиям. К примеру, компьютерные доказательства могут оказаться не такими чуждыми, как нам кажется, сказал Дедео. Недавно совместно со Скоттом Витери, специалистом по информатике из Стэнфорда, он провёл реверс-инжиниринг нескольких известных канонических доказательств (включая и некоторые из "Начал" Евклида) и десятков доказательств, сгенерированных компьютерной программой для доказательства теорем Coq в поисках схожих моментов. Они обнаружили, что ветвящаяся структура машинных доказательств была примечательно похожа на структуру доказательств, сделанных людьми. Это общее свойство, по его словам, может помочь исследователям найти способ заставить вспомогательные программы объясниться.

Машинные доказательства могут оказаться не такими загадочными, как кажется, сказал Дедео.

Другие говорят, что доказыватели теорем могут стать полезными инструментами для обучения как информатике, так и математике. В университете Джонса Хопкинса математик Эмили Риел разработала курсы, на которых студенты пишут доказательства при помощи доказывателей теорем. Это заставляет их быть очень организованными и ясно мыслить, сказала она. Студенты, пишущие доказательство впервые, могут не сразу понять, что от них требуется или осознать логическую структуру.

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

Доказыватели теорем также предлагают способ сохранения честности в математике. В 1999 году советский, российский и американский математик Владимир Александрович Воеводский, обнаружил ошибку в одном из своих доказательств. С тех пор вплоть до своей смерти в 2017 году он активно пропагандировал использование компьютеров для проверки доказательств. Хейлс сказал, что они с Фергюсоном нашли сотни ошибок в своих оригинальных доказательствах, проверив их при помощи компьютеров. Даже самые первые теоремы в Началах Евклида не идеальны. Если машина может помочь математикам избежать таких ошибок, почему бы этим не воспользоваться? Харрис предложил практическое возражение этому предложению, правда, неизвестно, насколько обоснованное: если математикам придётся тратить время на формализацию математики, чтобы её понял компьютер, это время они не смогут потратить на новую математику.

Однако Тимати Гауэрс, математик, лауреат Филдсовской премии из Кембриджа, хочет пойти ещё дальше: он представляет, как в будущем доказыватели теорем заменят людей-рецензентов в крупных журналах. Я вижу, как это может стать стандартной практикой если вы хотите, чтобы вашу работу приняли, вам нужно прогнать её через автоматизированного проверяющего.

Разговор с компьютерами


До того, как компьютеры смогут проверять или разрабатывать доказательства, исследователям сначала нужно преодолеть значительное препятствие: коммуникационный барьер между языками людей и компьютеров.

Сегодняшние доказыватели теорем разрабатывались без оглядки на дружественность к математикам. Первый их тип, ATP, обычно использовался для проверки истинности утверждения, часто через проверку всех возможных вариантов. Спросите ATP, можно ли проехать из Майами до Сиэтла, и он, вероятно, переберёт все города, к которым ведут дороги из Майами, и в итоге найдёт город, дорога из которого ведёт в Сиэтл.


Тимати Гауэрс из Кембриджского университета считает, что доказыватели теорем когда-нибудь заменят людей-рецензентов

Используя ATP, программист может закодировать все правила, или аксиомы, а потом задать вопрос, следует ли определённая гипотеза этим правилам. А потом всю работу делает компьютер. Вы просто вводите гипотезу, которую хотите доказать, и надеетесь получить ответ, сказал Дэниел Хуан, специалист по информатике, недавно ушедший из Калифорнийского университета в Беркли ради стартапа.

Но есть проблема: ATP не объясняет свою работу. Все вычисления идут внутри машины, а для человека они выглядят как длинная последовательность нулей и единиц. Хуан сказал, что просмотреть доказательство и проверить рассуждения невозможно, поскольку всё это выглядит как кучка случайных данных. Ни один человек не сможет посмотреть на такое доказательство и сказать: Всё понятно, сказал он.

У второй категории, ITP, есть огромные наборы данных, содержащие до десятков тысяч теорем и доказательств, при помощи которых они могут проверять точность доказательства. В отличие от ATP, работающих внутри чёрного ящика, который просто выдаёт ответы, ITP требуют взаимодействия и иногда указаний от человека, поэтому они не такие неприступные. Человек может сесть и разобраться в том, какие техники используются для доказательства, сказал Хуан. Такие доказательства и изучали Дедео и Витери.

В последние годы ITP становятся всё популярнее. В 2017 году троица, доказавшая булеву проблему пифагоровых троек использовала ITP под названием Coq, чтобы создать и проверить формальную версию своего доказательства. В 2005 году Жорж Гонтье из Microsoft Research Cambridge использовал Coq для формализации теоремы о четырёх красках. Хейлс тоже использовал ITP под названием HOL Light и Isabelle для формального доказательства гипотезы Кеплера (HOL это higher-order logic, логика высшего порядка).

Сегодня на переднем крае этой области пытаются объединить обучение с рассуждением. ATP часто комбинируют с ITP, интегрируя в них машинное обучение, чтобы улучшать эффективность обеих техник. Специалисты считают, что программы ATP/ITP могут использовать дедуктивные рассуждения и даже обмениваться математическими идеями так же, как это делают люди, или хотя бы похожим образом.

Пределы рассуждений


Джозеф Урбан считает, что такой комбинированный подход сможет поженить дедуктивное и индуктивное рассуждение, что необходимо для получения доказательств. Его группа создавала доказыватели теорем, работающие под управлением машинного обучения, позволяющего компьютерам самостоятельно обучаться на опыте. За последние несколько лет они изучали возможности нейросетей слоёв вычислительных единиц, помогающих машинам обрабатывать информацию способом, примерно похожим на работу нейронов нашего мозга. В июле их группа сообщила о новых гипотезах, сгенерированных нейросетью, обученной на доказательствах теорем.

Частью Урбан вдохновлялся работами Андрея Карпаты, который несколько лет назад обучил нейросеть на выдачу бессмыслицы математического вида, которая выглядела убедительно для непрофессионалов. Но Урбану не нужна была бессмыслица они с группой разработали собственный инструмент, который ищет доказательства, натренировавшись на миллионах теорем. Они использовали сеть для генерации новых гипотез и проверки их истинности при помощи ATP-программы под названием Е.

Сеть выдала более 50 000 новых формул, хотя десятки тысяч из них повторялись. Кажется, мы пока ещё не можем доказывать более интересные гипотезы, сказал Урбан.

Сзегеди из Google Research считает проблему автоматических рассуждений в компьютерных доказательствах частью гораздо более обширной области: обработки естественного языка, в которую входит распознавание закономерностей использования слов и предложений. Распознавание закономерностей также является основной идеей компьютерного зрения, которым Сзегеди ранее занимался в Google. Как и другие группы, его команда хочет создать доказыватели теорем, способные искать полезные доказательства и объяснять их.

Вдохновившись быстрым развитием ИИ-инструментов типа AlphaZero программы компании DeepMind, способной обыграть людей в шахматы, го и сёги группа Сзегеди хочет воспользоваться последними достижениями в области распознавания языка, чтобы записывать доказательства. Он сказал, что языковые модели могут демонстрировать неожиданно точные математические рассуждения.

Его группа в Google Research недавно описала способ использовать языковые модели которые часто используют нейросети для генерации новых доказательств. Обучив модель распознавать древовидную структуру доказанных теорем, они запустили свободный эксперимент, просто предлагая нейросети генерировать и доказывать теоремы без надзора. Из тысяч сгенерённых гипотез 13% оказались доказываемыми и новыми (не повторяющими другие теоремы в базе). Он сказал, что такой эксперимент говорит, что нейросети могут научиться в каком-то смысле понимать, как выглядит доказательство.

Нейросети способны выработать искусственное подобие интуиции, сказал Сзегеди.

Конечно, до сих пор не ясно, исполнят ли эти попытки пророчество Коэна сорокалетней давности. Гауэрс сказал, что он считает, что компьютеры смогут опередить математиков в рассуждениях к 2099 году. Он говорит, что сначала математики будут наслаждаться золотым веком, когда они будут заниматься интересными вещами, а компьютеры скучными. Но я думаю, что это продлится очень недолго.

Ведь если машины будут всё сильнее развиваться, и иметь доступ к огромному количеству данных, они должны научиться очень хорошо делать и интересные вещи. Они научатся делать собственные запросы, сказал Гауэрс.

Харрис не соглашается. Он не считает компьютерные доказыватели необходимыми, или что они в итоге сделают людей-математиков ненужными, Если специалисты по информатике, говорит он, когда-нибудь и смогут запрограммировать синтетическую интуицию, она всё равно не будет соперничать с человеческой. Даже если компьютеры будут понимать, они не будут понимать в человеческом смысле.
Подробнее..

Перевод Почему единственный вид деревьев не победил все остальные?

21.09.2020 10:09:24 | Автор: admin

Лес красив, дремуч, высок, писал Роберт Фрост. Этот ямб вспоминается мне каждый раз, когда я начинаю путь по расположенной недалеко от моего дома лесной тропе. Тропу назвали в честь Фроста, который провёл несколько лет в этой части Массачусетса, обучая мальчиков в блейзерах с медными пуговицами из Амхерстского колледжа. Прогуливался ли поэт среди именно этих деревьев? Такое возможно, хотя тогда, век назад, они были ещё молодняком. Как бы то ни было, если он и останавливался возле этого леса, то ненадолго, ведь ждут меня сегодня в срок, а до ночлега путь далек.

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

Сегодняшний вопрос: почему в смешанном лесу виды деревьев остаются смешанными?

Двигаясь по тропе Фроста, я провёл краткую перепись, зафиксировав тсугу канадскую, сахарный клён и не менее трёх видов дуба (красный, белый и болотный), бук и берёзу, карию яйцевидную, веймутову сосну и ещё два дерева, которые не смог идентифицировать даже с помощью руководства Питерсона и iNaturalist. Ближайшем к моему дому лесу доминирует тсуга канадская, но по склонам холмов несколькими милями дальше по тропе более распространены широколистные виды. На фотографии ниже показан перевал (который местные называют Notch) между двумя пиками хребта Холиок к югу от Амхерста. Я сделал этот снимок 15 октября прошлого года, в то время года, когда благодаря осенним цветам удобнее определять разнообразие видов.


Подобные леса покрывают большую часть восточной половины США. Виды деревьев варьируются со сменой широты и высоты, но любом месте полога леса чаще всего содержится от восьми до десяти видов. Отдельные изолированные участки ещё богаче; в некоторых долинах южных Аппалачей, называемых cove forests, насчитывается до 25 составляющих полог видов. А тропические дождевые леса населены сотней или даже двумястами видами высоких деревьев.

С точки зрения экологической теории такое разнообразие сбивает с толку. Можно было бы предположить, что в любой среде один вид будет чуть лучше адаптирован, а потому вытеснит все остальные, прийдя к доминированию на ландшафте.

Этот принцип впервые был описан Гарретом Хардином. Его математическую формулировку, вероятно, создал Вито Вольтерра.

Экологи называют эту идею принципом конкурентного вытеснения. Он гласит, что два или более вида, конкурирующих за одни ресурсы, не могут постоянно сосуществовать в одной среде обитания. Со временем все, кроме одного конкурента, придут к локальному вымиранию. Кажется, что деревья полога леса конкурируют за одинаковые ресурсы солнечный свет, CO2, воду, различные минеральные питательные вещества, поэтому постоянство смешанных лесов требует объяснения.

Вот небольшое демо конкурентного вытеснения. Два вида деревьев давайте назовём их оливками и апельсинами делят между собой один кусок леса, квадратную площадку, вмещающую 625 деревьев.

Исходный код шести компьютерных симуляций из этой статьи выложен на GitHub.


Изначально каждой точке случайным образом назначается дерево одного или другого вида. При нажатии на кнопку Start (или касании массива деревьев) вы запускаете цикл смертей и обновлений. На каждом этапе времени выбирается одно дерево, совершенно случайно и безотносительно его вида, и попадает под топор. Затем выбирается другое дерево в качестве родителя замены, определяя таким образом его вид. Однако этот выбор не полностью случаен, у него присутствует смещённость. Один из видов лучше приспособлен к среде, использует доступные ресурсы эффективнее, а потому имеет повышенный шанс на воспроизводство и размещении своего отпрыска на свободной точке. В панели управления под массивом деревьев есть ползунок fitness bias; при сдвиге влево он отдаёт предпочтение апельсинам, вправо оливкам.

Результат этого эксперимента не должен вас удивлять. Два вида играют в игру с нулевой суммой: какую бы территорию ни захватили оливки, апельсины должны с неё пропасть, и наоборот. Точка за точкой, более приспособленный вид захватывает всё. Если преимущество очень незначительно, то процесс может занять долгое время, но в конечном итоге менее эффективный организм всегда оказывается уничтоженным. (Что если два вида полностью равны? Чуть позже я вернусь к этому вопросу, а пока давайте просто притворимся, что такого никогда не бывает. И я хитро модифицировал симуляцию, чтобы вы не могли установить смещение на ноль.)

Конкурентное вытеснение не запрещает любого совместного существования. Допустим, оливковые и апельсиновые деревья используют два минеральных питательных вещества из почвы скажем, железо и кальций. Предположим, оба эти элемента находятся в дефиците, и их доступность ограничивает рост популяций деревьев. Если оливки лучше впитывают железо, а апельсины более эффективно поглощают кальций, то эти два вида могут создать систему, в которой они оба выживают.


В этой модели ни один вид не приходит к вымиранию. При стандартных настройках ползунков, когда железо и кальций находятся в среде в одинаковом соотношении, оливковые и апельсиновые деревья тоже сохраняют в среднем приблизительно равные количества представителей вида. Случайные флуктуации отталкивают их от этой точки равновесия, но не очень далеко и не очень надолго. Популяции стабилизируются благодаря отрицательной обратной связи. Если случайное отклонение увеличивает пропорцию оливковых деревьев, то каждое из этих деревьев получает меньшую долю доступного железа, уменьшая таким образом потенциал дальнейшего роста популяции вида. Апельсиновым деревьям меньше вредит дефицит железа, а поэтому их популяция возвращается к исходным значениям. Но если затем апельсиновые деревья их превысят, то их ограничит чрезмерное использование ограниченного снабжения кальцием.

Смещая ползунок влево или вправо, мы меняем баланс железа и кальция в среде. Соотношение 60:40 в пользу железа сдвинет равновесие между двумя видами деревьев, позволив оливкам занять больше территории. Но пока соотношение ресурсов не становится слишком большим, находящемуся в меньшинстве виду не грозит вымирание. Два вида деревьев придерживаются принципа живи и дай жить остальным.

В терминологии экологии, виды оливковых и апельсиновых деревьев избежали правила конкурентного вытеснения, потому что занимают отдельные ниши (роли) в экосистеме. Они являются специалистами, предпочитающими использовать разные ресурсы. Ниши не обязаны быть совершенно раздельными. В показанной выше симуляции они частично пересекаются: оливкам наряду с железом нужен и кальций, но только 25%; апельсиновые деревья имеют зеркально противоположные требования.

Позволит ли эта брешь в законе конкурентного вытеснения существование более чем двух видов? Да: N конкурирующих видов может сосуществовать, если есть как минимум N независимых ресурсов или природных аспектов, ограничивающих их рост, и если каждый вид имеет собственный ограничивающий фактор. Все должны иметь специализацию.

Принцип разбиения экосистемы на множество ниш устоявшаяся практика в сфере биологии. Именно так Дарвин объяснял разнообразие вьюрков на Галапагосских островах, где десяток видов различаются по среде обитания (почва, кустарники, деревья) или питанию (насекомые, зёрна и орехи разного размера). Лесные деревья могут быть организованы похожим образом, с различными микросредами, подходящими разным видам. Процесс создания такого разнообразного сообщества называется образованием экологических ниш.

Определённая нишевая дифференциация чётко заметна среди лесных деревьев. Например, камедные деревья и ивы предпочитают влажную почву. Однако в своём ближайшем лесу я не смог обнаружить никаких систематических различий участков, колонизированных клёнами, дубами, кариями и другими деревьями. Часто они являются близкими соседями на участках земли с одинаковой крутизной и высотой, растущими на почве, которая кажется мне одинаковой. Возможно, я просто ещё не научился распознавать эти тонкости роста деревьев.

Образование экологических ниш особенно ошеломляет в тропиках, где требуются от сотни и больше отдельных ограничивающих ресурсов. Каждый вид деревьев заведует собственной крошечной монополией, заявляя права на какой-нибудь фактор среды, который всем остальным совершенно не важен. В то же время все деревья ожесточённо конкурируют за самые важные ресурсы, а именно солнечный свет и воду. Каждое дерево стремится занять пустоту в пологе леса с прямым доступом к небу, где оно сможет расправить свои листья и ежедневно впитывать фотоны. Учитывая экзистенциальную важность победы в этой конкуренции за свет, кажется странным связывать разнообразие лесных сообществ с борьбой за другие, менее важные ресурсы.

При образовании экологических ниш каждый вид становится победителем в своём собственном небольшом соревновании; ещё одна теория снимает вопрос о конкуренции, предполагая, что деревья и не пытаются обойти другие виды. Они просто перемещаются случайным образом. Согласно этой концепции, называемой нейтральным экологическим дрейфом, все деревья одинаково хорошо адаптированы к своему природному окружению, а множество видов, появившееся в любом конкретном месте и в любое время вопрос вероятности. Участок может быть занят дубом, но там могут процветать и клён или берёза. Естественному отбору нечего отбирать. Когда дерево погибает, и на его месте вырастает другое, природу не волнует вид пришедшего на замену дерева.

Эта идея возвращает нас к тому вопросу, который я обошёл выше: что случится, если два конкурирующих вида имеют совершенно одинаковую приспособленность? Ответ будет одинаковым и для двух, и для десяти видов, поэтому ради визуального разнообразия мы рассмотрим более обширное сообщество.


Если вы запустите симуляцию и терпеливо дождётесь её завершения, то увидите монохромный массив деревьев. Я не могу угадать, каким будет единственный цвет, оставшийся на вашем экране или, другими словами, какой вид захватит весь участок леса, но я знаю, что останется только один вид. Остальные девять вымрут. В этом случае такой результат может считаться как минимум довольно неожиданным. Ранее мы выяснили, что если вид имеет даже малейшее преимущество перед соседями, то он захватит всю систему. Теперь мы видим, что не требуется даже преимущество. Даже когда все игроки абсолютно равны, один из них станет царём горы, а все остальные будут уничтожены. Жестоко, правда?

Вот график одного прогона программы, демонстрирующий количество представителей каждого вида как функцию от времени:


Вначале все 10 видов представлены приблизительно равным количеством, сгруппированным рядом со средней численностью, равной $625/10$. После запуска программы сетка начинает быстро и многократно менять цвета. Однако за первые 70 000 шагов времени все, кроме трёх видов, исчезают. Три выживших вида по очереди получают лидерство в процессе распространения контрастных цветов по массиву. Затем, приблизительно спустя 250 000 шагов, вид, представленный ярко-зелёной линией снижается до нулевой популяции, то есть вымирания. Последний этап борьбы один на один очень неравномерный оранжевый вид близок к полному доминированию, а бордовый барахтается рядом с границей вымирания, и всё-же это перетягивание каната длится ещё 100 000 шагов. (Как только система достигает состояния моновида, ничего не может больше измениться, поэтому программа останавливает выполнение.)

Такой перекошенный результат нельзя объяснить каким-нибудь скрытым смещением в алгоритме. В любой момент времени для всех видов вероятность получения нового представителя вида полностью равна вероятности потери представителя. Давайте ненадолго приостановимся, чтобы убедиться в этом факте. Допустим, вид $X$ имеет популяцию $x$, которая должна находиться в интервале $0 \le x \le 625$. Случайно выбранное дерево будет относиться к виду $X$ с вероятностью $x/625$; следовательно, вероятность того, что дерево относится к какому-то другому виду, должна быть $(625 - x)/625$. $X$ получает одного представителя, если это заменяющий вид, но не вид-жертва, такое событие имеет совместную вероятность $x(625 - x)/625$. $X$ теряет одного представителя, если он является жертвой, но не заменой, что имеет ту же вероятность.

Это честная игра, нет никаких шулерских костей. Тем не менее, кто-то срывает джекпот, а остальные игроки каждый раз теряют всё.

Спонтанное угасание разнообразия видов на этом симулируемом участке целиком вызвано случайными флуктуациями. Взглянем на популяцию $x$ как на метку, случайно блуждающую по отрезку прямой, где с одного конца $0$, а с другого $625$. На каждом шаге времени метка с равной вероятностью сдвигается на одну единицу вправо $(+1)$ или влево $(-1)$; при достижении любого из концов отрезка игра завершается. Наиболее фундаментальным фактом о таком блуждании является то, что оно всегда заканчивается. Блуждание, бесконечно колеблющееся между двумя границами, не невозможно, но имеет вероятность $0$; столкновение с одной или другой стеной имеет вероятность $1$.

Какой будет ожидаемая длительность такого случайного блуждания? В простейшем случае с одной меткой ожидаемое количество шагов, начавшихся в позиции $x$, равно $x(625 - x)$. Это выражение имеет максимум, когда блуждание начинается в середине отрезка прямой; максимальная длительность немного меньше $100000$ шагов. В симуляции леса с десятью видами ситуация более сложна, потому что коррелируют, или, скорее, антикоррелируют, несколько блужданий: когда одна метка делает шаг вправо, другая должна сдвинуться влево. Вычислительные эксперименты дают нам понять, что медианное время, необходимое десяти видам для сокращения до одного, приблизительно равно $320000$ шагов.

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

Здесь стоит не забывать о временном масштабе процесса. В симуляции время измеряется подсчётом циклов смертей и замен деревьев леса. Не знаю точно, как преобразовать это в календарные годы, но могу предположить, что 320 тысяч событий смертей и замен для 625 деревьев может занять 50 тысяч и более лет. У нас в Новой Англии это очень долгое время для жизни леса. Весь существующий ландшафт был очищен Лаурентидским ледниковым щитом всего 20 тысяч лет назад. Если местные леса теряют виды со скоростью случайного дрейфа, то у нас ещё не было времени дойти до конца игры.

Проблема в том, что это утверждение подразумевает, что в начале леса разнообразны и постепенно развиваются в сторону монокультуры, что не подтверждается наблюдениями. Скорее наоборот, похоже, разнообразие со временем увеличивается. Cove forests штата Теннесси, которые намного старше лесов Новой Англии, содержат больше, а не меньше видов. А сверхразнообразная экосистема тропических дождевых лесов, предположительно, существует миллионы лет.

Несмотря на эти концептуальные помехи, многие экологи активно защищают идею нейтрального экологического дрейфа. Наиболее примечательным примером является книга Стивена Хаббелла The Unified Neutral Theory of Biodiversity and Biogeography 2001 года. Основным аспектом защиты этой идеи Хаббеллом (если я понял его правильно) является то, что 625 деревьев не составляют лес, и уж точно не образуют всю экосистему планеты.

На теорию нейтрального дрейфа Хаббелла вдохновили предыдущие исследования биогеографии островов, в частности, совместная работа Роберта Макартура и Эдварда Уилсона, проводившаяся в 1960-х. Предположим, что наш небольшой участок из $625$ деревьев растёт на отделённом от континента острове. По большей мере остров эволюционирет в изоляции, но время от времени птица приносит семя из гораздо большего леса на материке. Мы можем симулировать эти редкие события, добавив в модель нейтрального дрейфа возможность иммиграции. В показанном ниже демо ползунок управляет уровнем иммиграции. При взятом по умолчанию значении $1/100$ каждое сотое приходящее на замену дерево берётся не из местного леса, а из стабильного резерва, в котором все $10$ видов имеют равную вероятность выбора.


В течение первых нескольких тысяч циклов эволюция леса происходит почти так же, как в модели с обычным дрейфом. Сначала краткий период полного хаоса, затем по лесу распространяются волны цветов, он становится то розовым, то бордовым, то ярко-зелёным. Отличие в том, что ни один из этих экспансивных видов никогда не сможет захватить весь массив целиком. Как показано на графике ниже, они никогда сильно не превышают 50% от общей популяции, а потом уходят в тень других видов. Позже ещё один цвет деревьев пытается создать империю, но встречает ту же судьбу. (Так как у этого процесса нет чёткой точки завершения, симуляция останавливается спустя 500 000 циклов.)


Иммиграция, даже при низких уровнях, обеспечивает качественное изменение в поведении модели и судьбе леса. Важное отличие заключается в том, что мы больше не можем сказать, что вымирание происходит навсегда. Вид может исчезнуть из массива с 625 деревьями, но рано или поздно будет заново импортирован из постоянного резерва. Следовательно, вопрос заключается не в том, живёт вид или вымер, а в том, есть он или нет на текущий момент времени. При коэффициенте иммиграции $1/100$ среднее количество существующих видов примерно равно $9,6$, то есть ни один из них не исчезает надолго.

При более высоком уровне иммиграции 10 видов остаются тщательно перемешанными, и ни один из них не делает никаких успехов на пути к мировому доминированию. С другой стороны, они имеют небольшой риск исчезновения, хоть и временного. Перетащите ползунок до конца влево, установив коэффициент иммиграции на $1/10$, и массив леса превратится в случайно мерцающие огоньки. На графике ниже нет ни одного случая вымирания.


Если перетащить ползунок до упора в другую сторону, то менее частая иммиграция позволит распределению видов намного сильнее отдалиться от состояния равенства. На графике, где иммигрант появляется на каждом $1000$-м цикле, в популяции бОльшую часть времени доминируют один-два вида; другие виды часто находятся на грани вымирания или за ним, но рано или поздно возвращаются. Среднее количество живых видов примерно равно $4,3$, а иногда остаётся всего два вида.


При коэффициенте $1/10000$ влияние иммиграции едва заметно. Как и в моделях без иммиграции, один вид захватывает весь участок; в показанном ниже примере для этого потребовалось около $400000$ шагов. После этого возникающие изредка события иммиграции создают небольшие всплески на кривой, но другой вид сможет заменить победителя ещё очень нескоро.


Островная структура этой модели позволяет понять, как спорадические, слабые связи между сообществами могут иметь огромное влияние на их развитие. Но острова в этой модели необязательны. Деревья, известные своей неподвижностью, редко имеют взаимосвязь на дальних расстояниях, даже если их не разделяют водоёмы. Хаббелл сформулировал модель экологического дрейфа, в которой множество небольших участков леса упорядочено в иерархическое метасообщество. Каждый участок одновременно является и островом, и частью более крупного резервуара разнообразия видов. Если подобрать подходящие размеры участков и коэффициенты миграции между ними, то можно поддерживать равновесие множества видов. Хаббелл также учитывает возникновение совершенно новых видов, которое тоже рассматривается как случайный или нейтральный к отбору процесс.

Теории образования экологических ниш и нейтрального экологического дрейфа вызывают у скептиков зеркальные вопросы. В случае образования экологических ниш мы смотрим на десятки или сотни сосуществующих видов деревьев, и задаёмся вопросом: Может ли каждый из них иметь уникальный ограничивающий ресурс?. В случае нейтрального дрейфа мы спрашиваем: Могут ли все эти виды иметь полностью одинаковую приспособленность?

Хаббелл отвечает на последний вопрос, переворачивая его с ног на голову. Сам факт того, что мы наблюдаем сосуществование, подразумевает равенство:

Все виды, которым удаётся сохраняться в сообществе в течение долгих периодов времени, должны демонстрировать уровни долгосрочного нетто-прироста популяции, приблизительно равные нулю. Если бы это было не так, т.е. если бы какие-то виды смогли бы достичь положительного уровня прироста на длительный период времени, то из нашего первого принципа биотической насыщенности ландшафтов следует, что они неизбежно устранили бы остальные виды из сообщества. Но если в локальных или региональных масштабах все виды имеют одинаковый нулевой уровень нетто-прироста популяции, то в силу самого этого факта они должны иметь одинаковую или почти одинаковую относительную приспособленность.

Герберт Спенсер провозгласил выживание наиболее приспособленного. У нас есть следствие из этого девиза: если все они выжили, то все должны быть одинаково приспособлены.

А теперь нечто совершенно иное.

Ещё одна теория разнообразия лесов была разработана специально как ответ на самый сложный вопрос загадку чрезвычайного разнообразия деревьев в тропических экосистемах. В начале 1970-х Джозеф Хёрд Коннелл и Дэниел Хант Дженсен, полевые биологи, работавшие независимо друг от друга в разных частях света, почти одновременно пришли к одинаковой идее. Они предположили, что в тропических лесах деревья применяют социальное дистанцирование как защитную меру против заражений, и это стимулирует разнообразие.

(Фраза социальное дистанцирование не встречается в написанных пятьдесят лет назад работах Коннелла и Дженсена, но сегодня я не могу удержаться от искушения описать их теорию таким образом.)

Выживать в тропическом дождевом лесу непросто. Деревья находятся под давлением частых атак банд мародёров: хищников, паразитов и патогенов. (Коннелл объединил всех этих злодеев общим названием враги.) Многие из врагов являются специалистами, нацеленными на деревья только одного вида. Специализацию можно объяснить конкурентным вытеснением: каждый вид деревьев становится уникальным ресурсом, поддерживающим один тип врага.

Предположим, что дерево атаковано крупной популяцией специфических по хозяину врагов. Рой злодеев нападает не только на взрослое дерево, но и на всех потомков хозяина, пустивших корни рядом с родителем. Так как молодые деревья уязвимее взрослых, будет уничтожена вся группа.


Ростки, находящиеся на большем расстоянии от родителя, имеют бОльшую вероятность остаться необнаруженными, пока они не вырастут и не станут достаточно устойчивыми, чтобы сопротивляться нападениям. Другими словами, эволюция, вероятно, благоволит тому яблоку, которое упало подальше от яблоньки. Дженсен иллюстрирует эту идею графической моделью, похожей на показанную выше. С увеличением расстояния от родителя вероятность появления и укоренения семени становится меньше (красная кривая), однако вероятность того, что такой росток доживёт до зрелости, становится больше (синяя кривая). Общая вероятность успешной репродукции является произведением этих двух факторов (фиолетовая кривая); её вершина находится на пересечении красной и синей кривых.

Теория Коннелла-Дженсена прогнозирует, что деревья одного вида будут обширно распределены по лесу, оставляя много пространства между собой для деревьев других видов, которые тоже будут иметь столь же рассеянное распределение. Этот процесс приводит к антикластеризации: деревья одного вида в среднем находятся друг от друга дальше, чем они находились бы в совершенно случайной структуре. Этот паттерн был подмечен Альфредом Расселом Уоллесом в 1878 году на основании его долгого опыта работы в тропиках:

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

В моей искусственной модели процесса социального дистанцирования реализовано простое правило. Когда дерево умирает, его не может заменить другое дерево того же вида, и эта замена не может совпадать по виду с любым из восьми ближайших соседей, окружающих освободившееся место. Поэтому между деревьями одного вида должно находиться хотя бы одно другое дерево. Иными словами, каждое дерево имеет вокруг себя зону отчуждения, в которой не могут расти другие деревья того же вида.


Оказывается, социальное дистанцирование довольно эффективный способ сохранения разнообразия. При нажатии на Start модель оживает и начинает лихорадочно работать, мерцая, как передняя панель компьютера из голливудского фильма 50-х годов. Потом она просто продолжает мерцать, больше ничего особого не происходит. Нет ни распространяющихся волн цветов, когда успешные виды захватывают территорию, ни вымирания. Флуктуации размеров популяций даже меньше, чем они были бы в совершенно случайном и равномерном размещении видов в массиве. Эта стабильность очевидна из показанного ниже графика, на котором десять видов держатся близко к средней численности 62,5:


Когда я закончил писать эту программу и впервые нажал на кнопку, то ожидал увидеть совсем не долгосрочное выживание всех десяти видов.


На мои ожидания повлияли карандашные зарисовки. Я убедился, что всего четырёх цветов достаточно для создания паттерна, в котором нет двух деревьев одного цвета, соседствующих друг с другом по горизонтали, вертикали или по обеим диагоналям. Один из таких паттернов показан выше. Я подозревал, что протокол социального дистанцирования может заставить модель конденсироваться в такое кристаллическое состояние с потерей видов, которые не выстроены в повторяющемся узоре. Я ошибался. Хотя четыре это и в самом деле минимальное количество цветов для двухмерной решётки с социальным дистанцированием, в алгоритме нет ничего, что стимулировало бы систему к поиску минимума.

Изучив программу в действии, я смог выяснить, благодаря чему же выживают все виды. Существует активный процесс обратной связи, отдающий приоритет редкости. Допустим, в текущий момент наименьшую частоту в общей популяции имеют дубы. В результате этого дубы с наименьшей вероятностью будут присутствовать в зоне отчуждения, окружающей любое свободное место в лесу, что в свою очередь означает, что они с наибольшей вероятностью будут выбраны в качестве замены. Пока дубы остаются более редкими, чем среднее, то их популяция имеет тенденцию к росту. Симметрично, представителям избыточных видов сложнее найти свободное место для своих потомков. Все отклонения от среднего уровня популяций саморегулируются.

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

Коннелл и Дженсен предполагали гораздо более обширные зоны отчуждения, а значит, и больший перечень видов. Для реализации такой модели нужно гораздо больше вычислений. В недавней статье Талла Леви и других авторов рассказывается о такой симуляции. Они выяснили, что количество выживших видов и их пространственное распределение остаются достаточно стабильными в течение долгих периодов времени (200 миллиардов событий смены деревьев).

Может ли механизм Коннелла-Дженсена работать и в лесах зоны умеренного климата? Как и в тропиках, деревья на более высоких широтах имеют специализированных врагов, и некоторые из них печально известны переносчики голландской болезни вяза и эндотиевого рака коры каштана съедобного, ясеневая изумрудная узкотелая златка, гусеницы непарного шелкопряда, уничтожающие листву дубов. Тсуга рядом с моим домом выдерживает яростные атаки высасывающего древесные соки жука хермеса. То есть силы, управляющие ростом разнообразия и антикластеризацией по модели Коннелла-Дженсена, здесь тоже присутствуют. Однако наблюдаемая пространственная структура северных лесов немного отличается. Социальное дистанцирование здесь не сохраняется. Распределение деревьев обычно немного кучкообразное, представители одного вида собираются в небольшие рощицы.

Управляемая заражениями диверсификация это увлекательная идея, однако как и остальные вышеупомянутые теории, она имеет определённые проблемы с правдоподобностью. В случае с образованием экологических ниш нам необходимо находить для каждого вида уникальный ограничивающий ресурс. В случае нейтрального дрейфа нам нужно убедиться, что отбор действительно нейтральный и присваивает равную приспособленность деревьям, которые выглядят очень разными. В модели Коннелла-Дженсена нам нужен для каждого вида специализированный вредитель, достаточно мощный, чтобы подавлять все соседние ростки. Может ли быть правдой, что у каждого дерева есть собственный заклятый враг?

Также существуют основания сомневаться в устойчивости модели, её сопротивлению к разрушению. Предположим, что в тропическом лесу появляется инвазивный вид новое для континента дерево, не имеющее врагов поблизости. Что тогда произойдёт? Показанная ниже программа предлагает нам ответ. Запустите её, а затем нажмите на кнопку Invade.

Можете нажать Invade несколько раз, потому что новоприбывший вид может вымереть ещё до того, как успеет закрепиться. Также обратите внимание на то, что я замедлил эту симуляцию, потому что в противном случае она бы завершилась за мгновение.


В отсутствие врагов захватчик может игнорировать правила социального дистанцирования, занимая любое освободившееся в лесу место без оглядки на соседей. Как только захватчик займёт большинство мест, правила дистанцирования становятся менее обременительными, но к тому времени для других видов уже слишком поздно.

Ещё одна полусерьёзная мысль о теории Коннелла-Дженсена: в войне между деревьями и их врагами человечество определённо выбрало одну из сторон. Мы бы с радостью уничтожили всех этих насекомых, грибы и других убивающих деревья вредителей, если бы знали, как это сделать. Все были бы рады вернуть вязы и каштаны, спасти восточные тсуги, прежде чем станет слишком поздно. В этом вопросе я разделяю убеждения обнимающих деревья хиппи. Но если Коннелл и Дженсен правы, и их теория применима и к лесам зоны умеренного климата, то уничтожение всех врагов на самом деле приведёт к разрушительному коллапсу разнообразия деревьев. Без давления вредителей за дело возьмётся конкурентное вытеснение, и повсюду у нас останутся леса из одного вида деревьев.

Разнообразие видов в лесах теперь сравнимо с разнообразием теорий в области биологии. В статье я рассмотрел три идеи образование экологических ниш, нейтральный дрейф и социальное дистанцирование; похоже, в сознании экологов все они сосуществуют. И почему бы и нет? Каждая из теорий оказалась успешной в том смысле, что она способна преодолеть конкурентное вытеснение. Кроме того, каждая теория делает чёткие прогнозы. В случае образования ниш каждый вид должен иметь уникальный ограничивающий ресурс. Нейтральный дрейф генерирует необычную динамику популяций, при которой виды постоянно появляются и исчезают, несмотря на то, что общее количество видов остаётся стабильным. Из социального дистанцирования следует пространственная антикластеризация.

Как же нам выбрать победителя среди этих (и, возможно, других) теорий? Научная традиция говорит, что последнее слово должно остаться за природой. Нам нужно провести эксперименты или, по крайней мере, выйти в поле и выполнить систематические наблюдения, а затем сравнить эти результаты с теоретическими прогнозами.

Было проведено уже довольно много экспериментальных тестов конкурентного вытеснения. Например, Томас Парк с коллегами проводили длившуюся десяток лет серию экспериментов с двумя близкородственными видами мучного хрущака. В конечном итоге всегда преобладал один или другой вид. В 1969 году Франциско Айала написал о похожем эксперименте с фруктовыми мушками, в котором он наблюдал сосуществование при условиях, которые, как считалось, делают его невозможным. Его работа вызвала споры, но в конечном итоге результат привёл не к отказу от теории, а к совершенствованию математического описания того, к чему применимо вытеснение.

Разве не было бы прекрасно провести такие эксперименты с деревьями? К сожалению, их не так просто вырастить в пробирке. А проведение исследований множества поколений организмов, живущих дольше нас сложная задача. В случае с мучным хрущаком у Парка было время понаблюдать в течение десятка лет за более 100 поколений. В случае деревьев аналогичный эксперимент мог бы занять 10 000 лет. Но полевые исследователи-биологи изобретательный народ, и я уверен, что они что-нибудь придумают. Тем временем, я бы хотел сказать ещё несколько слов о теоретическом, математическом и вычислительном подходах к этой проблеме.

Экология стала серьёзной математической дисциплиной в 1920-х, после работ Альфреда Джеймса Лотка и Вито Вольтерры. Чтобы объяснить их методику и идеи, можно начать со знакомого нам факта: организмы воспроизводят себя, вызывая рост популяций. В математическом виде это наблюдение превращается в дифференциальное уравнение:

$\frac{d x}{d t} = \alpha x,$


которое гласит, что мгновенная скорость изменения в популяции $x$ пропорциональна самому $x$ чем больше представителей вида, тем больше их появится. Константу пропорциональности $\alpha$ называют собственным коэффициентом воспроизводства; этот коэффициент наблюдается, когда ничто не ограничивает рост популяции и не мешает ему. Уравнение имеет решение, дающее нам $x$ как функцию от $t$:

$x(t) = x_0 e^{\alpha t},$


где $x_0$ исходная популяция. Это рецепт неограниченного экспоненциального роста (если считать, что $\alpha$ положительна). В конечном мире такой рост не может длиться вечно, но это не должно нас сейчас беспокоить.

Давайте введём второй вид $y$, подчиняющийся тому же типу закона роста, но имеющий свой коэффициент воспроизводства $\beta$. Теперь мы можем задаться вопросом, что произойдёт, если эти два вида каким-то образом взаимодействуют. Лотка и Вольтерра (работая независимо друг от друга) ответили на него следующей парой уравнений:

$\begin{align} \frac{d x}{d t} &= \alpha x +\gamma x y\\ \frac{d y}{d t} &= \beta y + \delta x y \end{align}$


Решения этой системы не так просты, как решения для неограниченного роста одного вида. Происходящее зависит от знаков и величин четырёх констант: $\alpha$, $\beta$, $\gamma$ и $\delta$. Во всех случаях мы можем взять $\alpha$ и $\beta$ как положительные, потому что в противном случае вид вымрет. При этом остаётся четыре сочетания для $\gamma$ и $\delta$, коэффициентов перекрёстных членов $xy$:

  • $(\gamma +, \delta -)$: система хищник-жертва, в которой $x$ это хищник; $x$ получает преимущество, а $y$ страдает, когда произведение $xy$ велико (т.е. оба вида находятся в изобилии).
  • $(\gamma -, \delta +)$: система хищник-жертва, где $y$ хищник.
  • $(\gamma +, \delta +)$: симбиотическая или мутуалистическая система, в которой присутствие каждого вида идёт на пользу как другому, так и себе.
  • $(\gamma -, \delta -)$: конкуренция, при которой каждый вид замедляет рост другого.

В любой момент $t$ состояние системы из двух видов можно представить как точку на плоскости $x, y$, координатами которой являются уровни двух популяций. Для некоторых сочетаний параметров $\alpha$, $\beta$, $\gamma$, $\delta$ существует точка стабильного равновесия. Когда система достигает этой точки, она остаётся точкой, и возвращается к этой точек после любых незначительных колебаний. Другие равновесия нестабильны: малейшее отклонение от точек равновесия приводит к значительному сдвигу уровней популяций. А по-настоящему интересные случаи не имеют неподвижной точки; состояние системы описывает замкнутую петлю на плоскости $x, y$, постоянно повторяя весь цикл состояний. Эти циклы соответствуют колебаниям уровней двух популяций. Подобные колебания наблюдались во многих системах хищник-жертва. На самом деле, именно любопытство, вызванное периодическим увеличением и сокращением популяций в канадском пушном промысле и рыболовстве Адриатики, вдохновило Лотка и Вольтерру на работу над этой проблемой.

1960-е и 70-е годы принесли новые открытия. Исследования уравнений, очень похожих на систему Лотка-Вольтерры, раскрыли явление детерминированного хаоса, при котором точка, представляющая состояние системы, движется по чрезвычайно сложной траектории, однако это блуждание не случайно. За этим открытием последовали оживлённые дебаты о сложности и стабильности в экосистемах. Можно ли найти хаос в природных популяциях? Сообщество из множества видов и со множеством взаимосвязей между ними более или менее стабильно, чем более простое?

Если смотреть на это как на абстрактную математику, то в этих уравнениях есть большая красота, но иногда сопоставление математики с биологией происходит с трудом. Например, когда уравнения Лотка-Вольтерры были применены к видам, конкурирующим за ресурсы, в модели сами ресурсы не были представлены. Математическая структура описывает нечто больше похожее на взаимодействие хищник-хищник два вида, которые едят друг друга.

Даже сами организмы представлены в этих моделях всего лишь призрачно. Дифференциальные уравнения определены на множестве вещественных чисел и дают нам уровни или плотности популяций, но не отдельных растений или животных тех дискретных объектов, которые мы считаем как целые числа. Выбор типа чисел не так важен при больших размерах популяций, но приводит к странностям, когда популяция падает, допустим до 0,001 одного миллидерева. Использование уравнений в конечных разностях вместо дифференциальных уравнений позволяет избежать этой проблемы, но вычисления становятся более запутанными.

Ещё одна проблема заключается в том, что уравнения строго детерминированы. При одинаковых входящих данных мы всегда будем получать совершенно одинаковые результаты, даже в хаотической модели. Детерминизм исключает возможность моделирования чего-то типа нейтрального экологического дрейфа. Но и эту проблему можно решить при помощи стохастических дифференциальных уравнений, содержащих в себе источник шума или неопределённости. В моделях подобного типа получаемые ответы являются не числами, а распределениями вероятностей. Мы узнаём не популяцию $x$ в момент времени $t$, а вероятность $P(x, t)$ в распределении с определённым средним и дисперсией. Ещё один подход, названный Markov Chain Monte Carlo (MCMC), использует источник случайности для получения выборок из таких распределений. Однако метод MCMC переносит нас в мир вычислительных, а не математических моделей.

В общем случае вычислительные методы позволяют обеспечивать прямое сопоставление между элементами модели и моделируемыми сущностями. Мы можем поднять крышку, взглянуть внутрь и найти там деревья и ресурсы, рождения и смерти. Такие вычислительные объекты не особо гибки, но дискретны и всегда конечны. Популяция это не число и не распределение вероятностей, а множество отдельных объектов. Я считаю, что подобные модели интеллектуально менее требовательны. Для создания дифференциального уравнения, передающего динамику биологической системы, требуется вдохновение и интуиция. Создание программы для реализации нескольких базовых элементов жизни леса дерево умирает, его место занимает другое намного проще.

Шесть небольших моделей, включённых в эту статью, в основном служат в качестве визуализаций; по большей мере их вычислительные затраты заключаются в рисовании на экране цветных точек. Но реализация более масштабных и амбициозных моделей вполне возможна, примером этого стала работа упомянутая выше работа Таала Леви с коллегами.

Однако несмотря на бОльшую простоту создания вычислительных моделей, их может быть сложнее интерпретировать. Если запустить модель один раз и в ней вымрет вид $X$, то много ли информации можно из этого получить? Не особо. В следующем прогоне может оказаться, что $X$ и $Y$ сосуществуют. Чтобы делать правильные выводы, нужно собрать статистику на большом количестве прогонов, то есть ответ снова принимает вид распределения вероятностей.

Конкретность и явность моделей Монте-Карло обычно являются достоинством, но они имеют и тёмную сторону. Если модель дифференциальных уравнений может применяться к любой крупной популяции, то такое туманное описание может и не быть применимо в вычислительном контексте. Числу нужно дать имя, пусть этот выбор будет и произвольным. Размер моих моделей леса (625 деревьев) был выбран только для удобства. В более масштабной сетке, допустим $100 \times 100$, нам потребуется ждать миллионы шагов времени, прежде чем произойдёт что-то интересное. Разумеется, та же проблема возникает с экспериментами в лаборатории и в поле.

Оба типа модели всегда подвержены риску чрезмерного упрощения. Иногда из моделей выбрасываются важные аспекты. В случае моделей леса меня напрягает то, что у деревьев нет истории жизни. Одно дерево умирает, а другое сразу же появляется на его месте, полностью выросшее. Также в моделях нет опыления и распространения семян, а также редких событий (ураганов и пожаров), способных менять форму целого леса. Узнаем ли мы больше, если все эти аспекты жизни в лесу имели место в уравнениях или алгоритмах? Возможно, но где та черта, возле которой нам нужно остановиться?

Моё знакомство с моделями в экологии началось с книги Джона Мейнарда Смита Models in Ecology, опубликованной в 1974 году. Недавно я перечитал её, узнав больше, чем в первый раз. Мейнард Смит указывает на различие между симуляциями, полезными для ответов на вопросы о конкретных задачах или ситуациях, и моделями, полезными для тестирования теорий. Он рекомендует следующее: Хорошая симуляция должна содержать как можно больше подробностей, а хорошая модель как можно меньше.

Дополнительное чтение


Ayala, F. J. 1969. Experimental invalidation of the principle of competitive exclusion. Nature 224:10761079.

Clark, James S. 2010. Individuals and the variation needed for high species diversity in forest trees. Science 327:11291132.

Connell, J. H. 1971. On the role of natural enemies in preventing competitive exclusion in some marine animals and in rain forest trees. In Dynamics of Populations, P. J. Den Boer and G. Gradwell, eds., Wageningen, pp. 298312.

Gilpin, Michael E., and Keith E. Justice. 1972. Reinterpretation of the invalidation of the principle of competitive exclusion. Nature 236:273301.

Hardin, Garrett. 1960. The competitive exclusion principle. Science 131(3409): 12921297. (История первых сорока лет развития экологии. Кому-то нужно описать следующие шестьдесят.)

Hubbell, Stephen P. 2001. The Unified Neutral Theory of Biodiversity and Biogeography. Princeton, NJ: Princeton University Press.

Hutchinson, G. E. 1959. Homage to Santa Rosalia, or why are there so many kinds of animals? The American Naturalist 93:145159.

Janzen, Daniel H. 1970. Herbivores and the number of tree species in tropical forests. The American Naturalist 104(940):501528.

Kricher, John. C. 1988. A Field Guide to Eastern Forests, North America. The Peterson Field Guide Series. Illustrated by Gordon Morrison. Boston: Houghton Mifflin.

Levi, Taal, Michael Barfield, Shane Barrantes, Christopher Sullivan, Robert D. Holt, and John Terborgh. 2019. Tropical forests can maintain hyperdiversity because of enemies. Proceedings of the National Academy of Sciences of the USA 116(2):581586.

Levin, Simon A. 1970. Community equilibria and stability, and an extension of the competitive exclusion principle. The American Naturalist, 104(939):413423.

MacArthur, R. H., and E. O. Wilson. 1967. The Theory of Island Biogeography. Monographs in Population Biology. Princeton University Press, Princeton, NJ.

May, Robert M. 1973. Qualitative stability in model ecosystems. Ecology, 54(3):638641.

Maynard Smith, J. 1974. Models in Ecology. Cambridge: Cambridge University Press.

Richards, Paul W. 1973. The tropical rain forest. Scientific American 229(6):5867.

Schupp, Eugene W. 1992. The Janzen-Connell model for tropical tree diversity: population implications and the importance of spatial scale. The American Naturalist 140(3):526530.

Strobeck, Curtis. 1973. N species competition. Ecology, 54(3):650654.

Tilman, David. 2004. Niche tradeoffs, neutrality, and community structure: A stochastic theory of resource competition, invasion, and community assembly. Proceedings of the National Academy of Sciences of the USA 101(30):1085410861.

Wallace, Alfred R. 1878. Tropical Nature, and Other Essays. London: Macmillan and Company.
Подробнее..

Перевод Математики открыли новый фронт в битве с древней числовой задачей

24.09.2020 14:19:46 | Автор: admin

Не одно тысячелетие математиков интересовал вопрос существования нечётных совершенных чисел. В процессе его изучения они составили невероятный список ограничений для этих гипотетических объектов. Но новые идеи на этот счёт могут появиться благодаря изучению иных близких к ним объектов.



Если нечётные совершенные числа и существуют, им придётся удовлетворять абсурдно большому списку ограничений

Будучи ещё старшеклассником, Пэйс Нильсен в середине 90-х столкнулся с математическим вопросом, над которым бьётся и по сей день. Но он не расстраивается: очаровавшая его задача, гипотеза о нечётных совершенных числах, остаётся открытой уже более 2000 лет, что делает её одной из старейших нерешённых задач математики.

Частично таким долгоживущим шармом она обязана простоте формулировки. Число называется совершенным, если это положительное целое, n, сумма делителей которого даёт удвоенное число, 2n. Первый и самый простой пример это 6, делители которого, 1, 2, 3 и 6, в сумме дают 12, или 2*6. Затем идёт 28, с делителями 1, 2, 4, 7, 14 и 28, дающими в сумме 56. Следующие примеры 496 и 8128.

Леонард Эйлер формализовал это определение в XVIII веке, введя свою сигма-функцию, обозначающую сумму делителей числа. Таким образом, для совершенных чисел (n) = 2n.


Леонард Эйлер сформулировал множество формальных правил, касающихся работы с совершенными числами

Однако Пифагор знал о совершенных числах ещё в 500 году до н.э., а два столетия спустя Евклид вывел формулу для получения чётных простых чисел. Он показал, что если p и 2p 1 простые числа (делители которых только 1 и само это число), тогда 2p1 * (2p 1) будет совершенным числом. К примеру, если p = 2, то формула даёт 21 * (22 1), или 6. Если p = 3, то формула даёт 22 * (23 1), или 28 два первых совершенных числа. 2000 лет спустя Эйлер доказал, что эта формула выдаёт все чётные совершенные числа, хотя до сих пор неизвестно, конечно или бесконечно множество совершенных чисел.

Нильсен, сегодня работающий профессором в Университете Бригама Янга, увлёкся связанным с этим вопросом: существуют ли нечётные совершенные числа? Греческий математик Никомах Герасский около 100 года н.э. заявил, что все совершенные числа должны быть чётными, но никто не доказал этого утверждения.

Как и многие его коллеги из XXI века, Нильсен считает, что совершенные числа существует не особенно много. И, вместе с ними он считает, что доказательство этой гипотезы будет получено нескоро. Однако в июне он наткнулся на новый подход к этой задаче, возможно, способный продвинуть её далее. И он связан с ближайшим к нечётным совершенным числам объектом из всех пока обнаруженных.

Сжимающаяся сеть


Впервые Нильсен узнал о совершенных числах на соревновании по математике в школе. Он углубился в литературу, и наткнулся на работу 1974 года Карла Померанца, математика, сегодня служащего в Дартмутском колледже. Тот доказал, что у любого нечётного совершенного числа должно быть не менее семи различных простых множителей.

Я в своей наивности решил, что я могу сделать что-то в этой области, если в ней вообще возможен прогресс, сказал Нильсен. Это вдохновило меня на изучение теории чисел в колледже, и попытки развить прогресс. Его первая работа по нечётным совершенным числам, опубликованная в 2003 году, наложила дополнительные ограничения на эти гипотетические числа. Он показал, что не только количество нечётных совершенных чисел с k различными простыми делителями конечно, как доказал в 1913 году Леонард Диксон, но и что размер этого числа не должен превышать 24k.

И это было не первым и не последним ограничением, наложенным на гипотетические нечётные совершенные числа. К примеру, в 1888 году Джеймс Сильвестер доказал, что нечётное совершенное число не может делиться на 105. В 1960 году Карл К. Нортон доказал, что, если нечётное совершенное число не делится на 3, 5 или 7, у него должно быть не менее 27 простых делителей. Пол Дженкинс в 2003 году доказал, что крупнейший простой делитель нечётного совершенного числа должен быть больше 10 000 000. Паскаль Очем и Михаёль Рао после этого обнаружили, что нечётное совершенное число должно быть больше 101500, а потом отодвинули эту границу до 102000. Нильсен в 2015 году показал, что нечётное совершенное число должно иметь не менее 10 различных простых делителей.


Пэйс Нильсен, математик из Университета Бригама Янга

Даже в XIX веке количество ограничений было таким, что Сильвестер сделал вывод, что появление нечётного совершенного числа этакий побег от сложной сети условий, окружающих его со всех сторон будет практически чудом. Спустя более ста лет подобного развития событий существование таких чисел вызывает ещё больше сомнений.

Доказать существование чего-либо легко, если получится найти хотя бы один пример, сказал Джон Войт, профессор математики из Дартмута. Но доказать, что нечто не существует, может быть очень тяжело.

Основным подходом до сего момента было сравнение всех ограничивающих нечётные совершенные числа условий с тем, чтобы выяснить, не является ли какая-то парочка из них несовместимой то есть, что ни одно число не может удовлетворять обоим ограничениям сразу. Лоскутное одеяло условий, полученное на сегодняшний день, делает вероятность существования нечётных совершенных чисел крайне малой, сказал Войт, вторя Сильвестеру. И Пэйс много лет добавлял к этому списку новые пункты.

К сожалению, несовместимых свойств до сих пор не найдено. Поэтому кроме дополнительных ограничений на нечётные совершенные числа математикам, вероятно, потребуются и новые стратегии.

Для этого Нильсен уже рассматривает новый план атаки, основанный на распространённой тактике математиков: изучение множества чисел через изучение их близких родственников. В отсутствии нечётных совершенных чисел, пригодных для прямого изучения, они с командой изучают имитации нечётных совершенных чисел, которые очень похожи на настоящие, но обладают некоторыми интересными отличиями.

Разбираемся в совершенных числах


Сигма-функция обозначает сумму делителей числа. (n) = 2n, если это совершенное число.

Примеры:

(20) = 1 + 2 + 4 + 5 + 10 + 20 = 42; 2 * 20 42, поэтому 20 не совершенное число.
(28) = 1 + 2 + 4 + 7 + 14 + 28 = 56; 2 * 28 = 56, поэтому 28 совершенное число.

Правила Эйлера

1. (a b) = (a) (b) в том, и только в том случае, если a и b взаимно простые.
2. (pa) = 1 + p + p2 + + pa для любого простого p с положительной целой степенью a.

Примеры:

(20) = (22 5) = (22) (5) [по первому правилу] = (1 + 2 + 22)(1+5) [по второму правилу] = 42

(28) = (22 7) = (22) (7) [по первому правилу] = (1 + 2 + 22)(1+7) [по второму правилу] = 56


Новые соблазнительные промахи


Первую имитацию нечётного совершенного числа нашёл в 1638 году Рене Декарт и он был одним из первых выдающихся математиков, посчитавших возможным существование нечётных совершенных чисел. Я считаю, что Декарт пытался найти нечётные совершенные числа, и его вычисления привели его к первой имитации, сказал Уильям Бэнкс, специалист по теории чисел из Университета Миссури. Судя по всему, Декарт надеялся, что созданное им число можно изменить, получив настоящее нечётное совершенное число.

Но прежде чем углубиться в декартовскую имитацию, полезно будет немного разобраться в том, как математики описывают совершенные числа. Теорема времён Евклида утверждает, что любое целое число, большее 1, можно выразить в виде произведения простых чисел, возведённых в определённые степени. К примеру, 1260 можно так разложить на множители: 1260 = 22 32 51 71, и не перечислять все 36 множителей по отдельности.

Если число принимает такую форму, вычислять сигма-функцию Эйлера, суммирующую его делители, становится гораздо проще благодаря двум формулам, которые тоже доказал Эйлер. Во-первых, он продемонстрировал, что (a b) = (a) (b), тогда, и только тогда, когда a и b взаимно простые то есть, у них нет общих простых делителей. К примеру, числа 14 (2 7) и 15 (3 5) взаимно простые. Во-вторых, он показал, что для любого простого числа p в положительной целой степени a, (pa) = 1 + p + p2 + + pa.

Возвращаясь к нашему предыдущему примеру, (1 260) = (22 32 51 71) = (22) (32) (51) (71) = (1 + 2 + 22)(1 + 3 + 32)(1 + 5)(1 + 7) = 4 368. Обратите внимание, что в данном случае (n) не равняется 2n, а, значит, 1260 не совершенное число.


Рене Декарт нашёл первую имитацию совершенного числа

Теперь мы можем разобрать декартову имитацию число 198 585 576 189, или 32 72 112 132 22 0211. Повторяя описанные выше вычисления, мы обнаружим, что (198 585 576 189) = (32 72 112 132 22,0211) = (1 + 3 + 32)(1 + 7 + 72)(1 + 11 + 112)(1 + 13 + 132)(1 + 22,0211) = 397 171 152 378. И это равно удвоенному изначальному числу, что означает, что оно должно быть настоящим совершенным числом вот только число 22 021 не является простым.

Поэтому это число Декарта является имитацией. Если мы притворимся, что 22 021 простое, и применим правила Эйлера для сигма-функции, число Декарта ведёт себя как совершенное число. Однако 22 021 на деле является произведением 192 и 61. Если бы мы правильно записали число Декарта, как 32 72 112 132 192 611, тогда (n) не равнялась бы 2n. Ослабляя некоторые правила, мы получаем число, вроде бы удовлетворяющее нашим требованиям такова суть имитации.

На то, чтобы открыть второе число-имитацию нечётного совершенного числа, ушёл 361 год. Войт сделал это в 1999 году, и опубликовал открытие через четыре года. Почему так долго? Находка числа-имитации похожа на находку нечётного совершенного числа; оба они сходным образом арифметически сложны, сказал Бэнкс. Да и их поиски не были в приоритете у математиков. Однако Войта вдохновил отрывок из книги Ричарда Гая Нерешённые задачи теории чисел, где писали о поисках новых имитаций. Войт попытался, и в итоге нашёл новую имитацию, 34 72 112 192 (127)1, или 22 017 975 903.

В отличие от примера Декарта, тут все делители простые, но один из них отрицательный поэтому это число и является имитацией, а не истинным нечётным совершенным числом.

Имитации нечётных совершенных чисел


Число Декарта:

198 585 576 189, или 32 72 112 132 22 0211.

Повторим вычисления сигма-функции: (198 585 576 189) = (32 72 112 132 22,0211) = (1 + 3 + 32)(1 + 7 + 72)(1 + 11 + 112)(1 + 13 + 132)(1 + 22,0211) = 397 171 152 378 = 2 198 585 576 189.

Но число 22 021 не является простым, это 192 61. Число Декарта является лишь имитацией нечётного совершенного числа.

Число Войта:

22 017 975 903, или 34 72 112 192 (127)1.

Повторим вычисления сигма-функции: (22 017 975 903) = (34 74 112 192 (-127)1) = (1 + 3 + 32 + 33 + 34)(1 + 7 + 72)(1 + 11 + 112)(1 + 19 + 192)(1 + (-127)1) = -44 035 951 806 = 2 22 017 975 903

-127 число отрицательное, поэтому число Войта ещё одна имитация совершенного числа.


После того, как Войт в декабре 2016 года провёл семинар в университете Бригама Янга, он обсудил это число с Нильсеном, Дженкинсом и другими. Вскоре после этого команда университета отправилась на систематические вычислительные поиски других имитаций. Они выбирали наименьшие основания и показатели степени, вроде 32, и потом компьютеры прочёсывали варианты дополнительных оснований и степеней, которые давали бы имитацию совершенного числа. Нильсен решил, что этот проект просто будет неким стимулирующим опытом исследований для его студентов, однако результаты анализа превзошли его ожидания.

Просеивая возможности


После непрерывной работы 20 процессоров в течение трёх лет, команда обнаружила все возможные имитации совершенного числа, которые можно записать при помощи шести или менее оснований всего 21 штука, включая примеры Декарта и Войта а ещё две имитации с семью делителями. Искать имитации с большим числом делителей на компьютерах было непрактично, и слишком затратно по времени. Тем не менее, группа набрала достаточно примеров для того, чтобы открыть неизвестные ранее свойства имитаций.

Группа обнаружила, что для любого заданного количества оснований, k, существует конечно число имитаций, что совпадает с результатом Диксона от 1913 года для настоящих нечётных совершенных чисел. Однако если k уйдёт в бесконечность, количество имитаций также становится бесконечным, сказал Нильсен. Это было неожиданно, добавил он, учитывая, что начиная этот проект, он не был уверен в открытии хотя бы одной новой нечётной имитации, не говоря уже о том, чтобы показать, что их число бесконечно.

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

Некоторые находки команда обнаружила, ослабив требования в определении имитации, поскольку не существует чётких математических правил для их описания только то, что они должны удовлетворять равенству (n) = 2n. Исследователи допустили существование оснований, не являющихся простыми числами (как в примере Декарта) и отрицательных оснований (как в примере Войта). Однако они пошли дальше, разрешив имитациям иметь несколько одинаковых оснований. Одно основание, к примеру, может быть 72, а другое 73, и записываются они отдельно, а не как 75. Или они позволяли основаниям повторяться, как в имитации 32 72 72 131 (19)2. Член 72 72 можно записать как 74, но тогда имитации не получится, потому что раскрытие скобок в изменённой сигма-функции было бы другим.

Учитывая значительную разницу между имитациями и реальными нечётными совершенными числами, можно задать вопрос: и как же первые помогают в поисках вторых?

Путь вперёд?


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

Любое поведение более крупного множества должно соблюдаться и для мелкого подмножества, сказал Нильсен. Так что, если мы найдём поведение имитаций, неприменимое к более ограниченному классу, мы автоматически сможем отказаться от возможности существования нечётных совершенных чисел. Если, к примеру, можно будет показать, что все имитации делятся на 105 что невозможно для нечётных совершенных чисел, как Сильвестер показал в 1888 тогда задача будет решена.

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

Бэнкс считает такой подход стоящим. Исследование нечётных имитаций может быть полезным для понимания структуры нечётных совершенных чисел, если те существуют, сказал он. А если нечётных совершенных чисел не существует, изучение нечётных имитаций может привести к доказательству этого.

Другие эксперты по нечётным совершенным числам настроены не так оптимистично. Команда из университета Бригама Янга проделала отличную работу, сказал Войт, но я не уверен, что мы приблизились к вариантам атаки на проблему нечётных совершенных чисел. Это и правда задача на века, и, вероятно, она будет таковой оставаться.

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

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

Нильсен уже определил одну вероятную тактику, основываясь на том, что у всех найденных на сегодня имитаций, кроме оригинального примера Декарта, есть хотя бы одно отрицательное основание. Если доказать, что у всех остальных имитаций должно быть отрицательное основание, то это докажет, что нечётных совершенных чисел не существует, поскольку, по определению, их основания должны быть простыми и положительными.

Эта задача кажется более трудной, сказал Нильсен, поскольку она касается более крупной и общей категории чисел. Но иногда, когда ты преобразуешь задачу в более, казалось бы, сложную, можно увидеть путь к решению.

В теории чисел требуется терпение иногда вопрос легко поставить, но трудно найти на него ответ. Приходится размышлять о задаче, иногда подолгу, и проявлять к ней особое внимание, сказал Нильсен. Мы движемся вперёд. Мы копаем шахту. Надеемся, что если будем копать достаточно долго, то сможем найти алмаз.
Подробнее..

Геометрическое представление кривизны пространства в метрике Шварцшильда

25.09.2020 14:16:06 | Автор: admin
или два плюс два равно четыре.

Для понимания статьи достаточно школьного курса математики.

Форма множителя в метрике Шварцшильда давно не давала мне покоя своей изысканной двуличностью, и я решил уделить некоторое время изысканиям возможностей её преобразования. Сама метрика Шварцшильда получается в результате решения ОТО для вакуумного случая (тензор энергии-импульса равен нулю):

$ds^2 = - \left(1- 2 \frac{GM}{c^2 r}\right) c^2 dt^2 + \left(1- 2 \frac{GM}{c^2 r}\right)^{-1} dr^2 + r^2 \cdot d\theta^2 + r^2 \cdot \sin^2\theta \cdot d\phi^2$


Она описывает пространственно-временной континуум в окрестностях произвольного компактного массивного объекта. Компактного, значит, девиации формы незначительны в отношении к массе. Проще говоря, круглый и плотный. Обычно здесь приводят в пример чёрную дыру. Никто почему-то не приводит примеров некомпактных объектов. Герметичная палка из пенопласта в открытом космосе на бесконечном удалении от массивных объектов, например, некомпактный объект. Кубический конь на расстоянии, с которого можно разглядеть печаль в его глазах тоже.

Через объём 3-сферы


Произведём замену:

$M=\frac{E}{c^2}$


Тогда метрика станет такой:

$$display$$ds^2 = - \left(1- 2 \frac{GE}{c^\color{red}{4} r}\right) c^2 dt^2 + \left(1- 2 \frac{GE}{c^\color{red}{4} r}\right)^{-1} dr^2 + r^2 \cdot d\theta^2 + r^2 \cdot \sin^2\theta \cdot d\phi^2$$display$$


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

$r_s = 2 \cdot \frac{GE}{c^4}$


потому что метрика имеет особенность в этой точке. Здесь время, буквально, останавливается.
Вот так, в таком случае, выглядит вся метрика:

$ds^2 = - \left(1- \frac{r_s}{ r}\right) c^2 dt^2 + \left(1- \frac{r_s}{r}\right)^{-1} dr^2 + r^2 \cdot d\theta^2 + r^2 \cdot \sin^2\theta \cdot d\phi^2$


Но в продолжение рассуждений о физической сути явлений эта двойка:

$r_s = \color{red}{2} \cdot \frac{GE}{c^4}$


тоже должна быть осмыслена. Поэтому представим так:

$u = \frac{GE}{c^4}$


Это просто половина гравитационного радиуса $r_s$, и размерность у него такая же. Получим:

$ 1 - 2\frac{GE}{c^4r} = 1 - 2\frac{u}{r} $


Напрашивается:

$= \left( 1 - 2\frac{u}{r} + \frac{u^2}{r^2} \right) - \frac{u^2}{r^2} = \left( 1 - \frac{u}{r} \right)^2 - \frac{u^2}{r^2} = \left( \frac{r - u}{r} \right)^2 - \frac{u^2}{r^2} = $


$= \frac{(r-u)^2 - u^2}{r^2} \qquad \qquad (1)$


Уже неплохо. Зарисуем. Представим $r = OB$ конечным отрезком, $u = OA$ его частью, как показано на рисунке ниже. Очевидно, что $(r-u) = AB$.
image
Любопытно, кстати, что из $r_s = 2u$ следует, что точка $A$ находится за (под) горизонтом событий объекта энергии $E$. Вот так легко она находится, а мы не можем.
Теперь покажем, что отношение вида $(1)$ будет выполняться для всех точек, имеющих геометрическое место на перпендикуляре к $OB$ в точке $A$:

$\frac{(r-u)^2 - u^2}{r^2} = \frac{((r-u)^2 + a^2) - (u^2 + a^2)}{r^2} = \frac{b^2 - d^2}{r^2} \qquad \qquad (2) $


image
для любых $b = CB$ и $d = OC$.
Говоря проще, разность квадратов $(r-u)^2 - u^2$ эквивалентна разности любых величин, проекциями которых на $OB$ являются $AB$ и $OA$ соответственно, при условии, что точка $C$ у них общая.
Дальше предположим, что $u = u(E)$ и $(r-u)$, наоборот, проекции $r = OB$ на какие-то оси, то есть пифагорова сумма двух величин, в исходном виде перпендикулярных друг другу. Переводя это в требование, рассмотрим случай $\angle{OCB} = \pi/2$, для которого верно:

$b^2 = r^2 - d^2 \rightarrow (2) \rightarrow \frac{b^2 - d^2}{r^2} = 1 - 2\frac{d^2}{r^2} \qquad \qquad (3)$


image
Доработаем $(3)$ аналогично начальной итерации:

$1 - 2\frac{d^2}{r^2} = \left( 1 - 2\frac{d^2}{r^2} + \frac{d^4}{r^4} \right) - \frac{d^4}{r^4} = \frac{(r^2-d^2)^2 - d^4}{r^4} =$


$= \frac{b^4 - d^4}{\sqrt{b^2 + d^2}^4} = \frac{b^4 - d^4}{r^4}\qquad \qquad (4)$


Вот и четвёртая степень. Формула объёма 3-сферы:

$V = \frac{\pi^2 \cdot R^4}{2}$


Это я к тому, что если домножить и разделить $(4)$ на $\pi^2/2$:

$\frac{b^4 - d^4}{r^4} = \frac{\pi^2}{2} \cdot \frac{2}{\pi^2} \cdot \frac{b^4 - d^4}{r^4} = \frac{V_b - V_d}{V_r} \qquad \qquad (5)$


то множитель в метрике Шварцшильда превращается в разность объёмов двух 3-сфер, построенных вокруг двух радиальных проекций точки относительно центра поля, соотнесённой к объёму 3-сферы, образуемой полным расстоянием между точкой и центром поля.
С учётом того, что полный радиус задаётся проекциями, всю эту конструкцию весьма лаконично задают два параметра, один из которых связан с энергией, а второй нет. Там точно две координаты.

Выводы


Замечательными следствиями такого представления являются:
1. Из формы множителя видно, что поведение фотона ограничивает видимую зону пятимерного пространства-времени. За её пределами можно спрятать нечто гравитирующее, но невидимое.
2. Наличие второй спрятанной координаты избавляет от парадокса нулевого времени.
3. Раз кривизна пространства вокруг массивного тела может быть всегда разложена на две компоненты, одна из которых связана с энергией тела, а вторая исключительно с пространством, то следующим шагом надо решить уравнения ОТО для вакуумного случая пятимерного пространства-времени. Об этом в следующей статье.

Бонус. Через угол


Очевидно, что можно выразить значимость поля в точке через плоский угол, выражающий отклонение траектории движения от плоского пространства (в отсутствие гравитационных полей).
Выразим величины $b$ и $d$ через угол $\alpha = \angle{OBC}$: $b = r \cdot \cos\alpha; \ d = r \cdot \sin\alpha$. Назовём его угол кривизны траектории. Тогда множитель можно выразить очень по-разному:

$1 - 2\frac{GE}{c^4r} = \cos^2\alpha - \sin^2\alpha = \cos^4\alpha - \sin^4\alpha = 1 - 2 \sin^2\alpha = $


$= \frac{1-\tan^2\alpha}{1 + \tan^2\alpha} = \cos2\alpha \qquad \qquad (6) $


Особенно мне нравится вариант с тангенсами.
image
Подставим в исходный интервал:

$ ds^2 = -\cos 2\alpha \cdot c^2dt^2 + \cos^{-1} 2\alpha \cdot dr^2 + r^2 \cdot d\theta^2 + r^2 \cdot \sin^2\theta \cdot d\phi^2 $


Всё, как и должно, превращается в плоскую метрику Минковского при $\alpha = 0$.
Здесь точно должен быть пятый
Продолжение следует.
Подробнее..

Математика нужна программистам, или задача, которую мне пришлось решать

19.09.2020 00:21:00 | Автор: admin

Всем привет!

Я работаю над WebRTC - фреймворком для аудио-видео конференций (или звонков? проще говоря - real time communication). В этой статье я хочу описать интересную задачу и как она была решена. В задаче, по сути, потребовалось минимизировать lcm нескольких вещественных чисел с дополнительными ограничениями. Пришлось применить совсем чуть чуть теории чисел или хотя бы логики.

Если вам интересна только задача - то можете смело проматывать до секции "Формулировка задачи". Следующая секция объясняет откуда она такая взялась и в чем ее смысл.

Введение

Клиенты могут настроить WebRTC кодировать входящий поток сразу в нескольких разрешениях. Например, это может быть полезно в видео конференциях: каждый клиент посылает на сервер несколько потоков с разным разрешением и битрейтом, а сервер пересылает всем остальным только тот поток, который помещается в пропускную способность до клиента.

Но нельзя просто задать желаемые разрешения, нет - это было бы слишком просто. Дело в том, что источник (например, камера в хроме) может выдавать видео какого угодно разрешения. А еще есть механизм обратной связи и при высокой нагрузке на CPU входящее разрешение снижается. Короче говоря, пользователь задает коэффициенты масштабированияS_i \ge 1.0. Потом входящий кадр сжимается в заданное количество раз, кодируется и отправляется по сети получателям.

Проблема в том, что некоторые энкодеры не работают с произвольными изображениями - им обязательно нужны четные размеры. А еще есть всякие оптимизации при кодировании, если соотношение разрешения у разных изображений целое. А главное, если у разных потоков будет разное соотношение сторон то при переключении между ними будет весьма заметный рывок. Поэтому надо, чтобы входящее разрешение нацело делилось на все коэффициенты.

Самый эффективный способ этого добиться, это требовать от источника, чтобы разрешение делилось на некоторое заданное число: alignment. Например, для стандартных коэффициентов {1.0, 2.0, 4.0} и требования четности для энкодера, можно легко попросить у источникаalignment=8. Источник чуть-чуть обрежет изображения. Это приведет к незначительному искажению соотношения сторон у видео, зато сделает переключение между потоками незаметным. В итоге, входящее разрешение, кратное 8 можно спокойно делить в 1, 2 или 4 раза и получать четное разрешение, которое енкодер с радостью закодирует.

Но что делать, если заданы коэффициенты {1, 1.7, 2.3}? Минимальное целое, "делящееся" нацело на все эти коэффициенты - 391. А чтобы результат был четным, нужно вообще взять 782. Согласитесь, это весьма нагло требовать от источника выдавать разрешение, делящееся на 782. Это значит, что даже VGA (640x480) видео уже не послать вообще никак. На практике - максимально допустимое выравнивание, которое мы можем попросить должно быть ограничено, чтобы, во-первых, допускать маленькие разрешения и, во-вторых, не очень сильно искажать соотношение сторон.

Но, раз уж мы уже немного искажаем настройки пользователя, округляя входящее разрешение, то почему бы и не округлить чуть чуть коэффициенты масштабирования? Например, можно было бы взять коэффициенты {1, 1.6, 2.4} вместо {1, 1.7, 2.3} и получить необходимую делимость в 48 (сильно лучше 782). Если еще больше поменять коэффициенты, то можно получить и меньшее выравнивание.

Формулировка задачи

Дано: d \in \mathbb{N},\ S_i \ge 1, S_i \in \mathbb{R}, i=1..n

Найти: A \in \mathbb{N},\ A \le MaxA,\ S'_i \in \mathbb{R} ,\ S'_i \ge 1,\ i=1..n

При условии:

\sum_{i=1}^n\left(S_i -S'_i\right)^2 \rightarrow min\frac{A}{S'_i \cdot d} \in \mathbb{N}, i=1..n \ \ \ \ \ \ \ \ \ (1)

Или словами: минимизировать искажение коэффициентов и найти какое-то выравнивание А в допустимых пределах, так чтобы это значение делилось нацело на все новые коэффициенты, плюс требование по выравниванию энкодера.

Решение

В задаче итак много неизвестных, так что первая мысль - это зафиксировать хоть что-то. Переменная A отлично подходит на эту роль. Она может принимать лишь MaxA значений и из предметной области получается что MaxA довольно маленькое (эвристически оно было закреплено на значении 16). Поэтому первый шаг - перебрать все возможные значенияAрешить задачи и выбрать то решение, которое имеет наименьшее значение целевой функции.

Следующее наблюдение - можно оптимизировать все коэффициенты масштабирования независимо друг от друга, соблюдая условие (1), ведь в целевую функцию они входят отдельными слагаемыми. Далее сфокусируемся на i-ом коэффициенте.

Поскольку в условии A/(S'_i \cdot d), A, d \in \mathbb{N} , то получается, что S'_i \in \mathbb{Q} илиS'_i = N_i/D_i. Потому что только рациональные числа можно домножить и поделить на целое и получить в итоге целое.

Можно потребовать, чтобы дробь была неприводимая: GCD(N_i, D_i) = 1

Подставим дробь в (1) и получим \frac{A \cdot D_i}{N_i \cdot d} \in \mathbb{N} откуда следует, что

N_i \cdot d \vert A \cdot D_i \ \ \ \ \ \ \ (2)

(запись означает: левая часть делит правую).

Тут немного теории чисел или просто логики. N_i взаимно просто сD_i по условию, но делит правую часть. Значит N_i целиком содержится в оставшемся множителе или N_i \vert A , отсюда можно записать

A=N_i \cdot f,\ f \in \mathbb{N} \ \ \ \ \ \ (3)

Далее, домножим обе части уравнения (2) на f:

f \cdot N_i \cdot d \vert f\cdot A \cdot D_i

Подставим выражение (3) для A выше:

A \cdot d \vert f \cdot A \cdot D_i

сократим A

d \vert f \cdot D_i

Раз левая часть делит правую, то можно переписать уравнение так:

f \cdot D_i = k \cdot d, k \in \mathbb{N} \ \ \ \ \ \ \ \ \ \ (4)

Теперь вспомним выражение для S'_i в виде дроби и домножим числитель и знаменатель на f и применим (3) и (4):

S'_i = \frac{N_i\cdot f}{ D_i \cdot f} = \frac{A}{f \cdot D_i} = \frac{A}{k \cdot d},\ \ k \in \mathbb{N} \ \ \ \ \ \ \ \ (5)

Добавив к этому условие, что коэффициенты S'_i не могут быть меньше 1 (ведь растягивать изображения смысла вообще нет) мы получим:

k \le \frac{A}{d} \ \ \ \ \ \ \ (6)

Таким образом, из условия (1) мы получили (5) и (6), которые говорят, что искомый коэффициент должен быть представим в виде дроби у которой числитель равен A , а знаменатель делиться на d и не превосходит числитель. При чем любая такая дробь нам подходит. Из (6) следует что таких дробей мало, а значит их все можно перебрать.

А можно и не перебирать. Ведь целевая функция, если рассматривать непрерывнуюk выпуклая и имеет минимум, равный 0, в точке k^*=\frac{A}{S_i \cdot d} . Значит, достаточно рассмотреть 2 ближайших целых значения k=min\{\lfloor k^* \rfloor ,\ \lceil k^* \rceil\} . Все, что левее левой точки - хуже ее, ведь она сама левее минимума выпуклой функции. Аналогично и с правой точкой. Еще надо аккуратно проверить, что эти точки положительные и удовлетворяют (6).

Итого, получаем такое решение (тут для простоты нет проверок входных данных):

const int kMaxAlignment = 16;// Находит лучшее приближение scale_factor (S_i) при заданном // выравнивании энкодера (d) и результирующем выравнивании источника (A).// Ошибка приближения прибавляется к error_acc.float GetApprox(int encoder_alignment, int requested_alignment,                 float scale_factor, float *error_acc) {  int k = static_cast<int> ((requested_alignment + 0.0) /                             (encoder_alignment * scale_factor));  float best_error = 1e90;  float best_approx = 1.0;  for (int i = 0; i < 2; i++, k++) {    if (k == 0 || k * encoder_alignment > requested_alignment) continue;    float approx = (requested_alignment +0.0) / (k * encoder_alignment);    float error = (approx - scale_factor) * (approx - scale_factor);    if (error < best_error) {      best_error = error;      best_approx = approx;    }  }  *error_acc += best_error;  return best_approx;}// Решает задачу. Возвращает измененные коэффициенты (S'_i)// и результирующее выравнивание (A) в параметре requested_alignment.std::vector<float> CalulateAlignmentAndScaleFactors(    int encoder_alignment, std::vector<float> scale_factors,     int *requested_alignment) {  float best_error = 1e90;  int best_alignment = 1;  std::vector<float> best_factors;  std::vector<float> cur_factors;    for (int a = 1; a <= kMaxAlignment; ++a) {    float cur_error = 0;    cur_factors.clear();    for (float factor: scale_factors) {      float approx = GetApprox(encoder_alignment, a, factor, &cur_error);      cur_factors.push_back(approx);    }    if (cur_error < best_error) {      best_error = cur_error;      best_factors = cur_factors;      best_alignment = a;    }  }  *requested_alignment = best_alignment;  return best_factors;}

Заключение

Мне кажется, это хороший пример, когда программисту нужна математика. Без математических рассуждений выше совершенно непонятно, почему этот код находит лучшее решение и откуда он вообще взялся. Без формальной записи задачи вообще непонятно, как к ней подступиться.

Да, без математики еще можно убедить себя, что выданные этим кодом коэффициенты будут подходить под условие задачи (числитель делит вычисленное выравнивание, поэтому все поделиться нацело, а знаменатель дает делимость на необходимое выравнивание для энкодера). Но без цепочки рассуждений (1) => (4),(5) вообще неясно, как этот код находит оптимальное решение.

Подробнее..

Из песочницы Table-Makers Dilemma, или почему почти все трансцендентные элементарные функции округляются неправильно

21.09.2020 12:22:00 | Автор: admin
С удивлением обнаружил, что на русском языке трудно отыскать информацию по данной проблеме, как будто мало кого волнует, что математические библиотеки, используемые в современных компиляторах, иногда не дают корректно-округлённого результата. Меня эта ситуация волнует, так как я как раз занимаюсь разработкой таких математических библиотек. В иностранной литературе эта проблема освещена хорошо, вот я и решил в научно-популярной форме изложить её на русском языке, опираясь на западные источники и пока ещё небольшой личный опыт.



Друзья, для вашего удобства статья также доступна в формате презентации на видео (почти 34 минуты), такой формат больше подойдёт тем читателям, которым трудно выстраивать нужные математические образы в голове, так как в презентации много иллюстративного материала. Информация на видео полностью идентична содержанию статьи. Действуйте, пожалуйста, как вам удобно.




Повторюсь, что это не научная, а научно-популярная статья, прочитав которую, вы кратко познакомитесь вот с чем.

  • Трансцендентные элементарные функции (exp, sin, log, cosh и другие), работающие с арифметикой с плавающей запятой, округляются некорректно, иногда они допускают ошибку в последнем бите.
  • Причина ошибок не всегда кроется в лени или низкой квалификации разработчиков, а в одном фундаментальном обстоятельстве, преодолеть которое современная наука пока не смогла.
  • Существуют костыли, которые позволяют худо-бедно справляться с обсуждаемой проблемой в некоторых случаях.
  • Некоторые функции, которые должны делать вроде бы одно и то же, могут выдавать различный результат в одной и той же программе, например, exp2(x) и pow(2.0, x).

Чтобы понять содержание статьи, вам нужно быть знакомым с форматом чисел с плавающей запятой IEEE-754. Будет достаточно, если вы хотя бы просто понимаете что, например, вот это: 0x400921FB54442D18 число пи в формате с удвоенной точностью (binary64, или double), то есть просто понимаете, что я имею в виду этой записью; я не требую уметь на лету делать подобные преобразования. А про режимы округления я вам напомню в этой статье, это важная часть повествования. Ещё желательно знать программистский английский, потому что будут термины и цитаты из западной литературы, но можете обойтись и онлайн-переводчиком.

Сначала примеры, чтобы вы сразу поняли, в чём состоит предмет разговора. Сейчас я дам код на C++, но если это не ваш язык, то уверен, вы всё равно без труда поймёте что написано. Посмотрите, пожалуйста, на этот код:

#include <stdio.h>#include <cmath>int main() {  float x = 0.00296957581304013729095458984375f;  // Аргумент, записанный точно.  float z;  z = exp2f(x);  // z = 2**x одним способом.  printf ("%.8f\n", z);  // Вывод результата с округлением до 8 знаков после точки.  z = powf(2.0f, x);  // z = 2**x другим способом  printf ("%.8f\n", z);  // Такой же вывод.  return 0;}

Число x намеренно записано с таким количеством значащих цифр, чтобы оно было точнопредставимым в типе float, то есть чтобы компилятор преобразовал его в бинарный код без округления. Ведь вы прекрасно знаете, что некоторые компиляторы не умеют округлять без ошибок (если не знаете, укажите в комментариях, я напишу отдельную статью с примерами). Далее в программе нам нужно вычислить 2x, но давайте сделаем это двумя способами: функция exp2f(x), и явное возведение двойки в степень powf(2.0f, x). Результат, естественно, будет различным, потому что я же сказал выше, что не могут элементарные функции работать правильно во всех случаях, а я специально подобрал пример, чтобы это показать. Вот что будет на выходе:

1.002060531.00206041

Эти значения мне выдали четыре компилятора: Microsoft C++ (19.00.23026), Intel C++ 15.0, GCC (6.3.0) и Clang (3.7.0). Они отличаются одним младшим битом. Вот шестнадцатеричный код этих чисел:

0x3F804385  // Правильно0x3F804384  // Неправильно

Запомните, пожалуйста, этот пример, именно на нём мы рассмотрим суть проблемы чуть ниже, а пока, чтобы у вас сложилось более ясное впечатление, посмотрите, пожалуйста, примеры для типа данных с удвоенной точностью (double, binary64) с некоторыми другими элементарными функциями. Привожу результаты в таблице. У правильных ответов (когда они есть) стоят символы * на конце.

Функция Аргумент MS C++ Intel C++ GCC Clang
log10(x) 2.60575359533670695e129 0x40602D4F53729E44 0x40602D4F53729E45* 0x40602D4F53729E44 0x40602D4F53729E44
expm1(x) -1.31267823646623444e-7 0xBE819E53E96DFFA9* 0xBE819E53E96DFFA8 0xBE819E53E96DFFA8 0xBE819E53E96DFFA8
pow(10.0, x) 3.326929759608827789e-15 0x3FF0000000000022 0x3FF0000000000022 0x3FF0000000000022 0x3FF0000000000022
logp1(x) -1.3969831951387235e-9 0xBE17FFFF4017FCFF* 0xBE17FFFF4017FCFE 0xBE17FFFF4017FCFE 0xBE17FFFF4017FCFE


Надеюсь, у вас не сложилось впечатления, что я специально взял какие-то прямо совсем уникальные тесты, которые с трудом можно отыскать? Если сложилось, то давайте состряпаем на коленках полный перебор всех возможных дробных аргументов для функции 2x для типа данных float. Понятно, что нас интересуют только значения x между 0 и 1, потому что другие аргументы будут давать результат, отличающийся только значением в поле экспоненты и интереса не представляют. Сами понимаете:

$$display$$2^x = 2^{[x]}\cdot2^{\{x\}}.$$display$$



Написав такую программу (скрытый текст будет ниже), я проверил функцию exp2f и то, сколько ошибочных значений она выдаёт на интервале x от 0 до 1.

MS C++ Intel C++ GCC Clang
1910726 (0,97%) 90231 (0,05%) 0 0


Из программы ниже понятно, что число проверяемых аргументов x составило 197612997 штук. Получается, что, например, Microsoft С++ неверно вычисляет функцию 2x для почти одного процента из них. Не радуйтесь, уважаемые любители GCC и Clang, просто именно эта функция в данных компиляторах реализована правильно, но полно ошибок в других.

Код полного перебора
#include <stdio.h>#include <cmath>    // Этими макросами мы обращаемся к битовому представлению чисел float и double#define FAU(x) (*(unsigned int*)(&x))#define DAU(x) (*(unsigned long long*)(&x))    // Эта функция вычисляет 2**x точно до последнего бита для 0<=x<=1.    // Страшный вид, возможно, не даёт сразу увидеть, что     // здесь вычисляется аппроксимирующий полином 10-й степени.    // Промежуточные расчёты делаются в double (ошибки двойного округления тут не мешают).    // Не нужно пытаться оптимизировать этот код через FMA-инструкции,     // практика показывает, что будет хуже, но... процессоры бывают разными.float __fastcall pow2_minimax_poly_double (float x) {  double a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;  DAU(a0) = 0x3ff0000000000001;  DAU(a1) = 0x3fe62e42fefa3763;  DAU(a2) = 0x3fcebfbdff845acb;  DAU(a3) = 0x3fac6b08d6a26a5b;  DAU(a4) = 0x3f83b2ab7bece641;  DAU(a5) = 0x3f55d87e23a1a122;  DAU(a6) = 0x3f2430b9e07cb06c;  DAU(a7) = 0x3eeff80ef154bd8b;  DAU(a8) = 0x3eb65836e5af42ac;  DAU(a9) = 0x3e7952f0d1e6fd6b;  DAU(a10)= 0x3e457d3d6f4e540e;  return (float)(a0+(a1+(a2+(a3+(a4+(a5+(a6+(a7+(a8+(a9+a10*x)*x)*x)*x)*x)*x)*x)*x)*x)*x);} int main() {  unsigned int n = 0;  // Счётчик ошибок.  // Цикл по всем возможным значениям x в интервале (0,1)  // Старт цикла: 0x33B8AA3B = 0.00000008599132428344091749750077724456787109375  // Это минимальное значение, для которого 2**x > 1.0f  // Конец цикла: 0x3F800000 = 1.0 ровно.  for (unsigned int a=0x33B8AA3B; a<0x3F800000; ++a) {     float x;    FAU(x) = a;    float z1 = exp2f (x);// Подопытная функция.    float z2 = pow2_minimax_poly_double (x);// Точный ответ.    if (FAU(z1) != FAU(z2)) {// Побитовое сравнение.      // Закомментируйте это, чтобы не выводить все ошибки на экран (их может быть много).      //fprintf (stderr, "2**(0x%08X) = 0x%08X, but correct is 0x%08X\n", a, FAU(z1), FAU(z2));      ++n;    }  }  const unsigned int N = 0x3F800000-0x33B8AA3B;  // Сколько всего аргументов было проверено.  printf ("%u wrong results of %u arguments (%.2lf%%)\n", n, N, (float)n/N*100.0f);  return 0;} 


Не буду утомлять читателя этими примерами, здесь главное было показать, что современные реализации трансцендентных функций могут неправильно округлять последний бит, причём разные компиляторы ошибаются в разных местах, но ни один не будет работать правильно. Кстати, Стандарт IEEE-754 эту ошибку в последнем бите разрешает (о чём скажу ниже), но мне всё равно кажется странным вот что: ладно double, это большой тип данных, но ведь float можно проверить полным перебором! Так ли сложно это было сделать? Совсем не сложно, и я уже показал пример.

В коде нашего перебора есть самописная функция правильного вычисления 2x с помощью аппроксимирующего полинома 10-й степени, и написана она была за несколько минут, потому что такие полиномы выводятся автоматически, например, в системе компьютерной алгебры Maple. Достаточно задать условие, чтобы полином обеспечивал 54 бита точности (именно для этой функции, 2x). Почему 54? А вот скоро узнаете, сразу после того как я расскажу суть проблемы и поведую о том, почему для типа данных учетверённой точности (binary128) в принципе сейчас невозможно создать быстрые и правильные трансцендентные функции, хотя попытки атаковать эту проблему в теории уже есть.

Округление по-умолчанию и проблема, с ним связанная


Если вы не погружены в разработку математических библиотек, то нет ничего зазорного, чтобы забыть правило округления чисел с плавающей запятой по Стандарту IEEE-754, используемое по-умолчанию. Поэтому я вам его напомню. Если вы всё хорошо помните, всё равно бросьте взгляд хотя бы на конец этого раздела, вас ждёт сюрприз: я покажу ситуацию, когда округлить число может оказаться очень сложным делом.

О том, что такое округлить вверх (к плюс бесконечности), округлить вниз (к минус бесконечности) или округлить к нулю вы легко вспомните по названию (если что, есть википедия). Основные сложности у программистов возникают с округлением к ближайшему, но в случае равного удаление от ближайших к тому, у которого последняя цифра чётная. Да, именно так переводится этот режим округления, который западной литературе называют короче: Round nearest ties to even.

Данный режим округления используется по-умолчанию и работает следующим образом. Если в результате расчётов длина мантиссы оказалась больше, чем может вместить в себя результирующий тип данных, округление выполняется к ближайшему из двух возможных значений. Однако может возникнуть ситуация, когда исходное число оказалось ровно посередине между двух ближайших, тогда в качестве результата выбирается то, у которого последний бит (после округления) окажется чётным, то есть равным нулю. Рассмотрим четыре примера, в которых нужно выполнить округление до двух битов после двоичной запятой:

  1. Округлить 1,001001. Третий бит после запятой равен 1, но дальше есть ещё 6-й бит, равный 1, значит округление будет вверх, потому что исходное число ближе к 1,01, чем к 1,00.
  2. Округлить 1,001000. Теперь округляем вниз, потому что мы находимся ровно посередине между 1,00 и 1,01, но именно у первого варианта последний бит будет чётным.
  3. Округлить 1,011000. Мы посередине между 1,01 и 1,10. Придётся округлять вверх, потому что чётный последний бит именно у большего числа.
  4. Округлить 1,010111. Округляем вниз, потому что третий бит равен нулю и мы ближе к 1,01, чем к 1,10.

По этим примерам может показаться, что всё просто, но это не так. Дело в том, что иногда мы не можем точно сказать, действительно ли мы находимся посередине между двух значений. Посмотрите пример. Пусть мы снова хотим округлить до двух битов после запятой:

1,0010000000000000000000000000000000000001

Вам сейчас очевидно, что округление должно быть вверх, то есть к числу 1,01. Однако вы смотрите на число, в котором после запятой 40 битов. А что если ваш алгоритм не смог обеспечить 40 битов точности и достигает, скажем, только 30 битов? Тогда он выдаст другое число:

1,001000000000000000000000000000

Не подозревая, что на 40-й позиции (которую алгоритм рассчитать не в состоянии) будет заветная единичка, вы округлите это число книзу и получите 1,00, что неправильно. Вы неправильно округлили последний бит в этом и состоит предмет нашего обсуждения. Из сказанного выходит, что для того чтобы получить всего лишь 2-й бит правильным, вам придётся вычислять функцию до 40 битов! Вот это да! А если паровоз из нулей окажется ещё длиннее? Вот об этом и поговорим в следующем разделе.

Кстати, именно эту ошибку допускают многие компиляторы, когда переводят десятичную запись числа с плавающей запятой в результирующий двоичный формат. Если исходное десятичное число в коде программы слишком близко к середине между двух точнопредставимых двоичных значений, то оно не будет округлено правильно. Но это не тема данной статьи, а повод для отдельной истории.

Суть проблемы округления последнего значащего бита


Проблема проявляется по двум причинам. Первая намеренный отказ от трудоёмкого вычисления в пользу скорости. В этом случае лишь бы заданная точностью соблюдалась, а какие там будут биты в ответе дело второстепенное. Вторая причина Table Makers Dilemma, которая является основным предметом нашего разговора. Рассмотрим обе причины подробнее.

Первая причина


Вы, конечно, понимаете, что вычисление трансцендентных функций реализовано какими-то приближёнными методами, например, методом аппроксимирующих полиномов или даже (редко) разложением в ряд. Чтобы вычисления происходили как можно быстрее, разработчики соглашаются выполнить как можно меньшее количество итераций численного метода (или взять полином как можно меньшей степени), лишь бы алгоритм допускал погрешность не превосходящую половину ценности последнего бита мантиссы. В литературе это пишется как 0.5ulp (ulp = unit in the last place).

Например, если речь идёт о числе x типа float на интервале (0,5; 1) величина ulp = 2-23. На интервале (1;2) ulp = 2-22. Иными словами, если x находится на интервале (0;1) то 2x будет на интервале (1,2), и чтобы обеспечить точность 0.5ulp, нужно, грубо говоря, выбрать EPS = 2-23 (так мы будем обозначать константу эпсилон, в простонародье именуемую погрешность, или точность, кому как нравится, не придирайтесь, пожалуйста).

Для прикладных расчётов этого достаточно, а то что последние биты могут не совпадать с абсолютным результатом, это для почти 100% программистов не важно, потому что им важно не то, какие будут биты, а то, какая будет точность.

Для тех кто не понимает, приведу пример в десятичной системе счисления. Перед вами два числа: 1,999999 и 2,0. Допустим, что первое это то, что получил программист, а второе это эталон, что должно было бы получиться, будь у нас безграничные возможности. Разница между ними всего лишь одна миллионная, то есть ответ рассчитан с погрешностью EPS=10-6. Однако ни одной правильной цифры в этом ответе нет. Плохо ли это? Нет, с точки зрения прикладной программы это фиолетово, программист округлит ответ, скажем, до двух знаков после запятой и получит 2,00 (например, речь шла о валюте, $2,00), ему больше и не надо, а то, что он заложил в свою программу EPS=10-6, то молодец, взял запас на погрешность промежуточных вычислений и решил задачу правильно.

Иными словами, не путайте: точность и число правильных битов (или цифр) это разные вещи. Кому нужна точность (это почти 100% программистов), тех обсуждаемая проблема совершенно не касается. Кому нужно чтобы битовая последовательность совпадала с корректно округлённым эталоном, того эта проблема волнует очень сильно, например, разработчиков библиотек элементарных функций. Тем не менее, для общего развития об этом полезно знать всем.

Напомню, это было первое направление проблемы: последние биты ответа могут быть неправильными потому, что это намеренное решение. Главное оставить точность 0.5ulp (или выше). Поэтому численный алгоритм подбирается только из этого условия, лишь бы он работал предельно быстро. При этом Стандарт разрешает реализацию элементарных функций без корректного округления последнего бита. Цитирую [1, раздел 12.1] (англ.):
The 1985 version of the IEEE 754 Standard for Floating-Point Arithmetic did not specify anything concerning the elementary function. This was because it has been believed for years that correctly rounded functions would be much too slow at least for some input arguments. The situation changed since then and the 2008 version of the standard recommends (yet does not require) that some functions be correctly rounded.

Далее перечисляются эти функции, которые рекомендуется, но не требуется округлять корректно:

список функций


Вторая причина


Наконец-то мы перешли к теме разговора: Table Maker's Dilemma (сокращённо TMD). Её название я не смог адекватно перевести на русский язык, оно было введено Уильямом Кэхэном (отцом-основателем IEEE-754) в статье [2]. Возможно, если вы прочитаете статью, то поймёте, почему название именно такое. Если кратко, то суть дилеммы в том, нам нужно получить абсолютно точное округление функции z=f(x), как если бы у нас в распоряжении была бесконечная битовая запись идеально посчитанного результата z. Но всем ясно, что бесконечную последовательность мы не можем получить. А сколько битов тогда взять? Выше я показал пример, когда нам нужно увидеть 40 битов результата, чтобы получить хотя бы 2 корректных бита после округления. А суть проблемы TMD в том, что мы заранее не знаем, до скольки битов рассчитать величину z, чтобы получить правильными столько битов после округления, сколько нам требуется. А что если их будет сто или тысяча? Мы не знаем заранее!

Например, как я сказал, для функции 2x, для типа данных float, где дробная часть мантиссы имеет всего 23 бита, нам надо выполнить расчёт с точностью 2-54, чтобы округление произошло правильно для всех без исключения возможных аргументов x. Эту оценку нетрудно получить полным перебором, но для большинства других функций, особенно для типа double или long double (ставь класс, если знаешь что это), подобные оценки неизвестны.

Давайте уже разбираться с тем, почему так происходит. Я намеренно привёл самый первый пример в этой статье с типом данных float и просил вас его запомнить, потому что в этом типе всего 32 бита и проще будет на него смотреть, в остальных типах данных ситуация аналогична.

Мы начали с числа x = 0.00296957581304013729095458984375, это точнопредставимое число в типе данных float, то есть оно записано так, чтобы его конвертирование в двоичную систему float выполнялось без округления. Мы вычисляем 2x, и если бы у нас был калькулятор с бесконечной точностью, то мы должны были бы получить (Чтобы вы могли проверять меня, расчёт выполнен в онлайн-системе WolframAlpha):

1,0020604729652405753669743044108123031635398201893943954577320057

Переведём это число в двоичную систему, скажем, 64 бита будет достаточно:

1,000000001000011100001001000000000000000000000000000001101111101

Бит округления (24-й бит после запятой) подчёркнут. Вопрос: куда округлять? Вверх или вниз? Ясное дело, вверх, вы знаете это, потому что видите достаточно битов и можете принять решение. Но посмотрите внимательно

После бита округления у нас 29 нулей. Это означает, что мы очень-очень близко находимся к середине между двумя ближайшими числами и достаточно только чуть-чуть сдвинуться вниз, как направление округления изменится. Но вот вопрос: куда будет этот сдвиг? Численный алгоритм может последовательно шаг за шагом подбираться к точному значению с разных сторон и до тех пор, пока мы не пройдём все эти 29 нулей и не достигнем точности, которая превысит ценность самого последнего нуля в этом паровозе, мы не будем знать направление округления. А вдруг в действительности правильный ответ должен быть таким:

1,00000000100001110000100011111111111111111111111111111?

Тогда округление будет вниз.

Мы этого не знаем, пока наша точность не достигнет 54-го бита после запятой. Когда 54-й бит будет известен точно, мы будем знать точно, к какому из двух ближайших чисел мы на самом деле ближе. Подобные числа называются hardest-to-round-points [1, раздел 12.3] (критические для округления точки), а число 54 называется hardness-to-round (трудоёмкость округления) и в цитируемой книге обозначается буквой m.

Трудоёмкость округления (m) это число битов, минимально необходимое для того, чтобы для всех без исключения аргументов некоторой функции f(x) и для заранее выбранного диапазона функция f(x) округлялась корректно до последнего бита (для разных режимов округления может быть разное значение m). Иными словами, для типа данных float и для аргумента x из диапазона (0;1) для режима округления к ближайшему чётному трудоёмкость округления m=54. Это значит что абсолютно для всех x из интервала (0;1) мы можем заложить в алгоритм одну и ту же точность ESP=2-54, и все результаты будут округлены корректно до 23-х битов после двоичной запятой.

В действительности, некоторые алгоритмы способны обеспечить точный результат и на основе 53-х и даже 52-х битов, полный перебор это показывает, но теоретически, нужно именно 54. Если бы не было возможности провернуть полный перебор, мы бы не могли схитрить и сэкономить пару битов, как это сделал я в программе перебора, что была дана выше. Я взял полином степенью ниже, чем следовало, но он всё равно работает, просто потому что повезло.

Итак, не зависимо от режима округления, у нас возможны две ситуации: либо в области округления возникает паровоз из нулей, либо паровоз из единиц. Задача правильного алгоритма расчёта трансцендентной функции f(x) в том, чтобы уточнять значение этой функции до тех пор, пока точность не превысит ценность последнего бита этого паровоза, и пока не станет точно понятно, что в результате последующих флуктуаций численного алгоритма расчёта f(x) нули не превратятся в единицы, или наоборот. Как только всё стабилизировалось, и алгоритм вышел на такую точность, которая находится за пределами паровоза, тогда мы можем округлять так, как если бы у нас было бесконечное число битов. И это округление будет с корректным последним битом. Но как этого добиться?

Костыли


Как было сказано, основная проблема заставить алгоритм преодолеть паровоз из нулей или единиц, которые идут сразу после бита округления. Когда паровоз преодолён и мы видим его целиком, тогда это эквивалентно тому, что эти нули или единицы уже рассчитаны точно, и мы уже точно знаем, в какую сторону теперь произойдёт округление. Но если мы не знаем длину паровоза, то как же тогда проектировать алгоритм?

Первый костыль


Читателю может показаться, что ответ очевиден: взять арифметику с бесконечной точностью и заложить заведомо избыточное число битов, а если будет мало, то заложить ещё и пересчитать. В общем-то правильно. Так и делается, когда скорость и ресурсы компьютера не играют особой роли. У этого подхода есть имя: многоуровневая стратегия Зива (Zivs multilevel strategy) [1, раздел 12.3]. Суть её предельно проста. Алгоритм должен поддерживать расчёты на нескольких уровнях: быстрый предварительный расчёт (в большинстве случаев он же оказывается финальным), более медленный, но более точный расчёт (спасает в большинстве критических случаев), ещё более медленный, но ещё более точный расчёт (когда совсем худо пришлось) и так далее.

В подавляющем большинстве случаев достаточно взять точность чуть выше чем 0.5ulp, но если появился паровоз, то увеличиваем её. Пока паровоз сохраняется, наращиваем точность до тех пор, пока не будет строго понятно, что дальнейшие флуктуации численного метода на этот паровоз не повлияют. Так, скажем, в нашем случае если мы достигли ESP=2-54, то на 54-й позиции появляется единица, которая как бы охраняет паровоз из нулей и гарантирует, что уже не произойдёт вычитания величины, больше либо равной 2-53 и нули не превратятся в единицы, утащив за собой бит округления в ноль.

Это было научно-популярное изложение, всё то же самое с теоремой Зива (Zivs rounding test), где показано как быстро, одним действием проверять достигли ли мы желаемой точности, можно прочитать в [1, глава 12], либо в [3, раздел 10.5].

Проблема этого подхода очевидна. Нужно проектировать алгоритм вычисления каждой трансцендентной функции f(x) так, чтобы по ходу пьесы можно было увеличивать точность расчётов. Для программной реализации это ещё не так страшно, например, метод Ньютона позволяет, грубо говоря, удваивать число точных битов после запятой на каждой итерации. Можно удваивать до тех пор, пока не станет достаточно, хотя это довольно трудоёмкий процесс, надо признаться и не везде метод Ньютона оправдан, потому что требует вычисления обратной функции f-1(x), что в некоторых случаях может оказаться ничуть не проще вычисления самой f(x). Для аппаратной реализации стратегия Зива совершенно не годится. Алгоритм, зашитый в процессоре, должен выполнить ряд действий с уже предустановленным числом битов и это достаточно проблематично реализовать, если мы этого числа заранее не знаем. Взять запас? А сколько?

Вероятностный подход к решению проблемы [1, раздел 12.6] позволяет оценить величину m (напомню, это число битов, которого достаточно для корректного округления). Оказывается, что длина паровоза в вероятностном смысле чуть больше длины мантиссы числа. Таким образом, в большинстве случаев будет достаточно брать m чуть более чем вдвое больше величины мантиссы и только в очень редких случаях придётся брать ещё больше. Цитирую авторов указанной работы: we deduce that in practice, m must be slightly greater than 2p (у них p длина мантиссы вместе с целой частью, то есть p=24 для float). Далее в тексте они показывают, что вероятность ошибки при такой стратегии близка к нулю, но всё-таки положительна, и подтверждают это экспериментами.

Тем не менее, всё равно остаются случаи, когда величину m приходится брать ещё больше, и худший случай заранее неизвестен. Теоретические оценки худшего случая существуют [1, раздел 12.7.2], но они дают немыслимые миллионы битов, это никуда не годится. Вот таблица из цитируемой работы (это для функции exp(x) на интервале от -ln(2) до ln(2)):

p m
24 (binary32) 1865828
53 (binary64) 6017142
113 (binary128) 17570144


Второй костыль


На практике величина m не будет такой ужасно-большой. И чтобы определить худший случай применяется второй костыль, который называется исчерпывающий предподсчёт. Для типа данных float (32 бита), если функция f имеет один аргумент (x), то мы можем запросто прогнать все возможные значения x. Проблема возникнет только с функциями, у которых больше одного аргумента (среди них pow(x, y)), для них ничего такого придумать не удалось. Проверив все возможные значения x, мы вычислим свою константу m для каждой функции f(x) и для каждого режима округления. Затем алгоритмы расчёта, которые нужно реализовать аппаратно, проектируются так, чтобы обеспечить точность 2-m. Тогда округление f(x) будет гарантированно-корректным во всех случаях.

Для типа double (64 бита) простой перебор практически невозможен. Однако ведь перебирают! Но как? Ответ даётся в [4]. Расскажу о нём очень кратко.

Область определения функции f(x) разбивается на очень маленькие сегменты так, чтобы внутри каждого сегмента можно было заменить f(x) на линейную функцию вида b-ax (коэффициенты a и b, конечно же, разные для разных сегментов). Размер этих сегментов вычисляется аналитически, чтобы такая линейная функция действительно была бы почти неотличима от исходной в каждом сегменте.

Далее после некоторых операций масштабирования и сдвига мы приходим к следующей задаче: может ли прямая линия b-ax пройти достаточно близко от целочисленной точки?

Оказывается, что можно относительно просто дать ответ в форме да или нет. То есть да если потенциально опасные точки будут близки к прямой линии, и нет если ни одна такая точка в принципе не может подойти к линии близко. Прелесть этого метода в том, что ответ нет на практике получается в подавляющем большинстве случаев, а ответ да, который получается редко, вынуждает пройтись по сегменту полным перебором, чтобы определить какие конкретно точки оказались критическими.

Тем не менее, перебор аргументов функции f(x) сокращается во много-много раз и делает возможным обнаруживать критические точки для чисел типа double (binary64) и long double (80 битов!). Делается это на суперкомпьютерах и, конечно же, видеокартах в свободное от майнинга время. Тем не менее, что делать с типом данных binary128 пока никто не знает. Напомню, что дробная часть мантиссы таких чисел занимает 112 битов. Поэтому в иностранной литературе по данному поводу пока можно отыскать только полуфилософские рассуждения, начинающиеся с we hope... (мы надеемся...).

Подробности метода, который позволяет быстро определить прохождение линии вблизи от целочисленных точек, здесь излагать неуместно. Желающим познать процесс более тщательно рекомендую смотреть в сторону проблемы поиска расстояния между прямой и Z2, например, в статье [5]. В ней описан усовершенствованный алгоритм, который по ходу построения напоминает знаменитый алгоритм Евклида для нахождения наибольшего общего делителя. Приведу одну и ту же картинку из [4] и [5], где изображена дальнейшая трансформация задачи:

image

Существуют обширные таблицы, содержащие худшие случаи округления на разных интервалах для каждой трансцендентной функции. Они есть в [1 раздел 12.8.4] и в [3, раздел 10.5.3.2], а также в отдельных статьях, например, в [6].

Я приведу несколько примеров, взяв случайные строки из таких таблиц. Подчёркиваю, это не худшие случае для всех x, а только для каких-то небольших интервалов, смотрите первоисточник, если заинтересовались.

Функция x f(x) (обрезанное) 53-й бит и последующие
log2(x) 1.B4EBE40C95A01P0 1.8ADEAC981E00DP-1 10531011...
cosh(x) 1.7FFFFFFFFFFF7P-23 1.0000000000047P0 11890010...
ln(1+x) 1.8000000000003P-50 1.7FFFFFFFFFFFEP-50 10991000...


Как читать таблицу? Величина x указана в шестнадцатеричной нотации числа с плавающей запятой double. Сначала, как положено, идёт лидирующая единица, затем 52 бита дробной части мантиссы и буква P. Эта буква означает умножить на два в степени далее идёт степень. Например, P-23 означает, что указанную мантиссу нужно умножить на 2-23.

Далее представьте, что вычисляется функция f(x) с бесконечной точностью и у неё отрезают (без округления!) первые 53 бита. Именно эти 53 бита (один из них до запятой) указываются в столбце f(x). Последующие биты указаны в последнем столбце. Знак степени у битовой последовательности в последнем столбце означает число повторений бита, то есть, например, 10531011 означает, что сначала идёт бит, равный 1, затем 53 нуля и далее 1011. Потом троеточие, которое означает, что остальные биты нам, в общем-то, и не нужны вовсе.

Далее дело техники мы знаем худшие случаи для каждого интервала отдельно взятой функции и можем для этого интервала подобрать такую аппроксимацию, чтобы она своей точностью накрывала худший случай. Всего лишь годы расчётов на суперкомпьютерах дают возможность создавать быстрые и точные аппаратные реализации элементарных функций. Дело за малым: осталось научить хотя бы разработчиков компиляторов пользоваться этими таблицами.

Зачем это нужно?


Прекрасный вопрос! Ведь я выше неоднократно высказался о том, что почти 100% программистам не нужно знать элементарную функцию с точностью до корректно-округлённого последнего бита (часто им и половина битов не нужна), зачем учёные гоняют суперкомпьютеры и составляют таблицы ради решения бесполезной задачи?

Во-первых, задача носит фундаментальный характер. Интерес представляет скорее не то, чтобы получить точное округление ради точного округления, а чтобы в принципе понять, как можно было бы решить эту интересную задачу, какие тайны вычислительной математики откроет нам её решение? Как можно было бы эти тайны использовать в других задачах? Фундаментальные науки они такие, можно десятки лет заниматься какой-то ерундой, а потом через сто лет благодаря этой ерунде происходит научный провыв в какой-то другой области.

Во-вторых, вопрос переносимости кода. Если функция может позволить себе как угодно обращаться с последними битами результата, то значит на различных платформах и на различных компиляторах могут получаться чуть-чуть различные результаты (пусть даже в пределах заданной погрешности). В каких-то случаях это не важно, а в каких-то может быть существенным, особенно когда в программе есть ошибка, которая проявляется на одной, но не проявляется на другой платформе как раз именно из-за различных битов результата. Но зачем я расписываю вам прекрасно известную головную боль, связанную с различным поведением программы? Вы и без меня всё это знаете. Было бы здорово иметь математическую систему, которая работает абсолютно одинаково на всех платформах, чем бы её не компилировали. Вот для этого и нужно корректно округлять последний бит.

Список источников


[1] Jean-Michel Muller, Elementary Functions: Algorithms and Implementation, 2016

[2] William Kahan, A Logarithm Too Clever by Half, 2004

[3] Jean-Michel Muller, Handbook of floating-point arithmetic, 2018

[4] Vincent Lefvre, Jean-Michel Muller, Toward Correctly Rounded Transcendentals, IEEE TRANSACTIONS ON COMPUTERS, VOL. 47, NO. 11, NOVEMBER 1998. pp. 1235-1243

[5] Vincent Lefvre. New Results on the Distance Between a Segment and Z2. Application to the Exact Rounding. 17th IEEE Symposium on Computer Arithmetic Arith17, Jun 2005, Cape Cod, MA,
United States. pp.68-75

[6] Vincent Lefvre, Jean-Michel Muller, Worst Cases for Correct Rounding of the Elementary Functions in Double Precision, Rapport de recherche (INSTITUT NA TIONAL DE RECHERCHE EN INFORMA TIQUE ET EN AUTOMA TIQUE) n4044 Novembre 2000 19 pages.
Подробнее..

Как нарисовать звезду (и не только) в полярных координатах

22.09.2020 08:08:59 | Автор: admin
Вопрос о формуле для многоугольника в полярных координатах регулярно возникает на тематических ресурсах и так же регулярно остаётся без внятного ответа. В лучшем случае попадается решение через функцию остатка от деления что не является чистым с математической точки зрения, поскольку не позволяет производить над функцией аналитические преобразования. Видимо, настоящие математики слишком заняты решением проблем тысячелетия и поисками простого доказательства теоремы Ферма, чтобы обращать внимание на подобные банальные задачи. К счастью, в этом вопросе воображение важнее знания, и для решения этой задачи не нужно быть профессором топологических наук достаточно знания школьного уровня.



Формула для равностороннего многоугольника в полярных координатах выглядит очень просто

$\rho = \frac{\cos \left(\frac{2 \sin ^{-1}(k)+\pi m}{2 n}\right)}{\cos \left(\frac{2 \sin ^{-1}(k \cos (n \phi ))+\pi m}{2 n}\right)}$


и имеет следующие параметры:

$\phi$ угол;
$n$ количество выпуклых вершин;
$m$ определяет, через какое количество вершин стороны будут лежать на одной прямой. Для него допустимы и отрицательные значения от знака будет зависеть, в какую сторону будет выгибаться звезда;
$k$ жёсткость при $k=0$ мы получим окружность вне зависимости от прочих параметров, при $ k=1$ многоугольник с прямыми линиями, при промежуточных значениях от $0$ до $1$ промежуточные фигуры между окружностью и многоугольником.

С этой формулой можно нарисовать звезду двумя путями:

1) $n=5, m=3$


2) $n=5/4,m=0$. В этом случае требуется сделать два оборота вместо одного:


Параметр $m$ влияет на многоугольник следующим образом (здесь он изменяется от -1 до 5):


Параметр $k$ в анимации:


Комплексная форма и модификации


Можно переписать исходную формулу в комплексном виде, и, несмотря на наличие в ней мнимых единиц, значение радиуса по-прежнему будет оставаться действительным:

$\rho = \frac{4^{1/n} \left(\sqrt{1-k^2}+i k\right)^{-1/n} \left(1+\left(\sqrt{1-k^2}+i k\right)^{2/n} e^{\frac{i \pi m}{n}}\right) \left(\sqrt{1-k^2 \cos ^2(n \phi )}+i k \cos (n \phi )\right)^{1/n}}{4^{1/n}+e^{\frac{i \pi m}{n}} \left(2 \sqrt{1-k^2 \cos ^2(n \phi )}+2 i k \cos (n \phi )\right)^{2/n}}$


На первый взгляд это может показаться бессмысленным, поскольку формула стала чуть более громоздкой но не стоит спешить с выводами. Во-первых, в ней отсутствует арксинус, что полностью меняет математический смысл формулы и позволяет по-другому посмотреть на построение звёздчатого многоугольника. Во-вторых, из неё также можно получить компактные формулы для частных случаев, например $\frac{i^t \left(i^{n t}\right)^{1/n}}{1+\left(i^{n t}\right)^{2/n}}$. В-третьих (и самое интересное), её можно творчески модифицировать и получать другие, неожиданные формы. Для того, чтобы появление возможной мнимой компоненты в радиусе не вызывало неоднозначности при вычислении, можно её сразу привести к декартовым координатам умножением на $e^{i \phi }$. Вот примеры некоторых модификаций:

$\frac{(-1)^{2/3} e^{i \phi } \sqrt[3]{\sqrt{\sin ^2(3 \phi )}+i \cos (3 \phi )}}{(-1)^{2/3}+2^{2/3} \left(\sqrt{\sin ^2(3 \phi )}+i \cos (3 \phi )\right)^{2/3}}$



$\frac{e^{i \phi } \sqrt{\sqrt{\sin ^2(2 \phi )}+i \cos (2 \phi )}}{\sqrt{2} \left(\sqrt{\sin ^2(2 \phi )}+i \cos (2 \phi )\right)^{3/2}+e^{i/2}}$



$\frac{e^{\frac{1}{4} i (4 \phi +\pi )}}{2 \sqrt{\sqrt{\sin ^2(2 \phi )}+\sqrt[4]{-1} \cos (2 \phi )}+(-1-i)}$



Как вы наверняка заметили, вращение вектора перестало быть равномерным и именно из-за появления мнимой составляющей в радиусе.

Квадрокруги и прочее


У нашей формулы есть замечательный частный случай квадрат, формулу для которого можно выписать как

$\rho = \frac{2}{\sqrt{2+\sqrt{2+2 \cos (4 \phi )}}}$


или

$\rho = \sqrt{\frac{2}{1+\sqrt{1-\sin ^2(2 \phi )}}}$


(выбирайте, какая больше нравится).

В чуть более развёрнутом случае можно определить промежуточные фигуры между кругом и квадратом через точку $(k,k)$ на плоскости

$\rho = \sqrt{\frac{2}{1+\sqrt{1-\frac{\left(2 k^2-1\right) \sin ^2(2 \phi )}{k^4}}}}$




Можно также добавить вариативности этим фигурам с сохранением условия прохождения их через точку $(k,k)$ модулируя непосредственно сам параметр $k$ в зависимости от угла таким образом, чтобы при прохождении через диагонали его множитель был равен единице. Например, подставив вместо $k$ функцию $\frac{k}{1-z \cos ^2(2 \phi )}$, мы получим дополнительный параметр $z$, которым можно регулировать дополнительные изгибы. В частности, для $z=1/4$ получится следующее:



В ещё более развёрнутом случае можно определить не просто квадрат а прямоугольник, и по прежнему в полярных координатах:

$\rho = \sqrt{\frac{4 a^2 b^2}{\left(\left(b^2-a^2\right) \cos (2 \phi )+a^2+b^2\right) \left(\sqrt{1-\frac{4 a^2 b^2 k \sin ^2(2 \phi )}{\left(\left(b^2-a^2\right) \cos (2 \phi )+a^2+b^2\right)^2}}+1\right)}}$


И даже посчитать его площадь (через эллиптические интегралы):

$S=4 a b\frac{((k-1) K(k)+E(k))}{k}$

Примечание
Для крайних значений $k$ ($0$ и $1$) эта функция имеет особые точки, которые можно посчитать через предел и они ожидаемо будут равны $\pi a b$ и $4 a b$.

Это позволит делать профили с переходом из окружности в прямоугольник с контролируемой площадью сечения. Здесь площадь константна:



А здесь площадь расширяется по экспоненциальному закону:



Переход к декартовым координатам


Любую формулу в полярных координатах можно выразить через уравнение в декартовых координатах, причём как минимум двумя способами в зависимости от чего будет изменяться вид градиента на границе фигуры. Для этого достаточно посчитать угол через арктангенс от координат и привести формулу к константе через радиус-вектор вычитанием

$0=\sqrt{x^2+y^2}-\frac{\cos \left(\frac{2 \sin ^{-1}(k)+\pi m}{2 n}\right)}{\cos \left(\frac{2 \sin ^{-1}\left(k \cos \left(n \tan ^{-1}(x,y)\right)\right)+\pi m}{2 n}\right)}$


или делением

$1=\frac{\sqrt{x^2+y^2} \cos \left(\frac{2 \sin ^{-1}\left(k \cos \left(n \tan ^{-1}(x,y)\right)\right)+\pi m}{2 n}\right)}{\cos \left(\frac{2 \sin ^{-1}(k)+\pi m}{2 n}\right)}$


Второй вариант предпочтительнее, поскольку даёт прямые градиенты вдоль сторон многоугольника.

Примечание
Здесь также нужно помнить, что в точке (0,0) возникает неопределенность из-за деления на ноль которая, впрочем, легко разрешается через предел (который будет равным $-\cos \left(\frac{2 \sin ^{-1}(k)+\pi m}{2 n}\right) \sec \left(\frac{2 \sin ^{-1}\left(k \cos \left(\frac{\pi n}{2}\right)\right)+\pi m}{2 n}\right)$ в первом случае и нулю во втором).

Выражение $\cos \left(n \tan ^{-1}(x,y)\right)$ также можно упростить до $\frac{(x+i y)^n+(x-i y)^n}{2 \left(x^2+y^2\right)^{n/2}}$, коэффициенты числителя которого при разложении образуют знакочередующий вариант последовательности A034839.

Значение формулы из правой части уравнения (во 2-м случае) будет меняться от $0$ до $1$ если точка $(x,y)$ попадает внутрь фигуры, и от $1$ до бесконечности если нет. Выбирая различные функции для преобразования её в яркость, можно получать различные варианты растеризации. Для экспоненты ($e^{-x-1}$ для первого и $e^{-x}$ для второго варианта) получим
или, если с насыщением

Можно использовать классический фильтр нижних частот $\frac{1}{x^p+1}$, в котором $p$ порядок фильтра, определяющий степень затухания.

Для первого варианта:

И для второго:

Можно использовать и кусочно-непрерывную функцию, явно задавая границы интерполяции.

Помимо растеризации как таковой, можно задавать и деформации например, сжать шахматную доску в круг:


Или даже натянуть её на сферу:
формула

$x=\frac{u}{\sqrt{\frac{2}{1+ \left| \frac{u^2-v^2}{u^2+v^2} \right|}}}$


$y=\frac{v}{\sqrt{1+\frac{2}{\left| \frac{u^2-v^2}{u^2+v^2}\right| }}}$


$z=\sqrt{1-\frac{1}{2} u^2 \left(1+\left| \frac{u^2-v^2}{u^2+v^2}\right|\right)-\frac{1}{2} v^2 \left(1+\left| \frac{u^2-v^2}{u^2+v^2}\right|\right)}$




Appendix: как была получена формула


Классический стиль повествования в математических текстах состоит из чередования лемм/теорем и их доказательств как если бы доказуемые утверждения появлялись у авторов в голове откровением свыше. И хотя в этом и бывает доля истины, чаще появлению формул предшествует некоторая исследовательская работа, описание которой может дать большее понимание их смысла, чем формальное доказательство; а верность утверждений, в свою очередь, можно проследить через верность шагов, к ним приведших.

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

1) начинаем с самого простого случая задаче начертить прямую в полярных координатах. Для этого нужно решить уравнение $r \cos (\phi )=1$, решение которого очевидно $r\to \sec (\phi )$.



2) далее аргумент секанса нужно зациклить, чтобы обеспечить изломы в прямой. Именно на этом этапе другие решения используют грязный хак в виде остатка от деления. Здесь же используется последовательное взятие прямой и обратной функции синуса $\sin ^{-1}(\sin (\phi ))$



Такой подход позволяет производить стандартные математические операции над получившейся формулой,
например
можно её продифференцировать и получить функцию для прямоугольной волны:

$\frac{\partial \sin ^{-1}(\sin (\phi ))}{\partial \phi }=\frac{\cos (\phi )}{\sqrt{1-\sin ^2(\phi )}}$




Благодаря этой же записи можно упростить функцию квадрата в полярных координатах до более эстетического вида, используя представление тригонометрический функций в комплексном виде. В Wolfram Mathematica это можно сделать с помощью функций TrigToExp и ExpToTrig:
код
Sec[1/2 ArcSin[k Sin[2 \[Phi]]]]^2//TrigToExp//ExpToTrig//Sqrt[#]&//FullSimplify

$\frac{2}{\sqrt{2+2 \sqrt{1-k^2 \sin ^2(2 \phi )}}}$


Благодаря этой же записи можно получить гладкие промежуточные фигуры между кругом и квадратом с помощью дополнительного множителя $k$, благодаря которому аргумент арксинуса не дотягивает до единицы $\sin ^{-1}(k \sin (\phi ))$:


А для того, чтобы функция пересекала заданную точку, нужно просто составить уравнение и пересчитать $k$:

$\sqrt{\frac{2}{1+\sqrt{1-k' \sin ^2( \frac{\pi}{2} )}}}=k$


код
Solve[(Sqrt[2/(1+Sqrt[1-k Sin[2 \[Phi]]^2])] /. \[Phi]->Pi/4)==x, k] /. x->k

$k'\to \frac{4 \left(k^2-1\right)}{k^4}$



3) параметры $n$ и $m$ были просто добавлены творческим способом и их влияние исследовалось экспериментально, по факту.

4) Прямоугольник легко получить перейдя к параметрическому виду и растягиванием осей

$x=a \cos (t) \sqrt{\frac{2}{1+\sqrt{1-\sin ^2(2 t)}}}$


$y=b \sin (t) \sqrt{\frac{2}{1+\sqrt{1-\sin ^2(2 t)}}}$


Но после этого $t$ уже не будет значить угол, теперь $t$ это просто параметр, который описывает вектор через его проекции на координатные оси. Чтобы перейти обратно к полярным координатам нужно найти длину вектора (через корень суммы квадратов), угол (через арктангенс отношения), выразить этот угол через $\phi$ и подставить получившееся выражение вместо $t$.
код
With[{r = Sqrt[2/(1 + Sqrt[
1 - Sin[2 t]^2])]}, {Sqrt[(a r Cos[t])^2 + (b r Sin[t])^2],
ArcTan[(b r Sin[t])/(a r Cos[t])]}] // Simplify

$\left\{\sqrt{2} \sqrt{\frac{a^2 \cos ^2(t)+b^2 \sin ^2(t)}{\sqrt{\cos ^2(2 t)}+1}},\tan ^{-1}\left(\frac{b \tan (t)}{a}\right)\right\}$



Solve[ArcTan[(b Tan[t])/a]==\[Phi], t]

$t\to \tan ^{-1}\left(\frac{a \tan (\phi )}{b}\right)$



Sqrt[2] Sqrt[(a^2 Cos[t]^2 + b^2 Sin[t]^2)/(1 + Sqrt[Cos[2 t]^2])]
/. t -> ArcTan[(a Tan[\[Phi]])/b] // Simplify



$\sqrt{2} \sqrt{\frac{a^2 b^2 \sec ^2(\phi )}{\left(a^2 \tan ^2(\phi )+b^2\right) \left(\sqrt{\cos ^2\left(2 \tan ^{-1}\left(\frac{a \tan (\phi )}{b}\right)\right)}+1\right)}}$


Упростить такую формулу уже посложнее, и для этого потребуется несколько этапов:

1) перейти к декартовым координатам заменой $\phi \to \tan ^{-1}(x,y)$;
2) перейти к экспоненциальному виду;
3) упростить;
4) сделать обратную замену $x\to \cos (\phi )$ и $y\to \sin (\phi )$;
5) опять перейти к экспоненциальному виду;
6) упростить.

В результате получим такую формулу:
код
Sqrt[2] Sqrt[(a^2 b^2 Sec[\[Phi]]^2) /
((1 + Sqrt[Cos[2 ArcTan[(a Tan[\[Phi]])/b]]^2])
(b^2 + a^2 Tan[\[Phi]]^2))] /. \[Phi] -> ArcTan[x, y]
// TrigToExp // Simplify
// # /. {x -> Cos[\[Phi]], y -> Sin[\[Phi]]} &
// TrigToExp // Simplify // FullSimplify

$2 \sqrt{\frac{a^2 b^2}{\left(\left(b^2-a^2\right) \cos (2 \phi )+a^2+b^2\right) \left(1+\sqrt{\frac{\left(\left(a^2+b^2\right) \cos (2 \phi )-a^2+b^2\right)^2}{\left(\left(b^2-a^2\right) \cos (2 \phi )+a^2+b^2\right)^2}}\right)}}$





Заключение


Как видите, даже в такой простой и банальной вещи как многоугольник, можно найти и придумать что-то новое. И на этом история не заканчивается осталась неизвестной формула площади для общего случая, осталась неизвестной формула для произвольного, а не только правильного многоугольника, остались без рассмотрения разложения в степенные и тригонометрические ряды. Также, вероятно, подобного рода формула существует и для 3-мерного случая.

Поэтому если вам говорят, что в математике уже всё придумано и остались лишь задачи недоступные пониманию обычного человека не верьте. Есть много сугубо практических задач, о существовании которых настоящие математики не подозревают, или их решение им не интересно из-за отсутствия достаточного хайпа вокруг них, или потому что у них есть примерное представление путей достижения для их решения. Не бойтесь браться за задачи, решение которых отсутствует в википедии, не бойтесь публиковать их решения и не бойтесь читать комментарии под статьями о бесполезности всего сущего.
Подробнее..

Сертификация. Стоит ли пользоваться дампами и при чем здесь цепи Маркова?

02.09.2020 18:14:51 | Автор: admin
Более 20 лет назад я заинтересовался телекомом. Я начал с чтения книжек, одной из которых был курс CCNA. Тогда это была довольно тоненькая книжечка с FDDI, Token Ring, ISDN и подобными вещами, о которых новое поколение сетевиков только слышали. И тогда я впервые прочитал про CCIE сертификацию. У меня осталось впечатление, что обладатели этого статуса являются воистину IT богами. Конечно же, мне захотелось достичь этого сетевого самадхи.

С тех пор прошло много времени. Я получил CCNA потом CCNP, с успехом просрочил все эти сертификаты за ненужностью и лет через 10 стал CCIE. Я по-прежнему занимаюсь сетями, и, надо сказать, похоже, люблю свою работу, но романтизм, конечно, прошел. Двадцать лет назад отношение ко всему этому было совсем другим. Это был драйв, это было настоящее приключение.

Сейчас такого интереса уже нет, и отношение к сертификации у разных инженеров тоже разное, да и сам я отношусь к этому неоднозначно. Я ясно осознаю, что это своеобразная игра, часто не совсем честная игра, и, если вы не знаете правил, то, даже обладая прекрасными знаниями и навыками, у вас достаточно большой шанс проиграть. В этой статье мы с помощью несложной математики рассмотрим эти правила, что позволит вам составить свою по возможности максимально честную и одновременно выигрышную стратегию.

Честный подход


Давайте попробуем понять, какой подход оценки нашего знания был бы максимально честным?

  • В качестве первого условия я бы назвал ясное определение круга теоретических вопросов и практических навыков, проверяемых тестированием.
  • Далее, хотелось бы, чтобы была разумная детализированность и специфичность. Какой смысл узнавать насколько хорошо я знаю несущественные в моей работе детали, и могу ли я безошибочно сделать/вспомнить/объяснить то, что пригодится мне пару раз в жизни и что легко гуглится за одну минуту. Хочется, чтобы вопросы были по существу.
  • И третий, достаточно важный для данной статьи компонент это то, что я бы хотел, чтобы проверялись именно мои навыки и знание, необходимое мне в моей профессиональной деятельности, а не навыки сдавать данный тип экзамена.

Предположим, что первые два условия соблюдены. Давайте изучим зависимость честности нашего экзамена от третьего компонента.

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



Стрелки $p_{11}$, $p_{12}$, $p_{13}$ обозначают вероятности перехода из одного состояния в другое. Так, $p_{11}$ обозначает вероятность того, что вы после безуспешной попытки пытаетесь сдать экзамен еще раз. Если нет перехода между состояниями, то это значит, что вероятность перехода равна 0.

Например, вероятности могут быть распределены следующим образом



Суть тестирования данного типа в том, что тестируемые не имеют никакого инсайда, или инсайд никак не улучшает шансы пройти тест. Конечно, известны требования к практическим навыкам а также к объему и глубине теоретического знания, но нет никакой полезной информации по технике сдачи экзамена.

И именно такая ситуация на мой взгляд является идеально честной. В данном случае тестируется ваше знание, а не то, как вы научились сдавать экзамен.

Пример 1

Прекрасным примером для меня является то, как проходили устные экзамены по физике на физтехе. Это всегда очень индивидуальная беседа. Да, были билеты, но часто почти сразу переходили к задачам, вопросам, обсуждению. Все сильно зависело от преподавателя, от того, какая тема обсуждается, как вы начинали отвечать билет, а также от того, какая мысль вдруг пришла экзаменатору в голову и многих других факторов. Конечно, в данном случае сложно говорить о стандарте, и иногда сильные студенты получали меньший бал, чем слабые, но обычно все же справедливость соблюдалась.

Пример 2

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

Но если мы говорим про популярные сертификаты, то этот подход не работает. Причин для этого несколько, например, приведу две:

  • такой индивидуальной подход слишком дорог
  • нужна стандартизация

Справедливый подход


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

Пример

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

Вы можете натренировать этот навык заранее или путем нескольких попыток сдачи экзамена.

Этот подход по-прежнему можно считать довольно честным, потому что здесь все находятся в приблизительно в равных условиях, поэтому я назвал этот тип экзамена справедливым подходом.

Каким будет граф переходов в данном случае?



Мы имеем следующие состояния:

  1. Нет опыта. Вы решили сдавать экзамен абсолютно честно, то есть, не тренируя специально технику сдачи экзамена. Вы знаете теорию и обладаете необходимыми практическими навыками. Вам кажется, что этого должно быть достаточно, чтобы получить достойную оценку.
  2. Не сдал. Опыт. Это состояние, когда вы имеете дополнительное знание и навыки, касающееся процедуры проведения экзамена. Это знание было получено или в результате безуспешной попытки, или в результате целенаправленной подготовки. Еще раз напомню, что в данном случае речь не идет о дампах.
  3. Сдал.
  4. Сдался.

Стрелки $p_{12}$, $p_{13}$, $p_{14}$, обозначают вероятности перехода из одного состояния в другое. Если нет перехода между состояниями, то это значит, что вероятность перехода равна 0.

А теперь зададимся вопросом: насколько уменьшится вероятность вашей сдачи, если вы будете ожидать честного подхода, и просто понадеетесь на ваш опыт и знания и не будете специально готовиться к технике сдачи данного экзамена (то есть нас интересует $p_{13}$)? Конечно, если сложность экзамена такая же, как и в случае честного подхода, то ваши шансы будут такими же. Но мне кажется разумно сделать одно важное допущение, которое кардинально меняет ситуацию.

Давайте предположим, что вендор старается сохранять определенную сложность теста, и делает это, удерживая процент сдачи не выше определенного уровня. Предположим, что уровень сложности вопросов подбирается таким образом, чтобы проходной процент был бы не выше 50%.

Зададим для определенности следующие условия:

  • Вендор поддерживает сложность задания на таком уровне, чтобы в среднем не более 50% претендентов успешно выполняли бы задание. Мы будем проводить расчеты на этой границе.
  • Давайте предположим, что после каждой попытки 5% процентов от сдающих разочаровываются и больше никогда не пытаются пересдать экзамен (сдаются): $p_{14} = p_{24} = 0,05$
  • Опыт увеличивает ваши шансы. Давайте предположим, что вероятность несдачи экзамена из состояния 2 на 30% меньше, чем из состояния 1: $p_{23} = p_{12} + (1 - p_{12})*0,3$
  • При этом упростим задачу и будем считать, что такое увеличение опыта происходит лишь один раз. Это предположение упростит расчет и не изменит наши выводы.
  • Также будем считать, что на свой первый экзамен 50% идет с нулевым знанием об экзамене (то есть из состояния 1) и 50% специально готовятся к технике сдачи экзамена (из состояния 2). При этом мы считаем, что их реальное, полезное знание и навыки приблизительно одинаковы.

Какой процент успешной сдачи будет из состояния 1?

Теперь вероятность сдачи будет лишь 36% против 50% в случае честного подхода.
Вероятность сдачи из состояния 2 55%.

Полный же граф будет выглядеть следующим образом:



Здесь вы можете найти доказательство
Рассмотрим следующую цепь Маркова:



Мы добавили вероятности переходов, выделенные пунктирными линиями: $p_{41} = p_{31} = p_{42} = p_{32} = 0,5$. Т.к. мы не исследуем вероятность нахождения в состояниях 3 и 4, и нас интересуют только состояния 1 и 2 и вероятности перехода ИЗ них, то этот несколько искусственный прием допустим в данном случае. Эти добавленные переходы отражают тот факт, что на экзамене постоянно присутствует одинаковое количество претендентов: сколько убыло (сдало/сдалось) столько и добавилось. При этом коэффициент 0,5 обеспечивает выполнение последнего условия:

Также будем считать, что на свой первый экзамен 50% идет с нулевым знанием об экзамене (то есть из состояния 1) и 50% специально готовятся к технике сдачи экзамена (из состояния 2).

Матрица перехода для такой цепи будет следующей

$ A = \begin{pmatrix} 0 & 0 & 0,50 & 0,50\\ 0,59 & 0,40 & 0,50 & 0,50\\ 0,36 & 0,55 & 0 & 0\\ 0,05 & 0,05 & 0 & 0\\ \end{pmatrix} $



При этом вероятности состояний мы будем представлять в виде вектора:

$ p = \begin{pmatrix}p_1\\p_2\\p_3\\p_4\\\end{pmatrix} $



Где $p_1, p_2, p_3, p_4$ вероятности нахождения в состоянии 1,2,3,4 и

$ p_1 + p_2+p_3+p_4 = 1 $



Легко увидеть, что данная цепь Маркова обладает свойством эргодичности: мы можем попасть в любое состояние из любого состояния за n ходов, например, за 2. Из этого следует, что при достаточно большом количестве переходов (сдач экзамена) наступает стационарный процесс.

Найдем вектор состояний, соответствующий стационарному процессу:

$ Ap = p $



То есть нам нужно найти собственный вектор, соответствующий собственному числу равному 1.

Воспользуемся для этого любым онлайн сервисом нахождения собственных векторов и чисел, например, matrixcalc.org

Для $\lambda = 1$ мы получаем следующий собственный вектор:

$ p = p_4\begin{pmatrix}\displaystyle\frac{400}{73}\\\displaystyle\frac{1060}{73}\\\displaystyle\frac{727}{73}\\1\\\end{pmatrix} $


$p_4$ находится из условия

$ p_1 + p_2+p_3+p_4 = 1 $



Но в нашем случае нас лишь интересует соотношение вероятностей состояний 1 и 2. Легко увидеть, что в процентах это соотношение:

  • доля людей на экзамене в состоянии 1: $\displaystyle\frac{400}{1060+400} \approx 0,27$
  • доля людей на экзамене в состоянии 2: $\displaystyle\frac{1060}{1060+400} \approx 0,73$

Таким образом в стационарном режиме на экзамене будут сидеть 27% без опыта (состояние 1) и 73% претендентов с опытом (состояние 2). Это дает вероятность сдачи:

$ 0,27 * 0,36 + 0,73*0,55 = 0,50 $

что находится в соответствии с нашим условием сложности экзамена.

Это значит, что все наши заданные условия выполнены.

То есть, если вы пытаетесь сдавать экзамен честно, что в контексте данной статьи означает, что вы опираетесь на ваше теоретической знание и практические навыки в рамках данного экзамена, но без специфической подготовки собственно к самой процедуре экзамена, без попытки приспособить свой образ мышления к логике составителей теста, без некоторого инсайда, то вероятность того, что вы сдадите этот экзамен, уменьшается в полтора раза. Чтобы сдать таким образом, уровень вашего знания должен быть значительно выше среднего и на самом деле значительно выше того уровня, который требуется для этого экзамена.

Понятно, что при таком подходе, доля людей, пытающихся сдать честно будет уменьшаться, экзамен начинает усложняться (чтобы сохранилось условие 50%), что приводит к тому, что вероятность сдачи из состояния 1 становится все меньше и меньше.

И все-таки я считаю этот подход справедливым. Здесь нет нечестной конкуренции. Просто нужно понять, что нужно готовиться конкретно к экзамену. Без этого, даже обладая отличными знаниями, вероятность сдать тест невелика (но есть).

Теперь предположим, что есть дампы. Как изменится картина?

Дампы. Нечестная конкуренция


Теперь наша схема изменилась:



Появилось еще одно состояние Дампы. Это значит, что претендент может воспользоваться дампами экзамена, что даст ему 100% вероятность сдачи. Теперь давайте зададим конкретные условия.

  • По-прежнему будем считать, что вендор поддерживает сложность задания на таком уровне, чтобы в среднем не более 50% претендентов проходили бы этот экзамен (мы оговорили это выше).
  • Как и в предыдущем случае, считаем, что опыт уменьшает вероятность несдачи экзамена на 30%.
  • Немного изменим вероятности перехода в состояние 4. Давайте для идеалистов ($p_{14}$ ) увеличим этот коэффициент до 10%, для перехода $p_{24}$ оставим таким же, равным 5%. Это не сильно повлияет на результат, но это будет отражать тот факт, что шок от попытки сдать экзамен из состояния 1 будет достаточно большим (причины мы обсудим позже), и разумно предположить, что это будет чаще приводить к разочарованию.
  • Предположим, что изначально всего лишь 20% претендентов используют дамп. Распределение между состояниями 1 и 2 при первой попытке будем считать равным (по 40%).
  • Будем считать, что, если первая попытка (из состояния 1 или 2) оказалась неудачной, и претендент все же решил продолжать, то ровно половина из них воспользуется дампом: $p_{12} = p_{15}$ и $p_{25} = p_{22}$

Давайте посмотрим, как изменится картина. Вот решение:


Здесь вы можете найти доказательство
Как и в предыдущем примере рассмотрим эргодическую цепь Маркова



Мы добавили вероятности переходов, выделенные пунктирными линиями: $p_{41} = p_{31} = p_{42} = p_{32} = 0,4$ и $p_{45} = p_{35} = 0,2$. Логика такая же, как и в предыдущем случае. Таким образом мы возвращаем убывших претендентов и обеспечиваем следующее условие:

Предположим, что изначально лишь 20% претендентов используют дамп. Распределение между состояниями 1 и 2 при первой попытке будем считать равным (по 40%).

Матрица перехода для такой цепи будет следующей

$ A = \begin{pmatrix}0 & 0 & 0,40 & 0,40 & 0\\0,43 & 0,31 & 0,40 & 0,40 & 0\\0,05 & 0,34 & 0 & 0 & 1\\0,10 & 0,05 & 0 & 0 & 0\\0,42 & 0,30 & 0,20 & 0,20 & 0\\\end{pmatrix} $


Найдем вектор состояний, соответствующий стационарному процессу:

$ Ap = p $



Как и в предыдущем случае, воспользуемся для этого любым онлайн сервисом нахождения собственных векторов и чисел, например, www.wolframalpha.com

Получим что для $\lambda = 1$ мы имеем следующий собственный вектор:

$ p \approx p_5\begin{pmatrix}0,649\\1,344\\1,489\\0,132\\ 1\end{pmatrix} $


$p_5$ при этом находится исходя из условия нормировки вероятности

$ p_1 + p_2 + p+3 + p_4 + p_5 = 1 $


но для решения нашей задачи, как и в предыдущем случае, в этом нет необходимости.

Найдем доли людей на экзамены в состояниях 1,2,5:

  • доля людей на экзамене в состоянии 1: $\displaystyle\frac{0,649}{0,649+1,344 + 1} \approx 0,22$
  • доля людей на экзамене в состоянии 2: $\displaystyle\frac{1,344}{0,649+1,344 + 1} \approx 0,45$
  • доля людей на экзамене в состоянии 5: $\displaystyle\frac{1}{0,649+1,344 + 1} \approx 0,33$

Таким образом видно, что доля сдавших экзамен будет:

$ 0,22*0,05 + 0,45*0,34 + 0,33 = 0,494\approx 0,5 $



То есть данное решение удовлетворяет всем нашим условиям.

Мы видим, что вероятность сдать экзамен из состояния 1 стала всего 5%, что значит, что у вас практически нет шансов. Казалось бы, что для состояния 2 все не так уж и плохо 34%, но, в действительности, при небольших значениях $p_{13}$ такой расчет $p_{23}$ не совсем корректен. Дело в том, что наше условие опыт уменьшает вероятность несдачи экзамена на 30% искусственно ограничивает снизу вероятность $p_{23}$ тридцатью процентами (ниже быть не может), что, конечно же, не совсем правильно

Единственный вывод, который пока можно сделать это то, что при наших условиях мы можем забыть про возможность сдачи из состояния 1 (нет опыта). Очень скоро комьюнити, для которого этот экзамен представляет интерес, осознаёт это, и количество идеалистов, пытающихся сдать экзамен без соответствующей подготовки, становится пренебрежимо малым. Тогда мы получаем другую диаграмму:



При тех же условиях, что и в предыдущем примере, мы получим следующее решение:



При этом распределение на экзамене будет следующим:

Состояние 1 (опыт): $\approx66$%
Состояние 2 (дамп): $\approx34$%

Думаю, что метод доказательства очевиден из предыдущих двух примеров, поэтому я опускаю его.

Это значит, что при появлении дампов (и при тех условиях, которые мы наложили) мы получим падение вероятности сдачи для людей, которые специально готовились к экзамену или имели опыт неудачной сдачи и при этом не используют дампы с 55% до 25%, то есть более чем в 2 раза.

Теперь давайте вспомни условия.
При первой попытке у нас лишь 20% решили сразу воспользоваться дампом. После неудачной попытки лишь половина решает воспользоваться дампом. И это довольно мягкое условие привело к тому, что для претендентов, готовящихся к экзамену без дампов, сдать экзамен становится очень сложно. В среднем им придется сдавать этот экзамен 4 раза. Затраты на подготовку (время) и на сдачу становятся довольно большими. При этом можно сдать по дампу. Это потребует существенно меньшего времени на подготовку и даст практически 100% вероятность сдачи.

Итак, давайте посмотрим на плюсы и минусы, которые получает претендент в обоих случаях.

Что имеют те, кто пытаются сдавать без дампов?

  • (-) потраченное время на подготовку (деньги)
  • (-) потраченные деньги за неудачные попытки
  • (-) в случае неудачи руководство и коллеги начинают сомневаться в их компетенции
  • (+) хороший уровень знания

Те, кто сдают с дампами

  • (-) стоимость дампа (необязательно: дамп может находиться в свободном доступе)
  • (-) возможно, недостаточный уровень знаний (необязательно: никто вам не мешает хорошо подготовиться при этом)
  • (-) морально-этические издержки, связанные с нечестной конкуренцией (необязательно и очень индивидуально)
  • (+) минимально потраченные ресурсы
  • (+) уважение коллег и руководства (ведь не обязательно говорить, что вы воспользовались дампом)

В принципе, теоретически все это можно выразить ценой и просто сравнить цены честной сдачи и сдачи с дампом. При этом то, что помечено минусом (-) прибавляет стоимость, а то, что положительно, наоборот, убавляет. Понятно, что некоторые вещи из списка очень индивидуальны (например, морально-этические издержки), и поэтому цена сдачи с дампом и цена честной сдачи будут сильно варьироваться для разных людей.

Когда цена дампа становится меньше цены честной сдачи, человек начинает пользоваться дампами.

Состояние насыщения


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

В результате вендор вынужден предпринимать какие-то действия. Простейшим действием является увеличение частоты обновления тестового задания. В эти моменты дампы становятся неактуальными, и за счет этого вероятность сдачи с дампом может быть снижена.

Пример

Предположим, что экзамен меняется раз в 3 месяца, на появление нового актуального дампа уходит 2 недели, тогда вероятность сдачи с дампом падает со 100% до $\approx 85$%. Но этого может быть недостаточно, поэтому экзамен все усложняется и усложняется, появляются разные варианты, делается так, что даже с дампом требуется интенсивная тренировка.

Таким образом,

  • экзамен становится невероятно сложным
  • шансов сдать без дампов мало
  • даже чтобы сдать с дампами, вам приходится серьезно готовиться, фактически тренироваться
  • экзамены и темы часто и неожиданно меняются. В эти моменты те, кто готовился по дампам начинают терпеть фиаско, а пытающиеся сдать экзамен честно, получают преимущество

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

Пример

Таким экзаменом, на мой взгляд, до недавнего времени был CCIE R&S. Существуют прекрасные курсы для подготовки к CCIE, например, ine.com. Но, даже пройдя всю подготовку (что займет у вас около полугода, полностью потраченные на это), сдать экзамен все равно достаточно сложно. И параллельно с этим, во всяком случае еще несколько лет назад, вы легко могли найти бесплатные дампы в интернете, как письменных экзаменов, так и лабораторных работ.

Следствия


В действительности следствия от появления дампов с низкой стоимостью (в смысле, рассмотренном выше) являются довольно печальными. С одной стороны, мы имеем изощренно сложные тесты и лабораторные работы, которые реально очень сложно пройти. С другой стороны, полученный сертификат не гарантирует соответствующего уровня знаний.

Вы удивитесь, на какие вопросы не отвечают счастливые обладатели CCIE сертификатов. Не редко эксперты совершенно не готовы к честному собеседованию. И дело не в том, что они когда-то знали, но забыли. Дело в том, что они этого никогда и не знали. Зачем учить, тратить время, деньги, рисковать, когда можно просто зазубрить ответы на вопросы.

Конечно же, это приводит к падению авторитета экспертных сертификатов. От этого страдают не только и не столько вендоры, сколько инженеры, которые получили данные регалии честно. Происходит нивелирование того конкурентного преимущества, которое давал добытый потом и кровью статус.

Думаю, многие из вас наблюдали такую картину: сильный инженер, с хорошим практическим опытом, внимательно изучив рекомендуемую литературу, идет сдавать экзамен и проваливает его. Он обескуражен. Вопросы кажутся не относящимися к делу (и это нередко так), тексты слишком большими и запутанными, часто претендент просто не укладывается во времени. При этом его коллега, с гораздо меньшим опытом и знанием, сдает этот тест практически на 100%. Это типичный результат нечестной конкуренции.

Дампы и вендоры


Почему же вендоры не защищают авторитет своих сертификатов? Может быть это невозможно или очень дорого?

Уверен, что это возможно, и мы видим это на примере CISSP.

Для этого нужно повышать стоимость дампа. И делать это можно следующими путями:

  • делать случайную выборку вопросов из гораздо большего списка, например, из 10000 вопросов
  • знание, которое претендент получает при честной подготовке должно быть действительно ценным для него (что уменьшает полную стоимость честного/справедливого подхода)
  • моральная-этическая составляющая цены дампа должна быть высокой. В комьюнити должно быть однозначное осуждение использования дампов
  • также обычно не представляет труда найти и запретить ресурсы, распространяющие дампы

В соответствии с нашей моделью это должно вернуть тесты в русло справедливого подхода, с адекватной сложностью и глубиной проводимого тестирования.

К сожалению, из моего опыта я вижу, что вендоры часто ограничиваются только тем, что абсолютно бесплатно для них, а именно, сообщают вам, что дампами пользоваться нельзя. В принципе, это не эффективно. Если вы готовитесь к экзамену, то, конечно, вы ищите в интернете вопросы для тренировки, и, честно говоря, вы не знаете, совпадут ли какие-то вопросы с вопросами экзамена или нет.

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

Есть еще один хороший способ увеличить качество сертификата можно просто добавить собеседование.

Уверен, что вендоры прекрасно осознают ситуацию. Например, это легко увидеть по резкому падению процента сдающих при обновлении теста или лабораторной работы, или, например, по количеству сдавших сложный письменный экзамен с результатами близкими к 100%. То, что они ничего не делают с этим, говорит о том, что их вполне устраивает эта ситуация.

Заключение


В данной статье мы рассмотрели несколько моделей, которые я назвал

  • честный подход
  • справедливый подход
  • нечестная конкуренция, дампы

Надо понимать, что это всего лишь модели. Например, мы считали, что существует некое требование сложности, что в реальности может быть не так или не совсем так. Но все же мой личный опыт сдачи экзаменов, опыт прохождения и проведения собеседований и общения с другими инженерами говорит о том, что, по крайней мере для некоторых сертификаций, выводы этой статьи близки к реальности.

Каждый из данных подходов накладывает определенные требования к подготовке. Честный подход требует только хорошего знания теории и хороших практических навыков. Справедливый подход требует также специфической подготовки к экзамену.

Случай нечестной конкуренции несколько сложнее, он включает в себя, например, морально-этическую составляющую, что непросто оценить. Но некоторые выводы мы все же можем сделать:

  • у вас практически нет шансов сдать экзамен честно (в контексте данной статьи это значит, что вы пытаетесь пройти тест, опираясь лишь на необходимое теоретическое знание и практический опыт, без специальной подготовки именно к технике сдачи экзамена)
  • нужно специально тренироваться для сдачи этого экзамена, но, чтобы сдать без дампов, ваш уровень должен быть очень высок, в действительности гораздо выше, чем разумное требование, соответствующее целям и смыслу этого экзамена
  • наличие и широко практикуемая нечестная конкуренция во многом лежит на совести вендоров. На мой взгляд, именно они обязаны защищать авторитет своих сертификатов. В этих условиях, думаю, сложно осуждать инженеров, пользующихся дампами. Иногда без них цена честной сдачи становится непомерно высока, а требования к знанию многократно завышены

Казалось бы, зачем играть в эти игры? Однозначно, что это нужно не всем, но есть несколько факторов, которые важны:

  • иногда сертификаты нужны для работы бизнеса (например, если вы работаете в интеграторе)
  • сертификат реально увеличивает нашу вероятность найти хорошую работу
  • cертификация это прекрасный способ заставить себя выучить что-то

Для меня именно последний пункт всегда являлся самым важным, а сертификаты это приятные и иногда полезные бонусы!
Подробнее..

Случайный трамвай посреди незнакомого города

08.09.2020 14:23:50 | Автор: admin


Есть одна довольно любопытная задачка, она уже давно вошла в математический фольклор, а так же стала излюбленным испытанием на собеседованиях. Ее условия просты, а решение, кажется напрашивается само собой, однако не будем торопиться с выводами. Берите карандаш, чистый лист бумаги, усаживайтесь поудобней и давайте во всем разбираться.

В чем же, собственно, задача


Итак представьте, что Вы приехали в абсолютно незнакомый город и первый трамвай, который вам там повстречался, следовал под номером 17. Как оценить, сколько всего в этом городе трамвайных маршрутов?

Для простоты считайте, что трамвайные маршруты в городе пронумерованы без пропусков числами от 1 до N и изначально каждое из этих чисел с равными шансами могло оказаться номером трамвая, которого вы бы увидели первым.

Впервые задачу о Случайном трамвае я услышал от Николая Николаевича Васильева, моего знакомого питерского математика. Тогда же он поделился со мной наблюдением, что среди тех, кому он рассказывал эту задачу, а затем просил дать ответ не задумываясь, большинство людей назвало число 34, то есть "x2" от 17. На моем опыте самым экстравагантным был ответ моего товарища с мехмата: 17. Только через неделю я догадался, что им двигал спавший где-то на подкорке его мозга принцип максимизации правдоподобия. Хорошо, но 17 и 34, другими словами "x1" и "x2" это наивные и необдуманные ответы, а какой тогда правильный ответ, и вообще, существует ли он у этой задачи.

Подводные камни и область фантазии
Почему же стоит сомневаться насчет существования универсально-правильного ответа? К такой мысли легко прийти, если рассмотреть несколько простых, пускай и вымышленных вселенных. К примеру представьте, что на Земле в каждом городе ровно по 30 трамвайных маршрутов. Будет ли в этой вселенной 30 единственно верным ответом? Теперь представьте, что в другой вселенной на Земле 1000 городов, причем в 999 из них действует по 30 трамвайных маршрутов, а в оставшемся одном их ровно 17. Какой на сей раз ответ будет правильным и как на него повлияет то обстоятельство, что городов с 30-ю маршрутами очень много, а с 17-ю всего один? Сразу скажу, что пользоваться вероятностными соображениями здесь очень трудно, ведь человек, которого просят оценить число маршрутов, не знает, был ли город, в котором он сейчас гостит, выбран на карте случайно, или в этом выборе кроется некая причина и присутствует чей-то расчет.

Принцип крайнего математического пессимизма


Несмотря на заявленные трудности, в математике имеется принцип, при помощи которого для нашей задачи можно дать ответ, остающийся осмысленным даже в самых причудливых мирах.
Когда дело касается игр, этот принцип называют максимизацией гарантированного выигрыша,
и чтобы понять в чем кроется его суть, давайте рассмотрим один простой пример.

Представьте себе игру: скрытно от вас в один из своих кулаков я прячу маленький предмет, пусть это будет сухая горошина, затем вытягиваю руки вперед и прошу вас угадать, в какой из них эта горошина находится. Пусть у нас есть время, чтобы сыграть очень долгую партию игр, причем изначально вам не известно, пытаюсь ли я вас в ней обыграть, стараюсь намеренно поддаться, или сохраняю безразличие к вашему успеху. Предположим, что перед вами стоит цель: выиграть как можно больше игр. Какой стратегии вам в таком случае стоило бы придерживаться?

Для начала давайте попробуем проанализировать, преимущества и недостатки следующих четырех простеньких стратегий:
  1. Всегда выбирать правую руку.
  2. Начать с правой, а затем выбирать ту руку, в которой горошина была найдена в последний раз.
  3. В каждой игре перед тем, как сделать выбор, бросить на стол игральную кость. Если выпало 1 или 2, то выбрать правую руку, на 3, 4, 5 и 6 левую.
  4. Как и в предыдущей стратегии, в каждой игре бросить кость. В тех случаях, когда выпавшая грань оказалась четной, выбрать правую, а когда нечетной левую.

Если вы решите воспользоваться стратегией 1) и при этом я буду прятать горошину всегда в правой руке, то все игры завершаться в вашу пользу. Можно сказать, что сценарий развития партии, при котором я прячу горошину исключительно в правую руку, является для 1)наилучшим. Однако в своем наихудшем сценарии, сценарии, где я прячу горошину исключительно в левой руке, стратегия 1) не позволит вам выиграть ни одной игры, какой бы долгой не была наша партия.

Легко догадаться, что стратегия 2) страдает тем же пороком, а именно в наихудшем для нее сценарии, когда я начинаю с левой руки и затем в каждой из следующих игр чередую руки, она не позволит вам выиграть ни разу, как бы долго не длилась наша партия.

Займемся теперь стратегией, которая идет в нашем списке третьей. Если вы ею воспользуетесь, то ваши шансы на победу в каждой игре, где горошина будет спрятана в правую руку, составят 1/3, а в играх, в которых горошина будет находится в левой, 2/3. Понятное дело, что наихудший сценарий для 3) это, когда я имею привычку прятать горошину исключительно в правой руке, однако даже в этом случае в любой достаточно длинной партии игр, примерно треть из них завершится вашей победой. Теоретически вам конечно может и не повезти, и правильную руку вы не отгадаете ни разу, но практически, скажем в партии из 1000 игр, почти не вероятно, чтобы количество ваших побед было меньше, чем 333 4 (1000 1/3 2/3), то есть меньше чем 333 60, а в партии из миллиона игр возможный процент отличий будет еще меньше. По сути, выбрав стратегию 3), вы тем самым гарантируете себе, что примерно треть от игр партии останутся за вами.

Если вы воспользовались четвертой в списке стратегией, то наша игра приобретает интересную особенность: по сути для вас перестает быть важным, на сей раз я спрятал горошину в правой руке или положил ее в левую, потому как в обеих ситуациях ваши шансы на победу будут одинаковы и составят ровно 50%. Можно даже сказать, любая намеченная мною очередность рук, где я собираюсь прятать горошину, для вас будет является одновременно и наилучшим и наихудшим сценарием развития партии. Получается, что при любых раскладах в достаточно долгой партии игр примерно половина из них должны закончиться вашей победой, в этом смысле стратегия 4) дает вам самые большие гарантии, по сравнению со всеми остальными стратегиями, которые мы уже успели здесь рассмотреть.

Принцип максимизации гарантированного выигрыша предписывает вам перебрать все возможные стратегии игры, для каждой из них вычислить величину выигрыша в наихудшем для нее сценарии и объявить оптимальной ту стратегию, для которой величина этого выигрыша окажется наибольшей. Можно показать, что в игре с угадыванием местоположения горошины, оптимальной, а не только лучшей из списка, является стратегия под номером 4. По своей сути максимизация гарантированного выигрыша аналогична жизненному кредо тех людей, которые всегда склонны рассчитывать на худшее, но при этом все еще пытаются как-то улучшить свое положение в жизни.

Задача о случайном трамвае приобретает свой окончательный вид


Формализм
Чтобы воспользоваться принципом максимизации гарантированного выигрыша, представьте, что судьба играет с вами в игру: она раз за разом посылает вас в неизвестный город, расположенный в какой-то неизвестной вселенной, ждет пока вы не повстречаете там первый трамвай, а затем вопрошает, сколько же в этом городе всего трамвайных маршрутов. Будем считать, что ваш ответ считается приемлемым и игра заканчивается в вашу пользу, если только названное вами число отличается менее чем в два раза от истинного, причем как в большую, так и в меньшую стороны. Будем также считать, что это очень долгая партия игр, длинною в целую жизнь.

Теперь вполне правомерно задать вопрос: Какая стратегия в описанной игре будет для вас оптимальной в том смысле, что сможет обеспечить максимальное число гарантированно приемлемых ответов?

Подробный анализ простейших стратегий
В качестве первого боя с только что поставленной задачей попытаемся выяснить, насколько хороши для нее наивные стратегии "x1" и "x2".

Итак, судьба забросила нас в очередной незнакомый нам город. Как и прежде, буква $inline$N$inline$ обозначает количество трамвайных линий в этом городе. По условию все числа от $inline$1$inline$ до $inline$N$inline$ с равной вероятностью могут оказаться номером маршрута $inline$k$inline$, по которому будет следовать первый подмеченный нами трамвай.

Согласно стратегии "x1" оценкой $inline$V$inline$ для числа $inline$N$inline$ должно быть само $inline$k$inline$. Понятное, что $inline$k$inline$ никогда не будет больше $inline$N$inline$, поэтому не может оказаться так, чтобы $inline$V$inline$ была больше $inline$2N$inline$. Следовательно наша оценка будет неприемлемой только в одном случае: если $inline$V$inline$, то есть $inline$k$inline$, окажется меньше, чем $inline$N/2$inline$. Вероятность получить $inline$k$inline$ меньшее $inline$N/2$inline$ при нечетных$inline$N$inline$ не превосходит 50%, а при четных равна им. Отсюда следует, что при использовании стратегии "x1" в очень длинной партии игр по крайней мере (примерно) половина сделанных оценок гарантированно окажутся приемлемыми, какой бы там сценарий не уготовила судьба.

Теперь предположим, что мы решили использовать стратегию "x2". По правилам этой стратегии, увидев на трамвае номер $inline$k$inline$, мы должны в качестве $inline$V$inline$ назвать число $inline$2k$inline$. Как и в предыдущем случае, наша оценка никогда не превзойдет $inline$2N$inline$, и поэтому неприемлемой она будет считаться только при одном условии, если ее значение меньшее, чем $inline$N/2$inline$. В то же время, $inline$V$inline$ будет меньше $inline$N/2$inline$, только если номер трамвая $inline$k$inline$ окажется меньше, чем $inline$N/4$inline$. Вероятность последнего события для городов с $inline$N$inline$ кратным 4-ем равна $inline$1/4$inline$, а для остальных городов и того меньше. Следовательно, если мы решаем придерживаемся стратегии "x2", то тем самым обретаем гарантию, что в любой долгой партии игр по крайней мере (примерно) 3/4 всех наших ответов окажутся приемлемы независимо от того, насколько превратно поведет себя с нами судьба.

Сквозь тернии к звездам
Перед тем, как приступить к главной части повествования, где мы займемся поисками оптимальных стратегий, я слегка видоизменю условия задачи и буду считать, что $inline$N$inline$, $inline$V$inline$ и $inline$k$inline$ могут теперь принимать не только натуральные, но и любые действительные значения, большие нуля. Этот шаг необходим, чтобы избавиться от связанных с дискретностью надоедливых оговорок и скучного перебора особых случаев. Конечно, сейчас еще трудно представить себе город, в котором есть трамваи со всеми номерами от 0 до , однако этого вам и не понадобится. У нашей задачи в ее новой непрерывной модификации имеется простая и вполне реалистичная модель.

Представьте себе, что некто взял полоску фотографической пленки длинной $inline$N$inline$см и решил пронаблюдать за тем, как на ней будут оставлять свой след приходящие из космоса частицы. В масштабах эксперимента плотность вероятности попадания частиц на пленку будет описываться равномерным распределением на отрезке $inline$[0,\,N]$inline$. В этом опыте экспериментатор сообщает вам расстояние $inline$k$inline$ между левым краем пленки и точкой, куда угодила первая зарегистрированная частица. Как и прежде, от вас требуется дать приемлемую оценку $inline$V$inline$ для неизвестного вам $inline$N$inline$, то есть такую оценку, которая отличается от $inline$N$inline$ не более чем в два раза, как в большую, так и в меньшую стороны. Как и прежде, судьба ведет с вами долгую партию игр, каждый раз решая, каково будет $inline$N$inline$ в очередной игре.

В качестве простого упражнения покажите, что несмотря на изменившиеся условия стратегия "x1" по-прежнему гарантирует вам примерно 50%, а стратеги "x2" все те же примерные 75% приемлемых оценок соответственно, вне зависимости от того, какой сценарий выберет судьба.

Долгий путь к совершенству


Предварительный отсев
Наконец-то все приготовления завершены и в этом параграфе мы можем заняться поисками оптимальных стратегий. Для простоты, правда, мы будем рассматривать не все стратегии, а ограничимся теми, в которых оценка $inline$V$inline$ представима, как $inline$f(k)$inline$, где $inline$f()$inline$ это некоторая действительнозначная функция, определенная на интервале $inline$(0,\ +\infty)$inline$.

Представьте теперь, что в одном эксперименте расстояние от места попадания частицы до левого края фотопленки было равным $inline$k_1$inline$, а в другом эксперименте $inline$k_2$inline$, причем $inline$k_1<k_2$inline$. Не будет ли тогда разумным, длине фотопленки в первом эксперименте дать меньшую оценку, чем во втором. Если так, то из неравенства $inline$k_1<k_2$inline$ всегда должно следовать неравенство $inline$f(k_1)<f(k_2)$inline$, другими словами, функция $inline$f()$inline$ должна быть строго возрастающей. Не менее разумным выглядит предположение, что для близких по значению $inline$k_1$inline$ и $inline$k_2$inline$ оценки $inline$V_1=f(k_1)$inline$ и $inline$V_1=f(k_2)$inline$ тоже должны быть близки, то есть, функция $inline$f()$inline$ должна быть непрерывной.

Если проанализировать, какие ответы мы считаем приемлемыми, то получится еще одно
ограничение на $inline$f()$inline$. Смотрите, в нашем понимании задачи $inline$V$inline$ приемлема в том (и только в том случае), когда $inline$N/2\le f(k)\le2N$inline$. Расстояние $inline$k$inline$ между засвеченным космической частицей пятном и левым краем фотографической пленки никогда не превосходит длину пленки $inline$N$inline$, отсюда непосредственным образом получаем неравенство: $inline$2k\le2N$inline$. Из последнего неравенства следует, что было бы неразумно, узнав от экспериментатора расстояние $inline$k$inline$, оценивать $inline$N$inline$ числом $inline$V$inline$ меньшим $inline$2k$inline$. Действительно, если мы увеличим оценку $inline$V$inline$ до $inline$2k$inline$, то тем самым точно не сделаем ее чрезмерно большой, однако в тех случаях, когда $inline$V$inline$ изначально была неприемлемо малой, подобное переопределение способно ее даже исправить. Таким образом, в процессе поиска оптимальных стратегий нам достаточно рассмотреть только те функций $inline$f(k)$inline$, значения которых при всех $inline$k>0$inline$ подчинены неравенству
$inline$f(k)\geq2k$inline$.

Ее величество формула
Сейчас мы займемся тем, что для произвольной стратегии постараемся выразить величину гарантируемого ею выигрыша в виде некой общей аналитической формулы.

Итак, в очередном эксперименте по регистрации космических частиц используется фотопленка длины $inline$N$inline$, $inline$k$inline$ удаление точки попадания первой частицы от ее (пленки) левого края, а $inline$V=f(k)$inline$ наша оценка для $inline$N$inline$. Пусть эксперименту только предстоит состояться, какова тогда вероятность, что $inline$V$inline$ окажется приемлемой по отношению к $inline$N$inline$? Самое большое значение $inline$V$inline$, когда она еще считается приемлемой, это $inline$2N$inline$, самое маленькое $inline$N/2$inline$.

Поскольку $inline$f()$inline$ строго возрастает и непрерывна, то существует обратная к ней функция $inline$f^{-1}\left(v\right)$inline$, которая также строго возрастает и непрерывна. Для значений $inline$f()$inline$ всюду выполняется неравенство $inline$f(k)\geq2k$inline$, поэтому для значений $inline$f^{-1}()$inline$ должно выполнятся двойственное неравенство: $inline$f^{-1}\left(v\right)\le 1/2 v$inline$ (рис 1)


рис 1

Теперь нетрудно сообразить, что максимальным значением $inline$k$inline$, дальше которого $inline$V$inline$ становится уже неприемлемо большой, является $inline$k_{sup}=f^{-1}\left(2N\right)$inline$. Это следует из того, что $inline$f^{-1}\left(\right)$inline$ строго возрастает и $inline$k_{sup}=f^{-1}\left(2N\right)\le 2N$inline$. Аналогично, минимальным значением $inline$k$inline$, меньше которого $inline$V$inline$ становится неприемлемо малой, является $inline$k_{inf}=f^{-1}\left(N/2\right)$inline$. Из двух последних утверждений следует, что $inline$V$inline$ будет приемлемой по отношению к $inline$N$inline$ в том (и только в том) случае, если расстояние $inline$k$inline$ между засвеченным пятном и левым краем пленки окажется заключенным между $inline$k_{inf}$inline$ и $inline$k_{sup}$inline$. Вероятность последнего события, обозначим ее как $inline$p_{success}(f,\ N)$inline$, равна:

$$display$$\frac{k_{sup}-\ k_{inf}\ }{N}$$display$$


или более подробно:

$$display$$p_{success}(f,\ N)=\ \frac{f^{-1}\left(2N\right)-\ f^{-1}\left(N/2\right)\ }{N}$$display$$


Должно быть очевидно, что наихудшим для для $inline$f$inline$ сценарием будет последовательность таких экспериментов, в каждом из которых длинна фотографической пленки $inline$\bar{N}$inline$ минимизирует значение $inline$p_{success}(f,\ N)$inline$. Отсюда следует, что в очень длинных последовательностях экспериментов доля приемлемых ответов, гарантированная оценкой $inline$f(k)$inline$, будет даваться выражением:

$$display$${\rho\left(f\right)={inf}_N[\ p}_{success}(f,\ N)]$$display$$


Теперь мы можем утверждать, что любая оптимальная стратегия должна максимзировать $inline$\rho\left(f\right)$inline$, другими словами, $inline$\varphi(k)$inline$ будет оптимальной в том и только в том случае, если:

$$display$${inf}_N[p_{success}\left(\varphi,\ N\right)]={sup}_f\ {inf}_N[p_{success}\left(f,\ N\right)]$$display$$

.
Итак, проблема отыскания оптимальных оценок свелась к вопросу о том, как выглядит функция $inline$\varphi(k)$inline$, которая доставляет выражению

$$display$$(*)\ \ \ \ \ \ inf_N\left[\frac{f^{-1}\left(2N\right)-f^{-1}\left(N/2\right)}{N}\right]$$display$$


максимум в классе всех непрерывных строговозрастающих функций, определенных
на интервале (0, +), графики которых лежат не ниже $inline$l(k)=\ 2k$inline$. Не правда ли, что в такой постановке задача может показаться пугающе сложной? Я думаю, для вас это прозвучит неожиданным, но ответ на нее изысканно прост. Давайте вместе попытаемся его угадать.

Искусство правдоподобных рассуждений
Пожалуй, самое простое, с чего можно начать это выяснить, какая из функций вида $inline$f(k)=\ \lambda\cdot k$inline$ (здесь $inline$\lambda$inline$ любое действительное число 2) доставляет выражению (*) самое большое значение. Обратной для $inline$f(k)=\ \lambda\cdot k$inline$ служит функция $inline$f^{-1}\left(v\right)=\lambda^{-1}\cdot v$inline$, подставляя ее в выражение для $inline$p_{success}$inline$, имеем:

$$display$$p_{success}\left(N,\lambda\right)=\ \frac{\lambda^{-1}\cdot2N-\lambda^{-1}\cdot N/2}{N}=\frac{3}{2}\ \cdot\ \lambda^{-1}\ $$display$$


Как вы видите, $inline$p_{success}$inline$ не зависит от $inline$N$inline$ и принимает тем большее значение, чем меньшим по величине было $inline$\lambda$inline$. Таким образом, в классе функций $inline$f(k)=\ \lambda\cdot k$inline$, $inline$\lambda\geq2$inline$ наибольшее значение выражению (*), придает уже знакомая нам по функция $inline$l(k)=\ 2k$inline$.

Давайте теперь подумаем, что произойдет, если отсчитывать расстояние не в сантиметрах, а, скажем, метрах, дюймах или световых годах как тогда изменится вид функции $inline$\varphi$inline$ оптимальной оценки?
Пусть в сантиметрах оценка $inline$V$inline$ имеет вид $inline$V_{cm}=\ f_{cm}(k_{cm})$inline$, а $inline$V_m$inline$ и $inline$k_m$inline$ те же самые величины, выраженные в метрах, тогда:

$$display$$V_m=\frac{1}{100}V_{cm}=\frac{1}{100}f_{cm}({100\cdot k}_m)=\ f_m(k_m)$$display$$


В общем случае мы будем иметь дело со шкалой длин $inline$A$inline$ и шкалой длин $inline$B$inline$, которая получается из $inline$A$inline$ умножением на коэффициент $inline$\mu_{AB}$inline$. Каждая оценка $inline$V(k)$inline$ может быть вычислена как в единицах шкалы $inline$A$inline$, так и в единицах шкалы $inline$B$inline$. Пусть $inline$f_A(k_A)$inline$ ее представление в единицах шкалы $inline$A$inline$, а $inline$f_B(k_B)$inline$ представление в единицах шкалы $inline$B$inline$, тогда:

$$display$$f_B(t)=\mu_{AB}\cdot f_A({\mu_{AB}}^{-1}\cdot t)$$display$$


Вид последнего уравнения говорит о том, что функции $inline$f_A(t)$inline$ и $inline$f_B(t)$inline$ скорей всего различны ($inline$t$inline$ в данном случае это действительная переменная с нейтральным смыслом).

Как вы считаете, правдоподобно ли, что мы и какой-нибудь представитель далекой космической цивилизации получим для решаемой здесь задачи разные ответы лишь потому, что у нас с ним разные единицы измерения длины? Наверное, нет! Отсюда следует, что для любого $inline$\mu>0$inline$ и любой функции $inline$\varphi(t)$inline$, которая максимизирует выражение $inline$(*)$inline$, функция $inline$\psi(t)=\mu\cdot\varphi(\mu^{-1}\cdot t)$inline$ также должна максимизировать $inline$(*)$inline$. Если вдруг $inline$\varphi(t)$inline$ является единственной оптимальной для $inline$(*)$inline$ функцией, то при всех $inline$t>0$inline$ и $inline$\mu>0$inline$ выполняется тождество:

$$display$$\varphi(t)=\mu\cdot\varphi(\mu^{-1}\cdot t)$$display$$

Положив в этом тождестве $inline$\mu=t$inline$, мы тем самым находим вид функции $inline$\varphi$inline$:

$$display$$\varphi(t)=t\cdot\varphi(t^{-1}\cdot t)=t\cdot\varphi(1)$$display$$


Смотрите, что выходит: если все наши многочисленные предположения верны, то оптимальная функция $inline$\varphi(t)$inline$ принадлежит классу функций $inline$f(k)=\ \lambda\cdot k$inline$, но ранее мы уже выяснили, что внутри указанного класса максимальное значение выражению $inline$(*)$inline$ придает функция $inline$l(k)=\ 2k$inline$. Неужели оптимальной стратегией является "x2"?

Строгие выводы: оптимальность x2
Хорошо, у нас есть много намеков на то, что оценка, заданная функцией $inline$l(k)=\ 2k$inline$, является оптимальной. Давайте докажем эту гипотезу строго, а еще установим, при каких условиях других оптимальных оценок нет.

Возьмем произвольную непрерывную строго возрастающую функцию $inline$f(k)$inline$, удовлетворяющую неравенству $inline$f(k)\le2k$inline$, фиксируем некоторое $inline$v_0$inline$ из области ее значений и попытаемся для начала выяснить, какой геометрический смысл скрывается за величиной $inline$p_{success}\left(f,\ v_0\right)$inline$.


рис 2

Если на графике функции $inline$f^{\left(-1\right)}\left(v\right)$inline$ отметить точки $inline$A=({v_0/2,\ f}^{-1}(v_0/2))$inline$ и $inline$B=({{2v}_0,\ f}^{-1}({2v}_0))$inline$ (рис 2), а затем соединить их отрезком, то тангенс угла наклона этого отрезка к оси $inline$Ov$inline$ будет выражаться формулой

$$display$$\frac{f^{-1}\left(2v_0\right)-f^{-1}(v_0/2)}{3/2\ \cdot\ v_0}$$display$$

то есть по сути будет равен $inline$2/3$inline$-ям от $inline$p_{success}\left(f,\ v_0\right)$inline$. То же самое наблюдение можно выразить несколько иначе: для этого нужно на отрезок $inline$AB$inline$ посмотреть как на график некоторой функции $inline$i(v)$inline$. Внутри интервала $inline$(v_0/2,\ 2v_0)$inline$ функция $inline$i(v)$inline$ имеет, очевидно, постоянную производную и $inline$p_{success}\left(f,\ v_0\right)$inline$ равен $inline$3/2$inline$-ым ее величины.

Теперь уже не сложно показать, что функция $inline$f(k)$inline$ не может превзойти $inline$l(k)=\ 2k$inline$ по величине инфинума $inline$p_{success}$inline$, иными словами, стратегия "x2" оптимальна.

Рассуждения я начну с двух предварительных замечаний:
  1. $inline$f(k)\geq2k= l(k)$inline$, поэтому $inline$f^{\left(-1\right)}\left(v\right)\le l^{-1}\left(v\right)=1/2\cdot v$inline$ то есть график функции $inline$f^{\left(-1\right)}\left(v\right)$inline$ лежит не выше графика $inline$l^{-1}\left(v\right)$inline$
  2. величина значения $inline$p_{success}\left(l,\ v\right)$inline$ не зависит от $inline$v$inline$ и равна $inline$3/4$inline$, производная $inline$l^{-1}\left(v\right)$inline$ во всех точках равна $inline$1/2$inline$.


Чтобы потом прийти к противоречию, сначала предположим, что $inline$f(k)$inline$ строго лучше $inline$l(k)$inline$. Последнее возможно только в том случае, если существует некоторое $inline$\varepsilon>0$inline$ и при всех $inline$v$inline$ выполняется неравенство: $inline$p_{success} (f,v)3/4 + $inline$.

Для какого-нибудь $inline$u_0>0$inline$ отметим на графике функции $inline$f^{\left(-1\right)}\left(v\right)$inline$ последовательность точек

$$display$$B_0=(u_0/2,\ f^{\left(-1\right)}\left(u_0/2\right)),\ B_1=(2u_0,\ f^{\left(-1\right)}\left(u_0\right)),\ B_2=(8u_0,\ f^{\left(-1\right)}\left(8u_0\right)),\ ...$$display$$

, соединим их ломанной $inline$B_0\ B_1B_2...B_n...$inline$ и интерпретируем эту ломанную как график кусочно-линейной функции $inline$I(v)$inline$. В последовательности $inline$u_0/2,\ 2u_0,\ 8u_0, \ldots\ $inline$ каждое последующее значение в 4 раза больше предыдущего, поэтому на каждые два идущие друг за другом числа мы можем смотреть, как на некое $inline$v_0/2$inline$ и $inline$2v_0$inline$. Последнее означает, что на каждом звене $inline$B_nB_{n+1}$inline$ ломаной $inline$I(v)$inline$ ее производная будет не меньше чем $inline$2/3\cdot{inf}_v[p_{success}\left(f,\ v\right)]2/3(3/4 +) = 1/2 + 2/3$inline$.

Поскольку производная $inline$l^{-1}\left(v\right)$inline$ равна $inline$1/2$inline$, а производная $inline$I(v)$inline$ во всех точках больше $inline$1/2$inline$ по крайней мере на $inline$2/3$inline$, то вне зависимости от значения $inline$I(v)$inline$ в точке $inline$v_0$inline$, при безграничном увеличении $inline$v$inline$ рано или поздно ее график окажется выше графика $inline$l^{-1}\left(v\right)$inline$. (рис 3)


рис 3

В то же самое время нужно вспомнить, что вершины ломаной $inline$I(v)$inline$ расположены на графике функции $inline$f^{\left(-1\right)}\left(v\right)$inline$, поэтому (смотрите замечание 1)) они сами и вся ломанная должны находится не выше графика $inline$l^{-1}\left(v\right)$inline$. Полученное противоречие как раз и доказывает, что $inline$l\left(k\right)=2k$inline$ оптимальна.

Строгие выводы: единственность
Что будет, если в только что изложенном доказательстве построить ломанную по такой последовательности вершин, которая вместо того, чтобы бесконечно удалятся от оси $inline$OK$inline$, будет наоборот к ней стремится. На самом деле этим можно доказать, что в любой вселенной, где размеры полосок фотопленки никак не ограничены снизу, кроме стратегии с функцией $inline$l\left(k\right)=2k$inline$, других оптимальных стратегий нет. Давайте в этом убедимся.

Предположим, что существует функция $inline$f\left(k\right)$inline$, которая с одной стороны оптимальна, а с другой отлична от $inline$l\left(k\right)$inline$. В таком случае функция $inline$f^{\left(-1\right)}\left(v\right)$inline$ отлична от $inline$l^{-1}\left(v\right)$inline$, а поскольку выполнятся неравенство $inline$f^{\left(-1\right)}\left(v\right)\le l^{-1}\left(v\right)$inline$, то должно найтись хотя бы одно значение $inline$v$inline$, при котором $inline$f^{\left(-1\right)}\left(v\right)$inline$ окажется строго меньше $inline$l^{\left(-1\right)}\left(v\right)$inline$. Пусть $inline$u_0$inline$ одно из таких значений $inline$v$inline$. Аналогично тому, как мы действовали выше, на графике $inline$f^{\left(-1\right)}\left(v\right)$inline$ отметим последовательность точек

$$display$$B_0=(2u_0,\ f^{\left(-1\right)}\left(2u_0\right)),\ B_1=(u_0/2,\ f^{\left(-1\right)}\left(u_0/2\right)),\ B_2=(u_0/8,\ f^{\left(-1\right)}\left(u_0/8\right)),\ ...$$display$$

и проведем через них ломанную $inline$B_0\ B_1B_2...B_n...$inline$ (рис 4). Снова будем интерпретировать эту ломанную, как некоторую кусочно-линейную функцию $inline$I(v)$inline$. Ровно по тем же причинам, что и раньше, производная функции $inline$I(v)$inline$ на каждом звене звене $inline$B_nB_{n+1}$inline$ будет не меньше чем $inline$2/3\cdot{inf}_v[p_{success}\left(f,\ v\right)]$inline$. Поскольку $inline$f$inline$ оптимальна, то $inline${inf}_v[p_{success}\left(f,\ v\right)]$inline$ должен быть не меньше, чем $inline${inf}_v[p_{success}\left(l,\ v\right)]=3/4$inline$. Объединяя два последних утверждения, мы получаем, что производная функции $inline$I(v)$inline$ нигде не меньше $inline$2/3\cdot3/4 = 1/2$inline$.


рис 4

Обозначим символ $inline$\Delta$inline$ разность $inline$l^{\left(-1\right)}\left(u_0\right)-f^{\left(-1\right)}\left(u_0\right)$inline$ и введем еще одну функцию: $inline$g(v)=l^{-1}\left(v\right) - \Delta$inline$. Среди очевидных свойств $inline$g(v)$inline$ можно отметить следующие:
  1. в точке $inline$u_0$inline$ значение $inline$g(v)$inline$ совпадает со значением функции $inline$I(v)$inline$
  2. производная $inline$g(v)$inline$ при всех $inline$v$inline$ одинакова и равна $inline$1/2$inline$


Давайте проследим, как будут изменятся значения функций $inline$I(v)$inline$ и $inline$g(v)$inline$ при уменьшении аргумента $inline$v$inline$ от $inline$u_0$inline$ до $inline$0$inline$. Сначала значения $inline$I(v)$inline$ и $inline$g(v)$inline$ равны. Производная $inline$I(v)$inline$ ни в одной точке промежутка $inline$(0,u_0)$inline$ не меньше производной $inline$g(v)$inline$, поэтому значения функции $inline$I(v)$inline$ убывают не медленнее, чем убывают значения $inline$g(v)$inline$. Из перечисленных фактов следует, что на всем промежутке $inline$(0,\,u_0)$inline$$inline$I(v)\le g(v)$inline$.

Поскольку $inline$g(v)=1/2\cdot v\ +\Delta$inline$, то на промежутке $inline$(0,\,2\Delta)$inline$ значения $inline$g(v)$inline$ отрицательны, а так как $inline$I(v)\le g(v)$inline$ то и значения $inline$I(v)$inline$ на нем так же должны быть отрицательными. В то же время вершины $inline$I(v)$inline$ это точки графика функции $inline$f^{\left(-1\right)}\left(v\right)$inline$, функции, которая может принимать исключительно положительные значения ($inline$f$inline$ определена только для положительных $inline$k$inline$), поэтому значения $inline$I(v)$inline$ не могут быть отрицательными. Полученное противоречие как раз и доказывает, что кроме $inline$l\left(k\right)=2k$inline$, других оптимальных оценок нет.

Дискуссионные вопросы


Постарайтесь самостоятельно приспособить решение задачи о случайной частице к условиям задачи о случайном трамвае. Какой у вас получился результат?

Представьте, что мы решаем задачу о случайной частице во вселенной, где фотопленка
не может быть короче 10 сантиметров. Покажите, что этих условиях оценка $inline$l\left(k\right)=2k$inline$ по-прежнему будет оптимальной, правда, она уже не единственная из таковых. Покажите, что оптимальной, например, является оценка $inline$l\left(k\right)=2k+10$inline$. Какие еще оптимальные оценки вам удалось найти?

К принципу максимизации гарантированного выигрыша появляется масса претензий, если заведомо известно, что ваш оппонент не имеет стремления вас обыграть. Этот принцип, например, трудно считать оправданным, когда партия игр ведется против погодных условий или против мирового рынка ценных бумаг. Какие принципы отбора стратегий в этих случаях вы бы могли предложить взамен, какие из них применимы к задаче о случайном трамвае?

Буду рад вашим мыслям и замечаниям.

Сергей Коваленко
2020 год
magnolia@bk.ru
Подробнее..

Из песочницы Каков вопрос таков ответ формализуя задачу мы уже предопределяем возможный ответ

19.09.2020 18:15:27 | Автор: admin
В интересной и поучительной статье Случайный трамвай посреди незнакомого города предлагается такой эксперимент:
Представьте себе, что некто взял полоску фотографической пленки длинной N см и решил пронаблюдать за тем, как на ней будут оставлять свой след приходящие из космоса частицы. В масштабах эксперимента плотность вероятности попадания частиц на пленку будет описываться равномерным распределением на отрезке от 0 до N. В этом опыте экспериментатор сообщает вам расстояние k между левым краем пленки и точкой, куда угодила первая зарегистрированная частица. Как и прежде, от вас требуется дать приемлемую оценку для неизвестного вам N.

Для решения этой задачи было сделано такое предположение:
Представьте теперь, что в одном эксперименте расстояние от места попадания частицы до левого края фотопленки было равным Р1, а в другом эксперименте Р2, причем Р1<Р2. Не будет ли тогда разумным, длине фотопленки в первом эксперименте дать меньшую оценку, чем во втором?

Мне стало интересно в цифрах всегда ли и насколько это разумно?

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

Для начала, я изменю, упрощу и приземлю эксперимент


Судьба или наш помощник имеет мешочек в котором лежат пронумерованные по порядку бочонки, как в лото. Помощник (мне его представить легче чем судьбу) в тайне от нас достает наугад бочонок и насыпает в первый сундук пронумерованных шаров по числу на бочонке. Затем он повторяет процедуру случайного извлечения бочонка и насыпает соответствующее число пронумерованных по порядку шаров во второй сундук. Перед нами стоят два сундука с неизвестным количеством шаров в каждом из них. Мы достаем наугад один шар из первого и один шар из второго сундука, и делаем разумное предположение, что шару с большим номером соответствует сундук с большим количеством шаров.
Оценим насколько предположение разумно?

Формализуем и уточним задачу


1. Раз уж бочонки лежат в мешочке, то они должны быть ограничены каким-то числом. Помня о первоисточнике про число трамвайных линий, ограничил пока число бочонков в 30.

2. А как поступить если мы вынули из сундуков шары с одинаковыми номерами? У нас есть варианты:

2.1 признать исход неудачным, не принимать решения и попросить помощника сделать новое заполнение сундуков.

2.2 бросить монетку и наугад решить в каком сундуке шаров больше. В этом варианте не будет неудачных исходов.

2.3 решить что раз номера одинаковые, то и количество шаров в сундуках тоже одинаковое. В этом варианте тоже не будет неудачных исходов.

Тут я хочу заметить, что я не выбираю какой вариант лучше. Моя цель посмотреть как разные варианты скажутся на полученном ответе.

3. Раз у нас появилось разное количество исходов, то встает вопрос: А от какого количества исходов считать долю правильных ответов? От всех опытов или только от удачных исходов? Посчитаем оба варианта.

4. Вот вынул помощник первый бочонок, посмотрел номер, насыпал соответствующее число шаров в первый сундук. Стоп! А что он сделал с вынутым бочонком затем? У него два варианта: положить бочонок назад в мешок, а можно не класть назад в мешок. Или что тоже самое, помощник мог достать сразу два бочонка и насыпать шары в сундуки по вынутым числам на бочонках, помощники бывают ленивые, а мы не видим что он там творит. В этом случае у нас никогда не будет равное число шаров в сундуках, и следовательно неудачных исходов. Этот пункт явным образом отступает от задачи из цитаты, там бочонок возвращается назад в мешок, но у меня другие цели, да и невозвращение бочонка это типичная ситуация в жизни, посчитаем и такой вариант.

Итак, у нас есть три варианта как считать исходы опыта при которых номера шаров одинаковы, два варианта подсчета доли правильных ответов и два варианта наполнения сундуков шарами. Итого 12 вариантов результатов эксперимента!

Как вероятность правильного ответа будет зависеть от количества бочонков в мешке судьбы, то есть от максимально возможного числа шаров в сундуке? Может быть все варианты будут одинаковыми? Может быть варианты будут иметь одинаковую тенденцию? Вот в этот момент я попробовал проверить свою интуицию заполнив такую табличку:



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

Чтобы не утомлять формулами, которые хоть и красивы, но рекуррентны, а свести рекуррентные формулы к замкнутым мне не под силу, опишу общий алгоритм расчета:

1. Для каждого числа бочонков в мешке, мы можем составить список всех вариантов наполнения сундуков шарами.

Пример: Если число бочонков 4, то получим 16 вариантов наполнения двух сундуков по количеству шаров: 1и1, 1и2, 1и3, 2и1, 2и2 4и4.

2. Для каждого варианта наполнения сундуков подсчитываем число правильных ответов для трех вариантов подсчета равных шаров.

Пример: Для наполнения сундуков 2и3, (в первом сундуке 2 шара, во втором 3) получится следующая таблица.



3. Для выбранного числа бочонков складываются все правильные ответы для каждого варианта наполнения сундуков.

4. Вычисляем долю правильных для двух вариантов подсчета (по отношению к общему числу опытов и к числу успешных).

5. Считаем так же пункты с 3 по 4 для варианта когда бочонок не возвращается в мешок, то есть когда у нас не может быть равное число шаров в сундуках.

Я подсчитал для числа бочонков с 1 до 8 и 30, чтобы была видна тенденция. Приведу графики.

Сначала для варианта когда бочонок возвращается в мешок




При увеличении числа бочонков в мешке, а следовательно увеличении возможного числа шаров в сундуках вероятность правильной оценки растет и разница между вариантами уменьшается. Любопытно, что вероятность не всегда выше 0,5. Так же любопытен желтый график, на нем есть спад и только потом подъем. Вообще, диапазон от 1 до 7 оказался не очевидным для меня.

Получается, что если шаров меньше 8, то для варианта подсчета Равные считаются неудачей. Доля правильных считается от всех опытов случайный ответ даст лучший результат чем следование правилу Больше номер шара, значит сундук содержит больше шаров.

Графики для варианта когда бочонок не возвращается в мешок и следовательно в сундуках не может быть одинаковое число шаров




Графика три, так как два совпадают, они обозначены красным цветом.

Для четырех вариантов вероятность правильного ответа падает и стремится, видимо, к 0,5!(?) Другими словами, в этих вариантах для большого числа шаров в сундуках, можно вообще не проводить опыта, а просто подбрасывать монетку результат одинаков. Собственно, вот ради этого я и решил просчитать различные варианты, я ждал каких-то неожиданностей. Я не имею строгого доказательства, что вероятность стремится именно к 0,5. Это опять моя интуиция, а она часто подводит.

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

P.S. Как и хотелось, мне удалось не использовать формул и употребить специальный термин рекуррентная формула всего один раз.

P.P.S. Если лень смотреть Википедию, то рекуррентная формула это когда вам требуется прийти в дом 30, но вы обязаны предварительно посетить все предыдущие дома с номерами от 1 до 29.
Подробнее..

Категории

Последние комментарии

© 2006-2020, personeltest.ru