codeforces-go中的几何模板:计算几何算法汇总
概述
计算几何(Computational Geometry)是算法竞赛中的重要领域,涉及点、线、面等几何元素的计算与处理。codeforces-go项目的copypasta模块提供了全面的几何算法模板,涵盖向量运算、线段处理、圆相关计算等核心功能。本文将系统介绍geometry.go中的关键实现,帮助开发者快速掌握计算几何的实用技巧。
核心数据结构
向量与点
向量(Vector)是计算几何的基础,用于表示点和方向。在geometry.go中,向量通过vec结构体实现,包含x和y两个整数分量:
type vec struct{ x, y int }
该结构体提供了丰富的向量运算方法,包括:
sub(b vec) vec:向量减法dot(b vec) int:点积(Dot Product)det(b vec) int:叉积(Cross Product)的z分量值len2() int:向量长度的平方rotateCCW90() vec:逆时针旋转90度
线与线段
直线和线段通过line结构体表示,包含两个端点:
type line struct{ p1, p2 vec }
关键方法包括:
vec() vec:获取方向向量onSeg(a vec) bool:判断点是否在线段上intersection(b lineF) vecF:计算两直线交点
圆
圆通过circle结构体表示,包含圆心和半径:
type circle struct {
vec // 圆心坐标
r int // 半径
}
提供了圆心角对应点计算、两圆交点求解等功能。
基础运算
点积与叉积
点积和叉积是向量运算的核心,在geometry.go中实现如下:
// 点积:判断两向量方向关系
func (a vec) dot(b vec) int { return a.x*b.x + a.y*b.y }
// 叉积:判断两向量相对位置
func (a vec) det(b vec) int { return a.x*b.y - a.y*b.x }
点积结果的符号表示两向量的夹角关系:
- 正:夹角为锐角
- 零:垂直
- 负:夹角为钝角
叉积结果的符号表示相对位置:
- 正:b在a左侧
- 零:共线
- 负:b在a右侧
距离计算
距离计算是几何问题的基础,实现了多种距离相关方法:
// 两点间距离的平方
func (a vec) dis2(b vec) int { return b.sub(a).len2() }
// 两点间距离
func (a vec) dis(b vec) float64 { return b.sub(a).len() }
// 点到直线的距离
func (a vecF) disToLine(l lineF) float64 {
v, p1a := l.vec(), a.sub(l.p1)
return math.Abs(v.det(p1a)) / v.len()
}
几何算法应用
极角排序
极角排序用于将平面上的点按与原点的夹角排序,在凸包计算等场景中广泛应用:
func polarAngleSort(ps []vec) {
// 按极角 (-π, π] 排序
sort.Slice(ps, func(i, j int) bool {
return ps[i].polarAngle() < ps[j].polarAngle()
})
// 优化:将向量统一转为正方向后用叉积排序
for i := range ps {
ps[i] = ps[i].up()
}
sort.Slice(ps, func(i, j int) bool {
return ps[i].det(ps[j]) > 0
})
}
三角形五心计算
几何模板实现了三角形的外心、垂心和内心计算:
// 外心(外接圆圆心)
func circumcenter(a, b, c vecF) vecF {
a1, b1, a2, b2 := b.x-a.x, b.y-a.y, c.x-a.x, c.y-a.y
c1, c2, d := a1*a1+b1*b1, a2*a2+b2*b2, 2*(a1*b2-a2*b1)
if math.Abs(d) < eps { // 三点共线
return vecF{math.NaN(), math.NaN()}
}
return vecF{a.x + (c1*b2-c2*b1)/d, a.y + (a1*c2-a2*c1)/d}
}
// 垂心
func orthocenter(a, b, c vecF) vecF {
return a.add(b).add(c).sub(circumcenter(a, b, c).mul(2))
}
// 内心(角平分线交点)
func incenter(a, b, c vecF) vecF {
bc, ac, ab := b.dis(c), a.dis(c), a.dis(b)
sum := bc + ac + ab
return vecF{(bc*a.x + ac*b.x + ab*c.x)/sum, (bc*a.y + ac*b.y + ab*c.y)/sum}
}
最小圆覆盖
最小圆覆盖问题要求找到能覆盖所有给定点的最小圆,采用Welzl's算法实现:
func smallestEnclosingDisc(ps []vecF) circleF {
rand.Shuffle(len(ps), func(i, j int) { ps[i], ps[j] = ps[j], ps[i] })
o := ps[0]
r2 := 0.0
for i, p := range ps {
if p.dis2(o) < r2+eps {
continue
}
o, r2 = p, 0.0
for j, q := range ps[:i] {
if q.dis2(o) < r2+eps {
continue
}
o = vecF{(p.x + q.x)/2, (p.y + q.y)/2}
r2 = p.dis2(o)
for _, x := range ps[:j] {
if x.dis2(o) > r2+eps {
o = circumcenter(p, q, x)
r2 = p.dis2(o)
}
}
}
}
return circleF{o, math.Sqrt(r2)}
}
该算法通过随机增量法实现,期望时间复杂度为O(n)。
圆与矩形碰撞检测
判断圆与矩形是否重叠是游戏开发等场景的常见问题:
func isCircleRectangleOverlap(r, ox, oy, x1, y1, x2, y2 int) bool {
// 计算圆心到矩形的最近点
cx := clamp(ox, x1, x2)
cy := clamp(oy, y1, y2)
// 计算最近点与圆心的距离平方
dx, dy := ox-cx, oy-cy
return dx*dx + dy*dy <= r*r
}
精度控制
计算几何中浮点数精度是关键问题,geometry.go采用以下策略:
- 定义全局精度常量:
const eps = 1e-8
- 比较时使用精度控制:
func (a vecF) equals(b vecF) bool {
return math.Abs(a.x-b.x) < eps && math.Abs(a.y-b.y) < eps
}
- 大浮点数比较采用相对误差:
// 大浮点数相等判断
a == b a*(1-eps) < b && b < a*(1+eps)
应用场景
算法竞赛
codeforces-go的几何模板已在多项算法竞赛题目中得到应用,例如:
- 最小圆覆盖:ABC151F
- 圆与矩形碰撞:LeetCode 1401
- 曼哈顿距离转切比雪夫距离:ABC178E
实际应用
几何算法在多个领域有广泛应用:
- 计算机图形学:碰撞检测、光线追踪
- 地理信息系统:空间分析、路径规划
- 机器人技术:运动规划、障碍物规避
总结与扩展
codeforces-go的几何模板提供了计算几何的核心实现,涵盖从基础向量运算到复杂几何问题求解的完整功能。开发者可基于此模板:
- 扩展三维几何功能
- 优化特定场景的精度问题
- 结合具体问题开发新算法
建议深入阅读geometry.go源码,并通过实际题目练习巩固理解。后续可关注计算几何高级主题,如Voronoi图、 arrangements等。
资源与参考
- 官方文档:geometry.go
- 推荐读物:《计算几何:算法与应用》
- 在线资源:OI-Wiki 计算几何
- 练习题集:Codeforces Geometry标签题目
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



