19.8 混合精度计算

相关章节

在本节中你将学到…

  • 混合精度计算的设计动机
  • Lazy_exact_nt的实现原理
  • 过滤机制的工作方式
  • 精度控制策略
  • 在CGAL中实现鲁棒的几何计算

19.8.1 概念解释

什么是混合精度计算?

混合精度计算是一种结合不同精度数值类型的计算策略。在几何计算中,通常结合:

  • 低精度快速计算(如double):用于大多数情况
  • 高精度精确计算(如Gmpq):用于关键决策
  • 区间算术:用于判断何时需要精确计算

类比理解: 想象你是一位侦探破案:

  • 快速筛选(double):快速排除明显无关的线索
  • 精确分析(Gmpq):对关键证据进行详细分析
  • 不确定性评估(interval):判断哪些线索需要进一步调查

为什么需要混合精度?

几何计算面临一个基本矛盾:

┌─────────────────────────────────────────────────────────────┐
│                     几何计算的困境                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   精确计算                    vs              快速计算       │
│   ─────────                                  ─────────      │
│   • 有理数运算(Gmpq)                         • 浮点运算     │
│   • 无精度误差                              • 硬件加速      │
│   • 结果可靠                                • 内存高效      │
│                                                             │
│   缺点:                                     缺点:          │
│   • 慢100-1000倍                            • 精度误差      │
│   • 内存开销大                              • 可能出错      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

解决方案:智能地结合两者——大部分时候用快速计算,只在必要时使用精确计算。


19.8.2 核心组件

1. 延迟精确数(Lazy_exact_nt)

Lazy_exact_nt是CGAL混合精度系统的核心。它同时维护:

  • 近似值(Interval_nt):用于快速判断
  • 精确值(ET):延迟计算,只在需要时求值
┌─────────────────────────────────────────────────────────────┐
│                  Lazy_exact_nt 结构                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌──────────────┐         ┌──────────────┐                │
│   │  Interval_nt │         │   ET (Gmpq)  │                │
│   │  [inf, sup]  │         │   (optional) │                │
│   │              │         │              │                │
│   │  总是存在    │         │  延迟计算    │                │
│   │  快速访问    │         │  缓存结果    │                │
│   └──────────────┘         └──────────────┘                │
│                                                             │
│   决策流程:                                                │
│   1. 使用Interval判断                                       │
│   2. 如果确定(全正/全负)→ 返回结果                        │
│   3. 如果不确定(包含0)→ 计算精确值                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2. 过滤机制(Filtering)

过滤是混合精度的关键策略:

// 三级过滤策略
Sign orientation_filtered(const Point& p, const Point& q, const Point& r) {
    // 第1级:静态过滤器(编译期误差界)
    double static_bound = compute_static_bound();
    double approx = compute_approximate(p, q, r);
    if (std::abs(approx) > static_bound) {
        return approx > 0 ? POSITIVE : NEGATIVE;
    }
    
    // 第2级:区间算术
    Interval interval = compute_interval(p, q, r);
    if (interval.is_positive()) return POSITIVE;
    if (interval.is_negative()) return NEGATIVE;
    
    // 第3级:精确计算
    return compute_exact(p, q, r);
}

3. 区间算术(Interval Arithmetic)

区间算术维护数值的可能范围,而非单一值:

计算 a + b:
  a ∈ [1.0, 2.0]
  b ∈ [3.0, 4.0]
  ─────────────
  a+b ∈ [4.0, 6.0]  (向下/向上舍入)

如果结果区间不包含0,则可以确定符号!

19.8.3 代码示例

基础示例:理解Lazy计算

#include <iostream>
#include <memory>
#include <math>
 
// ============================================
// 简化的Lazy_exact_nt实现
// ============================================
 
// 区间类
class Interval {
    double inf_, sup_;
public:
    Interval(double i, double s) : inf_(i), sup_(s) {}
    explicit Interval(double v) : inf_(v), sup_(v) {}
    
    double inf() const { return inf_; }
    double sup() const { return sup_; }
    
    bool is_positive() const { return inf_ > 0; }
    bool is_negative() const { return sup_ < 0; }
    bool contains_zero() const { return inf_ <= 0 && sup_ >= 0; }
    
