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

Алгоритм

Что такое алгоритм???? Часть 101 Эволюция поведения

25.04.2021 20:19:15 | Автор: admin

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


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


Почему для понимания алгоритма необходим разговор о поведении???


Где в формализации алгоритма есть место эмоциям????


И все эти вопросы в ограниченной объемом статье. Задача не проста, и тем интереснее начать...


Title


Задача


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


Базовое утверждение, необходимое в дальнейших рассуждениях, звучит так:


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

Следующие утверждение частично содержатся в первом базовом утверждении и поясняют его детали:


  • Алгоритм поведения это повторимый способ воздействия биологической особью на состояние окружающей среды

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

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


И для заключительного утверждения потребуется немного переформулировать результат первой статьи серии "Что такое алгоритм...":


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

Утверждения не очень сложны. Но зачем они? И какие преимущества можно получить, нарекая поведение Алгоритмом?


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


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


Этот способ Эволюция.


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


Базовое поведение


Любой организм из соображений сохранения существования своего вида вынужден исполнять "базовые стратегии", собранные в следующем списке:


  • стратегия "Поддержание своей целостности" (важность структуры составляющих организм объектов);
  • стратегия "Поддержание своей жизнеспособности" (важность диапазонов параметров и наличия внешних веществ среды, обеспечивающих осуществимость жизненных процессов организма);
  • стратегия "Исполнение процессов своего размножения" (важность выполнения организмом производства своих копий одним из доступных вариантов: например, самокопированием путем деления, бесполым или половым размножением).

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


Но остановимся с биологией. Зачем это необходимо разбирать в контексте изучения алгоритма?


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


Эволюционный способ


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


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


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


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


торт


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


Ситуация Добавляемый алгоритм Полученный сложный алгоритм
- Базовая стратегия "Поддержание своей жизнеспособности"
??? Мобильность организма "Пищевое поисковое поведение"
Голодный человек пожевал зерна Съедобность злаковых Поиск и употребление злаковых в пищу
Одно зерно случайно очистилось, например, при переносе, и обнаружено, что так есть удобнее Очистка злакового зерна Поиск и употребление в пищу очищенных зерен злаковых
При ручной чистке большого количества зерен стираются пальцы. Необходима и проста замена использования пальцев в шелушении зерна на использование нескольких камней Способ очистки злакового зерна камнями Поиск и употребление в пищу зерен злаковых, очищенных камнями
Случайно зерна перетерлись камнями, и получились не целые зерна, а измельченные Способ измельчения зерна камнями в муку Поиск и употребление в пищу зерен злаковых, измельченных камнями
При поедании муки ощущение сухости стимулирует добавление воды. Возможен второй вариант при случайном намокании перетертого зерна Способ изготовления теста Поиск и употребление в пищу зерен злаковых, измельченных камнями и затем намоченных
Игра и лепка намокшим тестом Способ изготовления лепешек Поиск и употребление в пищу зерен злаковых, измельченных камнями и затем намоченных и слепленных лепешкой
Случайное высыхание слепленной ранее лепешки, а сухая лепешка долго хранится Способ изготовления "сухих" лепешек Поиск и употребление в пищу зерен злаковых, измельченных камнями и затем намоченных и слепленных в лепешку, которая высушена
Ускорение медленного высыхания лепешки помещением в теплое место рядом с огнем Способ изготовления печеных лепешек Поиск и употребление в пищу зерен злаковых, измельченных камнями и затем намоченных и слепленных в лепешку, которая выпечена рядом с огнем
Добавление сладости, чтобы повысить вкусовые качества Способ изготовления сладких печеных лепешек Поиск и употребление в пищу зерен злаковых, измельченных камнями и затем намоченных сладкой водой и слепленных в лепешку, которая выпечена рядом с огнем
Добавление яиц, чтобы обеспечить клейкость и целостность лепешки при малом количестве муки Способ изготовления сладких печеных лепешек из жидкой муки Поиск и употребление в пищу зерен злаковых, измельченных камнями и затем намоченных сладкой водой с добавлением яиц и слепленных в лепешку, которая выпечена рядом с огнем
Взбивание яиц до пенного состояния, чтобы обеспечить воздушность лепешки Способ изготовления сладких воздушных печеных лепешек из жидкой муки Поиск и употребление в пищу зерен злаковых, измельченных камнями и затем намоченных сладкой водой с добавлением взбитых яиц и слепленных в лепешку, которая выпечена рядом с огнем

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


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


Но отвлечёмся от торта и обобщим, присоединяя к нашим размышлениям опыт разработки программного обеспечения:


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

  • Эффективнее усложнять алгоритм добавлением к нему маленького дополнительного алгоритма.

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


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


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


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


Эмоции


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


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


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


мультфильм головоломка


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


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


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


  1. Базовая стратегия "Поддержание своей целостности"


    1.1. Противостояние повреждениям:


    • Механорецепторы давления, растяжения (ощущения структурного повреждения)
    • Тепловые и холодовые терморецепторы (ощущение теплового ожога),
    • Хеморецепторы (ощущение химического ожога)
    • Боль (защитный алгоритм поведения, вынуждающий реагировать на предвестников повреждения, устраняя их)
    • Эмоция Страх (защитный алгоритм поведения, вынуждающий менять текущее поведение для избегания запуска предвестников повреждения) страх
    • Эмоция Злость (алгоритм поведения на основе выполнения атаки, обеспечивающей защиту от нападения организма-агрессора, стремящегося вызвать повреждения) злость
    • Коммуникация с сородичами, предупреждающая об опасности: торибоны феромоны страха и тревоги, звуковое сигнальное взаимодействие (алгоритмы коллективного поведения для избегания повреждения)

  2. Базовая стратегия "Поддержание своей жизнеспособности"


    2.1. Поддержание контакта с веществами, необходимыми в жизненных процессах


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

    2.2. Поддержание параметров, необходимых для протекания жизненных процессов


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

    2.3. Устранение контакта с веществами, мешающими жизненным процессам


    • Избегание внешних вредных и ненужных веществ: хеморецепторы и вкусовые рецепторы (алгоритмы избегания химических отравлений и поедания бесполезных веществ)
    • Избавление от накопленных ненужных веществ жизнедеятельности: (алгоритмы ощущения усталости (молочная кислота в мышцах), алгоритмы обеспечивающие позывы и исполнение выделений)
    • Эмоция "Брезгливость" (алгоритмы поведения, помогающие избегать контакта с вредными и ненужными веществами) брезгливость
    • Коммуникация с сородичами, информирующая о вредных и ненужных веществах

  3. Базовая стратегия "Исполнение процессов своего размножения"


    3.1. Половое размножение (один из способов эволюционного развития биологических генетических алгоритмов)


    • Выделение и обнаружение партнеров на основе феромонов (эпагонов половых аттрактантов)
    • Алгоритмы поискового поведения, обеспечивающие обнаружение партнера для взаимодействия и полового размножения
    • Эмоция "Влюбленность" (так умело обойденная в рассматриваемом детском мультфильме рейтинга 6+). Отчего бы здесь вместо персонажа-эмоции не появиться спутнику всех статей этой серии яблоку (на этот раз в образе "запретного плода"): влюбленность
    • Эмоция "Радость" (стимулирующий алгоритм поведения, сопровождающий процессы развития поискового поведения для запоминания полезных с точки зрения размножения состояний и локаций среды с возможностью возвращения к ним) радость
    • Коммуникация и взаимодействие с сородичами, обеспечивающие информацией для благоприятного выбора партнера

    3.2. Забота о потомстве (один из способов эволюционного развития алгоритмов поведения)


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


Да, опять сложный и длинный список. Его можно представить в виде не очень съедобного разрезанного на куски торта (досталось бы мне тут от Карлсона). Целый торт всё поведение человека, описываемое всем списком. Кусочки торта отделяемые по наследованию от базовых стратегий пункты этого списка. При анализе выделенных "кусочков торта" видно, что эмоции, предлагаемые психологией для описания поведения человека, не являются очень удачным разбиением поведения на части. Так, например, эмоции "Злость", "Радость", "Страх" оказываются на разных "кусочках", разграничиваемых опорными базовыми стратегиями. Иногда ситуация сложнее: "кусочек" один и эмоция одна, но она характеризует два различающихся алгоритма поведения. Так, например, различны алгоритмы радости родителя и алгоритмы радости ребенка. Это не так страшно для психологии, но очень неудобно для работы с алгоритмами поведения и отслеживания их эволюции. Поэтому в наших последующих размышлениях мы будем указывать не эмоцию, а полный путь к разбираемому алгоритму от опорной базовой стратегии.


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


Выводы


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


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


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


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


В завершение осталось пояснить игру с номером в названии текущей статьи. Это число не сто один, а $101_2$. То есть 5. И части номер 4 еще не было, ввиду проведенного в предыдущей статье голосования по выбору темы для статьи последующей. В голосовании выиграла тема "Алгоритмы и человеческие эмоции" с "сокрушительным" перевесом в 1 голос. Теперь долг перед проголосовавшим читателем исполнен, и уже ничто не удерживает от написания статьи 4. В ней будет рассмотрен путь эволюции от алгоритмов поведения к алгоритмам математики.


Спасибо Вам за внимание.


Отзывы


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


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


Ссылки


Подробнее..

Что такое алгоритм! Часть 31 Математика

08.05.2021 08:23:50 | Автор: admin

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


Title


Задача


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


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


Цель всей работы это способы создавать алгоритмы без участия человека (формализация и автоматизация этого процесса).


Для достижения обозначенной цели необходимо много составляющих. Прежде всего и самое главное необходимо найти подсказки, как работать с алгоритмом. Можно попробовать дать определение слова "Алгоритм". Вы скажете определение "Алгоритма" уже существует, и легко найти его, например, на Вики. И да, и нет. На страницах Википедии есть множество определений разных понятий. Давайте рассмотрим простой пример ну, скажем, возьмем "Молоток". Значение (читай "определение" этого слова) тоже есть на Вики:


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

Молоток для крокета


Тут дан некоторый набор слов. Что из них определение, а что инструкция к применению? Является ли фламинго молотком для крокета. Если ли в приведенном значении слова "Молоток" сведения, помогающие в изготовлении молотка? Думаю, что подсказок в этом "определении" так же мало, как и в определении слова "Алгоритм", взятом на странице Вики.


А почему этих подсказок нет. Первым делом можно сказать, что это связано с типом приведенного "определения". Это определение неформальное. Оно для человека. А не для математика. И почти потому оно не подходит и для станка, изготавливающего молотки. В чем проблема? Конечно, тут нет проблем. Если этим пользуется человек, то всё нормально. А вот если нам все же нужно сделать станок для производства молотков, то нужно думать. И изобретать формальное определение. Например, это будет "чертеж" или программа для изготовления этого молотка на автоматическом фрезерном станке или какой-то другой вариант описания. И это будет формальное "определение" для изготовления. Формальное "определение" для использования тоже потребуются, если этим молотком будет пользоваться не человек, а машина.


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


Если быть совсем честными, то формальное определение Алгоритма в работе уже сформировано. Но в этом определении есть недостаток. Это определение математика. Для того чтобы им начали пользоваться (и может быть добавили в Вики), необходимо предоставить способы применения этой математики к практическим задачам. И таких способов слишком много, и только самые важные появляются в статьях этой серии. И способ, которым является сама Математика, будет рассмотрен в текущей статье. Да, мы будем с помощью Математики описывать как работает Математика. Думаю, Льюис Кэрролл порадовался бы такому приключению для Алисы. С чего же нам начать? Ах, да...


Следуй за белым кроликом. Тук-тук...

Кролик


Алгоритм сложения


В предыдущей статье этой серии с загадочным номером 101, мы уже на "небольшом" примере изготовления торта познакомились с тремя способами создавать алгоритмы: это случайное обнаружение алгоритма, объединение алгоритмов и эволюционная аккумуляция алгоритма. Эти способы хороши с их использованием алгоритмы успешно, но очень медленно развивались, и они развивались бы и дальше. Но так случилось, что на их основе были сформированы следующие два. Эти способы: алгоритмический перенос и трансляция алгоритма из модели. И с этой парой, как у Алисы со встречей Траляля и Труляля, всё стало развиваться значительно веселее и, что самое главное, это позволило гораздо быстрее формировать новые и сложные алгоритмы. Что же это за близнецы Перенос и Трансляция? И почему необходимо их все же различать для продолжения разговора об Алгоритме?


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


И в этой статье будет особенный алгоритм. Для наших целей нам очень подойдёт простой и одновременно уже знакомый нам "Алгоритм сложения". Мы встречались с ним в первой статье этой серии.


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


Сложению тебя обучили? спросила Белая Королева. Сколько будет один плюс один плюс один плюс один плюс один плюс один плюс один плюс один плюс один плюс один?

Я не знаю, ответила Алиса. Я сбилась со счета.

Экзамен по арифметике


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


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


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


Алгоритм обеспечения одной коровы сеном на зиму прост:


Работай полдня, укладывая сено рядом с этой коровой.

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


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


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


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

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


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


Близнецы


Перенос


Для использования чисел еще рановато, но мы уже близко. Возьмем вместо них пока нечто попроще и породнее. Алисе я бы посоветовал посмотреть на пальцы. Ну а для "пастуха" пальцев будет маловато. Но всегда можно найти объект поменьше коровы, который можно поместить в руках, например, камешки. И что нужно с ними сделать? Правильно сопоставить! Палец сопоставим слову "один". А камешек сопоставим одной корове. Легко сказать "сопоставить". А каким алгоритмом это можно сделать?


У Алисы ситуация проще. На каждое услышанное слово "один" достаточно "комплементарно" загнуть один палец на своих руках. Для "пастуха" это сопоставление чуть сложнее, но принцип схож. Стадо тоже необходимо выстроить в структуру и лучше всего использовать структуру линейную, совсем как у слов "один" во фразе Королевы. Например, это можно сделать утром, когда коровы выходят из коровника с узкой дверью, пропускающей за раз только одну корову. Необходимо заранее запастись камешками. И для каждой выходящей коровы брать камешек и "загнуть его", ну, нет, конечно, это же не палец Алисы. Нужно положить его в отдельное выбранное место. Так формируется кучка камней. И когда последняя корова выйдет, в кучке будет лежать столько же камней сколько коров в стаде. А зачем нам эти загнутые пальцы и кучка камней? Правильно! С ними проще работать, чем с растворяющейся в памяти длинной фразой или большим стадом. С ними можно выполнить подсчет. С кучкой камней легко придумать алгоритм формирования запаса сена на зиму в отдельном от коровника хранилище. И можно выполнить много еще чего. Мы перенесли опору алгоритма запаса сеном на зиму с использования коров на использование камешков. И упростили жизнь "пастуху", сделав его уже немного "математиком".


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


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


Но, а как же второй близнец "Трансляция"? Да, да он тоже нам нужен, и тоже нужен Алисе.


Трансляция


Сложения не знает, сказала Черная Королева.

А Вычитание знаешь? Отними из восьми девять.

Этого я не знаю, но зато

Зато? Что так смутило Алису? Ответ прост, и подсказкой будет то, что этот момент смущал те только Алису, он сильно мучил и "древних математиков". А сложность этого момента сводится к простому утверждению: не все математические действия с "камешками" однозначно соответствуют (то есть переносятся) на стадо коров. И отрицательные числа, наверно, стали самой простой и только первой проблемой, с которой столкнулась математика. Ведь очень непонятно какой корове соответствует отрицательное число -1.


И дальше встает вопрос. Отказываться ли от этих "странных" отрицательных чисел? Или можно использовать их, но не переносить в коровы? Со знаниями, которыми обладает современный школьник старших классов, ответ тривиален. Конечно, использовать! И, видимо, Алисе придётся все же изучить и такое "странное" вычитание. Но "древним математикам" было не так легко. И только польза от алгоритмов, использующих отрицательные числа, помогла принять это сложное решение и ответить на заданный вопрос утвердительно. Да, нужно использовать отрицательные числа!



Такие же странные вопросы, подобные вопросу об "отрицательных числах", впоследствии вставали перед математиками не один раз. Вопросы были запутанными совсем как у Гусеницы, и каждый раз новая абстракция становилась всё "страньше" и "страньше". Иррациональные числа вместо рациональных (например, для алгоритма нахождения длины окружности по диаметру). Квадратный корень из отрицательного числа ("мнимая единица"), например, для алгоритма решения кубического уравнения. "Бесконечность", например, для нахождения значения предела сходящейся суммы бесконечного ряда (еще древнегреческий философ Зенона размышлял над этой странной задачей в парадоксе "Ахиллес и черепаха"). Парадоксов перед математиками было много. Некоторые все же исключались, потому что не было возможности использовать их в полезных алгоритмах. Так было, например, с парадоксом "Множество всех множеств". Но основой всех таких размышлений и решений было одно наличие полезных алгоритмов, в которых использовались эти "странности". И тут "естественный отбор" тоже работал. И эволюционный способ формирования математических алгоритмов, медленным и в дополнение к нему быстрым накоплением привел к тому, что мы сейчас называем слово "Математика".


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


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


хорошо бы получше провести время

Все понятно! с торжеством сказал Шляпа.

Провести время?! Ишь чего захотела! Время не проведешь! Да и не любит он этого!

