二、算法分析

程序和算法的区别:

    算法是对问题的解决的分步描述,程序是采用某种编程语言实现的算法。

算法分析概念:

    比较程序好坏:  

      代码风格、执行效率等(python代码有很强的可读性,代码风格统一)

算法分析方法:

  运行时间检测分析(不可靠)

    运行时间检测可以用python的time模块(运行时间检测分析有问题,因为编程语言和运行环境时间都会有影响)

    衡量计算内存可以评判

 

算法时间度量指标:

    问题规模影响算法执行时间

    大O表示法

       大O表示法:算法的时间复杂度通常用大O符号表述,定义为T[n] = O(f(n))。称函数T(n)以f(n)为界或者称T(n)受限于f(n)。 如果一个问题的规模是n,解这一问题的某一算法所需要的时间为T(n)。T(n)称为这一算法的“时间复杂度”。当输入量n逐渐加大时,时间复杂度的极限情形称为算法的“渐近时间复杂度”。

 算法的目标

容易理解

编码和调试
优秀的算法通常是简洁而清晰的,这样带来的直接好处就是易于编码和理解,同时这样算法也必定是健壮的,如果一个算法晦涩难懂,则很可能其中会隐藏较多的错误。

最小的代价

算法的代价的最小化是指其执行时间最短且占用的存储空间最少,它们之间 往往是相互矛盾的,然而一般而言,算法的执行时间是主要的评价标准。

算法的执行时间

算法的执行时间等于它所有基本操作执行时间之和, 而一条基本操作的执行时间等于它执行的次数和每一次执行的时间的积,
如下:
算法的执行时间 = 操作1 + 操作2 + ... + 操作n
操作的执行时间 = 操作执行次数 X 执行一次的时间
然而存在一个问题,不同的编程语言,不同的编译器,或不同的CPU等因素将导致执行一次操作的时间各不相同,这样的结果会使算法的比较产生歧义, 于是我们假定所有计算机执行相同的一次基本操作所需时间相同,而把算法中基本操作所执行的最大次数作为量度。就是说我们把算法的执行时间简单地用基本操作的执行次数来代替了。
那么除此之外,基本操作是什么? 它可以是基本运算赋值,比较,交换等,如在排序中,基本操作指的是元素的比较及交换。而在线性查找中,它是数据的比较。
时间复杂度和大O表示法
当问题规模即要处理的数据增长时, 基本操作要重复执行的次数必定也会增长, 那么我们关心地是这个执行次数以什么样的数量级增长。所谓数量级可以理解为增长率。这个所谓的数量级就称为算法的渐近时间复杂度(asymptotic time complexity), 简称为时间复杂度。如何分析这个数量级呢? 由于基本操作的执行次数是问题规模n 的一个函数T(n), 所以问题就是我们要确定这个函数T(n)是什么, 然后分析它的数量级, 拥有相同数量级的函数 f(n) 的集合表示为 O(f(n)), O是数量级的标记。如果T(n)的数量级和f(n)相同,显然T(n) ∈ Of(n)。这个函数的值表示当我要处理的数据量增长时,基本操作执行次数以什么样的比例增长。即n增长时,T(n)增长多少?

例子


首先看一个简单的示例:
1
2
3
4
5
6
7
int num1,num2;
    for(int i=0; i<n; i++){
        num1 += 1;
        for(int j=1; j<=n; j*=2){
            num2 += num1;
        }
    }
分析:
语句int num1,num2;的频度为1;
语句i=0;的频度为1;
语句i<n; i++; num1+=1; j=1; 的频度为n;
语句j<=n;j
 =2; num2+=num1;的频度为n
 
 n;
T(n) = 2 + 4n +3n
 
 n
忽略掉T(n)中的常量、低次幂和最高次幂的系数
f(n) = n*
 n
lim(T(n)/f(n)) = (2+4n+3n*
 n) / (n*
 n)
= 2*(1/n)*(1/
 n) + 4*(1/
 n) + 3
当n趋向于无穷大,1/n趋向于0,1/
 n趋向于0
所以极限等于3。
T(n) = O(n*
 n)
简化的计算步骤
再来分析一下,可以看出,决定算法复杂度的是执行次数最多的语句,这里是num2 += num1,一般也是最内循环的语句。
并且,通常将求解极限是否为常量也省略掉?
于是,以上步骤可以简化为:
⒈ 找到执行次数最多的语句
⒉ 计算语句执行次数的数量级
⒊ 用大O来表示结果
继续以上述算法为例,进行分析:
执行次数最多的语句为num2 += num1
T(n) = n*log2n
f(n) = n*log2n
// lim(T(n)/f(n)) = 1
T(n) = O(n*log2n)
--------------------------------------------------------------------------------
 
 
变位词: 

  变位词指的是一个单词可以通过改变其他单词中字母的顺序来得到,也叫做兄弟单词,如army->mary。