    // 区间运算
    Interval operator+(const Interval& o) const {
        return Interval(inf_ + o.inf_, sup_ + o.sup_);
    }
    Interval operator-(const Interval& o) const {
        return Interval(inf_ - o.sup_, sup_ - o.inf_);
    }
    Interval operator*(const Interval& o) const {
        double p1 = inf_ * o.inf_, p2 = inf_ * o.sup_;
        double p3 = sup_ * o.inf_, p4 = sup_ * o.sup_;
        return Interval(
            std::min({p1, p2, p3, p4}),
            std::max({p1, p2, p3, p4})
        );
    }
};
 
// 精确数(简化版,实际使用Gmpq)
class ExactRational {
    long num_, den_;
public:
    ExactRational(long n = 0, long d = 1) : num_(n), den_(d) {
        if (den_ < 0) { num_ = -num_; den_ = -den_; }
    }
    
    ExactRational operator+(const ExactRational& o) const {
        return ExactRational(num_ * o.den_ + o.num_ * den_, den_ * o.den_);
    }
    
    ExactRational operator-(const ExactRational& o) const {
        return ExactRational(num_ * o.den_ - o.num_ * den_, den_ * o.den_);
    }
    
    ExactRational operator*(const ExactRational& o) const {
        return ExactRational(num_ * o.num_, den_ * o.den_);
    }
    
    bool is_positive() const { return num_ > 0; }
    bool is_negative() const { return num_ < 0; }
    bool is_zero() const { return num_ == 0; }
    
    double to_double() const { return static_cast<double>(num_) / den_; }
    
    friend std::ostream& operator<<(std::ostream& os, const ExactRational& r) {
        os << r.num_;
        if (r.den_ \!= 1) os << "/" << r.den_;
        return os;
    }
};
 
// 延迟精确数
template <typename ET>
class LazyExact {
    mutable std::unique_ptr<ET> exact_;  // 延迟计算的精确值
    Interval interval_;                   // 近似区间
    mutable bool computed_;
    
public:
    // 从double构造
    explicit LazyExact(double d) 
        : exact_(nullptr), interval_(d), computed_(false) {}
    
    // 从精确值构造
    LazyExact(const ET& e)
        : exact_(std::make_unique<ET>(e)), 
          interval_(e.to_double()),
          computed_(true) {}
    
    // 延迟加法
    LazyExact operator+(const LazyExact& o) const {
        LazyExact result;
        result.interval_ = interval_ + o.interval_;
        // 不立即计算精确值
        result.computed_ = false;
        return result;
    }
    
    // 获取区间(快速)
    const Interval& interval() const { return interval_; }
    
    // 获取精确值(可能触发计算)
    const ET& exact() const {
        if (\!computed_) {
            compute_exact();
        }
        return *exact_;
    }
    
    // 判断符号(使用过滤)
    int sign() const {
        // 第1级:区间判断
        if (interval_.is_positive()) return 1;
        if (interval_.is_negative()) return -1;
        if (\!interval_.contains_zero()) {
            return interval_.inf() > 0 ? 1 : -1;
        }
        
        // 第2级:精确计算
        const ET& e = exact();
        if (e.is_positive()) return 1;
        if (e.is_negative()) return -1;
        return 0;
    }
    
private:
    LazyExact() : exact_(nullptr), interval_(0), computed_(false) {}
    
    void compute_exact() const {
        // 这里简化处理,实际会递归计算操作数
        exact_ = std::make_unique<ET>(ET(static_cast<long>(interval_.inf())));
        computed_ = true;
    }
};
 
int main() {
    // 创建延迟精确数
    LazyExact<ExactRational> a(ExactRational(1, 3));  // 1/3
    LazyExact<ExactRational> b(ExactRational(1, 6));  // 1/6
    
    // 延迟加法(不立即计算)
    auto c = a + b;  // 应该是 1/2
    
    // 使用区间快速判断
    std::cout << "Interval: [" << c.interval().inf() 
              << ", " << c.interval().sup() << "]" << std::endl;
    
    // 获取符号(使用过滤)
    std::cout << "Sign: " << c.sign() << std::endl;
    
    // 获取精确值(触发计算)
    std::cout << "Exact: " << c.exact() << std::endl;
    
    return 0;
}

