一个几何算法的建立过程,往往要经过三个阶段。
退化:算法或机器学习模型因输入数据分布变化(Data Drift)或环境动态性,预测准确性下降。
例如,推荐系统模型因用户兴趣迁移,推荐结果逐渐偏移实际需求。
鲁棒性:计算机系统在面临异常输入、资源波动、攻击或意外故障时,仍能维持核心功能、稳定性和性能的能力。
算法鲁棒性例子:
- 场景:算法对噪声数据、缺失值或分布偏移的适应性。
- 例子:图像识别模型在低光照、模糊图像中仍能准确分类。
-
第一个阶段,需要理解我们正在处理的几何概念。
然而,思路总是会被一些问题打乱,因此要尽力去忽略这些问题。这类令人讨厌的问题,有的来自多点共线,有的来自垂直线段。在设计或理解算法的最初阶段,暂且将这些退化情况搁到一边,是一种十分有益的策略。
-
第二个阶段,必然对前一阶段所设计的算法进行调整,使之即使对退化情况也依然能正确处理。在完成这一任务时,初学者往往会将一大堆的特殊情况引入到算法之中。
然而在很多情况下,还有更好的办法——通过对问题的几何性质做再次的分析,往往可以将各种特例集成到一般情况当中。例如在凸包算法中,只要使用字典序来代替x坐标顺序,就可以处理多个点具有相同x坐标的问题。
在本书的大多数算法中,我们始终都尽量去采用这种集成式的方法,来处理特殊情况。当然,在你初次阅读这些算法的时候,还是不要过于这些特殊情况,这样才可以使你更好地理解算法。只有在理解了算法对一般情况的处理过程之后,才能开始考虑有关退化的问题。
当今计算几何界的大多数研究人员都已经意识到:自己所做的"一般性位置假设"(general position assumption),在实际应用中并不成立;一般而言,集成式的处理方法,是处理特殊情况的最佳方法。此外,还有若干一般性的方法——所谓的"符号扰动发"(symbolic perturbation scheme)。在算法的设计与实现过程中,借助于这类手段,你可以不必考虑退化情况——即使退化情况出现了,算法依然可以正常运行。
-
最后一个阶段是具体的实现。这时,需要考虑到基本的操作(比如,测试某个点究竟是位于一条有向直线的左侧、右侧,还是落在其上)。
在具体实现的阶段还会出现另一个问题——企图"对实数进行精确运算"是不现实的——因此对于其后果,我们不可不有所了解。在几何算法的实现过程中,所遇到的种种麻烦,究其根源,往往可以归结为鲁棒性(robustness)的问题。
一种方法就是借助于某个(根据具体的问题可能采用整数、有理数甚至代数数来)支持精确运算(exact arithmetic)的软件包,然而运行速度会因此变得很慢。
另一种方法是对算法本身作适当调整,使之能够检测到可能出现的不一致问题,并采取适当的措施以避免程序崩溃。此时,就不能保证算法的输出一定正确,因此,确定其输出的精确性就变得很重要。(这也是前一节设计凸包算法时所做的一项工作——尽管算法给出的多边形有可能并不凸,但是我们还是可以肯定,输出的多边形在结构上是正确的,而且它与凸包十分接近)。
最后,还可以根据具体的输入,以数值的形式预测出,为了得到问题的正确结果,究竟需要达到多高的精度。