Время


Выводы


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


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


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


Вроде бы разработали помощь Алисе в сложении для правильных ответов Королеве.


Развлеклись?


Спасибо Вам за внимание.


Отзывы


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


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


Ссылки


Подробнее..

Что такое алгоритм Часть He Физика

15.05.2021 16:19:24 | Автор: admin

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


Title


Задача


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


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


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


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


И давайте обсуждать вместе. Ведь достижение цели, под флагом которой разные слова собираются с заголовком "Что такое алгоритм ?!", будет полезным нам всем?


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


Молоток


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


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


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


Следуй за белым кроликом. Тук-тук...

Кролик


Физика


Располагаемся удобнее, провожающих просьба покинуть вагоны, наш поезд отправляется из пункта "А" в пункт "Б". Пункт "А" в нашей дороге Математика. Пункт назначения, конечно, страна "Физика".


Сразу скажем, что продвижение по обозначенному маршруту не будет тривиальным, ведь у древнего математика и древнего физика совсем разные прикладные области интересов. Но нам поможет то, что они используют один инструмент. И этот инструмент Алгоритм.


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


Алиса в поезде


Первой работой "древнего физика" было наблюдение за окружающим миром и поиск процессов, которые будут полезны для выживания. И для каждого обнаруживаемого повторимого процесса оценивалась возможность его использования, а потом и возможность его переноса в иную прикладную область. Кинуть камень рукой, и кинуть ядро катапультой для алгоритма нанесения повреждения. Детская игра с качелями и плечевые весы для алгоритмов взвешивания. Дети разного веса, качающиеся на качелях, и рычаг для алгоритма подъема тяжелых грузов. Погружение в ванну с её переполнением и погружение короны для алгоритма измерения объема. Много, много повторимых процессов и их переносов...


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


Антиподы


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


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


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


Торт


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


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


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

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


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


Смайлик на хабре


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


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


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


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


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


Яблоня


Определение алгоритма


И здесь стоит еще раз обратить внимание на существующее определение алгоритма. Согласно этому определению "Падение яблока" алгоритмом не является. В нём же нет "последовательности действий"? Вроде бы нет. Но все же не будем сразу совсем категоричны. Давайте пока проигнорируем это несоответствие, и обратим внимание на другую часть существующего определения алгоритма. Подумаем, какая "задача" может быть у падения яблока? И тут ответ прост. Самое интересное, что в этом ответе искомая задача совсем не "человеческая". Ведь это просто замечательно! Нам как раз необходимо избавиться от присутствия человека при работе с алгоритмом? Итак, одна из самых важных задач алгоритма "Падение яблока" это выживание вида яблони, на которой это яблоко росло.


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


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


Маятник


Мы совсем забыли про Трансляцию, напоминает Алиса.


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


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


И пожалуй стоит остановиться. Вот и Алиса уже не выдержала и спит. Добрых и сказочных ей снов.


Алиса спит


Выводы


Хорошенько мы поиграли с выдуманными терминами? Игра это всегда хорошее подспорье в изучении. Поэтому в этой серии статей спрятано несколько таких игр. И игра с номером в заголовке статьи продолжается. Указанный номер это совсем не частица русского языка "не". Он созвучие между нумерацией и особенной единицей измерения, используемой в физике. Какая это единица, и почему с номерами нужно играть, можно обсудить в комментариях.


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


В этой статье мы познакомились с "физическим алгоритмом" и его способами Переноса и Трансляции.


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


И помогли нашей маленькой Алисе заснуть.


Вроде бы еще раз развлеклись?


Спасибо Вам за внимание.


Отзывы


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


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


Ссылки


Подробнее..

Что такое алгоритм?? Part three and a quarter. Язык

10.06.2021 08:18:29 | Автор: admin

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


.

Title


Задача


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


Это Слово. Это Смысл. Это сложности перевода. Наконец, это разбор появления коммуникации.


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


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


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


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


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


И давайте обсуждать вместе. Ведь это "жжж" рядом с заголовком "Что такое алгоритм ?!" неспроста.


А зачем тебе жужжать, если ты не пчела? По-моему, так

А зачем на свете пчелы? Для того, чтобы делать мёд.

Жжж


И, конечно, "алгоритмический" мёд не очень интересен сказочному медвежонку, но, возможно, он станет полезен кому-то другому.


Слово


Пора уже начать. И в этот раз совсем нет причин изменять привычному для этой серии статей формату. Мы опять будем "терзать" общепринятые термины из Википедии. На нашем пути термин "Слово":


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

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


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

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


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


мультфильм головоломка


Коммуникация


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


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


Ибо именно уникальность и коллективная идентифицируемость это первооснова любого "Слова"? Если бы сигнал "Опасность" и сигнал "Вкусная еда" были бы одинаковы, то алгоритмы избегания стадом хищника работали бы совсем неудовлетворительно, и в результате исчезло бы и стадо, а вместе со стадом и выполняемые им алгоритмы.


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


Итак, есть мостик от алгоритмов коллективного выживания к алгоритмам "именования предметов". Почему здесь использовано слово "алгоритмам" во множественном числе? Это связано с разной природой "именуемых" явлений. И совсем немного этому посвящена пара статей "Математика" и "Физика". Там намеренно отдельно в каждой статье рассмотрены особенности "переноса" в пространство модели для "стада коров" и для "падения яблока".


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


Именование трансляцией


Абстракция


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


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


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


Итак, на основе рассмотренного ранее мы с Вами уже можем еще неформально, но в формальных терминах (отмеченных курсивом) теоретической части этой работы, сформулировать своё определение "Слова":


Слово это объект в пространстве языковой алгоритмической системы, который:
  • формируется с использованием модельной трансляции (например, человеком) комплементарным сопоставлением с объектом или алгоритмом из прикладной области;
  • создается как продукт работы алгоритмов в языковом пространстве, необходимый для алгоритмов, исполняемых только внутри языкового пространства, и не переносимый в прикладную область.

Ведь очень похоже на определение из Вики? Неясными остаются только несколько терминов, которые ещё не были упомянуты в предыдущих статьях. Но все эти термины уже есть в теоретической части. И самый интересный из них "Алгоритмическая система". В определении "Слова" мимоходом констатируется, что "Язык" это один из видов "Алгоритмической системы". К сожалению, в текущей статье этот комплексный термин не уместить, а потому пока оставим его для статей последующих.


Копирование поведения


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


Смысл


Здесь можно немного поумничать и для поощрения дальнейшей непростой работы при чтении остатка текущей статьи легко ответить на вопрос "Что есть смысл жизни?" простой фразой: "Смысл любого слова кроется в алгоритмах, в которых это слово используется". Конечно, этот ответ, не подкрепленный объяснениями, совсем как красная ткань в корриде. И потому уместно использовать это и напасть на этот ответ в комментариях. Но объяснения к этой фразе уже есть. Они основаны на предлагаемом в этой серии статей значении для слова "Алгоритм", которое еще не настолько общепринято, чтобы его можно было использовать совсем без примеров.


Question of life


$\begin{gathered}42 \\ = \\ (-80538738812075974)^3 + \\ 80435758145817515^3 + \\ 12602123297335631^3\end{gathered}$



И если в текущий момент статьи сделать паузу и обратить внимание на предыдущее предложение, а еще сразу на все статьи серии. То можно отметить, что цель всего этого собрания слов в статьях только одна объяснить особый смысл слова "Алгоритм". А способ это осуществить используется такой же, как и для всех иных слов, с которыми сталкивается человек. Начиная с первого слова "мама" и заканчивая "большим адронным коллайдером" (да, конечно, тут 3 слова, но ведь смысл один?). И этот способ связывания слова со смыслом спрятан достаточно глубоко в устройстве человеческого мышления, а вернее в приемах используемых в процессе синтеза алгоритмов его поведения.


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


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


Но есть ли в этом "классифицирующем" пути к "Слову" способ понять его смысл? Ответ будет да. Но как уже было сказано, это будет только часть смысла. И эта часть отнюдь не самая важная. И эта часть уже есть в текущем определении слова "Алгоритм", размещаемом на Википедии. И она помогает нам отличить "Алгоритм", например, от "Логарифма" потому, что при работе с алгоритмом мы не всегда думаем о "показателе степени, в которую надо возвести основание". Но эта часть совсем не помогает нам работать с Алгоритмом! Обобщая можно подчеркнуть: путь разбиения явлений среды на кластеры, каждый из которых подписан уникальным словом дает нам представление только о части смысла, содержащегося в этих уникальных словах.


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


Главный смысл "Слова" содержится в полезных алгоритмах, которые используют это слово! Да, смысл совсем не внутри фонем, букв, слогов "слова" и не в способах именовать объекты окружающего пространства. Этот смысл "снаружи"! Он в использовании. Он в алгоритмах!!!


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


Для "гуманитарных" слов порой отсутствует "использующий смысл". И слово становится просто интересным алгоритмом, необходимым только для развлечения того кто его слышит. Вспомним для примера "Антиподов" упомянутых Алисой в предыдущей статье. До появления использования этого слова в статье "Физика" оно было просто развлечением и игрой Льюиса Кэрролла (или даже скорее переводчика его сказки на русский язык). Это было ироничное издевательство над алгоритмом "именования" с копированием того, как в своих играх этот алгоритм изучают дети. Но ситуация поменялась с появлением текущей статьи. В ней появилось новое использование слова "Антипод" в качестве лингвистического примера. И у этого "Слова" появился новый смысл! Ведь это совсем неожиданный для математика процесс?


Алиса и Аня


При обращении к примеру сказочной Алисы Льюиса Кэрролла был вскользь упомянут один сложный процесс, который целиком основан на смысле "Слова" и потому долгое время остаётся не до конца подвластен Математике. И этот процесс не может происходить только на основе "классифицирующего смысла". И он нуждается в "смысле использования". Вы уже догадались? Да, этот процесс работа переводчика, нацеленная на перенос смысла "Слова" из одного языка в язык другой.


Перевод


Вы можете сказать, что перевод на другой язык для отдельного "Слова" не очень интересен. И он с успехом происходит во всех двуязычных словарях. И будете, конечно, правы. Но для перехода к несомненно более сложному переводу на другой язык сложной совокупности "Слов", которой является "Предложение" и "Текст", ведь необходимо формализовать что есть "Предложение" и "Текст"? И, конечно, это тоже нам будет необходимо сделать, но чуть позже. А сейчас полезным является даже рассмотрение более "простого" варианта. И это ограничение только одним "Словом" своим упрощением даже является нам помощью. Потому что причины сложности перевода с одного языка на другой во многом обусловлены смыслом, который несёт каждое "Слово".


Думаю сейчас опять стоит обратиться к Википедии. И разобрать следующий существующий на её страницах термин слово "Перевод":


Перевод деятельность по интерпретации смысла текста на одном языке (исходном языке) и созданию нового эквивалентного ему текста на другом языке (переводящем языке).

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


О, опять целый рой вопросов. Как бы уложиться с ответами в последнем разделе приемлемого объема. Хорошо, что у нас уже есть опора для формирования ответов. И здесь нам очень поможет разбиение на "классифицирующий смысл" и "смысл использования".


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


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


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


Кузинатра


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


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


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


Выводы


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


Мы познакомились с термином "Слово" и нашли в нём два смысла, один из которых будет весьма полезным в доработке существующих систем перевода текстов на естественных языках.


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


Намечен эволюционный переход от естественных языков к языкам программирования.


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


Да, тут необходимо отметить, что все обозначенные в этой статье формализации очень "неформальны". И в них намеренно только обозначены опорные и ключевые моменты. А потому, конечно, пропущено много деталей, и их необходимо заполнить. И только частично это заполнение будет проводиться в оставшемся небольшом количестве статей этой серии. И только самые важные пояснения появятся на Хабре в автономных статьях. А до полного их объяснения в разрабатываемой теоретической книге пройдет еще очень много времени потому, что эта работа совсем не является сейчас самой приоритетной. И потому "атаку на пробелы" и их заполнение целесообразно разместить здесь (на Хабре). Удобной платформой для этого будет площадка комментариев и к этой, и к предыдущим статьям. И это заполнение будет идти только по Вашим запросам. Ибо пробелы, которые очень хорошо видны Вам, мне не видны совершенно. Отчасти поэтому в статьях появляются "висящие" термины. И в написании каждой новой статьи самым сложным становится процесс вспоминания: какие факты уже были рассказаны, а какие еще остались такими "пробелами". Ведь полный путь от "рождения" алгоритма к способам работы нашего мозга всё же достаточно сложен. И держать его целиком в памяти, к сожалению, никогда не будет мне посильно.


Спасибо Вам за внимание.


Отзывы


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


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


Ссылки


Подробнее..

Опыт создания логотипов с искусственным интеллектом от Студии Лебедева

09.01.2021 20:18:44 | Автор: admin

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

Содержание:

  1. Логика работы с нейросетью на момент написания статьи

  2. Отмеченные особенности процесса

  3. Логотипы, которые у меня получились

  4. Пример набора данных в архиве

  5. Отмеченные технические особенности

  6. Общие впечатления. Резюме

Дисклеймер. Моё мнение может отличаться от мнения других пользователей, которые поработали с Николаем Ироновым. Аналогично, поскольку я не связан со Студией Лебедева, не могу гарантировать, что моя интерпретация происходящего совпадает с тем, как это работает на самом деле.

Логика работы

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

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

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

Этап 2 - настройка и корректировка выбранного логотипа перед этапом формирования графического набора.

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

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

Основные особенности

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

Выбор символов, которые могут ассоциироваться с логотипом.Выбор символов, которые могут ассоциироваться с логотипом.Фрагмент диалога с опциями дополнительных параметров.Фрагмент диалога с опциями дополнительных параметров.

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

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

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

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

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

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

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

Что у меня получилось

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

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

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

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

Пример архива

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

  1. Собственно логотип

  2. Паттерн

  3. Визитка

  4. Бланк письма

  5. Варианты графических шапок для социальных сетей

  6. Набор цветов фирменного стиля

Отмечу, вся графика присутствует в наборе как в формате .png, так и в .svg, но до работы с векторными версиями у меня пока не доходили руки.

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

Технические особенности

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

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

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

Кстати, на момент написания статьи на основной страничке Николая Иронова Студия добавила симпатичные видео о том, как можно в ручном режиме видоизменять графику и текст в варианте логотипа. Так вот, в примере логотипа Birb птичку не только масштабируют, но и вращают, однако у себя в интерфейсе я такой возможности не нашёл. Вероятно, эта опция доступна не для всех графических решений, но возможно, что у пользователей её пока в принципе нет. Я посмотрел на нескольких логотипах и нашёл только ползунки масштабирования элементов логотипа.

Резюме

Краткая версия: очень круто, причём как по результату, так и по впечатлению.

Чуть подробнее. По крайней мере на данный момент, при цене в 9900 рублей, которые я заплатил, это невероятно эффективное вложение средств. Никакие фрилансеры по соотношению впечатления * качество / цена не смогут встать рядом. Если вспомнить стоимость работы профессионалов, то та же Студия Лебедева Экспресс-дизайн предлагала за 100 000, причём результаты были как высоко оценённые заказчиками, так и не очень. Здесь же есть возможность напрямую влиять на результат, причём не только при составлении брифа.

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

P.S.: Оказалось, что называть машину именем, которое ей дали создатели, даже если это человеческое имя Николай, не составило для меня никакой моральной заминки. И в переписке с поддержкой, и при написании статьи это далось вполне естественно.

Подробнее..

Практическое применение алгоритма для представления Цекендорфа

05.01.2021 20:17:13 | Автор: admin

Как то в прошлом

Я написал статью о рекурсивном алгоритме Цекендорфа : пост

Пример кода
def le_fib(limit, fib)  theoretical = fib[fib.count - 1] + fib[fib.count - 2]  return fib.last if theoretical > limit  fib << theoretical  le_fib(limit, fib)enddef main(target,result)  temporary = le_fib(target, [1,1])  result << temporary  return result if target - temporary <= 0  main(target - temporary, result)endpp main(gets.to_i,[])

Функцияle_fib- рекурсивно ищет ряд Фибоначчи с пределом, на то, что бы следующее число не было больше чем входное числоtarget. Здесь важно, что нас не интересует ряд Фибоначчи целиком, нам важно лишь его окончание.

Функцияmain- рекурсивно ищет масcив, числа которого есть числами Фибоначчи, и которые бы в сумме давали нам входное число.

Хотя по правде говоря в комментах предложили более изящное решение :

Один цикл и делов
n, F = 100, [1, 2]while F[-1] < n do  F << F[-2] + F[-1]endF.reverse.each do |f|  if f <= n    n -= f    print f    print '+' if n > 0  endend

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

Постановка задачи куда мы будем "впихивать этот алгоритм"

Есть некий набор продуктов, условно говоря :

[ Курица, томаты, лаваш, грибы ].

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

[ курица > томаты > грибы > лаваш ] .

Задача состоит в том что бы генерировать такой набор продуктов, который имел бы по крайней мере 1 "Low cost", и 1 "High cost" продукт.

Вот тут то я хочу приспособить этот алгоритм (Цекендорфа).

Главная идея в том что бы создать хэш (структура данных в Ruby) где ключ это число Фиббоначи, а значение собственно имя продукта.