中级示例:三级过滤系统

#include <iostream>
#include <math>
#include <algorithm>
 
// ============================================
// 三级过滤方向判断
// ============================================
 
enum Sign { NEGATIVE = -1, ZERO = 0, POSITIVE = 1, UNCERTAIN = 2 };
 
struct Point {
    double x, y;
    Point(double x_ = 0, double y_ = 0) : x(x_), y(y_) {}
};
 
// 第1级:静态过滤器(使用预计算的误差界)
class StaticFilter {
    static constexpr double epsilon = 1e-14;  // 误差界
    
public:
    static Sign orientation(const Point& p, const Point& q, const Point& r) {
        double pq_x = q.x - p.x;
        double pq_y = q.y - p.y;
        double pr_x = r.x - p.x;
        double pr_y = r.y - p.y;
        
        double result = pq_x * pr_y - pq_y * pr_x;
        
        // 计算误差界(简化版)
        double abs_sum = std::abs(pq_x * pr_y) + std::abs(pq_y * pr_x);
        double error_bound = epsilon * abs_sum;
        
        if (result > error_bound) return POSITIVE;
        if (result < -error_bound) return NEGATIVE;
        return UNCERTAIN;
    }
};
 
// 第2级:区间过滤器
class IntervalFilter {
public:
    static Sign orientation(const Point& p, const Point& q, const Point& r) {
        // 使用区间算术计算
        // 这里简化,实际使用Interval_nt
        
        double pq_x[2] = { q.x - p.x, q.x - p.x };
        double pq_y[2] = { q.y - p.y, q.y - p.y };
        double pr_x[2] = { r.x - p.x, r.x - p.x };
        double pr_y[2] = { r.y - p.y, r.y - p.y };
        
        // 模拟区间乘法(考虑舍入)
        double prod1[2] = {
            std::nextafter(pq_x[0] * pr_y[0], -INFINITY),
            std::nextafter(pq_x[1] * pr_y[1], INFINITY)
        };
        double prod2[2] = {
            std::nextafter(pq_y[0] * pr_x[0], -INFINITY),
            std::nextafter(pq_y[1] * pr_x[1], INFINITY)
        };
        
        double result_inf = std::nextafter(prod1[0] - prod2[1], -INFINITY);
        double result_sup = std::nextafter(prod1[1] - prod2[0], INFINITY);
        
        if (result_inf > 0) return POSITIVE;
        if (result_sup < 0) return NEGATIVE;
        if (result_inf <= 0 && result_sup >= 0) {
            // 检查是否为精确0
            if (result_inf == 0 && result_sup == 0) return ZERO;
        }
        return UNCERTAIN;
    }
};
 
// 第3级:精确计算(使用扩展精度)
class ExactFilter {
public:
    static Sign orientation(const Point& p, const Point& q, const Point& r) {
        // 使用扩展精度(long double)
        long double pq_x = (long double)q.x - p.x;
        long double pq_y = (long double)q.y - p.y;
        long double pr_x = (long double)r.x - p.x;
        long double pr_y = (long double)r.y - p.y;
        
        long double result = pq_x * pr_y - pq_y * pr_x;
        
        if (result > 0) return POSITIVE;
        if (result < 0) return NEGATIVE;
        return ZERO;
    }
};
 
// 完整的过滤系统
Sign robust_orientation(const Point& p, const Point& q, const Point& r) {
    // 第1级:静态过滤
    Sign s = StaticFilter::orientation(p, q, r);
    if (s \!= UNCERTAIN) {
        std::cout << "[Static filter succeeded] ";
        return s;
    }
    
    // 第2级:区间过滤
    s = IntervalFilter::orientation(p, q, r);
    if (s \!= UNCERTAIN) {
        std::cout << "[Interval filter succeeded] ";
        return s;
    }
    
    // 第3级:精确计算
    std::cout << "[Exact computation required] ";
    return ExactFilter::orientation(p, q, r);
}
 
