компьютерное, сложность
Dec. 18th, 2006 11:13 pmТеоретический анализ сложности программ и алгоритмов сам по себе вполне самодостаточен с математической точки зрения. То, что такой-то алгоритм работает такое-то время, например O(n) или O(n2) (т.е. асимптотически пропорционально количеству входных данных или пропорционально квадрату количества), является строгим математическим фактом.
Однако практическое применение этих теоретических знаний опирается, по-моему, на определенные допущения, которые далеко не столь математически точны, как теоретические факты. Под "практическим применением" я имею в виду то, что мы, например, склонны предпочесть алгоритм O(n) эквивалентному алгоритму O(n2) - конечно, это простейший случай, и на практике все бывает намного сложнее, плюс есть вопрос затрат на память, а не только затрат времени, но суть ясна. Есть математические факты, и есть их интерпретации в виде предпочтения тех или иных алгоритмов, попыток улучшить, прагматичных советов и указаний, итд. итп.
Эти предпочтения, советы итд. опираются на некоторые неявные допущения. Допущения эти, во-первых, иногда неверны, а во-вторых, что более важно, нет (по-моему) удовлетворительного объяснения тому, что обычно они верны. Отсутствие такого объяснения - своего рода дыра в теоретическом фасаде анализа программ и алгоритмов.
Вот два примера.
Во-первых, мы обычно пользуемся оценками для худшего случая (worst-case complexity). Если разные значения входных данных приводят к разным затратам времени (или памяти) алгоритмом, мы выбираем худшие результаты (для данного фиксированного количества входных данных), и их используем для оценки функции затрат в общем случае. Однако худший случай и общий случай - весьма разные вещи. На практике может оказаться, что худший случай случается достаточно редко, а в типичном случае алгоритм ведет себя намного лучше, чем утверждает наша пессимистичная оценка.
Известный пример - алгоритм quicksort. Он выполняется в худшем случае за время O(n2), а в среднем или типичном случае - за O(nlogn). Есть алгоритмы сортировки, которые всегда выполняются за O(nlogn), и в худшем случае тоже, т.е. теоретически они как бы лучше, чем quicksort, но на практике часто оказывается, что quicksort реально быстрее.
Но интересно не то, что есть пример того, как теоретическая основа приводит к неверной интерпретации. Интересно то, что это происходит так редко! Quicksort - известное исключение, но таких исключений мало; обычно сложность худшего случая хорошо отражает "практическую" пользу от алгоритма. Почему? Это не обязано априори быть так. Априори я не вижу причин, почему в огромном количестве самых обычных алгоритмов не может быть небольшого количества очень плохих случаев, как в quicksort, которые бы "искажали статистику". Но на практике это обычно не так, и мы пользуемся этим в качестве неявного допущения. Есть ли объяснение этому?
Кстати, почему мы вообще обычно пользуемся оценкой сложности по худшему случаю? Как это сложилось? Есть ли в этом выборе какой-то культурный смысл, может ли он нам сказать что-то о культуре поведения и мышления, создавшей цифровую цивилизацию? Могло ли другое общество изначально пользоваться намного более часто другими видами оценок? (конечно, я знаю, что и мы часто пользуемся другими видами оценок, например среднего случая, но все же оценка по худшему варианту преобладает).
Второй пример - само понятие асимптотической сложности. Не вполне ясно, почему оно вообще имеет какое-то отношение к окружающей нас действительно, где, грубо говоря, размеры всех входных данных и результатов ограничены на практике какой-то константой, скажем, 1035. Математика говорит нам, что асимптотическая оценка, вообще говоря, ничего не значит в случае "небольших" значений, где "небольшое" значит "меньшее сколь угодно большой заранее зафиксированной постоянной". Асимптотика проявляется только в стремлении к бесконечности. Почему она оказывается полезной в реальном мире?
Можно сформулировать этот вопрос более конкретно. Скажем, есть два алгоритма, один O(2n), экспоненциальный, другой O(n), линейный. Мы "знаем", что первый намного намного хуже второго. Но конечно же может быть так, что при первом стоит очень очень очень малый множитель, а при втором - очень очень очень большой, так что на практике, для всех размеров данных, которыми мы можем оперировать в реальном мире, первый будет намного намного лучше второго.
Но этого не происходит, за редкими исключениями. Почему? Есть ли у нас удовлетворительный ответ на этот вопрос?
no subject
Date: 2006-12-18 09:23 pm (UTC)Совершенно формальные способы обосновывать и то, и другое.
Только этими вещами программисты не пользуются, потому что там математика чуть-чуть бывает нетривиальная.
no subject
Date: 2006-12-18 09:36 pm (UTC)no subject
Date: 2006-12-18 09:39 pm (UTC)no subject
Date: 2006-12-18 09:44 pm (UTC)no subject
Date: 2006-12-18 09:45 pm (UTC)no subject
Date: 2006-12-18 09:53 pm (UTC)no subject
Date: 2006-12-18 10:04 pm (UTC)Почему исключений среди алгоритмов мало, хммм. Ну мне единственное что в голову приходит, это родственный вопрос -каких последовательностей "больше" - сходящихся равномерно, или же неравномерно ? Я, правда, и на него не отвечу, прочно забыто.
По-второму я бы снова переформулировал, да запутался в формулировках, уф. Здоровенных бессмысленных чисел быть не должно, я лично в это верю, скажем так. [Что никак не отрицает наличия асимптотически лучших алгоритмов с практически худшими свойствами на реальных диапазонах входных данных - такого-то хватало.]
no subject
Date: 2006-12-18 10:04 pm (UTC)По поводу квадратичного алгоритма с малым фактором и линейного с большим - довольно нелегко представить себе 2 таких алгоритма, делающих одно и то же, с фактором, скажем, 1000, не говоря уже о 1000000. Соответственно, в задачах, где размер исходных не сильно ограничен, квадратичный будет хуже.
no subject
Date: 2006-12-18 10:06 pm (UTC)Здоровенных осмысленных конечно, запуталсо окончательно.
no subject
Date: 2006-12-18 10:08 pm (UTC)Забиваясь на худший враиант, мы обеспечиваем некий гарантированный "запас прочности". Т.е. наш алгоритм точно будет работать не медленнее некоей определённой величины и в этом мы можем быть уверены на 100%. А если мы в критичном куске кода забьёмся на статистику, говорящую о том, что "с вероятностью 99% на практике всё работает быстро, но есть 1% случаев, когда будет медленно", мы лишаем себя запаса прочности. Т.е. если в случае понижения производительности у нас возникает исключительная ситуация, то мы обязаны забиваться на худший расклад. Это является следствием стремления человека делать всё с запасом.
Шоб ничё, если чё.
Это если говорить о бытовом уровне понимания.
Т.е. это простейшая логика, которая объясняет такую традицию в оценке.
Или Вы не об этом?
no subject
Date: 2006-12-18 10:10 pm (UTC)Во-первых, что касается QuickSort, обычно используется его вариант, когда разбиение ведётся по случайному элементу. Это -- вероятностный алгоритм, имеющий сложность именно O(n log n). Далее, есть вариант QuickSort'а, который будет работать за O(n log n) в худшем случае. Но им никто не пользуется, так как он становится куда медленнее любого вменяемого алгоритма.
Во-вторых, рассмотрим, например, алгоритмы умножения длинных чисел. Умножение «столбиком» работает за O(n²), но именно его стоит использовать в случае относительно небольших чисел. Немного более сложный алгоритм работает за время, если я не ошибаюсь, O(n^(log 4/log 3)). Наконец, наилучший известный на сей день алгоритм, основанный на дискретном преобразовании Фурье, работает за O(n log(n) log(log(n))). Но им пользуются только когда приходится осуществлять вычислений с совсем длинными числами, скажем от сотни тысяч знаков, а такая надобность всё-таки встречается нечасто. :)
Наконец, упомяну ещё тему. Некоторые используемые алгоритмы выдают результат с некоторой вероятностью ошибки. При этом, за счёт повторения эта вероятность может быть сколь угодно уменьшена. Таковы, например, широко используемые алгоритмы определения простоты числа. В данный момент большая работа ведётся по дерандомизации (устранению случайности) в данных алгоритмах. В прошлом году, например, был наконец найден детерменированный полиномиальный (от длины числа) алгоритм для проверки его на простоту. Тем не менее, эти усилия имеют, главным образом, теоретическую ценность, так как сложность вычислений в таких алгоритмах значительно выше.
no subject
Date: 2006-12-18 10:18 pm (UTC)no subject
Date: 2006-12-18 10:22 pm (UTC)no subject
Date: 2006-12-18 10:33 pm (UTC)Наверное, всё-таки множитель.
no subject
Date: 2006-12-18 10:41 pm (UTC)Ссылку не дадите?
no subject
Date: 2006-12-18 10:41 pm (UTC)Пусть есть два алгоритма - A1 с O(n), и A2 с O(n^2), работающий на БД.
1)Пусть в БД 100 записей. Пусть A1 работает 10 секунд, А2 - 2 секунды. Вроде бы, лучше А2.
(условно, у первого t=n*0.1, у второго 2*(n/100)^2
2)В БД стало 200 записей. A1 работает 20 секунд, А2 - 8 секунд. Вроде бы, лучше А2.
3)В БД стало 300 записей. A1 работает 30 секунд, А2 - 18 секунд. Вроде бы, лучше А2.
4)В БД стало 10 000 записей. A1 работает 1000 секунд, А2 - 20 000 секунд. Вроде бы, лучше А2.
4)В БД стало 100 000 записей. A1 работает 10000 секунд, А2 - 2 миллиона секунд. Вроде бы, лучше
А2.
И это ведь не зависит от фактора. А уж если брать не n^2 а 2^n, то можно быть уверенным: чуть станет больше данных, как он "свалится". Так что в данном случае теория очень практична:)
no subject
Date: 2006-12-18 10:42 pm (UTC)В прошлом году?!
no subject
Date: 2006-12-18 10:52 pm (UTC)no subject
Date: 2006-12-18 10:56 pm (UTC)no subject
Date: 2006-12-18 10:58 pm (UTC)В вики (http://en.wikipedia.org/wiki/Primality_test#Fast_deterministic_tests) написано буквально в двух словах. Насколько я понимаю, метод получен путём запуска какого-то из стандартных вероятностных тестов (вероятно, Соловэя-Штрассена) на полиномиальном количестве вариантов входных данных. Соответсвующее множество входов получается путём достаточно нетривиальных трюков.
no subject
Date: 2006-12-18 11:00 pm (UTC)no subject
Date: 2006-12-18 11:02 pm (UTC)no subject
Date: 2006-12-18 11:03 pm (UTC)А вот вполне обычный пример http://en.wikipedia.org/wiki/String_search , правда, без памяти.
no subject
Date: 2006-12-18 11:27 pm (UTC)И сложность действительно можно мерять по-разному. Например, как матожидание от сложности для каждого конкретного случая, и тогда для quicksort получится всё то же O(n log n). Но с худшим случаем тут получилась особая история: он слишком вероятный и показательный - и, что ещё более важно, недетектируемый за меньшие затраты чем сам алгоритм. Поэтому - спецмеры. А если бы детектирование того, что пошли куда-то не туда, занимало бы время значительно меньшее чем сама сортировка - был бы применён какой-то другой метод. Хотя это и так, похоже, возможно: если при разделении подотрезка получились слишком неравные длины - сменить медианное значение или применить другой метод разделения (например, ван Эмдена).
no subject
Date: 2006-12-18 11:28 pm (UTC)