大澳真的重要吗?


17

在学术界最糟糕的情况下,Big O会教给其他所有人。与空间复杂度相比,通常情况下的分析,复杂性之上的简化等等。

特别是对于游戏编程和行业,真正重要的是什么,为什么?

参考将非常有帮助。


大O =优化。花了我一些时间才算出big-0是什么。
AttackingHobo 2010年

12
Big-O不是“优化”。Big-O是一种分析形式,可告诉您随着作用的元素数量增加,不同算法在效率方面的表现。有关更多详细信息,请参见en.wikipedia.org/wiki/Big_O_notation
ZorbaTHut 2010年

2
我可以向您保证,提出八叉树和BSP / PVS的人都知道big-O。最后,唯一重要的是应用程序的性能。但是要到达那里,您必须考虑所有方式,包括处理大量数据的算法的渐近复杂性。
drxzcl

1
请记住何时进行优化的规则:1)不要这样做。2)(仅限专家)暂时不要这样做。
zaratustra 2010年

首先,Big O可以用于空间或计算复杂性,因此“在其他所有方面”并非完全正确。其次,Big O通常比正常的案例分析要简单得多,并且可以用来快速地检查您是否做错了什么。如果绘制一个精灵需要O(2 ^ n)时间,则可能应该选择其他算法。如果您想要更实用的软件设计方法,请查看SE实践而不是CS。CS本质上是理论上的,而SE更多地是基于行业的。
Deleter

Answers:


25

与关于“什么是唯一的真实路径”的所有其他问题一样,这些都是工具箱中的所有工具,在某些情况下,big-O胜过一切,而在任何地方都无关紧要(tm)。

您将“从不”编写物理求解器而无需担心big-O。如果不考虑排序算法,就不会实现排序算法(对于除最小数据集之外的任何数据集)。如果您正在编写网络游戏,则将需要关注每位用户的性能和网络流量扩展方式。

您可能不太关心big-O,但我确实无法想到某个时间,但是我敢肯定有一些。:)幸运的是,我们在游戏中所做的大多数事情都是线性扩展的;您想从光盘读取文件吗?这将花费与文件大小成线性比例的时间(不考虑寻找扇区大小的可能因素和可能造成的后果)。

但是,如果要在实体列表中找到特定实体怎么办?每次执行时都是线性搜索。如果您需要为世界上的每个实体找到一次玩家,那么这种方法将杀死您(除了最琐碎的游戏之外的所有游戏),即使这样,也有可能值得“优化”此查找以保持恒定的时间(例如,存储索引)或指向某个位置的玩家的指针),让您有更多的时间去做玩家实际可以看到的其他事情。

我想可以总结一下。每当处理器执行无法直接为播放器代表的操作时,都是在浪费时间。最大化处理器正在处理将显示给播放器的数据的时间,将最大化WOW!你在给玩家。


8
这个。重要的是要了解代码的性能特征。您永远不知道设计师什么时候会以您未曾想到的方式使用您添加的内容,突然之间,您认为仅需要处理5个项目的代码现在就可以处理5000个并被一次ping 100次。您是否优化了?你是否可以?多少实际上是合理的?个人资料只会告诉您速度有多慢,而不是为什么。了解复杂性将告诉您是需要优化代码还是将其替换为其他代码。
JasonD

3
同意 大学会教您“大O”,因为它可以解决您将面临的许多问题。当您被问到“哦,我们可以使这个无限而不仅仅是5个吗?测试人员讨厌培训的局限性。你不应该只说“不,我不能”。能够解决问题并说出“是的,我可以”是至关重要的。您的游戏需要可搜索的星系吗?'没问题'。那些百万单位需要订购吗?'没问题'。“研究不够”只是没有削减。
Rushyo 2010年

“在输入时,您可能不太担心big-O。”。我继承了一个输入系统,该系统竭尽全力使用查找表在恒定时间内解析键->动作映射。将其切换为线性搜索(键,动作)对的数组可以节省内存,并且不会影响性能,因为用户很少会在一个帧中按下多个键,并且该数组通常只有20至30个项目。它还可以让我们添加和弦(键,键,操作)。