Задача есть, теперь перейдем к решению.

Для начала анализируем сам алгоритм

Запустим его в цикле от x до y и посчитаем количество вхождений каждого числа.

  1. [1,100][1,100][1,1000][1,1000]
  2. Как видим на малых Y вероятность того что число будет использовано в последовательности - равномерно распределенно так как верхняя граница чисел Фибоначчи постоянно растёт пропорционально к текущему числу в итерации.

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

    Значит нам нужен отрезок с более равномерным распределением. Уменьшаем Y

    [1,143][1,143]
  3. Видим пик на крайних числах 1 и 89. Что собственно отвечает постановки задачи, но при этом мы не теряем случайное равномерное выпадение "Middle cost" продуктов.

    Предлагаю остановится на 3 варианте где x = 1 и y = 143.

Реализация

Программы куда будем прописывать алгоритм Цекендорфа, выглядит так :

  • Модуль-перечень продуктов (для возможной тематичности)

  • Collector, который загружает перечень продуктов и составляет Hash (где ключ -> число Фибоначчи, значение -> название продукта)

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

К слову говоря, всё это я делаю для Telegram бота , который создан по гайду описаном в другом посте.

Итак, написав небольшой парсер, на выходе получаем такой результат

Небольшой парсер
@fib = [1,2,3,5,8,13,21,34,55,89]    def collect_the_items      food_hash = Hash.new      (0..9).each do |iterator|        food_hash[@fib[iterator]] = FOOD.first[iterator]      end      puts food_hash.map{|key,value| "#{key} - #{value}"}    end

Следующим шагом, слегка изменим алгоритм представления Цекендорфа :

Алгоритм
def get_sequence(limit)    result = []  n, fib = limit, [1, 2]    while fib[-1] < n do    fib << fib[-2] + fib[-1]  end  fib.reverse.each do |f|    if f <= n      n -= f      result << f    end  end  resultend

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

Код функции
def generate_food          food_array = Collector.collect_the_items          food = []          rarity = rand(1..143)          get_sequence(rarity).each do |key|            food << food_array[key]          end          foodend

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

Результаты теста

примечание :
Low cost : ?
Mid cost : ?
High cost : ?

Результат

Применения алгоритма представления Цекендорфа меня полностью удовлетворяет. Тоесть - выполняет поставленную перед ним задачу.

Один из первых комментов под статьей на которой основано это практическое приминение, как раз таки и ставил перед мной вопрос : "а действительно, где это применить можно?". Как видим, вот для таких задач данный алгоритм вполне можно использовать.

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

Подробнее..

Как же писать эти грёбаные циклы?

20.02.2021 12:17:51 | Автор: admin

Когда мои ученики, уже выучив основы синтаксиса языка Python, понимают, что проблема вовсе не в незнании синтаксиса, моё лицо расплывается в самодовольной, но всё же доброй улыбке. Мысль "ну я же говорил" больше не возникает. Повзрослел, видимо, дяденька.

Немного лирики

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

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

Задание

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

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

Рассмотри эту задачу с точки зрения самого простого и, в то же время, самого популярного формата "ввод данных обработка данных вывод данных". Как вы уже догадались, присвоение переменной celebrities - это ввод данных.

