(2017/2/21:坚持学python,坚持写博客,这是第三篇;万事开头难,坚持会更难,希望我能坚持住!)
昨天薛老师留的作业题目是:“输入x1,y1;x2,y2;x3,y3 三个坐标构成一个三角形,随机输入一个坐标判断是否再三角形范围内。”
拿到这个题目,
首先想到的要如何从键盘输入三个参数,目前为止我对pyton键盘输入函数的了解仅限于raw_input() 函数,但是这个函数貌似一次交互只能读入一个参数;能否一次交互实现多个参数的输入呢?百度一下后发现raw_input()函数为python2中所有,input()函数未python3所有,而raw_input()函数还有一个split()方法,可以用诸如逗号、空格符之类的字符对对多个参数进行分隔。于是这个问题解决。
然后就是要对输入的三个参数进行判断,看看是否满足构成三角形的条件(任意两边之和大于第三边,任意两边之差小于第三边)
最后,当三个坐标参数满足三角形构成条件的时候,要交互式提示用户输入第四个点坐标,并判定第四个点是否在三角形内。这是个数学问题,但是当时并未上网查询此问题的算法,因为自己有个思路,就是第四个点的横坐标不能比三个顶点的横坐标最大值还大,也不能比三个顶点横坐标最小值还小;同样,第四个点的纵坐标也不能大于三个顶点纵坐标的最大值,并且不能小于三个顶点纵坐标的最小值。这两个坐标一旦有一个超出范围,必在三角形之外,所以想当然地以为,其补集的点就一定在三角形之内。于是按照思路写出了下面的代码,并用点(0,0),(4,0),(2,2)作为顶点,外点(0,2)和内点(2,1)进行判断测试,代码如下图:
这段代码运行没有错误,且能正常判定三角形是否构成,用点外点D(5,6)和内点D(2,1)进行测试,也都能成功判断D与三角形的位置关系,执行结果如下图:
外点测试:
内点测试结果如下图:
构不成三角形测试:
看到结果后窃喜,以为写的不错,结果回到宿舍,被舍友一眼挑出毛病,比如她找到一点D,如下图,本应在三角形外,但用我的程序会计算出它在形内:
这是为什么呢?想了一下,我用来判断点与三角形位置关系的条件是:第四个点D的横纵坐标中只要有一个超出范围,就必在形外,而其对立面,则认为必在形内;但是我的思路果然有漏洞,比如上图中D点,本在外面,却被判定为在内部了。好好反思一下,我的算法判定条件的几何意义应该是一个矩形,是这个三角形的最小外接矩形,所以用这个算法判断出的结果应该是点与这个外界矩形的拓扑关系,而不是它与三角形本身的关系。这实际上是一个数学问题,必然有与之对应的算法,只是觉得我认为是灵感突发的想法居然不对,未免有些失落。
然后反复斟酌这个问题,到网上查了一下相关算法,发现网上盛传的算法有这么4种,一是面积法,若点在三角形内,则该点和三个定点连线构成的三个小三角形面积之和与大三角形面积相等,如果不相等,则说明点在外部。(计算面积要用海伦公式,已知三边求面积)二是夹角法,若点在形内,则将该点到三顶点连线,计算这些连线与三角形各边夹角(当然要用余弦定理了),这些夹角的和应该等于大三角形的内角和,即180度,否则若不等,必在形外。法三是方向法,这是GIS教材上一道经典考试题,具体不做介绍。法四是垂心法,比较不好理解,也不做介绍。由于不想落于俗套,所以想找一种不常用而又简单的算法,于是晚上开启做梦模式。
一晚上的梦没有想出来,早上却想起来三边长度判定是否出问题了?想了半天,任意两边之和大于第三边,任意两边之差小于第三边,这两个条件到底用不用同时满足呢?
走到半路,就想出啦,就是个不等式的变换:
a+b>c => c-b<a ; c-a<b
b+c>a => a-c<b ; a-b<c
a+c>b => b-c<a ; b-a<c
上面每个式子都能推导出另外两个式子,也就是说,只要满足了任意两边之和大于第三边,那么任意两边之差必然小于第三边,这是隐含的,所以两个判定条件是等价的,只需满足其一即可。(这个问题说明,我的基础数学已经忘得差不多了,而往往基础的东西,越难完全理解和掌握)
这说明我用来判断三角形能否形成的条件没错。下面继续想点和三角位置关系的算法。想起了昨晚无意中看到网上一个人的一句话:用斜率判断最直观了。
当时没在意,到了公司就有了想法,这个思路是可行的,可是要怎么判定呢?画了画,有了思路,若D在ABC内,则直线AD的斜率必然介于AB和AC之间;同时BD斜率也必须介于BC和BA之间;并且DC斜率也必须同时介于CA和CB之间;否则D必将位于形外。根据这个思路,修改昨晚的代码并测试,如下:
这次用同事提出的那个点在测试,就没有问题了。这个算法成立。反思一下,我昨天的思路到底为什会出问题呢?结合今天的代码来看,其实昨天的思路未必就不可行。只要修改某些逻辑连接符就能使昨天的代码正确。
昨天判定的条件是(x4如果比max(x1,x2,x3)还大,或x4如果比min(x1,x2,x3)还小)或(y4如果比max(y1,y2,y3)还大或y4如果比min(y1,y2,y3)还小)则必在外部,即横纵坐标只要有一个超限,就在外面;思路是从反面考虑,认为分析的可以少一些,认为补集就在内部,但这种带有多个或连接的逻辑表达式,补集并不是在内部,而导致出错,那么这个思路就不能用吗?可以用,可以让X和Y同时满足不超限,那么这样的点D必在内部,这次它的补集就是外部。我们不可能找到一点,它的横纵坐标都不超限,却在外部的,这说明这个逻辑正确,算法可行。这个问题的本质是充分性和必要性的关系。有些时候,逻辑关系正推和反推是不等价的,所以不能颠倒。
这样,只需对昨晚代码进行简单地修改,只需修改点与三角形 拓扑关系判定的条件表达式就可以了,修改后代码如下:
至此,这个算法通过实验的验证,被证明可行。