乔,当然,尽管那是另一种担忧。您是否希望O(1)的常数因子较高,或者O(n)的常数n小而常数因子低?在这种情况下,知道big-O并不是问题,但是可以帮助您确定该解决方案是否适合使用该解决方案的情况。
2012年

13

我的经验法则是,除非您是O(scary),否则其他问题将更相关。

我的另一个经验法则是数据为王。除非您使用实际的数据集来分析代码,否则您只是在猜测。

编辑:要详细一点,您的大数据并不是那么重要,因为(至少以我的经验)大多数数据集都相对较小。当您使用少于几百个元素的数据结构时,您可能并不关心性能的上限。而且,如果您的列表中有100k +个元素,那么您确实需要考虑算法的各个方面。从我的经验来看,内存比CPU速度更受限制。取决于您的用例,更快的内存占用算法可能不如更精简的存储算法好,但较慢。


8

大O在大多数情况下都很重要,但有时理论上看似“更糟”的算法在实践中却要快得多。

看看Tony Albrecht的一个很好的例子:http : //seven-degrees-of-freedom.blogspot.com/2010/07/question-of-sorts.html

在游戏开发中到处都是这样,在这种情况下,操作中的项目数量很大,以至于一个非常不同的算法更快;或者很小,以至于一个笨拙的算法就足够了(或者适合缓存,因此它会覆盖效率)在的更好的算法)。

Big O的问题在于,它是任务复杂性的通用名称,没有考虑到现代目标硬件的复杂性,也没有提供关于设置时间开销的任何见解。

在许多情况下,最佳的最佳解决方案是两步。在实践中,游戏开发人员倾向于采用低O算法,但要在开发或调试时间上与成本保持平衡。找到合理的解决方案后,您始终必须查看硬件如何处理任务,以及如何让硬件在更短的时间内完成更多工作。


“大O问题”是人们似乎忘记了它是算法复杂性相对于大型数据集的性能。在游戏中,我们通常不会达到N的那些值,因此我们需要关注难题的其他部分。我怀疑当您有两个元素的列表时,冒泡排序始终会胜过快速排序。
dash-tom-bang 2010年

7

当我在引擎正在编码,我经常只关注一个固定的n:我已经有了一个空间分区限制对象的接收数量update()physics()以及render()大约那些在屏幕上和周边地区。通常,每场游戏的最大批处理量是非常明确的,尽管它总是比您计划的要大一些。

在这种情况下,我不太关心big-O,而是关心常数因子乘数和低阶项。对于具有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层,但即便如此,我们的运行时间仍超过半小时。目前,我们对这些可能性的很小一部分进行了采样,增加了粒度,直到经过了一定的时间(或者我们已经完成了任务,对于小型双层配置而言,可能会发生)。我们会根据先前被证明是错误的频率的先前统计数据来稍微提高此估算值,然后添加一些额外的填充以达到良好的效果。

另一个有趣的例子:在前一段时间的PC游戏中,首席工程师用跳过列表进行了一段时间的实验。内存开销最终导致更多的缓存效果,这给整个事务增加了一种非恒定的乘数-因此,对于small而言,它们根本不是一个好选择n。但是对于频繁搜索的较大排序列表,它们可以提供好处。

(我经常发现,朴素算法的big-O较高,在较小的数据集上更快,并且更易于理解;更有趣/更复杂的算法(例如patricia trie)对于人们来说更难于理解和维护,但是在较大的数据集上则具有更高的性能。数据集。)


4

它可能很方便,但也可能无关紧要。以我最近的游戏为例,它是Smash TV的克隆版本。自上而下的游戏,怪物从侧面倒入,然后射击它们。

现在,有很多聪明的方法可以确定碰撞。您可以使用KDtrees划分空间,这样就不会针对无法击中的怪物测试子弹。而且,可以肯定的是,我本可以很聪明,但我可以那样做。

但是我感到很懒惰,所以我只是将每个子弹与每个怪物进行比较。即使在最忙碌的情况下,以60fps的速度运行时,碰撞代码仍远远不到游戏CPU的10%。Big-O:无关紧要。