Этапы

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

  • [этап 0] Понять, что задачу стоит решать именно циклом, и именно циклом for.

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

    В нашем случае на входе - список, что является последовательностью (наряду со строками, словарями, множествами, кортежами, numpy.array'ями, и т. д.), а на выходе - опять же список. Причём, длины их совпадают. Кажется, каждый элемент входного списка напрямую связан с соответственным элементом выходного - так и есть!

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

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

  • этап [1] Определиться, какой конкретно результат должен быть по исходу каждой итерации, и выполнить первые несколько итераций вручную, вообще без цикла.

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

    Приступим. "Tom Cruise" превращаем в "T.C." любым путём (ведь пока для нас на первом месте - критерий успешности итерации).

Результат получили? Получили! Если вы не совсем новичок, то уже чуете, что так всё конечно же, не останется.

Это была первая итерация грядущего цикла. Проделаем теперь ещё и вторую:

Всё круто. Первый этап пройден.

  • этап [2] Привести итерации к однородному виду.

    Один и тот же результат всегда может быть получен разными путями. Сам результат обычно не несёт с собой информации о том, откуда он взялся. Самый простой пример будет понятен даже людям, далёким от мира программирования: положим, результат - число 11. С точки зрения математики, его могла породить сумма 5 + 6, разность 14 - 3, корень из 121 и куча других действий. Ведь так? И это была лишь арифметика. А если зайти на высшую математику? А если на территорию Python? Ведь команды в Python - это далеко не только вычисления!

    Ключевая команда из нашего алгоритма - это конвертация имени в инициалы:

Она состоит из константной (неизменной) части, и постоянно меняющейся. Чтобы иметь возможность такую "ручную" итерацию превратить в реальную итерацию цикла, все меняющиеся части нужно заменить на неизменные, чаще всего - просто на какие-либо команды. Индексы 4 и -5 - это то, что нам здесь не нравится.

Ну и вариант максимально независимый от конкретных имён из списка:

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

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

    В нашем случае это вовсе не группа, а всего лишь одно целое число, которое играет роль индекса после переменной celebrity. Поэтому для полноценной иллюстрации этого этапа лучше обратиться к другому примеру:

Здесь требовалось получить список, состоящий из копий одного и того же слова чётной длины, в котором по очереди будут подняты в регистре пары букв. Кстати, попробуйте эту задачу прогнать между делом по всем 5 этапам нашего алгоритма размышлений.

Как видим, здесь в ключевой команде - набор уже из 4 объектов. Для первой итерации - 0,0,2,2, для второй - 2,2,4,4, и т. д. В рамках каждой группы необходимо уследить внутреннюю связь между объектами. Здесь она довольно проста: мы имеем два одинаковых числа, и ещё два одинаковых, которые всегда больше первых ровно на 2. Приглядитесь, и увидите эту связь. Зачем нам это нужно? Напомню, у нас цель - написать цикл. Пока что итерации отличаются друг от друга, хоть и несильно. Тем не менее, наша задача - превратить их в универсальный кусок кода, который можно будет разместить в теле грядущего цикла. Внутренняя связь между объектами в группе позволит избежать, например, использования множественных переменных. Каждая группа теперь - n, n, n+2, n+2. Самую первую замену из этих четырёх назовём главной переменной. Если бы бы не заморачивались об этой связи, то набор был бы более напряжным - a, b, c, d. Поверьте, вы сами себе скажете "спасибо", когда уменьшите количество переменных в коде и увеличите число логических взаимосвязей между его частями.

  • этап [4] Уловить закономерность изменения главной переменной при переходе от предыдущей итерации к следующей.

    Для этого часто нужно понять смысл того объектам в алгоритме, который представлен этой главной переменной. В алгоритме со строкой "resident" имеем ряд 0,2,4,6. По смыслу - это индекс первой буквы, "поднятой по регистру". По внешнему виду - арифметическая прогрессия (сразу мысль о функции range()) с шагом 2, которая всегда начинается с 0 и кончается числом, на 2 меньшим, чем длина слова (длина слова "resident" равна 8).

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

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

  • этап [5] Завершение. Просто приписываем заголовок цикла по всем канонам к уже понятной универсальной итерации.

Заключение

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

Подробнее..

Ленивые операции над множествами в C

15.03.2021 12:08:42 | Автор: admin

В C++ нет понятия "множество". Есть std::set, но это всё-таки конкретный контейнер. Есть функции для работы с упорядоченными диапазонами: merge, inplace_merge, includes, set_difference, set_intersection, set_symmetric_difference, set_union, но это алгоритмы, они не ленивые, и при вызове сразу вычисляют результат. К тому же они предназначены для работы строго с двумя диапазонами.


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


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


В публикации Ленивые итераторы и диапазоны в C++ я разбирал, что такое ленивые диапазоны.


Содержание


  1. Операции с несколькими диапазонами
  2. Алгоритм слияния
  3. Переложение на итераторы
  4. Дизайн
    1. Базируется на итераторах
    2. Быстрое копирование
    3. Деструктивность по отношению к внутренним диапазонам
    4. Можно создать диапазон
    5. Быстрое создание
    6. Изменяемость
    7. Есть адапторы
  5. Пример с кодом
  6. Заключение
  7. Ссылки


Операции с несколькими диапазонами


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

Все операции с несколькими множествами сводятся к двум этапам:


  • Продвинуть диапазоны-множества так, чтобы достичь следующего элемента итогового множетсва.
  • Выбрать один элемент среди текущих элементов всех диапазонов-множеств;

Процесс удобно представлять в виде движущихся входных лент, с которых выбранные элементы переписываются на выходную ленту.


В качестве примера рассмотрим слияние нескольких множеств.


В начале у нас есть три ленты: первая состоит из элементов {1, 2, 3}, вторая из элементов {2, 3, 5}, а третья {3, 5, 6}. Текущий элемент каждой ленты это её первый элемент.


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


Шаг 1


После чего продвигаем первую ленту на один элемент. Затем выбираем элемент 2, снова с первой ленты.


Шаг 2


Продвигаем первую ленту. Теперь на первой ленте тройка, а на второй двойка. Двойка меньше, так что выписываем двойку на выходную ленту, продвигаем вторую ленту.


Шаг 3


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


Шаг 4


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


Шаг 5


И так далее.


В итоге на выходной ленте будет записано {1, 2, 2, 3, 3, 3, 5, 5, 6}. Что, собственно, и ожидалось.


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


Алгоритм слияния


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


Подготовка:


  1. Определим операцию сравнения двух множеств. Множество U будет считаться меньше множества V, если наименьший элемент множества U меньше наименьшего элемента множества V (напомню, что наши диапазоны упорядочены, так что наименьший элемент всегда стоит в начале диапазона);
  2. Пустые множества выбросим из рассмотрения;
  3. Сложим множества в пирамиду (кучу) таким образом, что на вершине пирамиды будет лежать наименьшее множество.

Шаг слияния:


  1. Вынимаем из пирамиды наименьшее множество;
  2. Выписываем наименьший элемент этого множества;
  3. Продвигаем это множество на один элемент вперёд;
  4. Если множество стало пусто, выбрасываем его;
  5. Если множество всё ещё непусто, кладём его обратно в пирамиду.

Таким образом мы выпишем все элементы всех множеств в порядке их неубывания за время O(k + logk * n), где k количество множеств, n суммарный размер всех множеств.


В результате можно написать следующий код.



Переложение на итераторы


Из описания алгоритма и кода видно, что он хорошо разбивается на три части:


  1. Предподготовка;
  2. Выбор текущего элемента (он всегда на вершине пирамиды);
  3. Переход к следующему элементу.

И это прекрасно ложится на то, что мы умеем делать с итераторами, а именно:


  1. Инициализация;
  2. Разыменование;
  3. Продвижение.

Поэтому, долго не думая, берём Iterator Facade и делаем свой итератор.


Код итератора тут: Burst Merge Iterator.




Дизайн


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



Базируется на итераторах


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


Внешний диапазон должен обладать произвольным доступом (random access range) именно он и будет выступать пирамидой, которую мы будем переупорядочивать в процессе слияния.


Внутренним же диапазонам достаточно однопроходности (input range), поскольку всё, что с ними происходит это проверка на пустоту, чтение первого элемента и продвижение на одну позицию вперёд.


Результат итератор слияния также будет однопроходным (input iterator).



Быстрое копирование


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


Поэтому мы поддерживаем "стандартные" свойства итератора, и не храним в нём никаких развесистых структур. В итераторе слияния хранится только только два итератора на внешний диапазон.



Деструктивность по отношению к внутренним диапазонам


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


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


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



Можно создать диапазон


Итератор-конец для итератора слияния это не совсем итератор, а ограничитель (sentinel в терминах C++20). Простейший пример такого ограничителя это ноль для классической сишной строки: у нас нет указателя на конец строки, но мы знаем, что если значение разыменованного итератора равно нулю, то это и есть конец строки.


Это значит, что можно не мучить пользователя, и сразу создавать диапазон, минуя создание отдельных итераторов.


const auto odd = std::vector{1, 3, 5, 7};const auto even = std::vector{0, 2, 4, 6, 8};auto range_to_merge =    std::vector    {        boost::make_iterator_range(odd),        boost::make_iterator_range(even)    };const auto merged_range = burst::merge(range_to_merge);const auto expected = {0, 1, 2, 3, 4, 5, 6, 7, 8};assert(merged_range == expected);

Код построения диапазона на основе итератора здесь: Burst Merge



Быстрое создание


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


Можно просто передать кортеж из ссылок на исходные контейнеры:


const auto odd = std::vector{1, 3, 5, 7};const auto even = std::vector{0, 2, 4, 6, 8};const auto merged_range = burst::merge(std::tie(odd, even));const auto expected = {0, 1, 2, 3, 4, 5, 6, 7, 8};assert(merged_range == expected);

В этом случае библиотека берёт на себя создание и обработку промежуточного хранилища для "технических" итераторов.



Изменяемость


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


auto first = std::vector{50, 100};auto second = std::vector{30, 70};auto merged_range = burst::merge(std::tie(first, second));boost::for_each(merged_range, [] (auto & x) { x /= 10; });assert(first[0] == 10);assert(first[1] == 5);assert(second[0] == 7);assert(second[1] == 3);


Есть адапторы


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


const auto first = std::vector{1, 4, 7};const auto second = std::vector{2, 5, 8};const auto third = std::forward_list{3, 6, 9};const auto square = [] (auto x) {return x * x;};const auto merged =    std::tie(first, second, third)        | burst::merged        | boost::adaptors::transformed(square);const auto expected = {1, 4, 9, 16, 25, 36, 49, 64, 81};assert(merged == expected);


Пример с кодом


Вы спросите: "зачем?!" А я отвечу: для упрощения написания и понимания кода.


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


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



Заключение


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


И всё это с сохранением эффективности и достаточно приятного интерфейса.



Ссылки


Подробнее..

Детская сказка программисту на ночь

21.03.2021 20:19:46 | Автор: admin

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


Репка - медаль

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


Задача


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


колобок Издание 1910 года. Рис. Елизаветы Бём

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


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


Мы учим ребёнка убегать от бабушки и дедушки? Или лежать на печи и ждать волшебной щуки, исполняющей желания? Конечно, нет. Ну, или очень странно, если да.


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


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


Рассаживаемся поудобнее. Жили-были дед и баба...


Введение


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



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


Где же тут будет о программировании? спросите Вы.


Итерация

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


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


acc = []while not condition:    v=next_item()    do_some(v)    acc.append(v)

Итерационный процесс


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


Ответим первыми строками этого ценного учебника по программированию циклов:


Жил-был старик со старухою. Просит старик: Испеки, старуха, колобок!

Да, это сказка "Колобок". Давайте рассмотрим основные элементы сюжета этой сказки.


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


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


  • убегание из предыдущей локации,
  • встреча с каким-то персонажем,
  • и исполнение этому персонажу песенки.

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


Результат всей сказки сводится к получению песенки, описывающей историю встреч колобка:


 Не ешь меня, лиса! Я тебе песенку спою,  сказал колобок и запел: Я Колобок, Колобок!Я по коробу скребен,По сусеку метен,На сметане мешон,Да в масле пряжон,На окошке стужон;Я от дедушки ушел,Я от бабушки ушел,Я от зайца ушел,Я от волка ушел,И от медведя ушел,А от тебя, лиса, и подавно уйду!

Цикл такого поведения Колобка никогда бы не закончился, если бы не оказалось условия его остановки. В русских сказках очень часто условием выхода из зацикленных ситуаций является Хитрость. Условием выхода из цикла "колобковых" приключений стала хитрость Лисицы, которая догадалась, что можно не только песенку послушать, но и голод утолить. Тем самым она получила полный набор, как хорошо сказано когда-то, из "хлеба и зрелищ".


Если приводить примеры подобных "Колобку" сюжетов, то сразу можно вспомнить тождественные истории в сказках других стран. Это, например, сказка "Пряничный человечек", у главного персонажа которой не было такой приметной шарообразной формы, но приключения были почти те же. В указателе Томпсона сюжет "Колобка" отнесен в группу Z33.1 Сбежавший блин: Женщина печёт блин, который убегает. Его тщетно пытаются поймать разные животные. В конце концов его съедает лиса.


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


Следствие ведут Колобки

Вызов подфункций


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


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


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

Петушок и бобовое зернышко

Задача ясна нужно спасать Петушка. А вот решение этой задачи отличается от решения "Задачи колобка". В нём больше сходства с сюжетом "Зайкиной избушки".


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


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

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


Так что есть зависимые действия? Какие есть элементарные действия? Каков способ организации этих действий с точки зрения программиста?


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


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


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


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


Рекурсивный процесс


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


Нужная нам сказка сразу начинается с задачи:


Посадил дед репку. Выросла репка большая-пребольшая...

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


Каждый персонаж выполняет следующее действие:


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

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


Репка


Это же гораздо нагляднее, чем примеры в университете с числами Фибоначчи, где рекурсия применяется для вычисления рекурсивно заданных чисел? После "Репки" сразу понятно для чего рекурсия может быть полезна, и в чём её главная ценность способность уменьшать размер задачи разделением на несколько зависимых и одинаковых действий.


Можно здесь вспомнить многие примеры рекурсивного программного решения задач на основе принципа "Разделяй и властвуй" ("Уменьшай и властвуй") та же быстрая сортировка или бинарный поиск в отсортированном списке. Но, уверен, не стоит пробовать рассказать это ребёнку в качестве полезного образца, чтобы научить поиску решения задач, которые встретятся ему в жизни.


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


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


Вред бесконечных циклов


Во всех предыдущих сказках задача успешно решалась, или выполнение прерывалось по некоторому условию. Находилась хитрая Лиса или хитрый Петушок. Находился "автономный" Кузнец. Мышка вытягивала цепочку помощников с репкой на верхушке. А что если такого хитрого, автономного и терминального персонажа в сказке нет? Вдруг Кузнецу понадобится масло для изготовления косы? Ведь задача не будет решена и действия будут продолжаться бесконечно? "Это будет плохо спроектированная сказка" скажете Вы. И будете правы. Но как это показать ребенку? Как указать ему на необходимость задать условие остановки попыток решения вставшей перед ним задачи?


Конечно, это надо сделать в сказке!


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


while true:    pass

def f():    f()

def f():    g()def g():    f()

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


Купи слона...

Вы говорите, что он Вам не нужен?


Все говорят,    что им слон не нужен. А ты возьми и купи слона...

Соглашаетесь купить?


Все соглашаются купить. А ты возьми и купи слона...

Окончания этой сказки не будет.


Все думают, что    окончания этой сказки не будет. А ты возьми и купи слона...

Кадр из мультфильма 'Следствие ведут Колобки'

Ну в целом понятно. Если попробовать вставить такую сказку целиком, то это будет очень вредно даже для текущей статьи. Поэтому killall fairytale_elephant.


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


Неразрешимые задачи


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


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


Правильно. И это тоже проще всего сделать сказкой!


И в этом нам поможет самая "бесполезная" и "бессмысленная", как представлялось мне долгие годы, сказка. Начнём с первых строк:


Жили-были дед да бабаБыла у них курочка ряба.Снесла курочка яичко, не простое - золотое...

Курочка ряба

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


Варианты окончания сказки не так важны, как важна основа её содержания:


Дед бил, бил - не разбил.Баба била, била - не разбила.

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


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


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

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


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


Выводы


Сказка ложь, да в ней намек...


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


Основной вывод, формулируемый в результате выполненного нами рассмотрения детских сказок, прост. Сказки определенно полезны. Это не просто развлечение. Как минимум их можно использовать в качестве иллюстрационного материала при обучении программированию. Но это только на поверхности. А в глубине вопрос. Если сказки так близки к процессам обучения созданию алгоритмов, то имеют ли они эти процессы своей целью? Совсем иначе стоял бы этот вопрос, если бы автором проанализированных нами сказок был развлекающийся программист или математик, как это было, например, со сказками "Алиса в Стране чудес" и "Алиса в Зазеркалье".


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


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


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


Спасибо Вам за внимание.


Отзывы


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


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


Ссылки


Подробнее..

Кто же ты такой, алгоритм?

13.04.2021 18:11:50 | Автор: admin

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

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

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

Бесконечность не предел

Но перед этим немного вспомним математику. Из школьного курса математики мы знаем, что чисел существует бесконечно много какое бы большое число мы не взяли, всегда можно прибавить единицу и получить число еще большее. Обычно в школе этим и ограничиваются. В университете на курсе высшей математики нам расскажут, что бесконечности на самом деле бывают разные: множества, элементы которого можно пронумеровать натуральными числами считаются счётно-бесконечными. К таким множествам относят сами натуральные числа (числу 1 мы дадим номер один, числу 2 номер два и т.д.), целые числа - натуральные плюс ноль и отрицательные целые числа (первый номер отдаем нулю, второй - числу 1, третий - числу -1, то есть каждой положительное число k получает номер 2k, а каждое отрицательное число -m получает номер 2m + 1). К счетно-бесконечным множествам относят четные, нечетные и даже рациональные числа (числа представимые в виде несократимой дроби m/n, где m - целое, n - натуральное). Получается, что натуральных чисел ровно столько же, сколько четных, и, в то же время, ровно столько же, сколько целых. Количество (мощность) множества натуральных чисел обозначается символом 0 (алеф-ноль).

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

Про бесконечные дроби

Не стоит делать ошибку, записывая в иррациональные числа все бесконечные дроби. Иррациональными являются только те числа, которые нельзя представить в виде несократимой дроби вида m/n. В десятичной системе счисления дроби 1/3 и 2/7 тоже окажутся бесконечными, однако их бесконечность" обусловлена выбранной системой счисления. В системе счисления по основанию 21 эти дроби будут иметь конечное представление, а вот, например, дробь 1/2 окажется бесконечной (периодической).

Говорят, что множество бесконечных десятичных дробей имеет мощность континуум, которая обозначается символом 1 (алеф-один). В дальнейшем нам понадобится следующее множество. Рассмотрим некоторый алфавит (конечное множество символов). Теперь представим множество всех конечных цепочек символов алфавита A*. Коль скоро алфавит конечен, и каждая цепочка конечна, то множество таких цепочек счетно (их можно пронумеровать натуральными числами).

На сколько велика бесконечность?

Допустим в наш алфавит вошли все придуманные на земле символы: русский алфавит, японские иероглифы, шумерская клинопись и т.д. Тогда в наше множество войдут все написанные когда-либо книги, все книги, которые будут написаны и все книги, которые никто не стал бы писать (например, хаотичные последовательности символов). Кроме того, представим книгу, толщиной в Солнечную систему и диагональю листа равной диаметру Млечного Пути, набранную 12-м шрифтом. В наше придуманное множество войдут все такие книги, отличающиеся хотя бы одним символов, и не только они, ведь вселенная бесконечна! Кто мешает представить себе книгу, размером в миллиарды световых лет? А все такие книги? Уже на этом этапе воображение может давать сбои, а ведь наше множество всего лишь счетное. Чтобы дополнить множество до континуума, нужно рассмотреть бесконечную книгу, по сравнению с которой, предыдущие книги детские игрушки. Но и одной бесконечной книги нам не хватит, нужно рассмотреть все бесконечные книги.

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

Алгоритмы и вычислимость

Суть работы компьютера заключается в проведении некоторого вычисления преобразования одной порции информации в другую порцию. Причем результатом работы не обязательно должно быть число, главное, чтобы информация была представлена в некоторой объективной форме. Обычно под такой формой имеют в виду конечные цепочки символов некоторого алфавита. Получается, компьютерное вычисление есть некоторая функция в сугубо математическом смысле, с областью определения и значений в рассмотренном выше множестве A*. Именно тут возникают определенные проблемы. Если мы можем вычислить функцию, то можем записать промежуточные вычисления в виде текста. Более того, в виде тексте можно описать вообще правила вычисления. Мы знаем, что множество всех текстов счетное. Однако выясняется, что множество всех функций над натуральными числами имеет мощность континуум. Если мы пронумеруем все тексты, то получается функций вида A* -> A* тоже континуум. Получается, что некоторые функции вычислимы, а некоторые нет.

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

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

Частично-рекурсивные функции и тезис Черча

Все началось с того, что математик Давид Гильберт в 1900 году предложил список нерешенных на тот момент математических проблем. Позже выяснилось, что десятая проблема (проблема решения произвольного диофантового уравнения) оказалось неразрешимой, но для доказательства этого факта пришлось составить целую новую математическую теорию. Вопросами того, какие задачи можно конструктивно решить, и что такое конструктивное решение, занялись математики Курт Гедель, Стивен Клини, Алонсо Черч и Алан Тьюринг.

Курт Гедель наиболее известен тем, что сформулировал и доказал 2 теоремы о неполноте. Между прочим, сделал он это в возрасте всего лишь 24 лет.Курт Гедель наиболее известен тем, что сформулировал и доказал 2 теоремы о неполноте. Между прочим, сделал он это в возрасте всего лишь 24 лет.

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

Частично-рекурсивные функции соответствуют вычислимым функциям в любом разумном понимании вычислимости.

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

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

Тезис Тьюринга

Формальная теория алгоритмов во многом построена аналогично теории вычислимости. Считается, что алгоритм есть некое конструктивное преобразование входного слова (цепочки символов некоторого алфавита) в некоторое выходное слово. Опять же, здесь мы имеем с функциями вида A*->A*. Конечно, предложенное описание не подходит под определение алгоритма, так как неясно, что же такое конструктивное преобразование. Хоть понятия алгоритма и вычислимой функции близки, не стоит их смешивать. Для одного и того же алгоритма может быть предъявлено сколько угодно его записей на каком-нибудь формальном языке, но соответствующая вычислимая функция всегда одна. Один из основателей формальной теории алгоритмов, Алан Тьюринг, предложил формальную модель автомата, известного как машина Тьюринга. Тезис Тьюринга гласит:

Каково бы не было разумное понимание алгоритма, любой алгоритм, соответствующий такому пониманию, может быть реализован на машине Тьюринга.

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

С помощью такого незамысловатого автомата можно формализовать любой алгоритм.С помощью такого незамысловатого автомата можно формализовать любой алгоритм.

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

Свойства алгоритмов

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

Обязательные свойства

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

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

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

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

Винегрет из свойств из того же учебника по информатике.Винегрет из свойств из того же учебника по информатике.

Необязательные свойства

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

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

Про зависающие программы

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

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

Заключение

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

Материал данной статьи во многом опирается на 1-ый том Программирование: введение в профессию А. В. Столярова. Тем, кто хочет подробнее изучить вопросы, связанные с алгоритмами и теорией вычислимости, кроме этой книги, советую Босс В От Диофанта до Тьюринга и трехтомник А. Шеня по математической логике и теории алгоритмов.


Дата-центр ITSOFT размещение и аренда серверов и стоек в двух дата-центрах в Москве. За последние годы UPTIME 100%. Размещение GPU-ферм и ASIC-майнеров, аренда GPU-серверов, лицензии связи, SSL-сертификаты, администрирование серверов и поддержка сайтов.

Подробнее..

Выбор хэш-функции в задаче шардирования данных

28.12.2020 02:07:52 | Автор: admin

Введение

Мы в Miro работаем над процессом шардирования баз Postgres и используем разные подходы в зависимости от бизнес-требований. Недавно перед нами встала задача шардирования новых баз, в ходе неё мы выбрали новый для нас подход к шардированию, основанный на согласованном хешировании (consistent hashing).

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

Oб архитектурном подходе

Есть много продуктов (mongo, redis, и т.д.), использующих согласованное хеширование для шардинга, и наша реализация будет сильно похожа на них.

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

Плюсами данного подхода являются:

  • равномерное распределение сущностей по шардам;

  • определение соответствия сущностей и шардов без дополнительного хранилища с минимум ресурсо-затрат;

  • возможность добавления новых шардов в кластер.

Из минусов:

  • неэффективность некоторых операций поиска, в которых необходимо делать запросы на все шарды;

  • достаточно сложный процесс решардинга.

Требования

Центральным местом решения является выбор java-реализации хэш-функции.

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

Критерии сравнения

Рассмотрим четыре распространенных критерия сравнения реализаций хэш-функций:

  1. Скорость, функция должна работать быстро, на любых входных данных;

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

  3. Устойчивость к коллизиям (первого и второго рода);

  4. Соответствие лавинному эффекту. Отражает зависимость всех выходных битов от каждого входного бита, на любых входных данных.

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

Отсутствие возможности атаки на характеристики функции делает для нас неважным третий критерий.

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

Реализации

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

  1. DJB2 (32-бита);

  2. SDBM (32-бита);

  3. LoseLose (32-бита);

  4. FNV-1 / FNV-1a (32-бита);

  5. CRC16 (16-бит) ;

  6. Murmur2/Murmur3 (32-бита).

Тестирование

Входные данные

В качестве входных данных мы будем использовать следующие наборы данных

  1. Набор реальных данных, составленный из 216,553 английских слов;

  2. Набор синтетических данных, составленный из рандомно сгенерированных символов в кодировке UTF-8.

В обоих тестовых наборах мы будем иметь группы строк с определенными длинами (кол-во символов) - "2", "4", "8", "16", "32", "64", "128", "256".

Метрики

Для сравнения различных критериев мы будем использовать следующие метрики:

  1. Для первого критерия, скорости - ops/ms (кол-во операций в миллисекунду работы);

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

Инструменты

Оценка скорости работы

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

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

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

  • Кол-во warmup-итераций - 50;

  • Кол-во measurement-итераций - 100;

  • Режим - throughput

  • Добавим ограничение по памяти -Xms1G, -Xmx8G

  • Для оценки расхода памяти добавим GCProfiler

Полный код тестов можно посмотреть здесь.

Оценка распределения результатов

Для проверки соответствия выходных значений функции нашим ожиданиям проверим гипотезу о том, что выборка результатов при уровне значимости =0,05, распределена по равномерному закону. Для проверки мы будем использовать критерий согласия Пирсона.

Алгоритм для проверки гипотезы следующий:

  1. Разобьем выборку на частичные интервалы, число которых найдем по формуле Стерджеса, а их длину найдем по правилу равноинтервальной группировки;

  2. Для каждого интервала подсчитаем его характеристики - среднее значение, частоты, относительные частоты;

  3. Подсчитаем выборочное среднее \overline{x_{b}} , среднеквадратическое отклонение \sigma_{b} = \sqrt{D_{b}} и теоретические частоты

    \hat{n_{i}}=np_{i},

    где n число элементов в выборке, а p_{i} вероятность попадания случайной величины в частичные интервалы, в нашем случае она равна -

    p_{i} = \frac{x_{length}}{b - a},

    где x_{length} - одинаковая длина интервалов, a параметры a и b - a = \overline{x_{b}} - \sqrt{3\sigma_{b}} , b = \overline{x_{b}} + \sqrt{3\sigma_{b}} ;

  4. Можем приступить к расчёту критерия согласия, по формуле

    \chi_{набл}^2 = \sum\frac{n_{i}-\hat{n_{i}}}{\hat{n_{i}}},

    где n_{i} - эмпирические частоты, полученные из выборки, \hat{n_{i}} - теоретические частоты, найденные по формулам выше;

  5. Определяем по таблице критических точек распределения \chi_{кр}^2(\alpha, k) , по заданному уровню значимости и числу степеней свободы k ;

  6. Если \chi_{набл}^2<\chi_{кр}^2 , то принимаем гипотезу, если же данное условие не выполняется отвергаем.

Код для расчёта критерия согласия и вероятностных характеристик выборок здесь.

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

Слова из каждого тестового набора мы сгруппируем по длине, при максимальном значении символов в 256. Затем создадим входные тестовые выборки разных размеров в диапазоне 16384, 8192, 4096, 2048, 1024, в выборки поместим слова из каждой группы, с одинаковой вероятностью.

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

Полный код тестов можно посмотреть здесь.

Результаты

Оценка скорости работы

Рассмотрим скорость работы (количество операций в миллисекунду) для различных имплементаций в зависимости от длины входных строк.

В диапазоне от двух до восьми символов:

ДиаграммаДиаграмма

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

В диапазоне от 16 до 256 символов:

ДиаграммаДиаграмма

Функция murmur2 явный фаворит, ей немного уступает murmur; crc16 и sdbm остались в аутсайдерах и на этой выборке.

Оценка распределения результатов

Рассмотрим таблицу результатов соответствия критерию Пирсона

Видно, что имплементации crc16, murmur2, murmur3 удовлетворяют критерию Пирсона о равномерном распределении практически на всех выборках.

Рассмотрим гистограммы относительных частот, в разрезе разных выборок.

На гистограммах ниже, для loseLose, Djb2, Sdbm, не прошедших тест, видно, что распределение далеко от равномерного и больше похоже на геометрическое:

ДиаграммаДиаграммаДиаграммаДиаграммаДиаграммаДиаграмма

Для проваливших тест Fnv1 и Fnv1a ситуация похожа, распределения отдалённо напоминают нормальное:

.

ДиаграммаДиаграммаДиаграммаДиаграмма

Смотрим на тройку победителей:

ДиаграммаДиаграммаДиаграммаДиаграммаДиаграммаДиаграмма

За исключением некоторых всплесков, crc16, murmur2, murmur3 удовлетворяют критерию Пирсона, что согласуется с характеристиками их гистограмм относительных частот.

Выводы

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

Скорость работы. Функции murmur2/murmur3 имеют лучшее время работы для входных строк длиной больше 8 символов.

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

Таким образом, исходя из двух критериев, лучшим выбором являются реализации murmur2/murmur3.

Подробнее..

Рекурсивный алгоритм представления Цекендорфа

31.12.2020 00:22:22 | Автор: admin

Приветики-омлетики

Спасибо добрым участникам Хабра, за то, что пригласили писать посты и получать на них фидбек.

Сегодня я бы хотел освятить тему представления чисел с помощью ряда Фибоначи и разумеется написать простенькое REST API c использованием Mongo DB алгоритм с помощью языка Ruby, который бы по заданому числу N возвращал его представление.

Часть 1 : представление Цекендорфа

Как гласит статья из Википедии :

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


100 = 89 + 8 + 3.

Я думаю, понимая что такое числа Фибоначи, не составит труда понять в чём заключенна данная теорема.

Цели которые должны быть достигнуты :

  • скорость работы

  • максимальная простота кода

Как язык програмирования я буду использовать Ruby, почему? Потому что Ruby - это

Лучший друг программиста.

Сначала теоретически нужно найти закономерность по которой и будет написан алгоритм.

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

Пример:
N = 51
F = 1 , 1 , 2 , 3 , 5 , 8 , 13, 21, 34.

Берём число 34, соседнее с ним число (21) можно сразу пропустить, ибо их сумма будет заведомо больше нашего входного числа N, ибо числа Фибоначи мы ищем пока они не превысят входное, ведь зачем нам числа больше входного :-).

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