int main() {
    // 测试1:明显的左转(静态过滤器应该成功)
    Point p1(0, 0), q1(1, 0), r1(0, 1);
    std::cout << "Test 1 (clear left turn): ";
    std::cout << robust_orientation(p1, q1, r1) << std::endl;
    
    // 测试2:接近共线(可能需要更高级别的过滤)
    Point p2(0, 0), q2(1, 0), r2(2, 1e-15);
    std::cout << "Test 2 (near collinear): ";
    std::cout << robust_orientation(p2, q2, r2) << std::endl;
    
    // 测试3:共线
    Point p3(0, 0), q3(1, 1), r3(2, 2);
    std::cout << "Test 3 (collinear): ";
    std::cout << robust_orientation(p3, q3, r3) << std::endl;
    
    return 0;
}

高级示例:完整的Lazy_exact_nt模拟

#include <iostream>
#include <memory>
#include <vector>
#include <math>
 
// ============================================
// 完整的Lazy_exact_nt模拟
// ============================================
 
// 精确数类型(模拟Gmpq)
class ExactNumber {
    long num_, den_;
public:
    ExactNumber(long n = 0, long d = 1) : num_(n), den_(d) {
        normalize();
    }
    
    void normalize() {
        if (den_ < 0) { num_ = -num_; den_ = -den_; }
        long g = gcd(std::abs(num_), den_);
        num_ /= g; den_ /= g;
    }
    
    static long gcd(long a, long b) {
        return b == 0 ? a : gcd(b, a % b);
    }
    
    ExactNumber operator+(const ExactNumber& o) const {
        return ExactNumber(num_ * o.den_ + o.num_ * den_, den_ * o.den_);
    }
    
    ExactNumber operator-(const ExactNumber& o) const {
        return ExactNumber(num_ * o.den_ - o.num_ * den_, den_ * o.den_);
    }
    
    ExactNumber operator*(const ExactNumber& o) const {
        return ExactNumber(num_ * o.num_, den_ * o.den_);
    }
    
    ExactNumber operator-() const {
        return ExactNumber(-num_, den_);
    }
    
    double to_double() const {
        return static_cast<double>(num_) / den_;
    }
    
    bool is_zero() const { return num_ == 0; }
    int sign() const {
        if (num_ > 0) return 1;
        if (num_ < 0) return -1;
        return 0;
    }
    
    friend std::ostream& operator<<(std::ostream& os, const ExactNumber& n) {
        os << n.num_;
        if (n.den_ \!= 1) os << "/" << n.den_;
        return os;
    }
};
 
// 区间数
class IntervalNumber {
    double inf_, sup_;
public:
    IntervalNumber(double i, double s) : inf_(i), sup_(s) {}
    explicit IntervalNumber(double v) : inf_(v), sup_(v) {}
    
    static IntervalNumber from_exact(const ExactNumber& e) {
        double d = e.to_double();
        return IntervalNumber(d, d);  // 简化,实际应该考虑舍入
    }
    
    double inf() const { return inf_; }
    double sup() const { return sup_; }
    
    bool is_positive() const { return inf_ > 0; }
    bool is_negative() const { return sup_ < 0; }
    bool contains_zero() const { return inf_ <= 0 && sup_ >= 0; }
    bool is_point() const { return inf_ == sup_; }
    
    IntervalNumber operator+(const IntervalNumber& o) const {
        return IntervalNumber(inf_ + o.inf_, sup_ + o.sup_);
    }
    
    IntervalNumber operator-(const IntervalNumber& o) const {
        return IntervalNumber(inf_ - o.sup_, sup_ - o.inf_);
    }
    
    IntervalNumber operator*(const IntervalNumber& o) const {
        double p[4] = { inf_ * o.inf_, inf_ * o.sup_,
                       sup_ * o.inf_, sup_ * o.sup_ };
        return IntervalNumber(
            *std::min_element(p, p + 4),
            *std::max_element(p, p + 4)
        );
    }
};
 
// 延迟精确数表示基类
template <typename ET>
class LazyRep {
public:
    virtual ~LazyRep() = default;
    virtual ET exact() const = 0;
    virtual int depth() const = 0;
};
 
// 常量表示
template <typename ET>
class LazyConstant : public LazyRep<ET> {
    ET value_;
public:
    explicit LazyConstant(const ET& v) : value_(v) {}
    ET exact() const override { return value_; }
    int depth() const override { return 0; }
};
 
