用数学编程可以有多悠美

0. 问题来源

  让我们从一个很简单的问题开始:如何判断线段(A1, A2) (B1, B2)是否相交(假设A2>A1, B2>B1)?

1. 欣赏一下

  对于以上问题,如果我给出如下代码:

  IsJoin := ABS(A1+A2-B1-B2) <= (A2-A1+B2-B1);

  此时,你一定对这行代码的有效性充满了疑惑。

  没关系,下面让我们从普通解法开始,一步步推导出上面的结论。

2. 普通解法

  根据分析,两条线段的关系可能有如下情况:

  1:  A1-------A2      B1---------B2

  2:  B1---------B2      A1-------A2

  3:  A1----B1---A2--------B2

  4:  B1----A1----B2-----A2

  5:  A1---B1---------B2----A2

  6:  B1------A1-------A2---B2

  可见,只要A1,A2,B1,B2四个点,只要有一个点落在另一条线段范围内,两条线段就相交。

  代码如下:

IsJoin := A1>=B1&&A1<=B2 || A2>=B1&&A2<=B2 || B1>=A1&&B1<=A2 || B2>=A1&&B2<=A2;

  这是我们马上就能想到的解法,相信不会有人怀疑它的有效性。

  但是这段代码最大的问题是重复度和相似度太高,很容易写错却很难发现。

3. 数学方法

  下面,我们通过数学方法,给出这个问题的数学解。

  从几何意义上,判断两条线段相交,可以通过判断两条线段中心点的距离小于线段长度和的一半来实现。

  而两个点X,Y的距离,可以用两点差的绝对值表示:ABS(X-Y)

  则两条线段中心点距离为:ABS((A1+A2)/2 - (B1+B2)/2)

  两条线段长度和一半可以表示为 (A2-A1)/2 + (B2-B1)/2

  因此,判断两条线段是否相交,可以表示为 ABS((A1+A2)/2-(B1+B2)/2) <= (A2-A1)/2+(B2-B1)/2

  两边同时乘以2并化简即得:ABS(A1+A2-B1-B2) <= (A2-A1+B2-B1)

 4. 扩展问题

  进一步,如果我们把问题更深入一步,描述为:

  如何判断线段(A1,A2)和(B1,B2)谁跟线段(C1,C2)相交更深。

  如以下情况,(B1,B2)与(C1,C2)相交更深。

  A1------C1-A2--------B1-----C2------B2

  相信此时,普通解法只能说抱歉打扰了,臣妾做不到哇。

  但此时,数学解法还可以进一步优化,如何判断两条线段的距离?

  实际上,通过判断两条线段中心点的距离跟线段长度和一半的比值,既可以判断两条线段是否相交,更能评价两条线段相交有多“深”。

  DistanceAC := ABS(A1+A2-C1-C2)/(A2-A1+C2-C1);
  DistanceBC := ABS(B1+B2-C1-C2)/(B2-B1+C2-C1);

  此时,判断线段A,C是否相交,只需要改为判断 DistanceAC<=1 即可。

  判断线段A,C是否深度相交,只需要判断 DistanceAC<=0.8 即可。

  相应的,判断AC和BC谁相交更深,只需要判断 DistanceAC<=DistanceBC 即可。

5. 结论

  通过以上解法分析,可以看到,程序的灵魂其实是数学原理。

  不要以为花三天时间学会了一门编程语言,会写几行代码就是合格程序员了。

  在计算机领域,成就最大的人,都不一定会写代码,但一定是优秀的数学家或擅长用数学思考的人。

  程序之美在于数学之美,隐藏在悠美代码背后的数学原理,才是程序的灵魂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值