И тогда появилась идея: а что если отнимать от N последнее число в ряду Фибоначи, и искать новый ряд где эта разница будет лимитом, и рекурсивно продолжать это действия пока разница не станет <= 0.

Пример
:
N = 51
F = 1 , 1 , 2 , 3 , 5 , 8 , 13, 21, 34.
ans = [34]

N = 51 - 34 = 17
F = 1 , 1 , 2 , 3 , 5 , 8 , 13.
ans = [34 , 13]

N = 17 - 13 = 4
F = 1 , 1 , 2 , 3.
ans = [34 , 13 , 3]

N = 4 - 3 = 1
F = 1
ans = [34 , 13 , 3, 1]

Сразу попробую записать в код :

def le_fib(limit, fib)  theoretical = fib[fib.count - 1] + fib[fib.count - 2]  return fib.last if theoretical > limit  fib << theoretical  le_fib(limit, fib)enddef main(target,result)  temporary = le_fib(target, [1,1])  result << temporary  return result if target - temporary <= 0  main(target - temporary, result)endpp main(gets.to_i,[])

Функция le_fib - рекурсивно ищет ряд Фибоначи с пределом, на то, что бы следующее число не было больше чем входное число target. Здесь важно, что нас не интересует ряд Фибоначи целиком, нам важно лишь его окончание.

Функция main - рекурсивно ищет масcив, числа которого есть числами Фибоначе, и которые бы в сумме давали нам входное число.

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

Пример работы для 20 случайных чисел до 1000

Оценка времени работы в зависимости от величины ряда (входного числа)

Как видим время работы даже на числах до 10^9 очень позитивное.

А общий обьем кода в 17 строк говорит о том что обе задачи выполнены успешно.

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

Подробнее..

Это непростое условное выполнение

