第一章:算法入门

1.1 接触算法

算法难以进行精准的定义,因为宽泛的来说,所有的编程代码都可以称为算法,因为它们都有循环、逻辑判断、输入输出、需要计算内存消耗等。从程序员的角度上来看,算法可以用以下公式表示:

数据 + 代码 → 解决具体问题=算法

例如:编写九九乘法表,这个挺简单的,直接附上代码:

for i in range(1, 10):
    for j in range(1, i+1):
        print('%d*%d=%d' % (j, i, i*j), end=' ')
    print('')

这里为了打印的比较好看,使用了两个print。
下面介绍算法的质量
一个算法质量的好坏主要有四个因素决定:

  1. 数据:好的数据,可以保证算法得到最佳的输出结果,坏的数据则会使算法产生很大的误差。
  2. 算法时间效率:解决同一问题可以用不同的算法,不同的算法会产生不同的时间效率问题。
  3. 算法空间效率:所有的算法都在内存中运行,占用尽量少的内存空间,实现算法的快速计算是算法必须要解决的问题。
  4. 硬件性能:比如有的算法需要在专门的芯片或开发板上运行,再比如深度学习往往需要借助GPU运行。

1.2 算法的时间

算法计算时间效率是所有coder必须考虑的问题,因此,在算法编程时,必须反复优化设计思路,降低算法时间。

1.2.1 比较累加器算法

例如:要计算1到10000的自然数累加和。
思路分析:这里很明显的一个思路就是直接用for循环进行累加,因为反正是计算机计算,大家也不会觉得累,但用for循环肯定会耗费大量时间。那么另一个思路就是高中时候学过的数列累加公式:
n ( n + 1 ) 2 \frac{n\left( n+1 \right)}{2} 2n(n+1)
代码展示:
首先是for循环累加代码:

import time


def add_n(n):
    result_n = 0
    for i in range(1, n+1):
        result_n += i
    return result_n


if __name__ == '__main__':
    start = time.time()
    result = add_n(10000)
    print('运行时间:', (time.time() - start))
    print('计算结果为:', result)

运行时间和结果为:
在这里插入图片描述
接下来用数列累加求和公式,看看时间消耗量为多少

import time


def add_n(n):
    result_n = (n * (n+1)) / 2
    return result_n


if __name__ == '__main__':
    start = time.time()
    result = add_n(10000)
    print('运行时间:', (time.time() - start))
    print('计算结果为:', result)

运行结果为:
在这里插入图片描述
可以看出,第二种方法时间消耗几乎为0.

1.2.2 算法时间点评

一个算法花费多少时间,可以让计算机进行测试,但有时我们只是需要比较几种算法之间的耗时关系,不需要得出确切的时间,这种情况下可以通过分析算法中语句执行次数进行判断。例如刚刚的算法,for循环累加计算了10000次,而数列公式法只计算了一次,显然后者时间更短。
在计算机中,用大写字母O表示算法复杂度。只执行一次的表示O(1),叫做常数阶;执行n次的表示O(n),叫做线性阶。其他表示方法还有O(logn),叫做对数阶;O(n^2)叫做平方阶;O(n!)叫做阶乘阶等。
O ( 1 ) < O ( log ⁡ n ) < O ( n ) < O ( n 2 ) < O ( n 3 ) < O ( n ! ) O\left( 1 \right) <O\left( \log n \right) <O\left( n \right) <O\left( n^2 \right) <O\left( n^3 \right) <O\left( n! \right) O(1)<O(logn)<O(n)<O(n2)<O(n3)<O(n!)
如图是算法时间复杂度:
在这里插入图片描述

1.3 算法的储存空间

代码必须在内存里面运行。当算法涉及大量的数据计算时,必须考虑算法对内存的使用要求,避免内存不足的问题发生。

1.3.1 算法空间的评估

此处使用抽纸牌算法的对比来说明优化算法对储存空间的使用要求。
例:要求一次抽取5张牌,并记录牌的内容。

Poker=['黑A','黑2','黑3','黑4','黑5','黑6','黑7','黑8','黑9','黑10','黑J','黑Q','黑K',
      '红A','红2','红3','红4','红5','红6','红7','红8','红9','红10','红J','红Q','红K',
      '梅A','梅2','梅3','梅4','梅5','梅6','梅7','梅8','梅9','梅10','梅J','梅Q','梅K',
      '方A','方2','方3','方4','方5','方6','方7','方8','方9','方10','方J','方Q','方K',
      '小王','大王']