// 一元操作表示
template <typename ET>
class LazyUnary : public LazyRep<ET> {
public:
    mutable std::shared_ptr<LazyRep<ET>> op;
    mutable ET cached_;
    mutable bool computed_;
    
    explicit LazyUnary(std::shared_ptr<LazyRep<ET>> o) 
        : op(std::move(o)), computed_(false) {}
    
    int depth() const override { return op->depth() + 1; }
    
protected:
    const ET& get_op_exact() const {
        if (\!computed_) {
            cached_ = op->exact();
            computed_ = true;
        }
        return cached_;
    }
};
 
// 取反操作
template <typename ET>
class LazyNegate : public LazyUnary<ET> {
public:
    using LazyUnary<ET>::LazyUnary;
    
    ET exact() const override {
        return -this->get_op_exact();
    }
};
 
// 二元操作表示
template <typename ET>
class LazyBinary : public LazyRep<ET> {
public:
    mutable std::shared_ptr<LazyRep<ET>> left, right;
    mutable ET cached_;
    mutable bool computed_;
    
    LazyBinary(std::shared_ptr<LazyRep<ET>> l, std::shared_ptr<LazyRep<ET>> r)
        : left(std::move(l)), right(std::move(r)), computed_(false) {}
    
    int depth() const override { 
        return std::max(left->depth(), right->depth()) + 1; 
    }
    
protected:
    const ET& get_left_exact() const {
        if (\!computed_) {
            cached_left_ = left->exact();
        }
        return cached_left_;
    }
    
    const ET& get_right_exact() const {
        if (\!computed_) {
            cached_right_ = right->exact();
        }
        return cached_right_;
    }
    
private:
    mutable ET cached_left_, cached_right_;
};
 
// 加法操作
template <typename ET>
class LazyAdd : public LazyBinary<ET> {
public:
    using LazyBinary<ET>::LazyBinary;
    
    ET exact() const override {
        return this->get_left_exact() + this->get_right_exact();
    }
};
 
// 延迟精确数主类
template <typename ET>
class LazyExact {
    std::shared_ptr<LazyRep<ET>> rep_;
    IntervalNumber interval_;
    
public:
    // 从精确值构造
    LazyExact(const ET& e) 
        : rep_(std::make_shared<LazyConstant<ET>>(e)),
          interval_(IntervalNumber::from_exact(e)) {}
    
    // 从double构造
    explicit LazyExact(double d)
        : rep_(std::make_shared<LazyConstant<ET>>(ET(static_cast<long>(d * 1000), 1000))),
          interval_(d) {}
    
    // 从表示构造
    LazyExact(std::shared_ptr<LazyRep<ET>> rep, const IntervalNumber& i)
        : rep_(std::move(rep)), interval_(i) {}
    
    // 获取区间(快速)
    const IntervalNumber& interval() const { return interval_; }
    
    // 获取精确值(可能触发计算)
    ET exact() const { return rep_->exact(); }
    
    // 获取表达式深度
    int depth() const { return rep_ ? rep_->depth() : 0; }
    
    // 判断符号(过滤)
    int sign() const {
        if (interval_.is_positive()) return 1;
        if (interval_.is_negative()) return -1;
        if (interval_.is_point() && interval_.inf() == 0) return 0;
        // 需要精确计算
        return exact().sign();
    }
    
    // 运算符
    LazyExact operator+(const LazyExact& o) const {
        auto new_rep = std::make_shared<LazyAdd<ET>>(rep_, o.rep_);
        return LazyExact(new_rep, interval_ + o.interval_);
    }
    
    LazyExact operator-() const {
        auto new_rep = std::make_shared<LazyNegate<ET>>(rep_);
        return LazyExact(new_rep, IntervalNumber(-interval_.sup(), -interval_.inf()));
    }
    
    LazyExact operator-(const LazyExact& o) const {
        return *this + (-o);
    }
    
    friend std::ostream& operator<<(std::ostream& os, const LazyExact& l) {
        os << "Lazy[" << l.interval_.inf() << ", " << l.interval_.sup() <> "]";
        return os;
    }
};
 