02.01.2021 18:15:24 | Автор: admin
Некоторое время назад я рассказывал о программном комплексе для выявления скрытого параллелизма в произвольном алгоритме и технологиях его, параллелизма, рационального использовании (http://personeltest.ru/aways/habr.com/ru/post/530078/). Одним из компонентов этого комплекса является т.н. универсальный вычислитель, выполненный в соответствии с архитектурой Data-Flow (далее DF, потоковый вычислитель, описание здесь habr.com/ru/post/534722).
Подробнее..

Динамика потокового вычислителя

31.01.2021 14:19:04 | Автор: admin
В публикации habr.com/ru/post/530078 я рассказывал о возможностях потокового (архитектуры Data-Flow, далее DF) параллельного вычислителя. Особенности выполнения программ на нём столь необычны и интересны, что о них следует сказать два слова. Эксперименты проводились на компьютерном симуляторе DF-машины, входящем в исследовательский комплекс для выявления параллелизма в произвольном алгоритме и выработке рационального расписания выполнения этого алгоритма на гомогенном или гетерогенном поле параллельных вычислителей (та же публикация).
Подробнее..

Бинарный поиск в микроконтроллере

10.02.2021 08:12:41 | Автор: admin

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

Одно время мы выпускали несложный контроллер облачной СКУД на 100 пользователей. В его основе лежал микроконтроллер PIC18F46K22. В качестве памяти для хранения кодов ключей пользователей использовалась FLASH-память с интерфейсом I2C ёмкостью 64 кБ. Сама флешка довольно быстрая, но на шине I2C находилась ещё микросхема часов DS1307, которая работает на скорости не выше 100 кбит/сек. Высокой скорости работы нам не требовалось, поэтому в итоге вся шина была запущена на частоте 100 кГц.

Однако со временем мы начали разрабатывать новую версию контроллера, поддерживающего уже 3000 пользователей. Не хотелось сильно менять архитектуру, поэтому основные узлы были сохранены, но при этом был увеличен объём FLASH-памяти до 256 кБ.

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

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

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

typedef struct{    uint32_t COD;    uint8_t nSchedule;} TSKUD_User;bool skudFindUserByCode(uint32_t pCOD){  TSKUD_User user;  for (uint8_t i = 0; i < SKUD_USERS_COUNT; i++)  {    skudReadUser(i, &user);    if (user.COD == pCOD)      return 1;  }  return 0;}

Функция skudReadUser считывала блок данных из I2C памяти, далее осуществлялась проверка на совпадение кода.

При ста пользователях в худшей случае (когда код находился в самом конце массива данных) время поиска занимало порядка 0,1 сек. При переходе же к 3000 пользователей время выросло 3 сек!

Поэтому для ускорения функция была переписана следующим образом:

bool skudFindUserByCode(uint32_t pCOD){  TSKUD_User user;  int16_t m, beg, end;  beg = 0;  end = SKUD_USERS_COUNT - 1;  while (beg <= end)  {    m = (beg + end) / 2;    skudReadUser(m, &user);    if (pCOD == user.COD)      return true;    if ((pCOD < user.COD) || (user.COD == 0))      end = m - 1;    else      beg = m + 1;  }  return false;}

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

О различных частных случаях при реализации бинарного поиска можно почитать в статье:Я не могу написать бинарный поиск (http://personeltest.ru/aways/habr.com/ru/post/146228).

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

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

  2. Если номер искомой карты меньше, то следует его искать в левой части массива. Тут мы отбрасываем сразу половину заведомо не подходящих вариантов. Индекс end теперь будет равен m 1.

  3. Если номер искомой карты меньше, то следует его искать в правой части массива. Так же отбрасываем сразу половину заведомо не подходящих вариантов, но меняем индекс beg (он будет равен m + 1).

Если в массиве данных вообще нет искомого значения ключа, то нам нужно выйти из цикла. Условием выхода является beg > end.

Очень важным является дополнительное условие user.COD == 0 в строке:

    if ((pCOD < user.COD) || (user.COD == 0))

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

Индекс

Значение

0

1307131

1

1308780

2

1318001

3

2174082

4

2290467

5

2291521

...

0

2996

0

2997

0

2998

0

2999

0

Можно было бы записывать туда значения 0xFFFFFFFF, но его мы используем в качестве сервисного для служебных нужд системы. Поэтому дополнительное условие user.COD== 0 всегда заставляет алгоритм искать код в левой половине массива данных.

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

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

А вот при бинарном поиске количество сравнений будет составлять log23000 11 шт!

Интересно, что если записей будет аж 4 миллиарда, то количество сравнений при использовании бинарного поиска увеличится всего лишь до 32!

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

Итерация

beg

end

m

Код искомой карты

Код карты в массиве по индексу m

1

0

2999

1499

2174082

0

2

0

1498

749

2174082

0

3

0

748

374

2174082

0

4

0

373

186

2174082

0

5

0

185

92

2174082

0

6

0

91

45

2174082

0

7

0

44

22

2174082

0

8

0

21

10

2174082

0

9

0

9

4

2174082

2290467

10

0

3

2

2174082

1318001

11

3

3

3

2174082

2174082

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

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

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

Вот работа линейного поиска:

А вот бинарного:

Результат, как говориться, на лицо!

Подробнее..

Параллелизм и плотность кода

05.03.2021 14:13:31 | Автор: admin

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

Естественным перед началом анализа будет указание ограничений на ширину и глубину исследований. Принимаем, что многозадачность в рассматриваемых параллельных системах осуществляется простейшим путём - перегрузкой всего блока (связки) выполняющихся операторов (одновременное выполнение операторов разных программ не предполагается) или же система работает в однозадачном режиме; в противном случае высказанное в предыдущей фразе утверждение может быть неверным. Минимизация объёма устройств временного хранения данных (описано здесь http://personeltest.ru/aways/habr.com/ru/post/534722/) проводиться не будет. На этом этапе исследований также не учитываются задержки времени на обработку операторов и пересылку данных между ними (для системы SPF@home формально эти параметры могут быть заданы в файлах с расширениями med и mvr).

В предыдущей публикации http://personeltest.ru/aways/habr.com/ru/post/540122/ была описана технология получения ПВПП на основе модели потокового (Data-Flow) вычислителя. Обычно считают, что правила выбора операторов для выполнения в такой машине подчиняются логике действия некоторых сущностей, совместно выполняющих определённые действия актёров (actors); при этом естественным образом моделируются связанные с характеристиками времени параметры обработки операторов. В общем случае при этом отдельные операторы выполняются асинхронно. В публикации показано, что описанный принцип получения ПВПП приемлем (при выполнении несложных условий) и для машин архитектуры VLIW (Very Long Instruction Word, сверхдлинное машинное слово), отличающихся требованием одновременности начала выполнения всех операторов в связке. В расчётах использовали модель ILP (Instruction-LevelParallelism, параллелизм уровня машинных команд).

В рассматриваемый программный комплекс http://personeltest.ru/aways/habr.com/ru/post/530078/ включен модуль SPF@home, позволяющей работать с гранулами параллелизма любого размера (оператор любой сложности). Основным инструментом этого модуля является метод получения ярусно-параллельной формы (ЯПФ) графа алгоритма (здесь используется информационный граф, в котором вершинами являются узлы преобразования информации, а дугами её передачи).

Реформирование ЯПФ может дать результат, идентичный полученному моделированием выполнения программы на Data-Flow -машине, но в некоторых случаях результаты расходятся. В самом деле, это во многом различные подходы. Не столь сложно представить себе рой самостоятельных, взаимодействующих программ-актёров, выполняющих действия по поиску готовых к выполнению операторов, свободных в данный момент отдельных вычислителей и назначающих обработку выбранных операторов конкретному вычислителю etc etc, но действия эти логично производятся в RunTime и именно на границе (линии фронта) между уже выполненными и ещё не выполненными операторами (метафора поиска в ширину, а не в глубину в теории графов). При этом естественным образом создаётся очень подробный план выполнения параллельной программы с точными временными метками начала и конца выполнения операторов с привязкой их к конкретным вычислителям. Такой план хорош (с точностью до математической модели, которая в конечном счёте всегда в чём-то ограничена), однако автор сомневается в степени достаточной безумности практического использования полученного т.о. ПВПП

Метод обработки ЯПФ в целом более теоретичен и от многих особенностей абстрагирован, однако он может ещё до выполнения программы (DesignTime) показать ПВПП сверху (учитывая не только параллельный фронт выполнения операторов, но и всю карту боевых действий). Следствием большего абстрагирования ЯПФ-метода является субъективность интерпретации связи моментов обработки операторов с собственно ярусом ЯПФ. В самом деле, можно рассмотреть несколько укрупнённых подходов к таким интерпретациям; см. рис. ниже слева варианты a) и б) соответственно. На этом рисунке операторы показаны красными прямоугольниками, а яруса (в целях большей выразительности сказанного) - горизонтальными линиями:

  • выполнение всех принадлежащих конкретному ярусу операторов начинается одновременно (тогда это фактически VLIW-идеологема и ПВПП изначально обладает ограниченностью, связанной с потерями времени на ожидание выполнения наиболее длительного оператора на ярусе; точное решение получается лишь при одинаковости времён выполнения всех операторов на ярусе);

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

При использовании ЯПФ в своих изысканиях Исследователь должен сам выбрать определённую модель и далее ей следовать. В рамках системы SPF@home, например, имеется возможность целевой реорганизации ЯПФ с конечной целью собрать на ярусах операторы с наиболее близкими длительностями обработки. Именно использование ЯПФ как нельзя лучше отвечает идеологеме EPIC (Explicitly Parallel Instruction Computing, явный параллелизм выполнения команд), позволяющей параллельной вычислительной системе выполнять инструкции согласно плану, заранее сформированному компилятором. Не следует игнорировать и субъективный фактор - бесспорным преимуществом ЯПФ является возможность простой и недвусмысленной визуализации собственно ПВПП.

Исходными данными для модуля SPF@home служат описания информационных графов алгоритма (программы) в стандартной DOT-форме (расширения файлов gv, могущие быть полученными импортом из модуля Data-Flow или иными путями). Допустимые (не нарушающие информационные связи в алгоритме) преобразования ЯПФ управляются программой на языке Lua, реализующей разработанные методы реструктуризации ЯПФ (дополнительная информация приведена в публикации http://personeltest.ru/aways/habr.com/ru/post/530078/). Эти методы неизбежно будут являться эвристическими вследствие невозможности прямого решения поставленных (относящихся к классу NP-полных) задач.

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

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

В качестве пациентов использовались имеющиеся в наличии информационные графы алгоритмов, в основном класса линейной алгебры (как одни из наиболее часто встречающиеся в современных задачах обработки данных). По понятным причинам исследования проводились на данных небольшого объёма в предположении сохранения корректности полученных результатов при обработке данных большего размера. Описанные в данной публикации исследования имеют цель продемонстрировать возможности имеющегося инструментария при решении поставленных задач. При желании возможно исследовать произвольный алгоритм, описав и отладив его в модуле Data-Flow (http://personeltest.ru/aways/habr.com/ru/post/535926/) с последующим импортом в форме информационного графа в модуль SPF@home.

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

Ниже приведены результаты моделирования трёх наиболее часто встречающихся типов задач (в реальном случае обычно требуется выполнение нескольких из них одновременно):

1.Расписание выполнения программ на минимальном числе параллельных вычислителей при сохранении высоты ЯПФ

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

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

Эмпирический метод 1-01_bulldozer имеет целью получение наиболее равномерного распределения операторов по ярусам ЯПФ без возрастания её высоты (сохранение времени выполнения программы); операторы переносятся только вниз (первоначально ЯПФ строится в верхнем варианте). Для этого метод старается перенести операторы с ярусов шириной выше среднего на яруса с наименьшей шириной. На каждом ярусе операторы перебираются в порядке очередности слева направо.

Метод 1-02_bulldozer является модификацией предыдущего с адаптацией. Для оператора с максимумом вариативности (назовём так диапазон возможного размещения операторов по ярусам ЯПФ без изменения информационных связей в графе алгоритма) в пределах яруса вычисляются верхний и нижний пределы его возможного расположения по ярусам.

В результирующей табл.1 рассматриваются: mnk_N программа аппроксимации методом наименьших квадратов N точек прямой, mnk_2_N то же, но квадратичной функцией, korr_N вычисление коэффициента парной корреляции по N точкам, slau_N решение системы линейных алгебраических уравнений порядка N прямым (не итерационным) методом Гаусса, m_matr_N - программа умножения квадратных матриц порядка N традиционным способом, m_matr_vec_N умножение квадратной матрицы на вектор, squa_equ_2 решение полного квадратного уравнения в вещественных числах, squa_equ_2.pred то же, но с возможностью получения вещественных и мнимых корней при использовании метода предикатов для реализации условного выполнения операторов, e17_o11_t6, e313_o206_t32, e2367_o1397_t137, e451_o271_t30, e916_o624_t89, e17039_o9853_t199 сгенерированные специальной программой по заданным параметрам информационного графа. Вычислительную трудность преобразования будем характеризовать числом перемещений операторов с яруса на ярус. Неравномерность распределения числа операторов по ярусам ЯПФ характеризуется коэффициентом неравномерности (отношение числа операторов на наиболее и наименее нагруженных ярусов соответственно).

Для удобства анализа в таблице цветом выделены ячейки, соответствующие случаю достижению снижения ширины без возрастания высоты ЯПФ, а жирным начертанием цифр величины для сравнения.

Как видно из табл.1, во многих случаях удается значительно (до 1,5-2 раз) снизить ширину ЯПФ, но почти никогда до минимальной величины (средне-арифметическое значение ширин ярусов). В целом эвристика 1-02_bulldozer несколько более эффективна, но проигрывает по вычислительной сложности. В большинстве случаев увеличение размера обрабатываемых данных повышает эффективность балансировки (очевидно, это связано с повышением числа степеней свободы ЯПФ).

Интересно, что для некоторых алгоритмов (напр., slau_N) ни один из предложенных методов не дал результата. Сложность балансировки ЯПФ связана с естественным стремлением разработчиков алгоритмов создавать максимально плотные записи последовательности действий.

2.Расписания выполнения программ на фиксированном числе параллельных вычислителей при возможности увеличения высоты ЯПФ

Практический интерес представляют методы с увеличением высоты ЯПФ (см. табл.2, в которой дано сравнение двух методов с метафорическими названиями Dichotomy и WidthByWidth); при этом приходится смириться с увеличение времени выполнения программы. В ходе вычислительных экспериментов задавалась конечная ширина преобразованной ЯПФ (отдельные столбцы в правой части табл.2). Количественные параметры преобразований выдавались в форме частного, где числитель и знаменатель показывают число перемещений (первая строка), высоту ЯПФ (вторая) и коэффициент ковариации CV (третья строка) для каждого исследованного графа. Характеризующий неравномерность распределения ширин ярусов коэффициент ковариации рассчитывался как CV=/W, где - среднеквадратичное отклонение числа операторов по всем ярусам ЯПФ, W - среднеарифметическое числа операторов по ярусам.

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

3.Расписание выполнения программ на фиксированном числе гетерогенных параллельных вычислителей

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

Модуль SPF@home поддерживает эту возможность путём сопоставления информации из двух файлов для операторов и вычислителей (*.ops и *.cls соответственно). Имеется возможность задавать совпадение по множеству свободно назначаемых признаков для любого диапазона операторов/вычислителей. Условием выполнимости данного оператора на заданном вычислителе является minVal_1Val_1maxVal_1 для одинакового параметра (Val_1, minVal_1, maxVal_1 числовые значения данного параметра для оператора и вычислителя соответственно).

Разработка расписания для выполнения программы на гетерогенном поле параллельных вычислителей является более сложной процедурой относительно вышеописанных и здесь упор делается на программирование на Lua (API-функции системы SPF@home обеспечивают минимально необходимую поддержку). Т.к. на одном ярусе ЯПФ могут находиться операторы, требующие для выполнения различных вычислителей, полезным может служить концепция расцепления ярусов ЯПФ на семейства подъярусов, каждое из которых соответствует блоку вычислителей c определёнными возможностями (т.к. все данного операторы яруса обладают ГКВ-свойством, последовательность выполнения их в пределах яруса/подъяруса в первом приближении произвольна). На схеме ниже слева показано расщепление операторов на одном из ярусов ЯПФ в случае наличия 6 параллельных вычислителей 3-х типов.

Пример плана выполнения программы на поле из 3-х типов параллельно работающих вычислителей (c количествоv 5,3,4 штук соответственно и номерами 1-5, 6-8, 9-12 по типам, всего 12 штук) приведён в табл.3. При расчёте в качестве исходной использовался конкретный алгоритм, характеризующийся ЯПФ с числом операторов 206 и дуг 323, ярусов 32 (после расчета подъярусов получилось 48). Первый столбец таблицы показывает (разделитель символ прямого слеша) номер яруса/подъяруса; в ячейках таблицы приведены номера операторов, сумма их числа по подъярусам равно числу операторов на соответствующем ярусе ЯПФ.

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

В самом деле, в рассматриваемом случае общее время T решения задачи определяется суммой по всем ярусам максимальных значений времён выполнения операторов на подъярусах данного яруса, т.к. группы операторов на подъярусах выполняются последовательно (первая сумма берётся по j, вторая по i, максимум по kj):

T=(maxtik),

где j - число ярусов ЯПФ, i - число подъярусов на данном ярусе, kj - типы вычислителей на j-том ярусе, tik - время выполнения оператора типа i на вычислителе типа k. Если ставится задача достижения максимальной производительности, вполне возможно определить число вычислителей конкретного типа, минимизирующее T (напр., для показанного табл.3 случая количество вычислителей типа II полезно увеличить в пику вычислителяv типа I).

Задача минимизации общего времени решения T усложняется в случае возможности выполнения каждого оператора на нескольких вычислителях вследствие неоднозначности tik в вышеприведённом выражении; здесь необходима дополнительная балансировка по подъярусам.

Описание параметров операторов располагается в файлах с расширением ops, параметров вычислителей cls; соответствующая API-функция (обёрнутая Lua-вызовом) возвращает значение, разрешающее или запрещающее выполнение данного оператора на заданном вычислителе. Описанные файлы являются текстовыми (формат данных определён в документации), что даёт возможность разработки внешних программ для генерации требуемого плана эксперимента с использованием модуля SPF@home в режиме командной строки.


В порядке обсуждения небезынтересно будет рассмотреть вариант ЯПФ в нижней форме (при этом все операторы перемещены максимально в сторону окончания выполнения программы). Такая ЯПФ может быть получена из верхней перемещениями операторов по ярусам как можно ниже или проще построением ЯПФ в направлении от конца программы к её началу. Ниже проиллюстрировано сравнение распределения ширин ЯПФ в верхней и нижней формах (изображения в строке слева и справа соответственно в 4-х рядах) для алгоритма умножения матриц традиционным способом при порядках матриц N=3,5,7,10. Здесь H, W и W высота, ширина и среднеарифметическая ширина ЯПФ (последняя показана на рисунках пунктиром; символ прямого слеша разделяет параметры для верхней и нижней ЯПФ (фиолетовый цвет ярус максимальной ширины, красный минимальной).

Соответствующие иллюстрации для процедуры решения систем линейных алгебраических уравнения порядков N=3,5,7,10 безытерационным методом Гаусса представлены ниже.

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

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


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

Выше использованные количественные характеристики неравномерности не дают информации о форме кривой, обладающей этой неравномерностью. В качестве дополнительной оценки неравномерности распределения операторов по ярусам ЯПФ может быть признан известный графо-аналитического метод определения дифференциации доходов населения, заключающегося в расчёте численных параметров расслоения (кривая Лоренца и коэффициент Джини), несмотря на зеркально-противоположную форму анализируемых кривых. Удобство сравнения ЯПФ различных алгоритмов достигается нормированием обоих осей графиков на общую величину (единицу или 100%).

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

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

Итак, программная система SPF@home выполняет практическую задачу по составлению расписаний выполнения заданных алгоритмов (программ) на заданном поле параллельных вычислителей и дает возможность проводить различного типа исследования свойств алгоритмов (в данном случае оценивать вычислительную сложность методов составления расписаний). Система нацелена в основном на анализ программ, созданных с использованием языков программирования высокого уровня без явного указания распараллеливания и в системах c концепцией ILP (Instruction-LevelParallelism, параллелизм на уровне команд), хотя возможности модуля SPF@home позволяют использовать в качестве неделимых блоков последовательности команд любого размера.

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

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


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

Подробнее..

Сколько стоит расписание

10.04.2021 22:12:11 | Автор: admin

Основные данные вычислительных экспериментов по реорганизации ярусно-параллельной формы (ЯПФ) информационных графов алгоритмов (ТГА) приведены в предыдущей публикации (http://personeltest.ru/aways/habr.com/ru/post/545498/). Цель текущей публикации показать окончательные результаты исследований разработки расписаний выполнения параллельных программ в показателях вычислительной трудоёмкости собственно преобразования и качества полученных расписаний. Данная работа является итогом вполне определённого цикла исследований в рассматриваемой области.

Как и было сказано ранее, вычислительную трудоёмкости (ВТ) в данном случае будем вычислять в единицах перемещения операторов с яруса на ярус в процессе реорганизации ЯПФ. Этот подход близок классической методике определения ВТ операций упорядочивания (сортировки) числовых массивов, недостатком является неучёт трудоёмкости процедур определения элементов для перестановки.

Т.к. в принятой модели ЯПФ фактически определяет порядок выполнения операторов параллельной программы (операторы выполняются группами по ярусам поочерёдно), в целях сокращения будем иногда использовать саму аббревиатуру ЯПФ в качестве синонима понятия плана (расписания) выполнения параллельной программы. По понятным причинам исследования проводились на данных относительно небольшого объёма в предположении сохранения корректности полученных результатов при обработке данных большего размера. Описанные в данной публикации исследования имеют цель продемонстрировать возможности имеющегося инструментария при решении поставленных задач. При желании возможно исследовать произвольный алгоритм, описав и отладив его в модуле Data-Flow (http://personeltest.ru/aways/habr.com/ru/post/535926/) с последующим импортом в формате информационного графа в модуль SPF@home для дальнейшей обработки.

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

Как и ранее, будем ссылаться на разработанные эвристические методы (иногда сокращённо именуемые просто эвристиками), реализованные на языке Lua и управляющие преобразованиями ЯПФ. Также не учитываем возможности одновременного выполнения команд нескольких процессов на параллельном поле вычислителей (хотя теоретически это может привести к повышению плотности кода).

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

Полученные результаты предназначаются для улучшения качества разработки расписаний выполнения параллельных программ в распараллеливающих компиляторах будущих поколений. При этом внутренняя реализация данных конечно, совсем не обязана предусматривать явного построения ЯПФ в виде двумерного массива, как для большей выпуклости показано на рис.2 в публикации http://personeltest.ru/aways/habr.com/ru/post/530078/ и выдаётся программным модулем SPF@home (http://vbakanov.ru/spf@home/content/install_spf.exe). Она может быть любой удобной для компьютерной реализации например, в наивном случае устанавливающей однозначное соответствие между формой ИГА в виде множества направленных дуг {k,l} (матрица смежности) и двоек номеров вершин ik,jk и il,jl, где i,j номера строк и столбцов в ЯПФ (процедуру преобразования ИГА в начальную ЯПФ провести всё равно придётся, ибо в данном случае именно она выявляет параллелизм в заданном ИГА алгоритме; только после этого можно начинать любые преобразования ЯПФ).

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

Для каждой из группы рассматриваемых задач (преобразования с сохранением высоты исходной ЯПФ или при возможности увеличения высоты оной) рассмотрим по две методики (эвристики, ибо так согласились именовать разработки) для перового случая это 1-01_bulldozer vs 1-02_bulldozer, для второго - WidthByWidtn vs Dichotomy. Мне стыдно повторять это, но высота ЯПФ определяет время выполнения программы

1. Получение расписания параллельного выполнения программ при сохранении высоты ЯПФ

Сохранение высоты исходной ЯПФ равносильно выполнению алгоритма (программы) за минимально возможное время. Наиболее равномерная нагрузка на все вычислители параллельной системы будет при одинаковой ширине всех ярусов ЯПФ (среднеарифметическое значение). В ЯПФ реальных алгоритмов и размерности обрабатываемых данных среднеарифметическое значение является довольно большим (практически всегда много большим числа отдельных вычислителей в параллельной системе). Т.о. рассматриваемый случай не часто встречается в практике, но всё же должен быть разобран.

Для сравнения выберем часто анализируемые ранее алгоритмы и два эвристических метода целенаправленного преобразования их ЯПФ эвристики 1-01_bulldozer и 1-02_bulldozer.

Результаты применения этих эвристик приведены на рис. 1-3; обозначения на этих рисунках (по осям абсцисс отложены показатели размерности обрабатываемых данных):

  • графики a), b) и с) ширина ЯПФ, коэффициент вариации (CV ширин ярусов ЯПФ), число перемещений (характеристика вычислительной трудоёмкости) операторов соответственно;

  • сплошные (красная), пунктирные (синяя) и штрих-пунктирные (зелёная) линии исходные данные, результат применения эвристик 1-01_bulldozer и 1-02_bulldozer cответственно.

Рисунок 1. Параметры плана параллельного выполнения при сохранении высоты ЯПФ для алгоритма умножения квадратных матриц 2,3,5,7,10-го порядков (соответствует нумерации по осям абсцисс) классическим методомРисунок 1. Параметры плана параллельного выполнения при сохранении высоты ЯПФ для алгоритма умножения квадратных матриц 2,3,5,7,10-го порядков (соответствует нумерации по осям абсцисс) классическим методомРисунок 2. Параметры плана параллельного выполнения при сохранении высоты ЯПФ для алгоритма вычисления коэффициента парной корреляции по 5,10,15,20-ти точкам (соответствует нумерации по осям абсцисс)Рисунок 2. Параметры плана параллельного выполнения при сохранении высоты ЯПФ для алгоритма вычисления коэффициента парной корреляции по 5,10,15,20-ти точкам (соответствует нумерации по осям абсцисс)Рисунок 3. Параметры плана параллельного выполнения при сохранении высоты ЯПФ для алгоритма решения системы линейных алгебраических уравнений (СЛАУ) для 2,3,4,5,7,10-того порядка (соответствует нумерации по осям абсцисс) прямым (неитерационным) методом ГауссаРисунок 3. Параметры плана параллельного выполнения при сохранении высоты ЯПФ для алгоритма решения системы линейных алгебраических уравнений (СЛАУ) для 2,3,4,5,7,10-того порядка (соответствует нумерации по осям абсцисс) прямым (неитерационным) методом Гаусса

Данные рис. 1-3 показывают, что во многих случаях удаётся приблизиться к указанной цели. Напр., рис. 1a) иллюстрирует снижение ширины ЯПФ до 1,7 раз (метод 1-01_bulldozer) и до 3 раз (метод 1-02_bulldozer) при умножении матриц 10-го порядка.

Коэффициент вариации ширин ярусов ЯПФ (рис. 1b) приближается к 0,3 (граница однородности набора данных) при использовании эмпирики 1-02_bulldozer и, что немаловажно, достаточно стабилен на всём диапазоне размерности данных.

Трудоёмкость достижения результата (рис. 1c) при использовании метода 1-02_bulldozer значительно ниже (до 3,7 раз при порядке матриц 10) метода 1-01_bulldozer.

Важно, что эффективность метода возрастает с ростом размерности обрабатываемых данных.

Не менее эффективным показал себя метод 1-02_bulldozer на алгоритме вычисления коэффициента парной корреляции (рис. 2).

Попытка реорганизации ЯПФ алгоритма решения системы линейных алгебраических уравнений (СЛАУ) порядка до 10 обоими методами (рис. 3) оказалась малополезной. Ширину ЯПФ снизить не удалось вообще (рис. 3a), снижение CV очень мало (рис. 3b), однако метод 1-02_bulldozer немного выигрывает в трудоёмкости (рис. 3c).

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

2. Получение расписания параллельного выполнения программ на фиксированном числе параллельных вычислителей

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

Ниже рассматривается распространенный случай выполнения программы на заданном гомогенном поле из W параллельных вычислителей (от W=W0 до W=1, где W0 ширина ЯПФ, а нижняя граница соответствует полностью последовательному выполнению). Сравниваем два метода реорганизации ЯПФ Dichotomy и WidthByWidtn:

  • Dichotomy. Цель получить вариант ЯПФ с c шириной не более заданного W c увеличением высоты методом перенесения операторов с яруса на вновь создаваемый ярус ниже данного. Если ширина яруса выше W, ровно половина операторов с него переносится на вновь создаваемый снизу ярус и так далее, пока ширина станет не выше заданной W. Метод работает очень быстро, но грубо (высота ЯПФ получается явно излишней и неравномерность ширин ярусов высока).

  • WidthByWidtn. Подлежат переносу только операторы яруса с числом операторов выше заданного N>W путём создания под таким ярусом число ярусов М, равное:

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

На рис. 4,5 показаны результаты выполнения указанных эвристик в применении к двум распространенным алгоритмам линейной алгебры - умножение квадратных матриц классическим методом и решение системы линейных алгебраических уравнений прямым (неитерационным) методом Гаусса; красные и синие линии на этих и последующих рисунках соответствуют эвристикам WidthByWidtn и Dichotomy соответственно. Не забываем, что ширина реформированной ЯПФ здесь соответствует числу команд в связке сверхдлинного машинного слова.

Рисунок 4. Возрастание высоты (ординаты) при ограничении ширины ЯПФ (абсциссы), разы; алгоритм умножения квадратных матриц классическим методом 5 и 10-го порядков рис. a) и b) соответственноРисунок 4. Возрастание высоты (ординаты) при ограничении ширины ЯПФ (абсциссы), разы; алгоритм умножения квадратных матриц классическим методом 5 и 10-го порядков рис. a) и b) соответственноРисунок 5. Возрастание высоты (ординаты) при ограничении ширины ЯПФ (абсциссы), разы; алгоритм решения системы линейных алгебраических уравнений прямым (неитерационным) методом Гаусса 5 и 10-го порядков рис. a) и b) соответственноРисунок 5. Возрастание высоты (ординаты) при ограничении ширины ЯПФ (абсциссы), разы; алгоритм решения системы линейных алгебраических уравнений прямым (неитерационным) методом Гаусса 5 и 10-го порядков рис. a) и b) соответственно

Как видно из рис. 4 и 5, оба метода на указанных алгоритмах приводят к близким результатам (из соображений представления ЯПФ плоской таблицей и инвариантности общего числа операторов в алгоритме это, конечно, гипербола!). При большей высоте ЯПФ увеличивается время жизни данных, но само их количество в каждый момент времени снижается.

Однако при всех равно-входящих соответствующие методу WidthByWidtn кривые расположены ниже, нежели по методу Dichotomy; это соответствует несколько большему быстродействию. Полученные методом WidthByWidtn результаты практически совпадают с идеалом высоты ЯПФ, равным Nсумм./Wсредн. , где Nсумм. общее число операторов, Wсредн. среднеарифметическое числа операторов по ярусам ЯПФ при заданной ширине ея.

Рисунок 6. Число перемещений операторов между ярусами - a) и коэффициент вариации CV - b) при снижении ширины ЯПФ для алгоритма умножения квадратных матриц 10-го порядка классическим методом (ось абсцисс ширина ЯПФ после реформирования)Рисунок 6. Число перемещений операторов между ярусами - a) и коэффициент вариации CV - b) при снижении ширины ЯПФ для алгоритма умножения квадратных матриц 10-го порядка классическим методом (ось абсцисс ширина ЯПФ после реформирования)Рисунок 7. Число перемещений операторов между ярусами - a) и коэффициент вариации CV - b) при снижении ширины ЯПФ для алгоритма решения системы линейных алгебраических уравнений 10-го порядка прямым (неитерационным) методом Гаусса (ось абсцисс ширина ЯПФ после реформирования)Рисунок 7. Число перемещений операторов между ярусами - a) и коэффициент вариации CV - b) при снижении ширины ЯПФ для алгоритма решения системы линейных алгебраических уравнений 10-го порядка прямым (неитерационным) методом Гаусса (ось абсцисс ширина ЯПФ после реформирования)

