http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&user=91.210.4.1&feedformat=atomВикиконспекты - Вклад участника [ru]2024-03-19T10:11:01ZВклад участникаMediaWiki 1.30.0http://neerc.ifmo.ru/wiki/index.php?title=%D0%A2%D1%8C%D1%8E%D1%80%D0%B8%D0%BD%D0%B3-%D0%BF%D0%BE%D0%BB%D0%BD%D0%BE%D1%82%D0%B0&diff=71078Тьюринг-полнота2019-04-18T16:18:15Z<p>91.210.4.1: </p>
<hr />
<div>==Введение==<br />
{{Определение<br />
|definition =<br />
Вычислительное устройство является '''Тьюринг-эквивалентным''' (англ. ''Turing-equivalent''), если оно может эмулировать [[Машина Тьюринга|машину Тьюринга]]. <br />
}}<br />
{{Определение<br />
|definition =<br />
Задача называется '''Тьюринг-полной''' (англ. ''Turing-complete''), если её можно решить, используя только машину Тьюринга или любую систему, являющуюся Тьюринг-эквивалентной. <br />
}}<br />
<br />
Зачастую Тьюринг-эквивалентные языки программирования называют Тьюринг-полными.<br />
<br />
В теории вычислимости ''исполнитель'' (множество вычисляющих элементов) называется '''Тьюринг-полным''', если на нём можно реализовать любую вычислимую функцию. Другими словами, для каждой вычислимой функции существует вычисляющий её элемент (например, машина Тьюринга) или программа для исполнителя, а все функции, вычисляемые множеством вычислителей, являются вычислимыми функциями (возможно, при некотором кодировании входных и выходных данных).<br />
<br />
Любой полный по Тьюрингу язык достаточно универсален, чтобы иметь возможность имитировать любой другой язык (хотя и с потенциальным замедлением в работе). Такие языки эквивалентны в рамках вычислений, которые могут произвести. Полные по Тьюрингу языки настолько распространены, что их можно обнаружить даже в примитивных на первый взгляд системах, например, [[Линейный клеточный автомат, эквивалентность МТ | клеточных автоматах]] или мозаичных системах.<br />
<br />
На практике полнота по Тьюрингу похожа на идеализацию. Компьютеры имеют ограниченное количество памяти, а неограниченная по времени их работа может быть прервана физическим воздействием (выключение, поломка), что как бы ограничивает число задач, которые они могут решить. На самом деле, физические ограничения в контексте Тьюринг-полноты не берутся во внимание: Тьюринг-полный исполнитель не должен быть ограничен по времени и памяти лишь самим исполнителем.<br />
<br />
==Критерии Тьюринг-полноты==<br />
<br />
Если на языке программирования можно реализовать машину Тьюринга, то такой язык Тьюринг-полон, и наоборот. Возможность реализации машины Тьюринга на конкретном языке программирования можно ''грубо'' описать как перечень требований, которым этот язык должен для этого удовлетворять:<br />
<br />
* Конечность (нет бесконечных символьных множеств и пр.).<br />
<br />
* Фиксированное описание (формальность<ref>[https://ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D1%8F%D0%B7%D1%8B%D0%BA Википедия — Формальный язык]</ref>).<br />
<br />
* Всегда достаточный объём доступной памяти — в идеале здесь имеется в виду неограниченная память, однако физические рамки не позволяют сделать память ЭВМ бесконечной, поэтому она просто должна быть ''"always big enough"''.<br />
<br />
* Неограниченность времени выполнения — любая программа в должна иметь возможность работать до тех пор, пока не завершится. <br />
<br />
* Возможность функциональной композиции (вызов одной функции из другой, рекурсия).<br />
<br />
* Наличие циклов <tex>{\bf while}</tex> с прерыванием или эквивалентных им конструкций.<br />
<br />
* Возможность останавливать выполнение (''halt'') или каким-то образом подавать сигнал о результатах выполнения.<br />
<br />
* Представление множества натуральных чисел, понятие нуля и следующего числа. Возможны другие подобные системы. <br />
<br />
* Поддержка входных и выходных данных (I/O), причём без формальных ограничений в объёме. Очевидно, что если любая программа, написанная на каком-то языке программирования, принимает на вход не более фиксированного n бит данных и возвращает не более n бит, этот язык не может быть Тьюринг-полным.<br />
<br />
==Тьюринг-полнота и неполнота некоторых языков программирования==<br />
<br />
Доказать Тьюринг-полноту языка программирования можно, предложив способ реализации машины Тьюринга на этом языке. Кроме того, можно предложить на нём интерпретатор Тьюринг-полного языка.<br />
<br />
===Assembly language===<br />
<br />
Язык Ассемблера достаточно примитивен относительно языков программирования высокого уровня: он рассчитан на архитектуру с конечной памятью и работает с конечным набором регистров. Однако, не был бы он полным по Тьюрингу, не были бы Тьюринг-полны и любые высокоуровневые языки программирования. <br />
<br />
Всё необходимое для машины Тьюринга на asm можно сделать примерно так:<br />
<br />
ADDS r0, r0, #1 <font color=green>; сдвиг ленты вправо</font><br />
ADDS r0, r0, #-1 <font color=green>; сдвиг ленты влево</font><br />
ADDS [r0], [r0], #1 <font color=green>; инкремент значения, на которое "указывает" головка ленты</font><br />
ADDS [r0], [r0], #-1 <font color=green>; декремент значения, на которое "указывает" головка ленты</font><br />
<br />
И далее использовать инструкцию <tex>\mathrm{BEQ}</tex> или ей подобную, чтобы выполнять определённую последовательность команд при определённом текущем значении, таким образом обеспечив ветвление.<br />
<br />
===Pascal===<br />
<br />
Язык Pascal позволяет смоделировать ленту машины Тьюринга с помощью двунаправленного списка из переменных, создаваемых оператором <tex>\mathrm{new}</tex>, семантика которого не предполагает отказа в создании переменной. Также с помощью списков<br />
можно смоделировать сколь угодно большие числа. Стандарт не накладывает никаких ограничений: указательный тип абстрактен, множество значений указательного типа языком не ограничено.<br />
В Паскале есть еще один тип данных с неограниченным множеством значений, файловый, также пригодный для моделирования ленты машины Тьюринга и представления больших чисел. Достаточно утверждений для очевидности Тьюринг-полноты языка Pascal.<br />
<br />
===C===<br />
<br />
В языке C нет высокоуровневого понятия переменной (в смысле Паскаля), есть объекты (object), хранящиеся в памяти как последовательно расположенные байты,имеющие адрес (байты в свою очередь состоят из неадресуемых битов). Целые типы ограничены (конечное множество значений), указатель отождествляется с адресом, постулируется возможность хранить адрес в целочисленной переменной (int или long — зависит от реализации), откуда следует ограниченность множества значений указателей, а стало быть, и ограниченность адресного пространства C-машины. То есть язык C, как и язык ассемблера, ориентирован на архитектуру с конечной памятью. Файл не является типом данных языка C, в отличие от Паскаля. Это вещь из окружения, для работы с которой есть операции над потоками в виде набора библиотечных функций. Тип fpos_t, принятый в стандарте C для позиционирования файлов, постулируется как «отличный от массива тип данных (object type)». Следовательно, множество значений этого типа конечно, а значит, максимальная длина файла в языке C ограничена сверху.<br />
<br />
===SQL===<br />
<br />
Сам по себе SQL никогда не считался полным по Тьюрингу языком. Однако, у него существует множество расширений, позволяющих делать рекурсивные запросы, циклы, списки, деревья и пр., например, с помощью PostgreSQL<ref>[http://assets.en.oreilly.com/1/event/27/High%20Performance%20SQL%20with%20PostgreSQL%20Presentation.pdf High Performance with PostgreSQL 8.4]</ref>. Более того, на в 2011 г. Habrahabr появилась статья, где показана машина Тьюринга на SQL<ref>[https://habrahabr.ru/post/113165/|Машина Тьюринга на чистом SQL]</ref> (в реализации Firebird 2.1, который ограничивает вложенность рекурсивных запросов до 2014 уровней). Тем не менее, всё ещё остаётся ограниченное query execution time.<br />
<br />
===HTML===<br />
<br />
HTML можно назвать языком программирования только в контексте формальной полемики. На деле он является языком гипертекстовой разметки и ни чем больше. Т. е. на HTML можно совершить только некоторую ограниченную совокупность действий, интерпретируемых браузером, однако никто не запрещает сделать язык, идентичный по синтаксису с HTML, но интерпретируемый совершенно по другому таким образом, чтобы он был полным по Тьюрингу.<br />
<br />
===Некоторые другие ЯП===<br />
<br />
{| style="background-color:#CCC;margin:0.5px"<br />
!style="background-color:#EEE;padding:2px 8px"| '''Название языка'''<br />
!style="background-color:#EEE;padding:2px 8px"| '''Год изобретения'''<br />
!style="background-color:#EEE;padding:2px 8px"| '''Парадигма'''<br />
!style="background-color:#EEE;padding:2px 8px"| '''Уровень'''<br />
!style="background-color:#EEE;padding:2px 8px"| '''Зависимость от архитектуры процессора'''<br />
!style="background-color:#EEE;padding:2px 8px"| '''Полнота по Тьюрингу'''<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| C<br />
|style="background-color:#FFF;padding:2px 8px"| 1972<br />
|style="background-color:#FFF;padding:2px 8px"| Процедурный<br />
|style="background-color:#FFF;padding:2px 8px"| Низкий<br />
|style="background-color:#FFF;padding:2px 8px"| зав. от ISO<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| C++<br />
|style="background-color:#FFF;padding:2px 8px"| 1983<br />
|style="background-color:#FFF;padding:2px 8px"| Мультипарадигменный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий/Низкий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| Язык Ассемблера<br />
|style="background-color:#FFF;padding:2px 8px"| 1950<br />
|style="background-color:#FFF;padding:2px 8px"| Полнофункциональный<br />
|style="background-color:#FFF;padding:2px 8px"| Низкий<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| SQL<br />
|style="background-color:#FFF;padding:2px 8px"| 1989<br />
|style="background-color:#FFF;padding:2px 8px"| Декларативный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| Haskell<br />
|style="background-color:#FFF;padding:2px 8px"| 1990<br />
|style="background-color:#FFF;padding:2px 8px"| Функциональный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| HTML<br />
|style="background-color:#FFF;padding:2px 8px"| 1986<br />
|style="background-color:#FFF;padding:2px 8px"| Декларативный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| CSS<br />
|style="background-color:#FFF;padding:2px 8px"| 1996<br />
|style="background-color:#FFF;padding:2px 8px"| Декларативный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| Java<br />
|style="background-color:#FFF;padding:2px 8px"| 1995<br />
|style="background-color:#FFF;padding:2px 8px"| Объектно-ориентированный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| JavaScript<br />
|style="background-color:#FFF;padding:2px 8px"| 1965<br />
|style="background-color:#FFF;padding:2px 8px"| Объектно-ориентированный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| Python<br />
|style="background-color:#FFF;padding:2px 8px"| 1991<br />
|style="background-color:#FFF;padding:2px 8px"| Объектно-ориентированный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| XML<br />
|style="background-color:#FFF;padding:2px 8px"| 1998<br />
|style="background-color:#FFF;padding:2px 8px"| Декларативный<br />
|style="background-color:#FFF;padding:2px 8px"| Высокий<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|style="background-color:#FFF;padding:2px 8px"| Нет<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| Brainfuck<br />
|style="background-color:#FFF;padding:2px 8px"| 1993<br />
|style="background-color:#FFF;padding:2px 8px"| Эзотерический<br />
|style="background-color:#FFF;padding:2px 8px"| Низкий<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|-<br />
|style="background-color:#FFF;padding:2px 8px"| Whitespace<br />
|style="background-color:#FFF;padding:2px 8px"| 2003<br />
|style="background-color:#FFF;padding:2px 8px"| Эзотерический<br />
|style="background-color:#FFF;padding:2px 8px"| Низкий<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
|style="background-color:#FFF;padding:2px 8px"| Да<br />
<br />
|}<br />
<br />
==Интересные случаи полноты по Тьюрингу==<br />
<br />
===Шаблоны C++===<br />
<br />
Шаблоны C++ позволяют производить сложные вычисления ещё на стадии компиляции программы. Впервые это было продемонстрировано Эрвином Унрухом, который реализовал рекурсивный алгоритм распознавания простых чисел в процессе компиляции. Позже в статье Университета Индиана было продемонстрировано кодирование машины Тьюринга в шаблонах C++<ref>[http://web.archive.org/web/20131101122512/http://ubietylab.net/ubigraph/content/Papers/pdf/CppTuring.pdf C++ Templates are Turing-complete]</ref>.<br />
<br />
===Java Generics===<br />
<br />
Аналогично C++ Templates, Generics, несмотря на свои отличия, тоже оказались полными по Тьюрингу, что было подтверждено Раду Григор в одной из статей Кентского Университета<ref>[http://arxiv.org/pdf/1605.05274.pdf Java Generics are Turing-complete]</ref>.<br />
<br />
===URISC===<br />
<br />
'''URISC''' (от англ. ''Ultimate RISC'') — предельный случай процессора типа RISC (буквально: компьютер с предельно сокращённым набором инструкций), который умеет выполнять одну-единственную инструкцию. Обычно это «вычесть и пропустить следующую инструкцию, если вычитаемое было больше уменьшаемого» (англ. ''«reverse-subtract and skip if borrow»''). Аналогичная концепция, основанная именно на «вычесть и перейти, если результат не положительный» (англ. ''«subtract and branch unless positive»''), называется '''SUBLEQ'''.<br />
<br />
'''URISC''' также известен в современной литературе как '''OISC''' (англ. One Instruction Set Computer) и является полным по Тьюрингу.<br />
<br />
===mov===<br />
<br />
Утилита M/o/Vfuscator превращает любую программу на языке C в огромную последовательность из инструкций <tex>\mathrm {mov}</tex><ref>[https://github.com/xoreaxeaxeax/movfuscator M/o/Vfuscator]</ref>. <br />
<br />
{| cellpadding="0" style= align="left";<br />
|[[Файл:gcc_asm.png|400px|thumb|left|Результат работы GCC]]<br />
|[[Файл:mov_asm.png|400px|thumb|left|Результат работы M/o/Vfuscator]]<br />
|}<br />
<br />
{| cellpadding="0" style= align="left";<br />
|[[Файл:Demo_mov.gif|thumb|500px|left|Простые числа с использованием одной инструкции]]<br />
|}<br />
<br />
===HTML5 + CSS3===<br />
<br />
Нововведения новых версий HTML/CSS позволяют построить<ref>[http://eli.fox-epste.in/rule110-full.html Rule 110]</ref> [https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%BE_110 правило 110], которое является Тьюринг-полным.<br />
<br />
===Excel===<br />
<br />
Excel имеет свой скриптовый язык. Однако, для кодирования Машины Тьюринга в таблице Excel достаточно использование только формул<ref>[http://www.felienne.com/archives/2974 Excel Turing Machine]</ref>.<br />
<br />
===Тьюринг-полнота в играх===<br />
<br />
* Minecraft<ref>[https://www.youtube.com/watch?v=7sNge0Ywz-M|Demonstrating the CPU. Then coughing a lot. Sorry about that. I may be dying.]</ref>. Возможно, достижение Тьюринг-полноты в игре-песочнице с возможностью создания логических элементов было неизбежным, но сложность компьютеров, собираемых из блоков в данной игре, достойна внимания.<br />
<br />
* Little Big Planet<ref>[https://www.youtube.com/watch?v=13GOFa1C4e4| LittleBigLife — The Game of Life in LittleBigPlanet]</ref>. Некоторые элементы игры представляют из себя ничто иное, как небольшие клеточные автоматы, которые можно запрограммировать.<br />
<br />
* Super Mario World<ref>[https://www.youtube.com/watch?v=hB6eY73sLV0 SNES Code Injection -- Flappy Bird in SMW]</ref>. Умелое обращение с багами игры позволяет сделать в ней буквально всё, что позволяет 2d-пространство Nintendo.<br />
<br />
* [[Неразрешимость игры Braid|Braid]].<br />
<br />
==Тьюринговская трясина==<br />
<br />
'''Тьюринговская трясина''' — жаргонное общее название для языков программирования, которые ''Тьюринг-полны'', но обладают крайне примитивными синтаксисом и семантикой. Они неудобны для практического программирования (из-за трудности написания программ и низкой производительности), зато хорошо подходят для некоторых других задач (доказательство невычислимости некоторых функций, иллюстрация базовых принципов программирования и т. д.). Поэтому они интересны для информатики.<br />
<br />
Первыми представителями &laquo;трясины&raquo; были ''лямбда-исчисление'', ''комбинаторная логика'' и сама машина Тьюринга. <br />
<br />
Многие эзотерические языки программирования также являются «трясинами Тьюринга» (напр. Brainfuck, Spoon, Malbolge, Whitespace).<br />
<br />
==Проблема остановки==<br />
{{Определение<br />
|definition= Проблема остановки {{---}} определить, остановится ли данная машина Тьюринга на данных входных данных когда-либо или нет.<br />
}}<br />
{{Теорема<br />
|statement= Проблема остановки неразрешима<br />
|proof= Докажем от противного. Предположим существует такая полностью вычислимая функция <tex>halts(f)</tex>, которая возвращает <tex>true</tex>, если функция <tex>f</tex> остановится когда-либо, и <tex>false</tex>, если функция <tex>f</tex> никогда не остановится.<br />
<br />
Рассмотрим следующую функцию <tex>g</tex><br />
<br />
'''void''' g():<br />
'''if''' halts(g):<br />
'''for'''(;;)<br />
<tex>halts(g)</tex> должна возвращать либо <tex>true</tex>, либо <tex>false</tex>.<br />
* Если <tex>halts(g)</tex> вернула <tex>true</tex>, то <tex>g</tex> никогда не остановится, получили противоречие<br />
* Если <tex>halts(g)</tex> вернула <tex>false</tex>, то <tex>g</tex> остановится, получили противоречие<br />
}}<br />
<br />
==Теорема Геделя о неполноте==<br />
Чтобы доказать теорему, можно воспользоваться проблемой остановки машины Тьюринга.<br />
{{Теорема<br />
|statement= Любая непротиворечивая формальная система аксиом <tex>T</tex>, способная выражать утверждения о натуральных числах и доказывать простые арифметические факты, неполна {{---}} существуют утверждения о натуральных числах, которые она не может ни доказать, ни опровергнуть.<br />
|proof= <br />
# Предположим, что система <tex>T</tex> еще и корректна (доказывает только истинные условия).<br />
# Предположим, что система <tex>T</tex> полна, т.е. доказывает или опровергает любое утверждение.<br />
# Сформулируем и запишем на языке арифметики утверждение <tex>O</tex> = "машина Тьюринга <tex>M</tex> точно остановится, если запустить ее с данными <tex>D</tex>".<br />
# Переберем все доказательства (<tex>P</tex> {{---}} истинно) и опровержения (<tex>\neg P</tex> {{---}} истинно) в системе <tex>T</tex>, чья длина совпадает с длиной <tex>O</tex>.<br />
# Так как система <tex>T</tex> полна, рано или поздно мы найдем опровержение или доказательство утверждения <tex>O</tex><br />
# Поскольку система <tex>T</tex> доказывает только истинные факты, мы фактически решили проблему остановки.<br />
Это очень простой способ доказательства теоремы Геделя о неполноте, но при этом он требует корректности <tex>T</tex> (тем не менее обычные системы аксиом арифметики всегда корректны).<br />
"Перекроим" доказательство, используя только непротиворечивость: если доказываемое утверждение оказалось ложным, мы все ещё решили проблему остановки.<br />
}}<br />
<br />
==См. также==<br />
<br />
* [[Машина Тьюринга]]<br />
<br />
* [[Лямбда-исчисление]]<br />
<br />
* [[Игра «Жизнь»]]<br />
<br />
* [[Busy beaver]]<br />
<br />
==Примечания==<br />
<br />
<references /><br />
<br />
==Источники информации==<br />
<br />
* [http://wiki.c2.com/?TuringComplete Cunningham & Cunningham, Inc. — Turing Complete]<br />
<br />
* [http://softwareengineering.stackexchange.com/questions/132385/what-makes-a-language-turing-complete Stackexchange — What makes a language Turing-complete?]<br />
<br />
* [http://ru.wikipedia.org/wiki/%D0%A2%D1%8C%D1%8E%D1%80%D0%B8%D0%BD%D0%B3%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D1%82%D1%80%D1%8F%D1%81%D0%B8%D0%BD%D0%B0 Википедия — Тьюринговская трясина]<br />
<br />
* [http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%BB%D0%BD%D0%BE%D1%82%D0%B0_%D0%BF%D0%BE_%D0%A2%D1%8C%D1%8E%D1%80%D0%B8%D0%BD%D0%B3%D1%83 Википедия — Полнота по Тьюрингу]<br />
<br />
* [http://cmcmsu.no-ip.info/download/alg.lang.completeness.pdf А. А. Вылиток. Об алгоритмической полноте некоторых языков программирования]<br />
<br />
* [http://ethclassic.ru/2016/10/21/turing-completeness-reality/ «Умные контракты»: полнота по Тьюрингу и реальность]<br />
<br />
* [http://habrahabr.ru/post/231897/ Хабрахабр — Является ли HTML языком программирования?]<br />
<br />
* [http://beza1e1.tuxen.de/articles/accidentally_turing_complete.html Andreas Zwinkau — Accidentially Turing-complete]<br />
<br />
* [https://ru.wikipedia.org/wiki/URISC Википедия — URISC]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Теория вычислимости]]<br />
[[Категория: Вычислительные формализмы]]<br />
[[Категория: Машина Тьюринга]]</div>91.210.4.1http://neerc.ifmo.ru/wiki/index.php?title=C%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0_%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_O(1)_%D0%B4%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8&diff=60100Cортировка слиянием с использованием O(1) дополнительной памяти2017-01-19T09:13:05Z<p>91.210.4.1: </p>
<hr />
<div>{{boring}}<br />
В конспекте содержится несколько ошибок, которые никто не исправлял уже лет 5, половины доказательств нету и вообще это стоит переписать. Если хотите заимплементить корректный алгоритм, придется дополнительно подумать головой. Алсо, про "сравнение в реальных условиях" - бред, по времени этот алгоритм в несколько раз медленнее std::sort. ~Yurik~<br />
<br />
На вход алгоритм получает массив, который состоит из двух отсортированных частей. Нам необходимо за <tex>O(1)</tex> дополнительной памяти и <tex>O(n)</tex> времени получить отсортированный массив.<br><br />
В реализации алгоритм весьма громоздкий. Но сравнение в реальных условиях реализации на C++ (на массивах длиной до <tex>10^8</tex>) показало, что по числу сравнений алгоритм выигрывает у qsort примерно 10%, а по общему времени работы – от <tex>1.2</tex> до <tex>1.3</tex> раз.<br />
<br />
== Алгоритм ==<br />
У нас есть массив, который состоит из двух отсортированных частей:<br />
<br />
[[Файл:Merge_O(1)_1.png|525px]]<br />
<br />
Разобьем наш массив на <tex>cnt</tex> подряд идущих блоков длиной <tex>len = \lfloor \sqrt{n} \rfloor </tex>. Остаток трогать не будем. <br />
<br />
[[Файл:Merge_O(1)_2.png|525px]]<br />
<br />
Найдем блок, содержащий конец первой отсортированной части. Поменяем его с последним блоком. В дальнейшем будем использовать его как буфер обмена. <br />
<br />
[[Файл:Merge_O(1)_3.png|525px]]<br />
<br />
Отсортируем блоки по возрастанию по первому элементу (если первые элементы равны, тогда по последнему). <br />
<br />
[[Файл:Merge_O(1)_4.png|525px]]<br />
<br />
Для этого подойдет любая квадратичная или более быстрая сортировка, которая требует <tex> O (1) </tex> дополнительной памяти. Для сохранения линейной асимптотики надо использовать алгоритм, линейный по числу обменов, т.е. подходит [[Сортировка выбором|сортировка выбором]]. Так как блоков <tex> \sqrt{n} </tex>, то количество операций на этом шаге <tex> O(n) </tex>.<br />
<br />
Следует заметить, что, после сортировки этих блоков, элементы, которые стоят левее заданного и больше его, находились в противоположной части отсортированного массива, также они находятся в пределах одной группы, поэтому количество инверсий для каждого элемента не больше <tex>\sqrt{n}</tex>.<br />
<br />
Пользуясь буфером обмена, последовательно сольем пары соседних блоков <tex>([0, ~ len - 1]</tex> и <tex>[len, ~ 2 ~ len - 1],</tex> потом <tex>[len, ~ 2 ~ len - 1]</tex> и <tex>[2 ~ len, ~ 3 ~ len - 1],</tex> и т.д.<tex>)</tex>.<br />
<br />
Попытаемся слить первый и второй блок. Поменяем местами первый блок с буфером обмена. И, как в обычном слиянии, пользуясь двумя указателями, сливаем второй блок и только что измененный буфер. Результат начинаем записывать с начала первого блока. Чтобы не потерять данные, вместо записи используем обмен элементов. Так как блоки имеют одинаковую длину и между указателем на второй блок и указателем на запись расстояние равно длине блока, то слияние произойдет корректно (пример использования буфера обмена приведен ниже).<br />
<br />
[[Файл:Merge_O(1)_5.png|525px]]<br />
<br />
Так как после предыдущего шага количество инверсий для каждого элемента не больше <tex>\sqrt{n}</tex>, то ему надо сдвинуться влево не больше, чем на <tex>\sqrt{n}</tex> элементов, поэтому в результате мы получим, что первые <tex>len \cdot (cnt - 1)</tex> элементов исходного массива отсортированы. Количество блоков <tex> \sqrt{n} </tex> и каждое слияние работает за <tex> О O(\sqrt{n}) </tex> , поэтому количество операций на этом шаге <tex> O(n) </tex>.<br />
<br />
<tex>S</tex> {{---}} размер остатка вместе с буфером. Используя квадратичную или более быструю сортировку, которая требует <tex> O(1) </tex> дополнительной памяти, отсортируем подмассив длиной <tex> 2S </tex>, который находится в конце.<br />
<br />
Так как <tex>S < 2 \sqrt{n}</tex>, то сортировка пройдет за <tex>O(n)</tex>.<br />
<br />
[[Файл:Merge_O(1)_6.png|525px]]<br />
<br />
Теперь на последних <tex> S </tex> местах будут находиться <tex> S </tex> максимальных элементов. Оставшаяся часть представляет собой массив, содержащий две отсортированные части, причем размер второй равен <tex> S </tex>. По аналогии с тем что делали раньше, только в обратную сторону, отсортируем оставшуюся часть, разделив ее на блоки длиной <tex>S</tex>, используя последние <tex>S</tex> как буфер обмена. Не забудем после отсортировать буфер обмена.<br />
<br />
[[Файл:Merge_O(1)_7.png|525px]]<br />
<br />
В результате мы получили отсортированный исходный массив.<br />
<br />
== Пример использования буфера обмена ==<br />
<br />
[[Файл:Merge_O(1)_buffer.png|355px]]<br />
<br />
== Источники информации ==<br />
*[http://habrahabr.ru/post/138146/ Habrahabr {{---}}Сортировка слиянием без использования дополнительной памяти ]<br />
*[http://e-maxx.ru/bookz/files/knuth_3.djvu Д.Е.Кнут {{---}} Искусство программирования (том 3) упр 18 к разделу 5.2.4]<br />
*[http://pastebin.com/hN2SnEfP PASTEBIN {{---}} Реализация алгоритма на JAVA]<br />
<br />
[[Категория: Дискретная математика и алгоритмы]]<br />
[[Категория: Сортировки]]</div>91.210.4.1