码迷,mamicode.com
首页 > 编程语言 > 详细

前端学算法之算法复杂度

时间:2018-01-05 19:53:35      阅读:152      评论:0      收藏:0      [点我收藏+]

标签:情况下   技术   机器   符号   内存   嵌套循环   img   eterm   search   

前面的话

  本文将详细介绍算法复杂度

 

大O表示法

  大O表示法是描述算法的性能和复杂程度。 分析算法时,时常遇到以下几类函数

符号             名称
O(1)            常数的
O(log(n))        对数的
O((log(n))c)    对数多项式的
O(n)            线性的
O(n2)            二次的
O(nc)            多项式的
O(cn)            指数的

  如何衡量算法的效率?通常是用资源,例如CPU(时间)占用、内存占用、硬盘占用和网络占用。当讨论大O表示法时,一般考虑的是CPU(时间)占用

  下面用一些例子来理解大O表示法的规则

【O(1)】

function increment(num){ 
  return ++num;
}

  假设运行increment(1)函数,执行时间等于X。如果再用不同的参数(例如2)运行一次increment函数,执行时间依然是X。和参数无关,increment函数的性能都一样。因此,我们说上述函数的复杂度是O(1)(常数)

【O(n)】

  现在以顺序搜索算法为例:

function sequentialSearch(array, item){ 
  for (var i=0; i<array.length; i++){
    if (item === array[i]){ //{1} 
      return i;
    }
  }
  return -1;
}

  如果将含10个元素的数组([1, ..., 10])传递给该函数,假如搜索1这个元素,那么,第一次判断时就能找到想要搜索的元素。在这里我们假设每执行一次行{1} ,开销是1。

  现在,假如要搜索元素11。行{1}会执行10次(遍历数组中所有的值,并且找不到要搜索的元素,因而结果返回 -1)。如果行{1}的开销是1,那么它执行10次的开销就是10,10倍于第一种假设

  现在,假如该数组有1000个元素([1, ..., 1000])。搜索1001的结果是行{1}执行了1000次(然后返回-1)

  sequentialSearch函数执行的总开销取决于数组元素的个数(数组大小),而且也和搜索的值有关。如果是查找数组中存在的值,行{1}会执行几次呢?如果查找的是数组中不存在的值,那么行{1}就会执行和数组大小一样多次,这就是通常所说的最坏情况

  最坏情况下,如果数组大小是10,开销就是10;如果数组大小是1000,开销就是1000。可以得出sequentialSearch函数的时间复杂度是O(n),n是(输入)数组的大小

  回到之前的例子,修改一下算法的实现,使之计算开销:

function sequentialSearch(array, item){
 var cost = 0;
 for (var i=0; i<array.length; i++){
  cost++;
  if (item === array[i]){ //{1}
    return i;
  }
 }
 console.log(cost for sequentialSearch with input size  +  array.length +  is  + cost);
 return -1;
} 

  用不同大小的输入数组执行以上算法,可以看到不同的输出

O(n2)】

  用冒泡排序做O(n2)的例子:

function swap(array, index1, index2){
 var aux = array[index1];
 array[index1] = array[index2];
 array[index2] = aux;
}
function bubbleSort(array){
 var length = array.length;
 for (var i=0; i<length; i++){ //{1}
  for (var j=0; j<length-1; j++ ){ //{2}
    if (array[j] > array[j+1]){
      swap(array, j, j+1);
    }
  }
 }
} 

  假设行{1}和行{2}的开销分别是1。修改算法的实现使之计算开销:

function bubbleSort(array){
 var length = array.length;
 var cost = 0;
 for (var i=0; i<length; i++){ //{1}
  cost++;
  for (var j=0; j<length-1; j++ ){ //{2}
    cost++;
    if (array[j] > array[j+1]){
      swap(array, j, j+1);
    }
  }
 }
 console.log(cost for bubbleSort with input size  + length +  is  + cost);
} 

  如果用大小为10的数组执行bubbleSort,开销是100(102)。如果用大小为100的数组执 行bubbleSort,开销就是10 000(1002)。需要注意,我们每次增加输入的大小,执行都会越来越久

  时间复杂度O(n)的代码只有一层循环,而O(n2)的代码有双层嵌套循环。如 果算法有三层遍历数组的嵌套循环,它的时间复杂度很可能就是O(n3)

 

时间复杂度

  下图比较了前述各个大O符号表示的时间复杂度:

技术分享图片

  下表是常用数据结构的时间复杂度

技术分享图片

  下表是图的时间复杂度: 

技术分享图片

  下表是排序算法的时间复杂度: 

技术分享图片

  下表是搜索算法的时间复杂度: 

技术分享图片

 

NP

  一般来说,如果一个算法的复杂度为O(nk),其中k是常数,我们就认为这个算法是高效的,这就是多项式算法

  对于给定的问题,如果存在多项式算法,则计为P(polynomial,多项式)

  还有一类NP(nondeterministicpolynomial,非确定性多项式)算法。如果一个问题可以在多项式时间内验证解是否正确,则计为NP。如果一个问题存在多项式算法,自然可以在多项式时间内验证其解。因此,所有的P都是NP。然而,P=NP是否成立,仍然不得而知。NP问题中最难的是NP完全问题,它满足以下两个条件:(1)是NP问题,也就是说,可以在多项式时间内验证解,但还没有找到多项式算法;(2)所有的NP问题都能在多项式时间内归约为它。为了理解问题的归约,考虑两个决策问题L和M。假设算法A可以解决问题L,算法B可以验证输入y是否为M的解。目标是找到一个把L转化为M的方法,使得算法B可以用于构造算法A

  还有一类问题,只需满足NP完全问题的第二个条件,称为NP困难问题。因此,NP完全问题也是NP困难问题的子集

  下面是满足P < > NP时,P、NP、NP完全和NP困难问题的欧拉图: 

技术分享图片

 

  非NP完全的NP困难问题的例子有停机问题和布尔可满足性问题(SAT)。 NP完全问题的例子有子集和问题、旅行商问题、顶点覆盖问题等等

  我们提到的有些问题是不可解的。然而,仍然有办法在符合要求的时间内找到一个近似解。启发式算法就是其中之一。启发式算法得到的未必是最优解,但足够解决问题了。启发式算法的例子有局部搜索、遗传算法、启发式导航、机器学习等

 

前端学算法之算法复杂度

标签:情况下   技术   机器   符号   内存   嵌套循环   img   eterm   search   

原文地址:https://www.cnblogs.com/xiaohuochai/p/8203717.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!