Анализ результатов, приведённый на рис. 6 и 7, более интересен (хотя бы потому, что имеет чисто практический интерес вычислительную трудоёмкость преобразования ЯПФ). Как видно из рис. 6 и 7, для рассмотренных случаев метод WidthByWidtn имеет меньшую (приблизительно в 3-4 раза) вычислительную трудоёмкость (в единицах числа перестановок операторов с яруса на ярус) относительно метода Dichotomy (хотя на первый взгляд ожидается обратное). Правда, при этом метод (эвристика) WidthByWidtn обладает более сложной внутренней логикой по сравнению с Dichotomy (в последнем случае она примитивна).

Т.о. проведено сравнение методов реорганизации (преобразования) ЯПФ конкретных алгоритмов с целью их параллельного выполнения на заданном числе вычислителей. Сравнение проведено по критериям вычислительной трудоёмкости преобразований и неравномерности загрузки параллельной вычислительной системы.

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

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


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

Подробнее..

Корни разные нужны, корни разные важны

14.06.2021 16:14:45 | Автор: admin

Вместо вступления

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

Интересен алгоритм sqrxi32 от @Sdima1357 Пример 1, далее для краткости именуемый как _i32. Алгоритм _i32 безусловно выполняет главное условие задачи округление до ближайшего целого на всём множестве значений аргумента [ 0 .. 0xFFFFFFFF ], при этом показывает высокую производительность.

Пример 1: Вычисление квадратного корня из целого с округлением до ближайшего целого.

uint16_t sqrxi32( uint32_t y ){if ( y == 1 )return 1;uint32_t xh = y > 0x10000ul ? 0x10000ul : y;uint32_t xl = 0;uint32_t xc;for ( int k = 0; k < 16; k++ ){xc = ( xh + xl ) >> 1ul;if ( xc * xc - xc >= y ){xh = xc;}else{xl = xc;}}return ( xh + xl ) >> 1ul;}

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

О чём этот текст

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

Исходный код содержит содержит решение одной задачи разными алгоритмами.

Анализ результатов наблюдений за рамками настоящей публикации.

Условия и допуски

Для сокращение текста принимаем:

  • аппаратных платформ для тестов 3 платформы;

  • вариантов оптимизации сборки 3 значения

Для сборки двоичного кода применяем:

  • Одну единицу компиляции теста (файл main.c)

  • Компиляцию в моно-поточный исполняемый файл

  • Единую сборочную среду: CubeIDE (она же Eclipce CDT)

  • Стандартные настройки профиля сборки RELEASE в среде CubeIDE

  • Единый диалект компилятора: ISO C11 + gnu extensions (-std=gnu11)

  • Применительно к микроконтроллерам:

    • CubeMX default settings, +48MHz, +USART1, +HAL;

    • Runtime lib: Reduced C ( --spec=nano.specs );

    • Use float with printf from new lib nano ( -u _printf_float );

Таблица 1: Варианты сборки исполняемого кода

Таблица 2: Общие характеристики аппаратных платформ

Таблица 3: Технические характеристики аппаратных платформ

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

Для оценки FPU платформы M4 в тестовый набор добавлена функция sqrt_fps, решающая вычислительную задачу с применением коротких действительных (float), именуемая далее _fps (Float Point Short) Пример 2.

Пример 2: Квадратный корень из целого с точностью float

uint16_t sqrt_fps( uint32_t number ){if ( number < 2 )return (uint16_t) number;float f_rslt = sqrtf( number );uint32_t rslt = (uint32_t) f_rslt;if ( !( f_rslt - (float) rslt < .5 ) )rslt++;return (uint16_t) rslt;}

Функция _fps работает без ошибок с аргументом менее 22-х бит, что соответствует десятичному порядку 1+E5 Иллюстрация 1.

Иллюстрация 1: Ошибки функции "_fps" на порядках 1+E6+

Для всех наблюдаемых алгоритмов ограничиваем диапазон аргумента множеством значений
[0 .. 1+E5].

Таблица 4: Список наблюдаемых алгоритмов

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

Относительная производительность платформ

Ожидаемо, производительность платформы x86 выше производительности платформы ARM Cortex безотносительно характера оптимизации сборки. Последнее демонстрирует левая часть графика Иллюстрация 2.

Иллюстрация 2: Относительная производительность аппаратных платформ

На левой части графика по оси Y отображается среднее время последовательного выполнения всех тестов (Таблица 4), измеренное в секундах. На оси X аппаратные платформы.

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

График каждой платформы, в свою очередь, представлен тремя столбцами, демонстрирующими зависимость производительности от варианта оптимизации сборки: -O0, -Os, -O3.

Правая часть графика (Иллюстрация 2) показывает относительный прирост производительности у каждой аппаратной платформы в зависимости от варианта оптимизации сборки: -O0, -Os, -O3.

Производительность 100% демонстрирует двоичный код, собранный без оптимизации ( -O0 ). Это базовая производительность платформы.

Чем выше высота столбика относительно базовой производительности (O0) в правой части графика, тем лучше оптимизационные возможности программно-аппаратной платформы.

Наблюдаем наибольший прирост производительности от оптимизации на этапе сборки наплатформе M4.

Платформа x86

На графике (Иллюстрация 3) по оси Y отображается число цикличных вызовов наблюдаемых функций за одну миллисекунду. На оси X наблюдаемые функции (Таблица 4).

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

Цветом на оси X обозначен способ оптимизации на этапе сборки. Соответствие цвета и характера оптимизации отражает легенда.

Иллюстрация 3: Производительность алгоритмов на платформе x86

Платформа x86 максимально раскрывает преимущества алгоритмов с плавающей точкой перед целочисленными.

Заслуживает отдельного внимания часть графика в оранжевом контуре.

Производительность кода без оптимизации (O0) лучше на 39% для алгоритма _fpu (Os) и на 16% для алгоритма _fps (O3). Другими словами, любая оптимизация на этапе сборки снижает производительность платформы x86 на действительных числах.

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

Платформа M4

Платформа M4 демонстрирует предсказуемый результат (Иллюстрация 4).

Иллюстрация 4: Производительность алгоритмов на платформе M4

Модуль с плавающей точкой M4 даёт ожидаемый прирост производительности для алгоритма _fps, основанного на коротком действительном float.

Последнее подтверждается результатом сравнения производительности алгоритмов при отключенном модуле FPU на платформе M4 Иллюстрация 5.

Наблюдая графики помним, что точность вычислений алгоритма _fps гарантируется в диапазоном 1+E5 (см. Иллюстрация 1) без относительно того, включен ли модуль FPU на M4 или нет.

Иллюстрация 5: Производительность алгоритмов на платформе M4 без FPU

Платформа M0

Результаты платформы M0 похожи на результаты платформы M4безFPU (Иллюстрация 5), только медленнее Иллюстрация 6.

Заметим, тактовая частота при тестировании устанавливалась одинаковой и для M4, и для M0 48 MHz. Однако, производительность M0 хуже в два с лишним раза, чем M4, в условиях равенства прочих характеристик.

Иллюстрация 6: Производительность алгоритмов на платформе M0

Алгоритм _fps на платформе M0 ожидаемо опережает в два раза алгоритм _fpu.

Целочисленные алгоритмы опережают алгоритмы с плавающей точкой.

По странному стечению обстоятельств в заключительном графике (Иллюстрация 6) снова есть место для оранжевого контура.

При сборке без оптимизации (O0) алгоритм _evn быстрее алгоритма _i32. И алгоритм _evn медленнее, чем _i32, если сборка проводится с оптимизацией.

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

Вместо заключения

Производительность программы зависит от многих причин.

Оптимизация на этапе сборки способна ухудшать производительность, замедлять программу, что демонстрируют экспериментальные данные в оранжевых контурах выше (Иллюстрация 3 и Иллюстрация 6).

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

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

Приложение 1. Порядок тестирования платформы x86

  1. Создать в среде CubeIDE (Eclipse CDT) проект C штатным способом

  2. Написать текст программы Пример 3

  3. Добавить в проект файл sqrt_cmp.h Пример 6

  4. Осуществить сборку и запуск программы:

    1. штатными средствами IDE;

    2. или из командной строки Пример 4

  5. Меняя вид оптимизации ( -O0, -O3, -Os ) наблюдать результат.

Пример 3: Исходный текст программы x86 main.c

#include sqrt_cmp.hint main( void ){main_Of_SqrtComp();return 0;}

Пример 4 Запуск теста на платформе x86 из терминала

gcc main.c -o main -I. -Wall -lm -std=gnu11 -O3 && ./main

Запуск теста из терминала платформы x86 предполагает, что файлы main.c и sqrt_cmp.h располагаются в одном каталоге, и этот каталог выбран рабочим (pwd).

Иллюстрация 7: Запуск теста из терминала x86

Приложение 2. Порядок тестирования платформы STM32

  1. Создать в среде CubeIDE проект STM32 штатным способом (CubeMX)

  2. Добавить файл sqrt_cmp.h в проект STM32 Пример 6

  3. Включить sqrt_cmp.h в файл main.c Пример 5

  4. Осуществить сборку и запуск программы штатными средствами IDE

  5. Меняя вид оптимизации ( -O0, -O3, -Os ) наблюдать результат

Пример 5: Исходный текст для STM32 (с пропусками < ... >) main.c

<  >/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#include "sqrt_cmp.h"/* USER CODE END Includes */<  >/**  * @brief  The application entry point.  * @retval int  */int main(void){<  >  /* Infinite loop */  /* USER CODE BEGIN WHILE */  main_Of_SqrtComp();  while (1)  {    /* USER CODE END WHILE */    /* USER CODE BEGIN 3 */  }  /* USER CODE END 3 */

Приложение 3. Порядок тестирования других алгоритмов и платформ

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

Для отличных от упомянутых выше аппаратных платформ (Таблица 3), вероятно, потребуется косметическая модификация файла sqrt_cmp.h.

Пример 6: Содержание файла sqrt_cmp.h