print('一副牌有%d张'%(len(Poker)))
import random
Show=random.sample(Poker,5)        #随机抽取5张,展现
print(Show)

此代码会将列表里面所有牌的名称事先写入内存,然后再随机抽5张。
接下来看高效空间利用方法
思路这里默认规则为黑A到黑K顺序编号为1到13,红A到红K编号为14到26,梅A到梅K编号为27到39,方A到方K编号为40到52,小王编号53,大王编号54

shape=['J','Q','K','小王','大王']
Show1=[]
Show2=[]
def getCards(name,r,Show2,shape=None):      #从同一花色13张牌里取一张牌
    if r==1 : 
        Show2.append(name+'A')
    elif r<=10:
        Show2.append(name+str(r))
    else:
        Show2.append(name+shape[r-11])  #考虑J、Q、K
   

for x in range(5):                #随机抽取5个值
    y=random.randint(1,55)        #在[1,54]范围里随机抽取一个整数
    Show1.append(y)
for r in Show1:                   #确定5张牌
    if r<=13:                     #取黑桃里的13张牌
       getCards('黑',r,Show2,shape)
    elif r>13 and r<=26:          #取红桃里的13张牌
       getCards('红',r-13,Show2,shape) 
    elif r>27 and r<=39:          #取梅花里的13张牌
       getCards('梅',r-26,Show2,shape)  
    elif r>40 and r<=52:          #取方块里的13张牌
       getCards('方',r-39,Show2,shape)
    else:
        Show2.append(shape[r%50])  #取小王或大王
print(Show2)                       #取出的5张牌

高效空间内存法只占用了五个元素的空间,而第一种方法占用了54个元素的空间。(高效空间内存法除了在shape上会产生五个空间,在show1,show2和其他变量上都会产生空间要求,只不过上述对象储存空间总要求比第一种方法所需要的更少,所以在储存空间使用上更占优势)

1.3.2 空间换时间

当具体应用计算时间要求远远高于储存空间要求时,空间换时间也是解决问题的一种思路。
例如:战斗机执行任务,需要较快的计算速度,所以这个时候就应该牺牲空间换取运行时间。
同理,上述第一种方法的空间占用虽高,但时间复杂度只有O(1),而第二种方法的时间复杂度至少为O(15)。所以,当时间要求远远高于存储空间要求时,应选择第一种方法。

1.4 算法实践基础

解决算法问题需要经历四步:确定需求设计算法算法代码实现验证算法
1.确定需求:
确定需求就是明确确定需求内容的规则要求,即读懂题意。
例如上述求自然数的阶乘的问题,就需要知道什么叫自然数、什么叫阶乘或阶乘的规则、什么叫输入输出规则
2.设计算法
对于简单的算法问题,可以直接别写代码。但对于复杂的算法问题,应该做好算法设计这一步。这里介绍以下人工算法思路图
人工算法思路图就是通过数据和整体实现的逻辑思路展现,限定算法的实现框架,为算法代码实现提供设计思路,并提供人工计算结果(或代表性部分计算结果)。以求阶乘的问题为例,如下图即为人工算法思路图具体体现:
在这里插入图片描述
例如:开始求10的阶乘,依据上述思路编写代码:

def get_factorial(n):
    if n == 0:
        return 1
    else:
        m = 1
        for i in range(1, n+1):
            m *= i
        return m


print(get_factorial(10))

求得10的阶乘结果为3628800
4.验证算法
由于步骤2中的图已经计算好了结果,所以算是进行了算法验证。当算法比较复杂时,该图还具有代码调试校对的作用,用来检查代码步骤与设计思路是否一致。

1.5 算法实现技巧

  1. 算法偏爱循环。绝大多数算法都会涉及循环(递归)操作,当看到迭代、反复、递归、回溯、累加、累乘等字眼,能快速地想到要用循环语句解决问题。
  2. 人工数据代入测试法。在纸上,用手算或者数据代入可以加深理解,明白算法设计思路。
  3. 前后对照法。对于需求不清晰的问题,可以对照实现代码和输出过节进行对照。
  4. 输出中间值法。即学会用debug或者print语句进行算法理解。
  5. 嵌套或组合法。一些复杂的问题需要考虑算法嵌套或者组合不同的算法解决问题。此时可采用逐个击破的方法,即先解决局部问题,再组合解决整体问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值