例子1:判断两个字符串s1和s2是否为变位词。

  经典的字符串变位词检测问题是比较不同数量级函数算法的一个典型例子。如果一个字符串是 另一个字符串的重新排列组合,那么这两个字符串互为变位词。比如,”heart”与”earth”互为变位 词,”python”与”typhon”也互为变位词。为了简化问题,我们设定问题中的字符串长度相同,都是由 26 个小写字母组成。我们需要编写一个接受两个字符串,返回真假,代表是否是一对变位词的布尔 函数。

法①:检查标记【时间复杂度为O(n2)】

  思路:检查第一个字符串中的所有字符是不是都在第二个字符串中出现。 如果能够把每一个字符都“检查标记”一遍,那么这两个字符串就互为变位词。检查标记一个字符 要用特定值 None 来代替,作为标记。然而,由于字符串不可变,首先要把第二个字符串转化成一个列表。第一个字符串中的每一个字符都可以在列表的字符中去检查,如果找到,就用 None 代替以示标记。

复制代码
def anagram(s1,s2):
    s2=list(s2)
    still_ok=True
    i=0
    while i<len(s1) and still_ok:
        found=False
        j=0
        while j<len(s2) and not found:
            if s1[i]==s2[j]:
                Found=True
            else:
                j+=1
        if found:
            s2[j]==None
        else:
            still_ok=False
        i+=1
    return still_ok
复制代码

法②:排序比较【时间复杂度为O(n2)】

  思路:尽管 s1 和 s2 并不相同,但若为变位词它们一定包含完全一样的字符,利用这一特点,我们可以 采用另一种方法。我们首先从 a 到 z 给每一个字符串按字母顺序进行排序,如果它们是变位词,那么 我们将得到两个完全一样的字符串。此外,我们可以先将字符串转化为列表,再利用 Python 中内建
的 sort 方法对列表进行排序。下面代码展示了这种方法。
第一眼看上去你可能会认为这个算法的复杂度是 O(n),毕竟排序后只需要一个简单的循环去比较 n 个字符。然而对 Python 内建的 sort 方法的两次使用并非毫无消耗。事实上,正如我们在后面的章节 中将要看到的,排序方法的复杂度往往都是 O(n²)或者 O(n㏒n),所以排序贡献了这个函数主要的循 环操作。最终,这个算法和排序的复杂度相同。

复制代码
def anagram(s1,s2):
    s2=list(s2)
    s1=list(s1)
    list_s1=sorted(s1)
    list_s2=sorted(s2)
    still_ok=True
    i=0
    while i<len(list_s1):
        if list_s1[i]==list_s2[i]:
            still_ok=True
        else:
            still_ok=False
        i+=1
    return still_ok
复制代码

 

法③:计数比较法【时间复杂度O(n)】

解决变位词问题的最后一个方法利用了任何变位词都有相同数量的 a,相同数量的 b,相同数量 的 c 等等。为判断两个字符串是否为变位词,我们首先计算每一个字符在字符串中出现的次数。由于
共有 26 个可能的字符,我们可以利用有 26 个计数器的列表,每个计数器对应一个字符。每当我们 看到一个字符,就在相对应的计数器上加一。最终,如果这两个计数器列表相同,则这两个字符串 是变位词。下面展示了这种方法:

复制代码
def anagram(s1,s2):
    counter1=[0]*26
    counter2=[0]*26
    for i in s1:
        counter1[ord(i)-ord('a')]+=1
    for i in s2:
        counter2[ord(i)-ord('a')]+=1
    if counter1==counter2:
        return True
    else:
        return False
复制代码

 

 

 Python数据类型性能
    对比lsit 和dict操作:
    

 

 

算法优化法则:

  

 

 

 

  

 

 

例子:

    

 

 

       

 

 

       

 

 

    

 

 

       

        

 

 

       

 

 

        

        

 

 

         

 

 

           

 

 

          

 

字典

   

 

 

   

 

 

   

 

       

 

       

 

 

 

 

      

       

 

 

          

posted @ 2020-06-15 11:31  CQY_23  阅读(326)  评论(0)    收藏  举报