同样,我有一个4x风格的游戏,您在岛上建造城市,有时城市被摧毁。我本可以很聪明,然后试图从收入变量中减去被摧毁城市的收入。但是我没有。每当发生任何变化时,我都会抹去收入,并从头开始重新计算。与CPU完全无关。

Big-O在游戏中和其他所有方面一样重要:也就是说,直到它变得至关重要之前,它都完全不重要。

去写一些代码。如果速度太慢,则对其进行分析。


3

Big-O分析很重要,但这并不是游戏开发中首先要考虑的问题。由于制作游戏涉及许多复杂的代码,因此我始终建议将“代码简单性”作为算法的首要条件。具有复杂簿记的算法只会浪费您的时间。

我认为在开发过程中游戏始终以60 fps运行非常重要。当您低于此值时,要做的第一件事就是运行探查器。一旦发现瓶颈,就可以对其进行攻击。很多时候,您需要做一些非编码的工作,例如告诉关卡设计师在一个区域中放置更少的东西(并为他们提供工具)。

有时,您实际上会确定一些需要加速的代码。我觉得这是有趣的工程!我希望我有更多机会这样做。当然,您想一次迭代更改一件事情并衡量性能。我发现的典型问题是:

  1. 确保您没有在每个帧中调用new或malloc(这始终是#1问题)
  2. 减少工作量:减少射线投射,减少人员等。
  3. Big-O算法类型问题
  4. 缓存一致性:将内容放入数组而不是分散的内存中
  5. 不要在调试模式下使用STL。(并且您总是希望调试模式能够正常工作)

2

大O表示法从定义上讲是渐进复杂性-即,它显示了N(或您拥有的任何变量)“非常”大时的时间比例。再次重申Tetrad的评论(我赞成)“数据为王”。如果N在您的特定情况下“很大”,那很重要,如果N“很小”,那没关系。经验和实践会带给您如何量化“非常大”和“非常小”的感觉。

显然,始终要先进行概要分析,然后再进行优化(除非您正在进行功能可行性研究)。


1

Big-O在您的软件中的重要性为O(N 2)。随着N的增长,拥有正确算法的重要性越来越高。:)


这不取决于该算法被调用的频率吗?
bobobobo

在某种程度上。但是,如果要花3天时间运行,则只调用一次就可能无关紧要。:)
Kylotan

1

Big-O只是一个准则-可以告诉您可以从算法获得的粗略性能- 以及随着数据集大小的增加如何扩展性能。您必须记住关于Big-O的两件事:

1)如果您有两种算法大多数都做相同的事情,但是其中一种算法的O更好,那么您可能应该选择那种算法(显然)

2)大O与渐近分析有关。只有当n大时, Big-O才真正起作用。例如,对于小n,O(n)算法的性能可能与O(n ^ 2)one ...非常相似。如果您要谈论的算法是每个顶点需要n ^ 2个操作,但是n = 2或n = 3,那么就没有为O(n ^ 2)算法相差无几(以4个9 OPS RESP)和一个O(n)一个(分别为2和3个操作)。但是,如果n = 9,那么您突然谈论的是O(n ^ 2)算法的81个操作,而O(n)的算法只有9个操作-差异更大-如果n = 100,则您是谈论100个操作数与10000个操作数-差异更大。

因此,您必须始终从这种角度考虑Big-O:这是为了比较当n变大时基于最坏情况的性能 执行相同操作的算法。当n非常小时,算法之间的差异几乎可以忽略不计。


0

我没有任何参考资料,但是在分析问题和讨论时,Big O至少很容易意识到。另一方面,当然,如果O(log n)版本比O(n)版本涉及O的方式更多,那将是比较无聊的比较。与所有事物一样,总会有一个权衡。空间复杂性可能是一个问题,尽管通常也可以用O表示。普通案例分析...少了一点,因为您也不想让异常值飙升。在我看来,简单性相对于复杂性在游戏开发中相对没有用,因为速度几乎总是一个问题,因此除非简单性导致加速(但是这意味着您的复杂情况由于错误的原因是错误的),简单性就必须走窗外的速度。但是Big O绝对有用


0

