Big O действительно имеет значение?

17

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

Что особенно важно для программирования и индустрии игр, и почему?

Ссылки будут очень полезны.

Дэвид Янг
источник
Big-O = Оптимизация. Мне понадобилось немного, чтобы понять, что такое Big-0.
AttackingHobo
12
Big-O - это не «оптимизация». Big-O - это форма анализа, которая говорит вам, как различные алгоритмы будут вести себя с точки зрения эффективности, когда число элементов, на которые воздействуют, увеличивается. Смотрите en.wikipedia.org/wiki/Big_O_notation для более подробной информации.
ZorbaTHut
2
Я могу заверить вас, что люди, которые придумали октры, и BSP / PVS знали все о big-O. В конце концов, единственное, что имеет значение, это производительность приложения. Но чтобы попасть туда, вам нужно учесть всевозможные вещи, включая асимптотическую сложность алгоритмов, которые обрабатывают много данных.
drxzcl
1
Помните правило, когда нужно оптимизировать: 1) Не делайте этого. 2) (только для экспертов) Пока не делайте этого.
Заратустра
Ну, во-первых, Big O может быть использован для пространственной или вычислительной сложности, поэтому «над всем остальным» не совсем верно. Во-вторых, Big O, как правило, гораздо проще вычислить для чего-то, чем обычный анализ случая, и его можно использовать как быструю проверку того, делаете ли вы что-то неправильно. Если рисование спрайта занимает O (2 ^ n) времени, вам, вероятно, следует выбрать другой алгоритм. Если вы хотите более практичный подход к разработке программного обеспечения, обратите внимание на практики SE, а не на CS. CS носит теоретический характер, тогда как SE в большей степени основан на промышленности.
Deleter

Ответы:

25

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

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

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

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

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

штрих-кот-бэнг
источник
8
Эта. Важно понимать характеристики производительности вашего кода. Вы просто никогда не знаете, когда дизайнер будет использовать то, что вы добавили так, как вы этого не ожидали, и вдруг кусок кода, который, как вы думали, должен был обрабатывать только 5 элементов, теперь обрабатывает 5000 и проверяется 100 раз за кадр. Вы оптимизируете это? Ты можешь? Сколько на самом деле разумно? Профиль только скажет вам, насколько он медленный, а не почему. Знание сложности покажет вам, нужно ли оптимизировать код или заменить его чем-то другим.
JasonD
3
Согласовано. Университеты учат вас «Big-O», потому что он решает многие проблемы, с которыми вы столкнетесь. Когда вас спросят «о, мы можем сделать это бесконечным, а не только 5? тестеры ненавидят ограничение, «когда обучение окупается. Вы не должны просто сказать «нет, я не могу». Очень важно уметь решать проблемы и говорить «да, я могу». Ваша игра нуждается в поиске галактики? 'Нет проблем'. Эти миллионы единиц должны быть заказаны? 'Нет проблем'. «Не изучил это достаточно» просто не сокращает это.
Rushyo
«Вы, возможно, не очень беспокоитесь о big-O, когда ...» при обработке клавиш ввода. Я унаследовал систему ввода, которая пошла на многое, чтобы разрешить сопоставления клавиш и действий за постоянное время, используя таблицу поиска. Переключение на линейный поиск по массиву пар (ключ, действие) сэкономило память и не оказало влияния на производительность, поскольку пользователи редко нажимают более нескольких клавиш в кадре, а длина массива обычно составляет всего 20-30 элементов. Это также позволило нам добавить (ключ, ключ, действие) для аккордов.
Джо, конечно, хотя это совсем другое дело. Вы хотите O (1), где постоянный фактор высокий, или O (n) с небольшим «n» и низким постоянным фактором? Знание big-O в этом случае не является проблемой, но может помочь вам понять, имеет ли решение смысл для обстоятельств, в которых оно будет использоваться.
dash-tom-bang
13

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

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

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

тетрада
источник
8

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

Посмотрите отличный пример от Тони Альбрехта: http://seven-degrees-of-freedom.blogspot.com/2010/07/question-of-sorts.html

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

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

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

Ричард Фабиан
источник
«Проблема с Big O» заключается в том, что люди, похоже, забывают, что это алгоритмическая сложность по сравнению с большими размерами наборов данных. В играх мы (обычно) не достигаем этих значений N, поэтому нам нужно заботиться о других частях головоломки. Я подозреваю, что пузырьковая сортировка всегда будет превосходить быструю сортировку, если у вас есть список из двух элементов.
dash-tom-bang
7

