基本编程与时间复杂度

与生活中的其他方面一样,在计算机编程中,存在解决问题的不同方法。 这些不同的方法可能意味着不同的时间,计算能力或您选择的任何其他指标,因此我们需要比较不同方法的效率以选择正确的方法。

您可能已经知道,计算机现在可以根据算法解决问题。

算法是告诉计算机做什么和如何做的过程或指令(步骤集)

如今,它们进化得如此之多,以至于即使完成相同的任务,它们也可能有很大的不同。 在最极端的情况下(顺便说一句,这很普遍),用不同编程语言编程的不同算法可能会告诉具有不同硬件和操作系统的不同计算机以完全不同的方式执行同一任务。 太疯狂了,不是吗?

事实是,虽然一种算法需要几秒钟才能完成,但是另一种算法甚至需要很小的数据集也要花费几分钟。 我们如何比较不同的性能并选择最佳算法来解决特定问题?

幸运的是,有很多方法可以做到这一点,我们无需等待算法就可以知道它能否快速完成工作,或者在输入的压力下是否会崩溃。

当我们考虑算法的复杂性时,我们不应该真正在乎执行的操作的确切数目。 相反, 我们应该关注操作数与问题大小之间的关系

试想一下:如果问题大小加倍,操作次数是否保持不变? 他们加倍吗? 它们会以其他方式增加吗? 要回答这些问题,我们需要测量算法的时间复杂度。

时间复杂度表示执行一条语句的次数 。 算法的时间复杂度不是执行特定代码所需的实际时间,因为这取决于其他因素,例如编程语言,操作软件,处理能力等。

时间复杂度背后的想法是,它只能以仅依赖于算法本身及其输入的方式来衡量算法的执行时间。

为了表示算法的时间复杂度,我们使用了一种称为“大O表示法”的方法Big O表示法是一种用于描述算法时间复杂度的语言。 这是我们比较问题的不同方法的效率并帮助我们做出决定的方式。

大O表示法以算法相对于输入的增长速度 (此输入称为“ n”) 来表示算法的运行时间 。 这样,例如,如果我们说算法的运行时间“按输入大小的顺序增长”,则将其表示为“ O(n)”。

如果我们说算法的运行时间“增长到输入大小的平方的数量级”,我们将其表示为“ O(n²)”。 但这到底是什么意思?

了解时间复杂度的关键是了解事物可以增长的速度。 这里讨论的速率是每个输入大小所花费的时间。 时间复杂度有不同类型,因此让我们检查一下最基本的时间复杂度。

恒定时间复杂度:O(1)

当时间复杂度恒定(表示为“ O(1)”)时,输入(n)的大小无关紧要。 具有恒定时间复杂度的算法与n的大小无关,需要恒定的运行时间。 它们不会响应输入数据而更改运行时间,这使它们成为目前最快的算法。

例如,如果您想知道数字是奇数还是偶数,则可以使用时间复杂度恒定的算法。 无论数字是1还是90亿(输入“ n”),该算法只会执行一次相同的运算,并为您带来结果。

另外,如果您要打印一次像经典的“ Hello World”这样的短语,那么您也将以恒定的时间复杂度运行该短语,因为使用该短语或任何其他短语的操作量(在本例中为1)将保持相同,无论您使用的是哪种操作系统或机器配置。

为了保持恒定,这些算法不应包含循环,递归或对任何其他非恒定时间函数的调用。 对于恒定时间算法,运行时间不会增加:数量级始终为1。

线性时间复杂度:O(n)

当时间复杂度与输入大小成正比增长时,您将面临线性时间复杂度或O(n)。 具有这种时间复杂度的算法将以“ n”个运算来处理输入(n)。 这意味着随着输入的增长,该算法将成比例地花费更长的时间才能完成。

在这种情况下,您必须查看列表中的每个项目才能完成任务(例如,找到最大值或最小值)。 或者,您也可以考虑日常任务,例如阅读书籍或在CD堆栈中查找CD(还记得吗?):如果必须检查所有数据,则输入大小越大,操作数量越多。

线性运行时间算法非常普遍,它们与以下事实有关:算法访问输入中的每个元素。

对数时间复杂度:O(log n)

具有这种复杂性的算法使计算速度惊人地快。 如果算法的时间执行与输入大小的对数成正比,则称该算法以对数时间运行。 这意味着代替增加执行每个后续步骤所花费的时间,该时间以与输入“ n”成反比的幅度减少。

这有什么秘诀? 这些类型的算法永远不必遍历所有输入,因为它们通常通过在每个步骤中丢弃大量未经检查的输入来工作。 这种时间复杂性通常与每次将问题分成两半的算法有关,这是一个被称为“分而治之”的概念。 分而治之算法使用以下步骤解决问题:

