- 算法零基础一本通(Python版)
- 洪锦魁
- 1207字
- 2022-07-29 15:07:44
1-2 不好的算法与好的算法
1-2-1 不好的算法
一个好的算法能在一秒内就得到答案,相同的问题用了一个不好的算法,可能计算机执行了上千亿年也得不到答案。
假设一个数列有2个数,分别是1和2,这个数列的排序方式有下列2种。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45492.jpg?sign=1739547130-uvi1YT7kG9kSG6xEE2p0PbguCtxuNFoP-0-045ba11f1560fdfffdbfee0aa403c2b2)
假设一个数列有3个数,分别是1、2和3,这个数列的排序方式有下列6种。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45493.jpg?sign=1739547130-6WOfalRES5V3Wo4S8Hh7zAgEPYwFcY9e-0-91ee97023a0bd594129affc8ca3633e5)
上述可以列出所有排列的可能方法称枚举方法(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=1739547130-4EDzCgMiffrAJCGTQ6WRNqLmBpzU1XZk-0-d041b830ecaeab1c2daa5b41b99c5349)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45500.jpg?sign=1739547130-sq1CnYG7eEhEU0sr3aAK2PQL2fJDw029-0-f38871ad99d82f881f43dfcc679720b6)
注 在程序语言内部是使用栈(stack)处理递归式的调用,本书在1-4-2节与5-5节会一步一步拆解此程序有关栈内存的变化。
假设有一个数列内含30个数,则组合种数如下:
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45639.jpg?sign=1739547130-B0XWsXBpKID3QicJOXJ7eZuJdNpAkn3y-0-7a17f87d7b087e83997aa7f6049a477d)
假设一个数列有30个数,分别是1~30,我们要将数列从小到大排列成1, 2, …,30。假设所使用的方法是枚举方法,对所有的排列一个一个处理,如果不是从小排到大,则使用下一个数列,直到找到从小排到大的数列。由阶乘得到的排列组合方式的种数,就是将数列数据从小排到大,最差状况需要核对的次数。
注 枚举方法的特色是一定可以找到答案。
程序实例ch1_2.py:延续前面概念,假设超级计算机每秒可以处理10兆个数列,运气最差的话,请计算需要多少年可以得到从小排到大的数列。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45640.jpg?sign=1739547130-LVFPWX62aG7zudfbnvTL32cqCGSMyr6b-0-4a1e5fa5c03533483b0da520ad46684d)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45641.jpg?sign=1739547130-U9gg0bZQrqPoJl7zxEh5wjrTaMgMxM5R-0-2f173130f9261c8b4bef176957bd7a52)
从上述执行结果可知,仅仅对含30个数的数列排序需要8411亿年才可以得到结果,读者可能觉得不可思议,笔者也觉得不可思议。一个程序,从宇宙诞生运行至今仍无法获得解答。
1.宇宙诞生
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45645.jpg?sign=1739547130-WoX97W8HWmuOcWrdvyFqYf0XAGLzStJk-0-fa0f9aed69fb5e6f9cc68c843efb1615)
2.银河系诞生,距宇宙诞生约7亿年
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P15_45646.jpg?sign=1739547130-nFiZRR498W0OLCgjqDqmRrvGcA0hvY4J-0-8d2e8b707dd21edaffbd8cdf34b7ae94)
图片由智利伯瑞纳天文台拍摄,取材自下列网址
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=1739547130-PjmuGkPSH3wPK8vl1MPLyJeAxBSTeOD1-0-a3093157b981e903c50704c34716d31a)
4.现代,距宇宙诞生约137亿年
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P15_45653.jpg?sign=1739547130-b27o08pxevDSwS2TXiuOeoHE3ZrHj5f2-0-54c5228319cbd1ffbb3c02a1d3ea801f)
Python有一个itertools模块,此模块内有permutations( )方法,这个方法可以枚举列出元素所有可能的位置组合。
程序实例ch1_3.py:列出列表元素1、2、3所有可能的组合。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_65736.jpg?sign=1739547130-JdkWCD7oO3z73Ae1snlloI54hnHjvVDR-0-fc3028e469cb333b0cdc57ecb65de652)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_45655.jpg?sign=1739547130-a1T550PwlZnOEfwoaEW94rYJvcXjIkBJ-0-665433f6bc2da20b0d9c84d12bbe2e9d)
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=1739547130-4qoL4psYuAAKHn4MxSrXv3699zNiWnvQ-0-10d179fca23d7228bde12fd12e0fbd02)
从上述公式也可以得到下列结果:
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_45657.jpg?sign=1739547130-GUal6SgxJy8j7B3lWOCQGPHxuSLbcSkW-0-c0e844268cb9e7b2c90e18ce9e803489)
假设这个数列有30个数,相当于n等于30,可以得到n2等于900,前一小节我们假设超级计算机每秒可以处理10兆(1013)个数列,故采用这种算法所需时间如下:
900 / 1013
结果远远低于1秒。所以在设计与使用算法时,好的算法和不好的算法有着天壤之别。