计算几何学(Computational Geometry) - 算法导论第33章


计算几何学(Computational Geometry) - 算法导论第33章
1)叉积(Cross Product)
  
     叉积是线段算法的中心,图 a) 两个向量 p1,p2,可以把 p1 ×p2看作是由点(0, 0),p1,p和 p+ p2= (x+ x2,y1 + y2) 所形成平行四边形的面积,另一种等价定义是把叉积定义为一个矩阵的行列式:
                                 
     如果 p1×p为正数,则相对于原点来说,p在 p2 的顺时针方向上;如果 p1×p2 为负数,则 p1 在 p2 的逆时针方向上。如图b) 浅阴影区域包含了 p 的顺时针方向上的向量。深阴影区包含了 p 的逆时针方向上的向量。

1)已知两条有向线段p0p1,和p0p2,相对于它们的公共端点p0来说,p0p1是否在p0p2的顺时针方向上?

解决方法是,将p0当做原点即可,计算叉积:
               
     如果叉积为政,则 p0p在 p0p2 的顺时针方向上,如果为负,则 p0p1 在 p0p2 的逆时针方向上。
double Direction(Point p1, Point p2, Point p0 ) {
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
2)确定线段旋转方向

2)已知两条线段p0p1和p1p2,如果先通过p0p1再通过p1p2,在点p1处是不是要向左旋? 

如果知道叉积,那再简单不过了,相对于有向线段 p0p1,检查有向线段 p0p2 是在其顺时针方向还是逆时针方向即可,只需计算叉积(p2 – p0)× (p1 – p0),如果叉积为负,则 p0p2 在 p0p1 逆时针方向,因此左旋,反之,同理。
3)判断两个线段是否相交 - 线段p1p2和p3p4是否相交?
     第三个问题,以下两个条件,至少满足一个,即可断定两线段相交:

1)每个线段都跨越(straddle)包含了另一线段的直线(即最普通的相交)

2)一个线段的某一个端点位于另一线段上(边界情况,包含重合)
     SEGMENTS-INTERSECT 用于判断是否相交,DIRECTION利用叉积方法,计算线段方位;ON-SEGMENT确定一个与某一线段共线的点是否位于该线段上。伪代码:
SEGMENTS-INTERSECT(p1, p2, p3, p4)
1  d1 ← DIRECTION(p3, p4, p1)
2  d2 ← DIRECTION(p3, p4, p2)
3  d3 ← DIRECTION(p1, p2, p3)
4  d4 ← DIRECTION(p1, p2, p4)
5  if ((d1 > 0 and d2 < 0) or (d1 < 0 and d2 > 0)) and
             ((d3 > 0 and d4 < 0) or (d3 < 0 and d4 > 0))
6     then return TRUE
7  elseif d1 = 0 and ON-SEGMENT(p3, p4, p1)
8     then return TRUE
9  elseif d2 = 0 and ON-SEGMENT(p3, p4, p2)
10     then return TRUE
11  elseif d3 = 0 and ON-SEGMENT(p1, p2, p3)
12     then return TRUE
13  elseif d4 = 0 and ON-SEGMENT(p1, p2, p4)
14     then return TRUE
15  else return FALSE
DIRECTION(pi, pj, pk)
1  return (pk – pi) × (pj – pi)
ON-SEGMENT(pi, pj, pk)
1  if min(xi, xj) ≤ xk ≤ max(xi, xj) and min(yi, yj) ≤ yk ≤ max(yi, yj)
2     then return TRUE
3     else return FALSE
     伪代码很容易看懂,判断过程,有四种情况需要选择,a)相交,叉积异号;b)不想交,叉积同号;c)p3和 p1,p共线,4)p和 p1,p2共线,但是不相交。(最容易忽略就是第四种情况)