Когда я кодирование в движке, я часто касается только с фиксированным n: Я уже получил раздел пространственного ограничивающего числа объектов приема update(), physics()и render()примерно тех , кто на экране и прилегающих районах. Максимальный размер партии обычно довольно четко определен для каждой игры, хотя он всегда немного больше, чем вы планировали.

В этом случае меня интересует не столько биг-о, сколько множитель с постоянным множителем и члены более низкого порядка. Для функции со средой выполнения, подобной a*n^2 + b*n + c(которая есть O(n^2)), я часто гораздо больше озабочен сокращением aи, возможно, устранением c. Стоимость установки или демонтажа cможет стать пропорционально большой по сравнению с небольшой n.

Однако это не означает, что big-O (или, более конкретно, big-theta ) является отличным индикатором запаха кода. Посмотрите O(n^4)где-нибудь или, что еще хуже, O(k^n)геометрическое время, и пришло время убедиться, что вы рассматриваете другие варианты.

Я, как правило, гораздо больше беспокоюсь об оптимальности Big-O и прыгаю через обручи, чтобы найти алгоритмы с более низким Big-O, когда я имею дело с инструментами создания данных. Хотя количество объектов в данном уровне / области потоковой передачи, как правило, четко определено, общее количество объектов / художественных ресурсов / файлов конфигурации / и т. Д. Во всей игре может не совпадать. Это также намного большее число. Даже запуская параллельное создание данных, мы все еще ждем порядка минуты (я знаю, скулить - записи данных для консолей могут занять часы - мы в основном небольшие портативные игры), чтобы пройти jam data-clean && jam dataцикл.

Чтобы привести конкретный пример: это действительно вышло из-под контроля с помощью алгоритма фоновой потоковой передачи потоков, который обрабатывает 8x8 256-цветных листов. Полезно разделять потоковые буферы между фоновыми «слоями», и у нас может быть до 6 из них на данном уровне, совместно использующих один и тот же буфер. Проблема состоит в том, что оценка размера необходимого буфера основана на возможных позициях всех 6 слоев - и если они имеют простое число ширина / высота / скорость прокрутки, вы быстро начнете выполнять исчерпывающий поиск - который начинает приближатьсяO(6^numTiles)- которая во многих случаях относится к категории «дольше, чем будет вселенная». К счастью, в большинстве случаев это всего 2-3 слоя, но даже в этом случае мы работаем выше получаса. На данный момент мы выбираем очень небольшое подмножество этих возможностей, увеличивая степень детализации до тех пор, пока не пройдет заданный промежуток времени (или мы выполнили задачу, которая может произойти для небольших двухслойных конфигураций). Мы немного повысили эту оценку на основе предыдущих статистических данных о том, как часто мы ошибались, а затем добавили немного дополнительных отступов для хорошей оценки.

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

(Я часто нахожу, что наивный алгоритм выше, чем big-O, быстрее для небольших наборов данных и легче для понимания; более интересные / сложные (например, patricia trie) людям труднее понять и поддерживать, но более высокую производительность при больших наборы данных.)

Leander
источник
4

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

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

Но мне было лень, поэтому я сравнил каждую пулю с каждым монстром. Даже в самых напряженных ситуациях код столкновения использовал намного меньше 10% игрового процессора при 60 кадрах в секунду. Big-O: неважно.

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

Big-O так же важен в играх, как и во всем остальном: то есть совершенно неважно, вплоть до критического уровня.

Иди напиши какой-нибудь код. Если это слишком медленно, то профилируйте это.

ZorbaTHut
источник
3

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

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

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

  1. Убедитесь, что вы не вызываете new или malloc каждый кадр (это всегда проблема № 1)
  2. Сократите работу: меньше бросков лучей, меньше парней и т. Д.
  3. Проблемы типа алгоритма Big-O
  4. Согласованность кэша: помещайте содержимое в массивы, а не в разбросанную память
  5. Не используйте STL в режиме отладки. (и вы всегда хотите, чтобы режим отладки работал)
пройдоха
источник
2

Нотация Big-O по определению является асимптотической сложностью, т. Е. Показывает, как масштабируется время, когда N (или какие-либо переменные, которые у вас есть) становятся «очень» большими. Чтобы повторить комментарий Тетрада (который я поднял) «данные - король». Если N «очень большой» в вашей конкретной ситуации, это имеет значение, если N «очень маленький», это не имеет значения. Опыт и практика дадут вам представление о том, как количественно определить «очень большой» и «очень маленький».

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

Crowley9
источник
1

Важность Big-O в вашем программном обеспечении - O (N 2 ). С ростом N важность правильного алгоритма возрастает еще больше. :)

