正如我在<自然语言处理(NLP) - 数学基础(1) - 总述>一文中所提到的NLP所关联的概率论(Probability Theory)知识点是如此的多, 饭只能一口一口地吃了, 我们先开始最为大家熟知和最基础的知识点吧, 排列组合.
虽然排列组合这个知识点大家是相当地熟知, 也是相当地基础, 但是却是十分十分十分地重要.
NLP届掌门人斯坦福大学的Daniel Jurafsky(D. 朱夫斯凯)和科罗拉多大学James H. Martin(J. H. 马丁)在其NLP巨作《自然语言处理综论》一书第二版第5页中提到:“几乎所有的语音处理和语言处理问题都可以这样来表述: 对于某个歧义的输入给出N个可能性, 选择其中概率最高的一个.”
现在让我们来看看排列组合概念的定义吧: 所谓排列,就是指从给定个数的元素中取出指定个数的元素进行排序。所谓组合则是指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。
See, 与掌门人上面这句话相比, 是如此的相似!
排列组合有两条基本原理组成:
- 加法原理(分类计数法)- 做一件事,完成它可以有n类办法,在第一类办法中有m1种不同的方法,在第二类办法中有m2种不同的方法,……,在第n类办法中有mn种不同的方法,那么完成这件事共有N=m1+m2+m3+…+mn种不同方法。每一种方法都能够直接达成目标。
- 乘法原理(分步计数法). 做一件事,完成它需要分成n个步骤,做第一步有m1种不同的方法,做第二步有m2种不同的方法,……,做第n步有mn种不同的方法,那么完成这件事共有N=m1×m2×m3×…×mn种不同的方法。
如何区分这两个原理呢?
要做一件事,完成它若是有n类办法,是分类问题,第一类中的方法都是独立的,则使用加法原理;做一件事,需要分n个步骤,步与步之间是连续的,只有将分成的若干个互相联系的步骤,依次相继完成,这件事才算完成,则使用乘法原理。
完成一件事的分“类”和“步”是有本质区别的,因此也就将两个原理区分开来了。
根据以上原理衍生出很多方法, 包括且不限于:
- 捆绑法. 指在解决对于某几个元素要求相邻问题时,先整体考虑,将相邻元素视作一个整体参与排序,然后再单独考虑这个整体内部各元素间顺序。注意:其首要特点是相邻,其次捆绑法一般都应用在不同物体的排序问题中。
- 插空法. 某些元素不相邻的排列组合问题,即不邻问题,可采用插空法,即在解决对于某几个元素要求不相邻的问题时,先将其它元素排好,再将指定的不相邻的元素插入已排好元素的间隙或两端位置,从而将问题解决的策略。用这种方法解题思路清晰、简便易懂。
- 插板法. 指在解决若干相同元素分组,要求每组至少有一个元素,采用将比所需分组数目少1的板插入元素之间形成分组的解题策略。
因为本节是第一节, 所以在做习题和代码示例之前, 我们需要去安装Python 3和对应的开发工具Visual Studio Code, 同时建议先照着Getting Started with Python in VS Code过一遍. (预计耗时要1-3个小时,包括update Xcode, 要注意安排好时间).
如果是时间赶, 可以使用python online editor去做. https://www.tutorialspoint.com/python/index.htm
从学术和参加算法比赛的角度来讲, 我们应该尽量不要用任何库. 然而现在我是从工程的角度出发, 所以我会使用到大名鼎鼎的NLP工具包NLTK以及Python里的数学库Scipy 和 itertools (虽然本节不会用全三个库, 但是以后的章节会全用到的)
这点和前文所讲的"AI并不只是调现有云和库API即可"并不冲突, 因为:
- 现有云和库API是由各自的厂商和开发者写的, 会有各种差异. 而数学库却是不会有差异的, 用A厂商写出的库去算1+2会等于3, 换了B厂商写出的库去算1+2一样会等于3.
- 从下面的习题答案和对应的代码示例看到, 正确的解题思路才是关键, 解题思路对了接下来的求解就可以扔给Python对应的方法了. 然而解题思路却是要穿透现有云和AI库API的.
在开始写代码做习题之前我们先按照如下链接去热身和确认环境是好的:
https://blog.csdn.net/qq_41185868/article/details/79682406 安装Python的Scipy包
如果遇到ssl证书问题则参考这篇文章去解决https://www.cnblogs.com/jiyanjiao-702521/p/9960071.html
如果还是遇到错误, 那就去按照https://www.jianshu.com/p/dbf20c6792fe 这篇文章一劳永逸的解决问题, 不过需要下载Anaconda, 大概要五六百M, 记得是要用bash安装. 安装完之后就可以sudo conda install scipy了, 然后再用Anaconda navigator去lanunch VS Code.
https://www.geeksforgeeks.org/permutation-and-combination-in-python/
https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.perm.html
https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.comb.html
https://www.nltk.org/api/nltk.html#nltk.probability.ConditionalFreqDist 里的nltk.util.choose(n, k) (本节不会用到, 以后章节会)
然后开始做题
捆绑法
题目: 六个各不相同的元素进行排列, 其中指定的两个元素必须要排在一起, 总共有多少种排法?
答案: 这指定的两个元素需要捆绑成一个元素进行排列, 然后这捆绑在一起的元素内部在进行排列. 最后使用分步计数法(即乘法原理)进行相乘. 所以应该是n5.5 X n2.2 = 240种
代码实现:
from scipy.special import perm result = perm(5, 5, exact=True) * perm(2,2, exact=True) print(result)
插空法
题目: 对6个各不相同的A类元素和4个各不相同的B类元素进行排列, 任意两个B类元素不得相邻, 总共有多少种排法?
答案: 因为B元素不得相邻, 则需要使用插空法, 先排6个A元素A6,6 , 在6+1个空位置中再对4个B元素进行排列 a 4 7, 最后使用分步计数法(即乘法原理)进行相乘得出为604800种
代码实现
from scipy.special import perm result = perm(6, 6, exact=True) * perm(7,4, exact=True) print(result)
插板法
题目:将8个完全相同的元素分成3组, 每组至少要有一个元素, 总共有多少排法?
答案: 首先因为元素是完全相同的, 所以不需要排列, 因此这是个组合问题. 然后使用插板法, 将n个相同元素分成m组, 且每组必须有元素就相当于在n-1个空中插m-1个板, C2,7 共21种 (不是42种是因为是求解组合而不是排列)
代码实现
from scipy.special import comb result = comb(7, 2, exact=True) print(result)
如果时间紧急没有办法装Python环境这怎么办?
So easy, 使用https://www.calculator.net/permutation-and-combination-calculator.html 进行在线求解就行了,.
或者使用手机或者iPad下载microsoft math resolver去求解即可.
以上两个事实再次证明了, 正确的解题思路才是关键, 只要解题思路正确了, 后面的求解过程是很方便很easy的. 这点不就是我在<2019年总结>一文中所说到的”做正确的事情, 然后正确的做事, 比勤劳更重要”的体现吗?
不过再次提醒学术和要面试的同学们, 不要学我把求解过程扔给计算机, 这样会让你面试挂的. 我是从纯工程角度出发的.
如何得出解题思路关键点总结:
- 元素是否相同, 如果元素完全相同则可以直接上组合不用考虑排列.
- 元素类别是否相同
- 元素是否容许相邻
有用链接:
https://betterexplained.com/articles/easy-permutations-and-combinations/
https://www.csharp-console-examples.com/loop/foreach-statement/permutation-and-combination-calculator-in-c/ 这是一个C#的排列组合的简单示例,
https://www.beatthegmat.com/mba/2009/10/12/permutations-and-combinations-an-easy-method
https://study.com/academy/lesson/permutation-combination-problems-practice.html
https://www.wikihow.com/Calculate-Combinations
https://www.mathsisfun.com/combinatorics/combinations-permutations.html
为了方便搜索资料, 现在列出本节所用到的英文术语名词:
概率论 - Probability Theory
排列组合 - permutation and combination
加法原理(分类计数法)- Addition rule 或者Addition theorem 或者Addition Principle
乘法原理(分步计数法)- Multiplication rule 或者Multiplication theorem或者Multiplication Principle
捆绑法 - bonding method (百度百科上的翻译是错的, 这才是对的)
插空法 - Interpolation method
插板法 - plate insertion method (目前尚存疑)
感悟:
真的是长江后浪推前浪啊, 现在的本科生直接就用英语原版的数学教材, 一旦毕业再工作个三五年就能秒杀很多十几年经验的人. 中国很多程序员35岁失业其中两个原因就是第一大学教材越到后面越先进, 后来甚至用上了英语原版的教材. 第二就是当年读书的英语教学水平远差于现在的英语教学水平, 从而导致三到五年工作经验的程序员无论是理论基础还是英语水平都超越了十几年工作经验的程序员.
不过没关系啦, 虽然落后了, 努力再追上不就行了吗