// 使用示例
int main() {
    typedef LazyExact<ExactNumber> LazyNum;
    
    // 创建延迟精确数
    LazyNum a(ExactNumber(1, 3));  // 1/3
    LazyNum b(ExactNumber(1, 6));  // 1/6
    
    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
    
    // 构建表达式树(延迟计算)
    LazyNum c = a + b;  // 不立即计算
    std::cout << "c = a + b = " << c << std::endl;
    std::cout << "Expression depth: " << c.depth() << std::endl;
    
    // 判断符号(使用过滤)
    std::cout << "sign(c) = " << c.sign() << std::endl;
    
    // 获取精确值(触发计算)
    std::cout << "exact(c) = " << c.exact() << std::endl;
    
    // 复杂表达式
    LazyNum d = a + b + a;  // 1/3 + 1/6 + 1/3 = 5/6
    std::cout << "\nd = a + b + a = " << d << std::endl;
    std::cout << "Expression depth: " << d.depth() << std::endl;
    std::cout << "exact(d) = " << d.exact() << std::endl;
    
    return 0;
}

19.8.4 CGAL中的应用

CGAL的Lazy_exact_nt

// 来自CGAL/Lazy_exact_nt.h的核心部分(简化展示)
namespace CGAL {
 
template <class ET>
class Lazy_exact_nt 
  : public Lazy<Interval_nt<false>, ET, To_interval<ET> >
{
public:
    typedef ET ET_type;
    typedef Interval_nt<false> AT_type;
    typedef Lazy<AT_type, ET_type, To_interval<ET_type> > Base;
    
    // 从double构造(近似值)
    Lazy_exact_nt(double d) : Base(new Lazy_exact_Cst<ET>(d)) {}
    
    // 从精确值构造
    Lazy_exact_nt(const ET& e) : Base(new Lazy_exact_Ex_Cst<ET>(e)) {}
    
    // 延迟加法
    Lazy_exact_nt operator+(const Lazy_exact_nt& b) const {
        return Lazy_exact_nt(
            new Lazy_exact_Add<ET>(*this, b),
            this->approx() + b.approx()  // 同时计算近似值
        );
    }
    
    // 获取精确值(触发计算)
    const ET& exact() const {
        if (this->is_lazy()) {
            this->update_exact();  // 计算并缓存
        }
        return *this->ptr();
    }
    
    // 获取近似值(快速)
    const AT_type& approx() const { return this->approx_; }
};
 
} // namespace CGAL

CGAL的Exact_predicates_inexact_constructions_kernel

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
 
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
 
void example() {
    // 构造使用double(快速)
    Point_2 p(0.1, 0.2);  // 使用double存储
    Point_2 q(0.3, 0.4);
    Point_2 r(0.5, 0.6);
    
    // 谓词使用精确计算(鲁棒)
    K::Orientation_2 orientation;
    auto result = orientation(p, q, r);  // 内部使用过滤精确计算
}

CGAL的Exact_predicates_exact_constructions_kernel

#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
 
typedef CGAL::Exact_predicates_exact_constructions_kernel K;
typedef K::Point_2 Point_2;
 
typedef CGAL::Exact_predicates_inexact_constructions_kernel K_fast;
 
void comparison() {
    // 快速内核:构造快,但可能不精确
    K_fast::Point_2 p_fast(0.1, 0.2);
    K_fast::Point_2 q_fast(0.3, 0.6);
    // p_fast + (q_fast - p_fast) * 0.5 可能不精确!
    
    // 精确内核:构造使用Lazy_exact_nt
    K::Point_2 p_exact(0.1, 0.2);
    K::Point_2 q_exact(0.3, 0.6);
    // p_exact + (q_exact - p_exact) * 0.5 是精确的!
}

19.8.5 常见陷阱

陷阱1:过度使用精确计算

// 错误:所有计算都使用精确内核
typedef CGAL::Exact_predicates_exact_constructions_kernel K;
K::Point_2 p(0, 0);
 
for (int i = 0; i < 1000000; ++i) {
    p = p + K::Vector_2(1, 0);  // 每次迭代都创建延迟计算节点!
}
// 结果:内存爆炸,速度极慢
 
// 正确:只在需要时使用精确计算
typedef CGAL::Exact_predicates_inexact_constructions_kernel K_fast;
K_fast::Point_2 p_fast(0, 0);
 