在为游戏功能或游戏某个方面制作原型时,您根本不必担心对其进行优化。

在对其进行原型设计和了解该功能的特性的过程中,必要的优化将变得显而易见,并将在大多数情况下将其纳入最终设计中,例如第二自然。

不要流汗。


“当您为游戏功能或游戏某个方面制作原型时,根本不必担心对其进行优化。” 有时但并非总是如此。某些游戏(例如Dead Rising)依靠快速执行来使核心游戏机制(实时数百个僵尸)变得可行。

原型制作中游戏开发的百分比是多少?最终您要运送东西,对吗?
dash-tom-bang's

0

它不应该是万能的。但这确实有助于解决可能导致性能下降的明显问题。当您可以在O(log n)时间内执行相同的操作时,为什么还要在O(n ^ 2)时间内使用某些功能?

我认为它比其他大多数行业更适用于游戏,因为市场是最关注速度问题的市场。使用文字处理程序的人不会在乎执行动作X会有半秒的延迟,但是游戏玩家可能会说“哦,游戏Y太慢了,需要很长时间才能执行动作Z”。


0

在游戏(和其他大多数游戏)开发中,我们抱怨每个循环执行一个额外的操作:

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

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

大多数现代游戏都有物理学,您会发现n体模拟问题。在朴素的算法中,它是O(n ^ 2),但是有一个优化使它成为O(n log n)(但牺牲了一些准确性)。

您可能会说,您不是在编写重力和粒子相互作用,而是如何使一支(僵尸)军队的团队行为依赖于其他位置(更确切地说是蜂拥而至)移动呢?

在传统的碰撞检测算法中,时间复杂度像n体一样是O(n ^ 2)。但是,有一种更好的方法:将世界分成许多小部分,以便仅检测同一部分内的对象。参见http://www.videotutorialsrock.com/opengl_tutorial/collision_detection/text.php

如果您的游戏可以编写脚本,请不要让脚本编写者在脚本中编写O(n ^ 2)(及以上)数字运算算法,例如搜索用户的行李袋。而是在代码中创建一个内置函数。


1
您的两个代码示例均为O(n)。Big-O的讨论与“每个循环多进行一次操作”无关,而是“循环中每次迭代对所有内容进行一次额外搜索”。
dash-tom-bang 2010年

-2

在现实世界中,只有原始性能才重要。现在,算法的Big-O可以作为使用内容的第一个指示,但是根据硬件的不同,实现的效率可能非常低。例如,执行线性搜索通常比二进制搜索更快,因为您可以进行线性内存访问并且没有分支。

此外,由于当前多线程平台和体系结构的发展方向,Big-O失去了很多意义,因为它仅考虑了每个操作的内存或数据触摸的垂直可伸缩性,而不是考虑算法的方式。可以使用更多数量的线程进行扩展。


3
这是不正确的,Big O表示法用于显示与线性算法相同的并行算法的上限。Big O可用于并发读/并发写入体系结构等。您甚至可以执行疯狂的操作,例如使用n ^ 2处理器在O(1)中排序哈哈
David Young,2010年

大卫,您有真实的例子吗?我的意思是,我也可以大人一组可以携带的苹果的数量,但这并不意味着它已经使用或有用。根据我的经验,大多数时候gamedev都是基于原始性能而不是基于其增长功能来选择(并行)算法。
贾斯珀·贝克斯

3
“用n ^ 2个处理器对O(1)进行排序”我通常认为O的这种用法具有误导性,因为无论您采用何种方式对问题进行切片,资源的使用仍然为O(n ^ 2)。更大数量的线程不仅意味着每秒更多的cpu周期。
理查德·法比安

用n ^ 2个处理器对O(1)进行排序并不是最好的例子,这种Big-O表示法可能在学术界最常见。诸如此类的东西cs.bu.edu/~best/crs/cs551/homeworks/hw1/pram.html更现实的并行算法可以使用log(n)处理器。这种类型的东西更适合于重载到GPU处理或超级计算(其中有数百个内核可用)上。
David Young

嗯,我的意思是卸载,而不是过载。无法再编辑我的原始评论。
David Young,2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.