struct Point {
    double x, y;
};
double Direction(Point p1, Point p2, Point p0 ) {
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
bool OnSegment( Point p1, Point p2, Point p0) {
    if (min(p1.x, p2.x) <= p0.x && p0.x <= max(p1.x, p2.x) &&
        min(p1.y, p2.y) <= p0.y && p0.y <= max(p1.y, p2.y)) {
            return true;
    } else {
        return false;
    }
}
bool SegmentsIntersert(Point p1, Point p2, Point p3, Point p4) {
    double d1 = Direction(p3, p4, p1);
    double d2 = Direction(p3, p4, p2);
    double d3 = Direction(p1, p2, p3);
    double d4 = Direction(p1, p2, p4);
    if (d1 * d2 < 0 && d3 * d4 < 0) {
        return true;
    } else if (d1 == 0 && OnSegment (p3, p4, p1)) {
        return true;
    } else if (d2 == 0 && OnSegment (p3, p4, p2)) {
        return true;
    } else if (d3 == 0 && OnSegment (p1, p2, p3)) {
        return true;
    } else if (d4 == 0 && OnSegment (p1, p2, p4)) {
        return false;
    }
    return 0;
}
二、寻找凸包(Convex Hull)
点集Q的凸包是一个最小的凸多边形P,可以把Q中的每个点都想象成是露在一块板外的铁钉,那么凸包就是包围了所有这些铁钉的一条拉紧了的橡皮绳所构成的形状。如图点集Q和凸包CH(Q)。
     寻找凸包,可以使用Graham扫描法(Graham’s scan)。借助一个堆栈S,输入集合Q中的每个点都被压入栈一次,非 CH(Q) 中顶点的点最终将被弹出堆栈,算法结束时,堆栈S中仅包含CH(Q)中的顶点,其顺序为各点在边界上出现的逆时针方向排列的顺序。
伪代码:
GRAHAM-SCAN(Q)
1  let p0 be the point in Q with the minimum y-coordinate,
             or the leftmost such point in case of a tie
2  let 〈p1, p2, …, pm〉 be the remaining points in Q,
             sorted by polar angle in counterclockwise order around p0
             (if more than one point has the same angle, remove all but
             the one that is farthest from p0)
3  PUSH(p0, S)
4  PUSH(p1, S)
5  PUSH(p2, S)
6  for i ← 3 to m
7       do while the angle formed by points NEXT-TO-TOP(S), TOP(S),
                     and pi makes a nonleft turn
8              do POP(S)
9          PUSH(pi, S)
10  return S
     第一行找到 y 坐标最小的 p0,第二行根据相对于p0的极角排序(利用叉积),3~5 行堆栈初始化,第 6~9行的迭代对一个点执行一次,意图是:在对点pi进行处理后,在栈S中,按由底到顶得顺序,包含CH({p0,p1, ……,pi})中按逆时针方向排列的各个顶点。第 7~8 行由 while 循环把发现不是凸包中的顶点从堆栈中移去。沿逆时针方向通过凸包时,在每个点处应该左转。因此while循环每次发现在一个顶点处没有向左转时,就把该顶点从堆栈中弹出。图示:
     如图 a)→b) 过程中,原本p2在栈S中,到 b) 时,根据p1p3,p1p2的叉积计算,得知 p1p3 在 p1p2 的顺时针方向行,所以 p不在CH(Q)中,所以 p2 出栈,p3入栈,整个算法都循环这个过程,后续完整图示:
typedef struct {
    double x;
    double y;
}Point;
Point p[110], stack[110];
 
int N, top;
 
double Direction(Point p1, Point p2, Point p3) {
    return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
}
 