1.他们划分给定的问题到同类型的子问题。
2.他们递归地解决了这些子问题。
3.他们适当地组合了子答案来回答给定的问题。

考虑以下示例:假设您要在字典中寻找一个单词,并且每个单词都按字母顺序排序。 至少有两种算法可以做到这一点:

算法A:

  • 从书的开头开始并按顺序进行,直到找到所需的联系人为止。

算法B:

  • 在中间打开书并检查其中的第一个单词。
  • 如果您要查找的单词按字母顺序更大,那么它会出现在右半部分。 否则,它会出现在左半部分。

两者中哪个更快? 虽然算法A逐个单词O(n),但算法B在每次迭代O(log n)上将问题分成两半,从而以更加有效的方式实现了相同的结果。

对数时间算法(O(log n))是恒定时间算法(O(1))之后第二快的算法。

二次时间复杂度:O(n²)

在这种类型的算法中,运行所花费的时间与输入大小的平方成正比(如线性但平方)。

在大多数情况下,尤其是对于大型数据集,具有二次时间复杂度的算法需要大量时间来执行,因此应避免使用。

嵌套For循环以二次时间运行,因为您正在另一个线性运算中运行一个线性运算,即n * n等于n²。

如果您面对这些类型的算法,则可能需要大量资源和时间,或者需要提出一种更好的算法。

指数时间复杂度:O(2 ^ n)

在指数时间算法中,每增加一个输入(n),增长率就翻倍,通常迭代输入元素的所有子集。 只要输入单位增加1,它就会使您执行的操作数量增加一倍。 这听起来不好,对吧?

具有这种时间复杂性的算法通常用于您对最佳解决方案了解不多的情况,并且您必须尝试对数据进行每种可能的组合或排列。

指数时间复杂度通常在蛮力算法中可见。 这些算法盲目地迭代可能解决方案的整个域,以寻找满足条件的一个或多个解决方案。

他们尝试通过尝试所有可能的解决方案来找到正确的解决方案,直到碰巧找到正确的解决方案为止。 这显然不是执行任务的最佳方法,因为它将影响时间复杂度。

蛮力算法在密码学中被用作攻击方法,通过尝试随机攻击直到找到能够解锁系统的正确密码来破坏密码保护。

与二次时间复杂度一样,您应避免算法具有指数运行时间,因为它们无法很好地扩展。

如何测量时间复杂度?

一般而言,我们已经看到算法执行的运算越少,运算速度就越快。 这看起来是个不错的原则,但是我们如何将其应用于现实呢?

如果我们有一个算法(无论它是什么),我们如何知道它的时间复杂度?

在某些情况下,这可能相对容易。 假设您有一个外部For循环 ,它遍历输入列表中的所有项目,然后是一个嵌套的内部For循环 ,它又遍历输入列表中的所有项目。 执行的步骤总数为n * n,其中n是输入数组中的项目数。

但是,如何找到复杂函数的时间复杂度呢?

为了找到答案,我们需要将算法代码分解为多个部分,并尝试找出各个部分的复杂性。 是的,很抱歉告诉您,但是您没有可以按下的按钮可以告诉您算法的时间复杂度。 你必须自己做。

根据经验,最好尝试使函数在线性时间复杂度以下或在线性时间复杂度范围内运行,但显然并非总是可能的。

Big O表示法有不同的表示法 ,例如“最佳情况” ,“ 平均情况”“最坏情况” ,但真正重要的是最坏情况 ; 那些会严重破坏一切。

他们直接指出了时间复杂性为何重要的核心,并指出了为什么某些算法如果不花费数十亿年的时间就无法解决问题。

最坏情况分析给出了算法执行期间必须执行的最大基本操作数。 它假定输入处于最坏的状态,必须做最大的工作才能正确处理。

例如,对于旨在按升序对数组进行排序的排序算法,最坏的情况是当输入数组按降序排列时。

在这种情况下,必须执行最大数量的基本操作(比较和分配)才能将阵列设置为升序。

这样想:如果必须通过读取每个名称直到找到正确的名称来在目录中搜索名称,最坏的情况是所需的名称是目录中的最后一个条目。

综上所述, 算法时间复杂度越高,算法在实际中进行工作的速度就越快。

在设计或管理算法时,应考虑到此问题,并考虑到它对算法的实用性或完全无用性有很大的影响。

对这些主题感兴趣? 在LinkedinTwitter上关注我

(最初在此处发布)

From: https://hackernoon.com/essential-programming-versus-time-complexity-8ab536o6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值