for (int i = 0; i < 1000000; ++i) {
    p_fast = p_fast + K_fast::Vector_2(1, 0);  // 使用double,快速
}

陷阱2:忽略区间不确定性

// 问题:假设区间总是确定的
Lazy_exact_nt<Gmpq> a = ...;
Lazy_exact_nt<Gmpq> b = ...;
 
if (a.approx().is_positive()) {  // 可能不确定!
    // 错误:区间可能包含0
}
 
// 正确:处理不确定性
if (a.approx().is_positive()) {
    // 确定为正
} else if (a.approx().contains_zero()) {
    // 不确定,需要精确值
    if (a.exact().is_positive()) {
        // 实际上是正的
    }
}

陷阱3:比较延迟计算对象

// 危险:直接比较延迟计算对象
Lazy_exact_nt<Gmpq> a = ...;
Lazy_exact_nt<Gmpq> b = ...;
 
if (a == b) {  // 这可能触发精确计算!
    // ...
}
 
// 更好的做法:先比较近似值
if (a.approx().is_same(b.approx())) {
    // 快速路径:近似值相同
} else {
    // 需要精确比较
    if (a.exact() == b.exact()) {
        // ...
    }
}

19.8.6 最佳实践

实践1:选择合适的内核

// 场景1:只需要谓词(方向判断、点在内部等)
// 使用 EPEC(精确谓词,不精确构造)
typedef CGAL::Exact_predicates_inexact_constructions_kernel K1;
 
// 场景2:需要精确构造(交点计算)
// 使用 EPEC(完全精确)
typedef CGAL::Exact_predicates_exact_constructions_kernel K2;
 
// 场景3:需要平方根(距离计算)
// 使用带平方根的精确内核
typedef CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt K3;

实践2:批量处理策略

// 低效:逐个处理,频繁触发精确计算
for (auto v : mesh.vertices()) {
    auto neighbors = get_neighbors(v);
    if (neighbors.size() > 2) {  // 每次判断都可能触发精确计算
        process(v);
    }
}
 
// 高效:先批量收集,再统一处理
std::vector<Vertex> to_process;
for (auto v : mesh.vertices()) {
    auto neighbors = get_neighbors(v);
    to_process.push_back(v);  // 只收集,不判断
}
 
for (auto v : to_process) {
    process(v);  // 统一处理
}

实践3:监控性能

// 使用CGAL的性能计数器
#include <CGAL/Profile_counter.h>
 
void algorithm() {
    CGAL_PROFILER("algorithm_total");
    
    for (...) {
        CGAL_PROFILER("loop_iteration");
        
        // 谓词调用
        CGAL_PROFILER("orientation_test");
        auto orient = orientation(p, q, r);
    }
}
 
// 输出统计信息
// 设置环境变量 CGAL_PROFILE 为 "y" 查看统计

实践4:混合使用不同精度

// 使用不同精度的内核
typedef CGAL::Exact_predicates_inexact_constructions_kernel Fast_kernel;
typedef CGAL::Exact_predicates_exact_constructions_kernel Exact_kernel;
 
// 快速计算
typedef Fast_kernel::Point_2 Fast_point;
// 精确计算
typedef Exact_kernel::Point_2 Exact_point;
 
// 在需要时转换
Fast_point fast_p(0.1, 0.2);
Exact_point exact_p(fast_p.x(), fast_p.y());  // 转换为精确点

本节要点

  1. 混合精度平衡性能与鲁棒性:大部分计算使用快速近似,关键决策使用精确计算。

  2. Lazy_exact_nt延迟计算:构建表达式树,只在需要时计算精确值。

  3. 三级过滤策略:静态过滤 → 区间过滤 → 精确计算,逐级提升精度。

  4. 区间算术检测不确定性:通过维护数值范围,判断何时需要精确计算。

  5. 内核选择影响整体性能:根据应用需求选择合适的内核类型。

  6. 避免过度使用精确计算:精确计算有显著开销,应谨慎使用。


进一步阅读

  • CGAL文档:Number Types模块的Lazy_exact_nt章节
  • 《Robust Geometric Computation》:几何计算鲁棒性的经典教材
  • CGAL论文:“The Design and Implementation of CGAL”
  • 区间算术资源:IEEE 1788标准