double Dis(Point a, Point b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
 
int cmp(const void *a, const void *b) {
    Point c = *(Point *)a;
    Point d = *(Point *)b;
    double k = Direction(p[0], c, d);
    if(k < 0 || (!k && Dis(c, p[0]) > Dis(d, p[0]))) {
        return 1;
    }
    return -1;
}
 
void Convex() {
    for(int i = 1; i < N; i++) {
        Point temp;
        if(p[i].y < p[0].y || (p[i].y == p[0].y && p[i].x < p[0].x)) {
            temp = p[i];
            p[i] = p[0];
            p[0] = temp;
        }
    }
    qsort(p + 1, N - 1, sizeof(p[0]), cmp);
    stack[0] = p[0];       
    stack[1] = p[1];
    stack[2] = p[2];
    top =2;
    for(int i = 3; i < N; i++) {
        while(top >= 2 && Direction(stack[top - 1], stack[top], p[i]) <= 0) {
            top--;
        }
        top++;
        stack[top] = p[i];
    }
}
Java Implementaion
Java Program to Implement Graham Scan Algorithm to Find the Convex Hull
http://algs4.cs.princeton.edu/99hull/GrahamScan.java.html
http://www.dcs.gla.ac.uk/~pat/52233/slides/Hull1x1.pdf
三、寻找最近点对
     有 n 个点的集合 Q,找出最近的两点,这一问题可以应用于交通控制等系统。在空中或海洋交通控制系统中,需要发现两个距离最近的交通工具,以便检测出可能发生的相撞事故。
     暴力必然可以,但是时间复杂度太高,因为要查看 Cn2 = O(n2)个点对,算法导论中介绍的是一种分治策略,输入 P,X 和 Y 的递归调用首先检查是否 |P| <= 3。如果是,则用暴力枚举C|p|2个点对,并返回最近点对,如果 |P| > 3,则分治递归。
     找出一条垂直线,近似平分,左边的为 PL,右边的为 PR,类似地X数组划分为两个 XL 和 XR,分别包含 PL和 PR 中的店,按 x 坐标递增排序。类似的,Y 数组划分为连个 YL 和 YR,分别包含 PL 和 PR 中的店,按 y 坐标递增排序。
     把 P 分为 P和 PR 后,再两次递归调用,一次找出 PL 中的最近点对,另一次找出 PR 中的最近点对。第一次调用输入为 PL,XL,YL,第二次调用输入为 PR,XR,YR。设分别返回的最近距离为 δL 和 δR,则δ = min(δL,δR
     算出左右两边独自的最小距离后,还要计算跨域左右两边的最小距离,即,一个点在PL中,另外一个在PR中。比δ小,且在分布在两边,必定是以下情况:
    即,必定在(-δ,+δ)内,如图a),另外,最多有 8 个点可能处于 δ × 2δ 矩形区域内,原因是:比如左半边,因为 P中的所有点之间的距离至少为 δ 单位,所以至多有 4 个点可能位于该正方形内,P同理。这个结论的用处是,在暴力枚举可能范围内的点时,每个点只需考虑紧随其后的 7 个点即可。部分伪代码:
1  length[YL] ← length[YR] ← 0
2  for i ← 1 to length[Y]
3       do if Y[i] ∈ PL
4             then length[YL] ← length[YL] + 1
5                  YL[length[YL]] ← Y[i]
6             else length[YR] ← length[YR] + 1
7                  YR[length[YR]] ← Y[i]
struct Point {
    double x, y;
    int index;
}X[maxn], Y[maxn];
double ans;
 
bool Cmpx(const Point &a, const Point &b) {
    return a.x < b.x;
}
 
bool Cmpy(const Point &a, const Point &b) {
    return a.y < b.y;
}
 
double Dis(const Point &a, const Point &b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
 
//暴力枚举
double ForceMinDis(int a, int b) {
    for (int i = a; i < b; i++) {
        for (int j = i + 1; j <= b; j++) {
            double temp = Dis(X[i], X[j]);
            if (ans > temp) {
                ans = temp;
            }
        }
    }
    return ans;
}
 
double MinDis(int a, int b, Point *Y) {
    if (b - a <= 3) {
        return ForceMinDis(a, b);
    }
 
    int mid = (a + b) >> 1;
    Point *Yleft = new Point[mid - a + 1];
    Point *Yright = new Point[b - mid];
 
    for (int i = 0, j = 0, k = 0; i <= b - a; i++) {
        if (Y[i].index <= mid) {
            Yleft[j++] = Y[i];
        } else {
            Yright[k++] = Y[i];
        }
    }
 
    double left_min = MinDis(a, mid, Yleft);
    double right_min = MinDis(mid + 1, b, Yright);
    ans = min(left_min, right_min);
 
    double line = X[mid].x;
    int ind;
    for (int i = 0, ind = 0; i <= b - a; i++) {
        if (fabs(Y[i].x - line) < ans) {
            Y[ind++] = Y[i];
        }
    }
    for (int i = 0; i < ind - 1; i++) {
        for (int j = i + 1; j <= i + 7 && j < ind; j++) {
            double temp = Dis(Y[i], Y[j]);
            if (ans > temp) {
                ans = temp;
            }
        }
    }
    delete Yleft;
    delete Yright;
    return ans;
}
 
int main() {
    int n, i;
    while (scanf("%d", &n), n) {
        ans = DBL_MAX;
        for (i = 0; i < n; i++) {
            scanf("%lf %lf", &X[i].x, &X[i].y);
        }
        sort(X, X + n, Cmpx);
        for (i = 0; i < n; i++) {
            X[i].index = i;
        }
        memcpy(Y, X, n * sizeof(Point));
        sort(Y, Y + n, Cmpy);
        printf("%lf\n", MinDis(0, n - 1, Y));
    }
    return 0;
}
Please read full article from 计算几何学(Computational Geometry) - 算法导论第33章

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts