![算法零基础一本通(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=1739528919-yqdxfVhVk35IFGszvay8ZuoicJkau8Xn-0-384378cc8265d9e53310560406d14899)
假设一个数列有3个数,分别是1、2和3,这个数列的排序方式有下列6种。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45493.jpg?sign=1739528919-IJrq4hbI24I7ytBwkk6aw1gNKsF7WbLb-0-d09289e8b52416808edf31909a2447a4)
上述可以列出所有排列的可能方法称枚举方法(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=1739528919-3cC5Y9HJGbyQDVOTXvGXDShkVETbeFGi-0-4bd7275aad89cf46f6ccad372af5e379)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P13_45500.jpg?sign=1739528919-zqz2605lrNbn7tUvnxRKe1zzY2FcjdhG-0-0642cb271a6f2403fb9ca842aa46b48b)
注 在程序语言内部是使用栈(stack)处理递归式的调用,本书在1-4-2节与5-5节会一步一步拆解此程序有关栈内存的变化。
假设有一个数列内含30个数,则组合种数如下:
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45639.jpg?sign=1739528919-C8Qbwkpb9VFbKzjnzL1qWYkrZAlLMiHV-0-0e954a71a9218a8e114f819b0e8362e4)
假设一个数列有30个数,分别是1~30,我们要将数列从小到大排列成1, 2, …,30。假设所使用的方法是枚举方法,对所有的排列一个一个处理,如果不是从小排到大,则使用下一个数列,直到找到从小排到大的数列。由阶乘得到的排列组合方式的种数,就是将数列数据从小排到大,最差状况需要核对的次数。
注 枚举方法的特色是一定可以找到答案。
程序实例ch1_2.py:延续前面概念,假设超级计算机每秒可以处理10兆个数列,运气最差的话,请计算需要多少年可以得到从小排到大的数列。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45640.jpg?sign=1739528919-1uuWrZZJWef4X6kxZDYGxqla8fVqVrIT-0-96120cb55329dd19d2ed928c989b7fda)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45641.jpg?sign=1739528919-L4AWGrKWifMPl40xzIeutvZ8zTW62exq-0-4bd894f25a9987ee71f50c7a1f7108bd)
从上述执行结果可知,仅仅对含30个数的数列排序需要8411亿年才可以得到结果,读者可能觉得不可思议,笔者也觉得不可思议。一个程序,从宇宙诞生运行至今仍无法获得解答。
1.宇宙诞生
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P14_45645.jpg?sign=1739528919-FWlbHknatVCTaFxktDeJJRKcluvXGEE6-0-d24cb992b2ae8dcde825a559c23dfb2c)
2.银河系诞生,距宇宙诞生约7亿年
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P15_45646.jpg?sign=1739528919-IhOxMsVeBA1vQj74xQAzsSlWryHV7cD8-0-9b89133e800e2b9568a70e88cf95f5c2)
图片由智利伯瑞纳天文台拍摄,取材自下列网址
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=1739528919-jM1x3uRRyVN3yYUqoeQAYlExYLD3ZM4H-0-6118d27d2087a40b42171e86438eba16)
4.现代,距宇宙诞生约137亿年
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P15_45653.jpg?sign=1739528919-2PUrZforzoW7pcesQYWcixbqATWQ00yk-0-258ce6b61b268bd77a4d052a2ae82c2b)
Python有一个itertools模块,此模块内有permutations( )方法,这个方法可以枚举列出元素所有可能的位置组合。
程序实例ch1_3.py:列出列表元素1、2、3所有可能的组合。
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_65736.jpg?sign=1739528919-2rDiQ4q1tTTh275tfA0G6LSpeR6z1e1c-0-c8feee0b9c4c960a93804586b2468200)
执行结果
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_45655.jpg?sign=1739528919-jtiGSVTU6iTGDcFzlWSXv6uMA7lyAyfn-0-2c5d9f48aff57a3c42f364d9bc7a310b)
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=1739528919-zAoPs7KIVI0VHf4bt4DBPoWpADf49jBg-0-d23b6bc8fdcea675441ceb1ded7a04ad)
从上述公式也可以得到下列结果:
![](https://epubservercos.yuewen.com/6BDBC6/23721658309542706/epubprivate/OEBPS/Images/Figure-P16_45657.jpg?sign=1739528919-xN5gn1hpDR5sCT70zfLlkhg65t1h1Pu8-0-60f0bcfed1c42dc931ec8fa5fd15f813)
假设这个数列有30个数,相当于n等于30,可以得到n2等于900,前一小节我们假设超级计算机每秒可以处理10兆(1013)个数列,故采用这种算法所需时间如下:
900 / 1013
结果远远低于1秒。所以在设计与使用算法时,好的算法和不好的算法有着天壤之别。