/****************************************************************************** * File: sqrt_cmp.h Created on 5 авг. 2020 г. * CC0 1.0 Universal (CC0 1.0) * Creative Commons Public Domain Dedication * No Copyright * * TAB Size .EQ 4 ********************************************************************************/#ifndef __SQRT_CMP_H#define __SQRT_CMP_H#include<math.h>#include<stdio.h>#include<stdint.h>#ifdef __cplusplusextern "C" {#endif/****************************************************************************** * Interface of the entry point for all sqrt tests ******************************************************************************/void main_Of_SqrtComp();/****************************************************************************** * test case selection: TEST_SET * select one of the test suite via a comment. ******************************************************************************/#define TEST_SETTEST_ALL//#define TEST_SETTEST_ROUNDING//#define TEST_SETTEST_PERFORMANCE/****************************************************************************** * Interfaces of test functions. * See implementation of them at the end of this file. ******************************************************************************/typedef uint16_t (*sqrt_func)( uint32_t number );uint16_t sqrt_fpu( uint32_t number );// floating point function from articleuint16_t sqrt_evn( uint32_t number );// integer function from articleuint16_t sqrxi32( uint32_t y );// integer function from comment byuint16_t sqrt_fps( uint32_t number );// optimized floating point function for Cortex M4// <-- insert interface of your function here/****************************************************************************** * Set to variable named as 'round_test_func' below * to the alias of one of the functions above. * The NULL will select last function in comp_list[] ******************************************************************************/sqrt_func round_test_func = sqrt_fps;// specific instance for the rounding test//sqrt_func round_test_func = sqrxi32;// specific instance for the rounding test//sqrt_func round_test_func = sqrt_evn;// specific instance for the rounding test//sqrt_func round_test_func = NULL;// last function in comp_list[]/****************************************************************************** * The array of test functions for competing routines is called comp_list[]. * Adding a new function to the test: - copy the implementation of the new function to the end of this file; - declare the function interface at the beginning of this file; - add the alias and declaration of the new function to end of array named comp_list[]. ******************************************************************************/// @formatter:offtypedef struct{sqrt_funcfsqrt;char *alias;} SCompFunc;SCompFunc comp_list[] =// competition list{{ sqrt_fpu, "_fpu" },{ sqrt_fps, "_fps" },{ sqrt_evn, "_evn" },{ sqrxi32,  "_i32" }// <-- insert your function name & alias here};/* @formatter:on *//****************************************************************************** * Platform-independent definitions ******************************************************************************/#define PUT_FORMAT_MSG(f_, ...) { \sprintf( (char *)s_buf, (char *)f_, ##__VA_ARGS__ ); \PUT_MSG( (char *)s_buf ); }#define MS_PER_SEC1000#define US_PER_SEC( MS_PER_SEC * MS_PER_SEC )#define ARRAY_SIZE(a) (sizeof a / sizeof *a)// size of static array at runtime#define SIRV(f) if ( f ) ;// suppress Ignore Return Value warning/****************************************************************************** * Platform-specific defines ******************************************************************************/#if defined( USE_HAL_DRIVER )// STM32 ARM Cortex platform#include<string.h>#include "main.h"//*****************************************************************************// Platform-specific defines for the helper functions#define SCALE_RATE1// must be .GE than 1#define X_CLOCKHAL_GetTick()#define X_DELAY( ms )HAL_Delay( ms )//*****************************************************************************// Platform-specific defines for the terminal output#define USART_HANDLEhuart1// set valid USART handler alias here defined by the config of MCU#define USART_TIMEOUT150// max timeout for HAL_UART_Transmitextern UART_HandleTypeDef USART_HANDLE;extern HAL_StatusTypeDef HAL_UART_Transmit ( UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout );#define PUT_MSG( msg ) \HAL_UART_Transmit( &USART_HANDLE, (uint8_t *)msg, strlen( (char *)msg ), USART_TIMEOUT )#define CPU_CLOCK_MHz( SystemCoreClock / US_PER_SEC )// CPU CLK in MHz#if defined( STM32F0 )#defineCPU_ID ( "STM32 ARM Cortex M0" )#elif defined ( STM32F3 )#defineCPU_ID ( "STM32 ARM Cortex M4" )#else#defineCPU_ID ( "Maybe STM32 ARM Cortex" )#endif#define PUT_SYS_INFOPUT_FORMAT_MSG( " %s @ "fdU()" MHz\n", CPU_ID, CPU_CLOCK_MHz )#else// #if defined( USE_HAL_DRIVER)#include <time.h>#include <stdlib.h>//*****************************************************************************// Platform-specific defines for the helper functions#define SCALE_RATE100// must be .GE than 1#define X_CLOCK(uint32_t) x_clock()#define X_DELAY( ms )x_delay( ms )uint32_t x_clock(){uint64_t result = (uint64_t) clock();result *= MS_PER_SEC;result /= CLOCKS_PER_SEC;return (uint32_t) result;}void x_delay( uint32_t ms ){uint64_t tm = x_clock();while ( ( x_clock() - tm ) < ms );}//*****************************************************************************// Platform-specific defines for the terminal output#define PUT_MSG( msg ) \printf( "%s", (char *)msg ), fflush ( stdout );#if defined( __unix__ )// anybody other platform for gcc#define PUT_SYS_INFOSIRV( system( "cat /proc/cpuinfo | grep 'model name' | head -1 | sed s/'model name\t:'/''/" ) )#else#define PUT_SYS_INFOPUT_MSG( "Undefined System & CPU" )#endif// #if defined( __unix__ )  // anybody other platform for gcc#endif// #if defined( USE_HAL_DRIVER)#if  ( __WORDSIZE == 64 )#define fdI(s)"%" #s "d"#define fdU(s)"%" #s "u"#define fdX(s)"%" #s "x"#else// let's say __WORDSIZE == 32#define fdI(s)"%" #s "ld"#define fdU(s)"%" #s "lu"#define fdX(s)"%" #s "lx"#endif// #if ( __WORDSIZE == 64 )#if defined ( DEBUG ) || defined ( _DEBUG ) // chk build mode of CubeIDE#defineBUILD_MODE"DEBUG"#else // Maybe Release#defineBUILD_MODE"RELEASE"#endif// #if defined ( DEBUG ) || defined ( _DEBUG )/****************************************************************************** * the helper data with testing ranges ******************************************************************************/// @formatter:offtypedef struct{uint32_tstart;uint32_tstop;uint32_trepeat;} STestRange;STestRangetest_rngs[] ={{ 0, 1000, 100 * SCALE_RATE },{ 0, 10000, 10 * SCALE_RATE },{ 0, 100000, 1 * SCALE_RATE }};uint32_t test_results[ARRAY_SIZE( test_rngs )][ARRAY_SIZE( comp_list ) + 1];#define MSG_BUFF_SIZE512uint8_t s_buf[MSG_BUFF_SIZE];// buffer for a terminal output/* @formatter:on *//****************************************************************************** * Test sets definitions. Do not change it. ******************************************************************************/#define TEST_ROUNDING1#define TEST_PERFORMANCE2#define TEST_ALL( TEST_ROUNDING | TEST_PERFORMANCE )#ifndef TEST_SET#defineTEST_SETTEST_ALL#endif#define HI_ROUND_TEST_RANGE_END0x007FFFFFUL#define HI_ROUND_TEST_RANGE_START( HI_ROUND_TEST_RANGE_END >> 4 )/****************************************************************************** * Interface of helper functions ******************************************************************************/void main_Header();void testRounding();void testPerformance();/****************************************************************************** * Implementation of the entry point for all sqrt tests ******************************************************************************/void main_Of_SqrtComp(){X_DELAY( MS_PER_SEC / 2 );// suppress the output of a previous instance// while the new instance is loading into the MCUuint32_t start_time = X_CLOCK;main_Header();// checking normal and extended ranges for roundingif ( TEST_SET & TEST_ROUNDING )testRounding();// checking normal ranges on execution timeif ( TEST_SET & TEST_PERFORMANCE )testPerformance();uint32_t test_time = X_CLOCK - start_time;uint32_t test_m = ( test_time / MS_PER_SEC ) / 60;uint32_t test_s = ( test_time / MS_PER_SEC ) % 60;uint32_t test_ms = test_time % MS_PER_SEC;PUT_FORMAT_MSG( "\ndone, spent time: "fdU()" m, "fdU()"."fdU()" s\n", test_m, test_s, test_ms );}/****************************************************************************** * Implementation of the helper functions ******************************************************************************/void main_Header(){PUT_MSG( "\n\n**********************************************************\n" );PUT_SYS_INFO;PUT_FORMAT_MSG( "*********** %s, built at %s\n", BUILD_MODE, __TIME__ );}void testPerformance(){uint32_t i_func, i_rpt, i_rng;uint32_t number, first, second, diff;uint64_t temp;PUT_MSG( "----------+ Performance test" );for ( i_rng = 0; i_rng < ARRAY_SIZE( test_rngs ); i_rng++ ){PUT_MSG( "\n" );PUT_FORMAT_MSG( "test range:["fdU()".."fdU()"], repeat="fdU()"\n", test_rngs[i_rng].start, test_rngs[i_rng].stop,test_rngs[i_rng].repeat );test_results[i_rng][0] = test_rngs[i_rng].stop;for ( i_func = 0; i_func < ARRAY_SIZE( comp_list ); i_func++ ){PUT_FORMAT_MSG( "%s ... ", comp_list[i_func].alias );first = X_CLOCK;for ( i_rpt = 0; i_rpt < test_rngs[i_rng].repeat; i_rpt++ )for ( number = test_rngs[i_rng].start; number < test_rngs[i_rng].stop; number++ )comp_list[i_func].fsqrt( number );second = X_CLOCK;diff = second - first;temp = ( test_rngs[i_rng].stop - test_rngs[i_rng].start ) * test_rngs[i_rng].repeat;test_results[i_rng][i_func + 1] = (uint32_t) ( temp / diff );if ( i_func < ARRAY_SIZE( comp_list ) - 1 )PUT_MSG( ", " );}}// small reportPUT_FORMAT_MSG( "\n----------+ Report: sqrt`s calls per ms\n%10s", "range" );for ( i_func = 0; i_func < ARRAY_SIZE( comp_list ); i_func++ )PUT_FORMAT_MSG( "%10s", comp_list[i_func].alias );for ( i_rng = 0; i_rng < ARRAY_SIZE( test_rngs ); i_rng++ ){PUT_MSG( "\n" );for ( i_func = 0; i_func < ARRAY_SIZE( comp_list ) + 1; i_func++ )PUT_FORMAT_MSG( fdU( 10 ), test_results[i_rng][i_func] );}PUT_FORMAT_MSG( "\n----------+\n%10s", "average" );for ( i_func = 0; i_func < ARRAY_SIZE( comp_list ); i_func++ ){temp = 0;for ( i_rng = 0; i_rng < ARRAY_SIZE( test_rngs ); i_rng++ )temp += test_results[i_rng][i_func + 1];temp /= ARRAY_SIZE( test_rngs );PUT_FORMAT_MSG( fdU( 10 ), (uint32_t)temp );}}void testRoundingFunction( uint32_t start, uint32_t finish, sqrt_func psqrt, char *fname );void testRounding(){uint16_t i_rng;uint16_t f_rng;PUT_MSG( "----------+ Rounding test\n" );// checking the existence for the test functionfor ( f_rng = 0; f_rng < ARRAY_SIZE( comp_list ); f_rng++ )if ( comp_list[f_rng].fsqrt == round_test_func )break;if ( !( f_rng < ARRAY_SIZE( comp_list ) ) ){f_rng = ARRAY_SIZE( comp_list ) - 1;PUT_FORMAT_MSG( "Value of 'round_test_func' not found.\n" );}PUT_FORMAT_MSG( "Function '%s' is tested for rounding.\n", comp_list[f_rng].alias );// checking standard rangesfor ( i_rng = 0; i_rng < ARRAY_SIZE( test_rngs ); i_rng++ )testRoundingFunction( test_rngs[i_rng].start, test_rngs[i_rng].stop, comp_list[f_rng].fsqrt, comp_list[f_rng].alias );// checking extended rangetestRoundingFunction( HI_ROUND_TEST_RANGE_START, HI_ROUND_TEST_RANGE_END, comp_list[f_rng].fsqrt, comp_list[f_rng].alias );}void turn_the_fan( uint32_t ms );void testRoundingFunction( uint32_t start, uint32_t finish, sqrt_func psqrt, char *fname ){uint32_t rf, ri;uint32_t n, c = 0;PUT_FORMAT_MSG( "test range:["fdU( 10 )".."fdU( 10 )"] ... ", start, finish );for ( n = start; n < finish; n++ ){rf = sqrt_fpu( n );ri = ( *psqrt )( n );if ( rf != ri ){if ( c++ > 3 ){PUT_FORMAT_MSG( "\b\n(!)too many mistakes in '%s', ", fname );break;}else{double d = sqrt( (double) n );PUT_FORMAT_MSG( "\b\n%s("fdU( 10 )")="fdU()" != "fdU(), fname, n, ri, rf );PUT_FORMAT_MSG( " (real value is %.6lf)", d );}}turn_the_fan( MS_PER_SEC );}if ( !c ){PUT_FORMAT_MSG( "\b done.\n" );}else{PUT_FORMAT_MSG( "test failed.\n" );}}void turn_the_fan( uint32_t ms ){static char ca[] = "|/-\\";static uint32_t cs = ARRAY_SIZE(ca) - 1;static uint32_t cn = 0;static uint32_t at = 0;uint32_t ct = X_CLOCK;if ( ct - at > ms ){at = ct;PUT_FORMAT_MSG( "\b%c", ca[cn++ % cs] );}}/****************************************************************************** * Implementation of the sqrt functions ******************************************************************************/// floating point arg & result with doubleuint16_t sqrt_fpu( uint32_t number ){if ( number < 2 )return (uint16_t) number;double f_rslt = sqrt( number );uint32_t rslt = (uint32_t) f_rslt;if ( !( f_rslt - (double) rslt < .5 ) )rslt++;return (uint16_t) rslt;}// floating point arg & result with floatuint16_t sqrt_fps( uint32_t number ){if ( number < 2 )return (uint16_t) number;float f_rslt = sqrtf( number );uint32_t rslt = (uint32_t) f_rslt;if ( !( f_rslt - (float) rslt < .5 ) )rslt++;return (uint16_t) rslt;}// unsigned integer arg & result// @formatter:offuint16_t sqrt_evn ( uint32_t number ){if ( number < 2 )return ( uint16_t ) number;uint32_t temp;uint32_t div;uint32_t rslt;if ( number & 0xFFFF0000L )if ( number & 0xFF000000L )if ( number & 0xF0000000L )if ( number & 0xE0000000L )div = 43771;elsediv = 22250;elseif ( number & 0x0C000000L )div = 11310;elsediv = 5749;elseif ( number & 0x00F00000L )if ( number & 0x00C00000L )div = 2923;elsediv = 1486;elseif ( number & 0x000C0000L )div = 755;elsediv = 384;elseif ( number & 0xFF00L )if ( number & 0xF000L )if ( number & 0xC000L )div = 195;elsediv = 99;elseif ( number & 0x0C00L )div = 50;elsediv = 25;elseif ( number & 0xF0L )if ( number & 0x80L )div = 13;elsediv = 7;elsediv = 3;rslt = number;while ( 1 ){temp = number / div;temp += div;div = temp >> 1;div += temp & 1;if ( rslt > div )rslt = div;else{if ( number / rslt == rslt - 1 && number % rslt == 0 )rslt--;return ( uint16_t ) rslt;}}}/* @formatter:on */// unsigned integer arg & resultuint16_t sqrxi32( uint32_t y ){if ( y == 1 )return 1;uint32_t xh = y > 0x10000ul ? 0x10000ul : y;uint32_t xl = 0;uint32_t xc;for ( int k = 0; k < 16; k++ ){xc = ( xh + xl ) >> 1ul;if ( xc * xc - xc >= y ){xh = xc;}else{xl = xc;}}return ( xh + xl ) >> 1ul;}// <-- insert implementation of your function sqrt here#ifdef __cplusplus}#endif#endif // __SQRT_CMP_H
Подробнее..

Как мы в СберМаркете боремся с товарами-призраками

14.01.2021 12:05:51 | Автор: admin
Так могла бы выглядеть наша команда, но мы на удаленкеТак могла бы выглядеть наша команда, но мы на удаленке

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

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

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

Рассказываем, как мы внедрили алгоритм автоматического отключения таких призраков и уменьшили долю ненайденных товаров на 25%.

Как работает СберМаркет

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

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

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

Идея алгоритма

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

Исторически 70% таких артикулов имеют очень высокий показатель ненайденных товаров за последние дни продаж более 50%. Значит, нужно срочно заблокировать позицию в каталоге, чтобы клиенты не смогли купить то, что мы не можем привезти.

Выше представлена динамика неуспешных сборок по товару из категории фрукты в одном магазине. С 21 июня по 1 июля показатель ненайденных товаров был 100%. Хотя ретейлер передавал нам другие данные.

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

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

И начали писать его на Python.

Как работает алгоритм

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

  2. Из базы данных наших заказов вытаскиваем историю последних сборок. Для каждого ненайденного товара берём историю THRESHOLD_ORDERS.

  3. Если в последних THRESHOLD_ORDERS заказах процент ненайденных товаров более THRESHOLD_CANCELLATION процентов, то эту позицию нужно заблокировать.

  4. Если товар ещё ни разу не блокировался или с момента последней блокировки уже прошло DAYS_NO_CANC дней, то товар блокируется на HIDE_1 дней.

  5. Если с момента последней блокировки товара прошло менее DAYSNOCANC дней, то:

    ~ товар блокируется на HIDE_2 дней, если текущая блокировка ставится второй раз подряд;
    ~ товар блокируется на HIDE_3 дней, если уже блокировался более двух раз подряд.

Пример параметров алгоритма для магазина:

'params': {  'DAYS_NO_CANC': 4,  'HIDE_1': 2,  'HIDE_2': 11,  'HIDE_3': 9,  'THRESHOLD_CANCELLATION': 0.4,  'THRESHOLD_ORDERS': 3  }

Параметры алгоритма подбирались отдельно для каждого магазина на исторических данных с использованием библиотеки hyperopt на Python. В процессе оптимизации максимизировалась метрика F-мера с beta 0.7.

Зачем так усложнять

Почему нельзя просто всегда блокировать товары на N дней? Зачем нам параметры DAYS_NO_CANC, HIDE_1, HIDE_2 и HIDE_3?

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

Кейс 1: товары нужно блокировать на маленький срок

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

Кейс 2: товары нужно блокировать на большой срок

У ретейлера B случился сбой в поставке яблочного сока. Возможно, новая партия доедет до магазина через 2-3 недели. Нет смысла блокировать товар на маленький срок, так как после такой блокировки клиенты все равно смогут заказать товар, но сборщик не сможет его собрать.

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

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

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

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

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

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

Чего вам не хватает в сервисах доставки продуктов или у нас? Делитесь в комментариях подумаем над решением.

Подробнее..

История очередного алгоритма или как одна зверюшка привела к созданию мира. Часть 1

01.02.2021 12:14:39 | Автор: admin

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

Ниже, просто интимная история про чувства, этапы, страхи, немного фантастики и муки выбора.

Давайте представим.

Вы придумали какой-то свой алгоритм для очередной сложной задачи.

Не важно в какой сфере, не важно на каких языках и технологиях.

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

Представили?

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

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

Было? Знакомо? Давно?

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

Энтузиазм увеличивается и без сна и отдыха воруя время у самого себя и любимых Творите! Творите и творите! Меняете инструменты и средства, выбираете наиболее подходящие технологии, пополняете поисковые запросы по словам неизвестных вам ранее решений. Оборачиваете красивыми классами и методами. Тестируете, опять на написанных "всё там же" исходных данных и снова тесты, проверки, тесты... Не получается Психуете удаляя в очередной раз все коммиты и бренчи. И начинаете заново И снова, и снова, и снова

Опять переписываете

Но эмоции от самоудовлетворения самим процессом и только вами видимого результата - зашкаливают.

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

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

И начинаются судорожные скитания в поисках пропитания для Вашего звереныша.

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

А она.. хрум, хрум - и съела.

Наступает полное торжество и обманчивое чувство осознания значимости созданной зверюшки! Вот она - Победа!Но, внутренний голос подсказывает, что зверюшка может, и самое главное ХОЧЕТ больше!

Начинаете искать еще больше пищи, больше данных. Находите. Готовите. Угощаете. И тут зверюшка, один раз Хрум и всё. Секунда, десять, минута а экран замер. Вы судорожно набираете htop и картина пугает. Всё улетело за 100%. И еще через несколько минут уже сами набираете kill, а потом еще и добиваете killall зверюшка, killall база, killall, killall, killall

И снова исправления, изменения, замены, удаления, все сначала, О! А можно вообще по другому и снова роль Творца и тесты, эксперименты, проверки

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

ПОЛУЧИЛОСЬ!

Корм съеден. Зверюшка, как ни в чем не бывало, радостно ждет следующую порцию.

Вы даете больше и больше. 100k, 1М, 10М, 40М порций. В один поток, два, десять, сто...

Хрум. Хрум. Хрум. Profit!

Back to the FutureBack to the Future

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

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

Вы клонируете мир ваших текущих забот и работ. Помещаете туда питомца.

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

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

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

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

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

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

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

Но однажды, наступает тот момент, когда приходит осознание, что ПОТОМ - начинает превращаться в НИКОГДА. И ты понимаешь, что НИКОГДА - еще страшнее, чем все другие страхи.

И после этой, открытой именно тебе, истины, ты делаешь шаг. Бросаешь всё и начинаешь Создавать.

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

Рано или поздно, муки выбора проходят и наконец вот она ТВОЯ СИСТЕМА, в которой будут созданы миры. Только твои!

Создаешь, соединяешь, закрываешь, разрешаешь, пробрасываешь, перекидываешь. Творишь...

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

Проходит время - и видишь красивейший кластер. Твой мир.

При заселении, ты уже знаешь, что один твой питомец не проживет. Ты

придумываешь новых и приглашаешь других из чужих безопасных миров.

http://personeltest.ru/aways/www.digitalspy.com/movies/a824289/fantastic-beasts-creature-design-animation/https://www.digitalspy.com/movies/a824289/fantastic-beasts-creature-design-animation/

Создаешь ландшафты и садишь деревьями, рисуешь облака и наполняешь морями и океанами.

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

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

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

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

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

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

Всё!

Пришло время открыться!

Проверка каждого уголка. И ты принимаешь решение пустить в него первых посетителей - других обитателей вселенной.

Запуск!

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

Всё отлично работает. Кто возмущается. Кому-то нравится. Кто-то подсказывает, где, что не так.

А кто-то тихо потешается над твоим водопадом, в котором вода течет не DESC, а ASC, рассказывая о этой находке всем кроме тебя.

И в целом - все хорошо. Ты расслабляешься продумывая дальнейшую жизнь.

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

Все планеты существуют в гармонии и мире. Созданы планеты позволяющие менять убранство других и пополнять хранилище.

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

Но мир создавал ты один. Один уже сделал то, про что остальные говорили - невозможно.

Значит пора делать очередной LevelUp. И снова начинать качать новые скилы.

http://personeltest.ru/aways/www.digitalspy.com/movies/a824289/fantastic-beasts-creature-design-animation/https://www.digitalspy.com/movies/a824289/fantastic-beasts-creature-design-animation/

И опять поиск. Опять прогулки в поиске даже не ответов, а только правильных вопросов. Куда? Кому? Как? Скольким или может быть одному? За сколько? И главное, снова - Зачем это все?

Опять муки выбора вариантов.

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

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

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

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

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

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

И еще много очень много других мучительных вариантов.

Выбор.

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

Из одной маленькой идеи каждый может создать новый мир.

С Уважением, Дмитрий.

Подробнее..

Категории

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

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru