![算法零基础一本通(Python版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/51/44510051/b_44510051.jpg)
1-2 不好的算法与好的算法
1-2-1 不好的算法
一个好的算法能在一秒内就得到答案,相同的问题用了一个不好的算法,可能计算机执行了上千亿年也得不到答案。
假设一个数列有2个数,分别是1和2,这个数列的排序方式有下列2种。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45492.jpg?sign=1739627217-OvMIjydFBcrS8kX78YxNlgBWGCpmvCxT-0-6b9befd11a789ff5f87277f9e891b1c2)
假设一个数列有3个数,分别是1、2和3,这个数列的排序方式有下列6种。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45493.jpg?sign=1739627217-f7IJ97r4dT1iUWyHZByUH36kDeasgTNj-0-580826c6288f8d07f1f2ad88b4a30f4e)
上述可以列出所有排列的可能方法称枚举方法(Enumeration method),特色是如果有n个数,就会有n!种组合方式,如下所示。
2! = 2 * 1 = 2 3! = 3 * 2 * 1 = 6
上述n!又称阶乘数,阶乘数概念是由法国数学家克里斯蒂安·克兰普(Christian Kramp,1760—1826)所发表,他虽学医但是却同时对数学感兴趣,发表了许多数学文章。
程序实例ch1_1.py:输入n,程序可以列出它的阶乘结果,这个程序相当于列出数列内含n个数的组合方式有多少种。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45499.jpg?sign=1739627217-yG7qeVdczxq4RioGYpxdioDgJtRcgos2-0-b01ec6651688db10364dea1987b5e20f)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45500.jpg?sign=1739627217-I2NHnK0qbN697pdgz7Mw761WCX4aR13w-0-f45cfcea1e1b3abf3a11aa01ba02ee72)
注 在程序语言内部是使用栈(stack)处理递归式的调用,本书在1-4-2节与5-5节会一步一步拆解此程序有关栈内存的变化。
假设有一个数列内含30个数,则组合种数如下:
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45639.jpg?sign=1739627217-p1eyFau9g7ckCx8l0aP3GpW36ZnJ7UH8-0-2b80c8c76ba4b2a6b8e8f8da5520c1de)
假设一个数列有30个数,分别是1~30,我们要将数列从小到大排列成1, 2, …,30。假设所使用的方法是枚举方法,对所有的排列一个一个处理,如果不是从小排到大,则使用下一个数列,直到找到从小排到大的数列。由阶乘得到的排列组合方式的种数,就是将数列数据从小排到大,最差状况需要核对的次数。
注 枚举方法的特色是一定可以找到答案。
程序实例ch1_2.py:延续前面概念,假设超级计算机每秒可以处理10兆个数列,运气最差的话,请计算需要多少年可以得到从小排到大的数列。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45640.jpg?sign=1739627217-XVtch5lxRyciX23y6v7VApd2v2O0WHw3-0-856f9d656d7552e1e24a4e26326434a2)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45641.jpg?sign=1739627217-M33L46QDzM34wbnZD3AqyQC41Elcb6E6-0-861e5d3c53515f5c2dedc54d9fd40e1e)
从上述执行结果可知,仅仅对含30个数的数列排序需要8411亿年才可以得到结果,读者可能觉得不可思议,笔者也觉得不可思议。一个程序,从宇宙诞生运行至今仍无法获得解答。
1.宇宙诞生
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45645.jpg?sign=1739627217-UecJMGcH0e62UAiuaOqYeYyVfUx3YAdT-0-38c33265d243098a55d4662ec336c6e2)
2.银河系诞生,距宇宙诞生约7亿年
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P15_45646.jpg?sign=1739627217-BocYEVIa7jlq589fNTgCWCH3Pt8PnACB-0-161dc6b84b941c2f374b48e737745772)
图片由智利伯瑞纳天文台拍摄,取材自下列网址
https://zh.wikipedia.org/zhtw/%E9%93%B6%E6%B2%B3%E7%B3%BB#/media/File:Milky_Way_Arch.jpg
3.地球诞生,距宇宙诞生约90亿年
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P15_45652.jpg?sign=1739627217-ZOz8aPvlj4lMOWphpmOzjhHkrALU08ds-0-0488b88d8d9570008c1bb7817c2c5c61)
4.现代,距宇宙诞生约137亿年
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P15_45653.jpg?sign=1739627217-6KwIQDjRgqqDhlCWVJrS94FK0SDU2Knr-0-58c28e553550ddfba213411a7851f9d4)
Python有一个itertools模块,此模块内有permutations( )方法,这个方法可以枚举列出元素所有可能的位置组合。
程序实例ch1_3.py:列出列表元素1、2、3所有可能的组合。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_65736.jpg?sign=1739627217-DQNIH0vprBDJJIxYWhEuXP0lfLBPdjfe-0-8cc1e6086535cdc1c879fd9df33b1c5d)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_45655.jpg?sign=1739627217-pwFbXZ26SCOOfRev3MU6mafmBwV7ca0L-0-9538f447899006b24613aa6240506d8f)
1-2-2 好的算法
相同问题如果使用好的算法,可能不用1秒就可以得到答案。下列是笔者使用选择排序法处理相同问题所需的时间。
第1循环是从n个数中找出最小值,放到新的数列内,此时需要确认n个数字。第2循环是从n-1个数中找出最小值,然后放到新的数列内,此时需要确认n-1个数字。第3循环是从n-2个数中找出最小值,然后放到新的数列内,此时需确认n-2个数字。最后执行n循环就可以产生新的从小排到大的数列。整个循环过程的数学概念表示如下:
n + (n-1) + (n-2) + … + 2 + 1
上述计算了所需确认的数字个数,也可以用下列方法表示:
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_45656.jpg?sign=1739627217-JLqCnUbMKpFCjFgxgfRQhMyOO6V4GMs2-0-f2f1aac37cde67d28305d5f4f8185d6a)
从上述公式也可以得到下列结果:
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_45657.jpg?sign=1739627217-Nk2LmmAZmbCaPZSLStCtJFw6D4GpFBmL-0-d629a2f0d605db0233ed5c30c5d8056f)
假设这个数列有30个数,相当于n等于30,可以得到n2等于900,前一小节我们假设超级计算机每秒可以处理10兆(1013)个数列,故采用这种算法所需时间如下:
900 / 1013
结果远远低于1秒。所以在设计与使用算法时,好的算法和不好的算法有着天壤之别。