Kylotan
источник
Разве это не зависит от того, как часто этот алгоритм называется ..?
Бобобобо
До некоторой степени. Но если для запуска требуется 3 дня, то, вероятно, не имеет значения, если вы позвоните только один раз. :)
Kylotan
1

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

1) Если у вас есть два алгоритма, которые в основном делают одно и то же, но у одного лучше O, вы, вероятно, должны пойти на этот (очевидно)

2) Большой О связан с асимптотическим анализом . Big-O действительно вступает в игру только тогда, когда n большое . Например, алгоритм O (n) может быть очень похож по производительности на алгоритм O (n ^ 2) .. для малых n . Если вы говорите об алгоритме , который требует п ^ 2 операций в вершине, а п = 2 или п = 3, то есть не большая разница между O (N ^ 2) алгоритм ( с 4 и 9 опс RESP) и O (N) один (2 и 3 операций соответственно). Однако, если n = 9, то вы вдруг говорите о 81 операции для алгоритма O (n ^ 2) и только 9 для алгоритма O (n) - большая разница - и если n = 100, то вы говорить о 100 опс против 10000 - гораздо большая разница.

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

bobobobo
источник
0

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

Кая
источник
0

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

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

Не парься.

Стив Н
источник
«Когда вы создаете прототип игровой функции или аспекта игры, вам не стоит беспокоиться об его оптимизации». Это правда иногда, но не всегда. Некоторые игры, такие как Dead Rising, полагаются на быстрое выполнение, чтобы сделать механику основной игры - сотни зомби в реальном времени - выполнимой.
Какой процент разработки игр составляет прототипирование? В конце концов, вы хотите что-то отправить , верно?
дэш-том-бэнг
0

Это не должно быть началом и концом всего. Но это помогает разобраться в очевидных проблемах, которые могут привести к снижению производительности; зачем использовать что-то за O (n ^ 2), когда вы можете сделать то же самое за O (log n)?

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

Коммунистическая утка
источник
0

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

for (int i = 0; i < array.length; i ++) { /* ... */ }

против

for (int i = 0, l = array.length; i < l; i ++) { /* ... */ }

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

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

В обычном алгоритме обнаружения столкновений временная сложность равна O (n ^ 2), как и n-body. Однако есть лучший способ: разделить мир на множество мелких частей, чтобы только объекты внутри одной части были обнаружены при столкновении. См. Http://www.videotutorialsrock.com/opengl_tutorial/collision_detection/text.php .

Если ваша игра написана для сценариев, НЕ заставляйте сценария писать в скрипте O (n ^ 2) (и более) алгоритмов перебора чисел, таких как поиск в сумке пользователя. Вместо этого сделайте встроенную функцию в коде.

Мин-Tang
источник
1
Оба ваших примера кода O (n). Обсуждения Big-O не имеют ничего общего с «одной дополнительной операцией на цикл», а скорее «одним дополнительным поиском по всему за каждую итерацию цикла по всему».
дэш-том-бэнг
-2

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

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

Джаспер Беккерс
источник
3
Это неверно, нотация Big O используется для отображения верхних границ параллельных алгоритмов, точно так же, как линейные алгоритмы. Big O можно использовать для параллельной архитектуры чтения / параллельной записи и т. Д. Вы даже можете делать сумасшедшие вещи, такие как сортировка в O (1) с n ^ 2 процессорами, ха
Дэвид Янг
Дэвид, у тебя есть примеры из жизни? Я имею в виду, я также могу Big-O количество яблок, которые может нести группа людей, но это не значит, что они используются или полезны. По моему опыту, большую часть времени gamedev выбирает свои (параллельные) алгоритмы, основываясь на исходной производительности, а не на их функциях роста.
Джаспер Беккерс
3
«сортировка в O (1) с n ^ 2 процессорами» Я обычно чувствую, что такое использование O вводит в заблуждение, поскольку использование ресурсов по-прежнему составляет O (n ^ 2) независимо от того, каким образом вы решаете проблему. Большее количество потоков означает не только большее количество тактов процессора в секунду.
Ричард Фабиан
Сортировка в O (1) с n ^ 2 процессорами - не лучший пример, этот тип записи Big-O, вероятно, чаще всего встречается в научных кругах. Примерно так: cs.bu.edu/~best/crs/cs551/homeworks/hw1/pram.html Более реалистичные параллельные алгоритмы могут использовать log (n) процессоры. Этот тип вещей лучше подходит для перегрузки на обработку на GPU или на суперкомпьютеры, где доступны сотни ядер.
Дэвид Янг
я имел в виду разгрузку, а не перегрузку. Больше не могу редактировать мой оригинальный комментарий.
Дэвид Янг