Как вы показываете, что один алгоритм более эффективен, чем другой алгоритм? - PullRequest
8 голосов
/ 08 января 2010

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

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

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

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

Заранее спасибо, Андреас

Ответы [ 11 ]

16 голосов
/ 08 января 2010

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

В качестве примера алгоритмы сортировки пузырьковой сортировки и кучи сортировки имеют сложность O (n 2 ) и O (n log n) соответственно.

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

6 голосов
/ 08 января 2010

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

В терминах стандартных определений эффективности часто используют обозначение Big-0 , однако в «реальном мире» вне академических кругов обычно можно профилировать / сравнивать оба уравнения, а затем сравнивать результаты

Часто сложно сделать общие предположения о нотации Big-0, так как это в первую очередь связано с циклом и предполагает фиксированную стоимость кода внутри цикла, так что бенчмаркинг будет лучшим путем

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

5 голосов
/ 08 января 2010

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

1) Два алгоритма одного порядка сложности, назовем их f и g, оба со сложностью O(N^2) могут отличаться во времени выполнения на несколько порядков. Нотация Big-O не измеряет количество отдельных шагов, связанных с каждой итерацией, поэтому f может сделать 100 шагов, а g - 10.

Кроме того, разные компиляторы или языки программирования могут генерировать больше или меньше инструкций для каждой итерации алгоритма, а тонкий выбор в описании алгоритма может ухудшить производительность кэша или процессора в 10–1000 раз без изменения порядок big-O или количество шагов!

2) Алгоритм O(N) может превзойти алгоритм O(log(N))

Нотация Big-O не измеряет количество отдельных шагов, связанных с каждой итерацией, поэтому, если O(N) делает 100 шагов, но O(log(N)) делает 1000 шагов для каждой итерации, то для наборов данных до определенного размера O(N) будет лучше.

Для компиляторов действуют те же проблемы, что и выше.


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

1 голос
/ 08 января 2010

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

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

1 голос
/ 08 января 2010

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

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

0 голосов
/ 08 января 2010

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

List<int> ls = new List<int>();           (1) O(1)
for (int i = 0; i < ls.count; i++)        (2) O(1)                                     
   foo(i);                                (3) O(log n) (efficient function)

Cost analysis:
    (1)  cost: O(1), constant cost
    (2)  cost: O(1), (int i = 0;)
               O(1), (i < ls.count)
               O(1), (i++)
               ----  total: O(1) (constant cost), but it repeats n times (ls.count)
    (3)  cost: O(log n) (assume it, just an example), 
                        but it repeats n times as it is inside the loop

Таким образом, в асимптотической записи это будет стоить: O(n log n) (не так эффективно), что в данном примере является разумным результатом, но возьмем этот пример:

List<int> ls = new List<int>();           (1) O(1)
for (int i = 0; i < ls.count; i++)        (2) O(1)                                     
  if ( (i mod 2) == 0) )                  (*) O(1)  (constant cost)
    foo(i);                               (3) O(log n)

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

Данные и так являются только примерами и могут быть неточными, просто пытаясь проиллюстрировать поведение записи Big O. Это в основном дает вам поведение вашего алгоритма, когда данные растут (ваш алгоритм будет линейным, экспоненциальным, логарифмическим, ...), но это не то, что все знают как «эффективность», или почти, это не единственный » эффективность "смысл.

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

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

Надеюсь, это поможет.

0 голосов
/ 08 января 2010

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

Да обоим - подведем итоги.

Метод «большого О», рассмотренный выше, относится к характеристикам наихудшего случая, указанным выше. Упомянутый вами «спидтест» - это способ оценить «среднюю производительность по делу». На практике может быть БОЛЬШАЯ разница между производительностью в худшем случае и производительностью в среднем случае. Вот почему ваш вопрос интересен и полезен.

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

В зависимости от приложения у вас могут быть очень разные требования. Одно приложение может потребовать, чтобы время отклика составляло менее 3 секунд 95% времени - это привело бы к определению границ производительности. Другой может потребовать, чтобы производительность НИКОГДА не превышала 5 секунд - это привело бы к анализу производительности в худшем случае.

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

Надеюсь, это поможет.

0 голосов
/ 08 января 2010

Предполагая, что скорость (а не память) является вашей основной задачей, и если вы хотите использовать эмпирический (не теоретический) способ сравнения алгоритмов, я бы предложил вам подготовить несколько наборов данных, различающихся по размеру с большим запасом, например, на 3 порядка. Затем запустите каждый алгоритм для каждого набора данных, синхронизируйте их и постройте результаты. Форма кривой времени в зависимости от размера каждого алгоритма даст хорошее представление о производительности big-O.

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

0 голосов
/ 08 января 2010

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

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

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

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

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

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

0 голосов
/ 08 января 2010

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

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

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

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