zoukankan      html  css  js  c++  java
  • Coursera Algorithms Programming Assignment 3: Pattern Recognition (100分)

    题目原文详见http://coursera.cs.princeton.edu/algs4/assignments/collinear.html

    程序的主要目的是寻找n个points中的line segment,line segment的要求就是包含不少于4个点。

    作业包含三部分程序实现:

    一、Point

    compareTo()用来比较本节点this与其他节点that的大小:假如this节点坐标(x0, y0),that节点坐标(x1, y1),只有y0 < y1或(y0==y1 && x0<x1)的时候this < that

    slopeTo()用来计算that节点到本节点this的斜率,计算方法为(y1 − y0) / (x1 − x0),特别注意的是,x0 != x1 && y0==y1时,slople为0,x0==x1 && y0!=y1时,slople应为positive infinity,x0==x1 && y0==y1时,slope应为negative infinity,

    slopeOrder()用来返回比较器,这个比较器的参照点是本节点p0(x0, y0),如果两个待比较节点分别是p1(x1, y1)和p2(x2, y2),此比较器的设计要求是当p1相对于p0的斜率大于p2相对于p0的斜率时,p1>p2。比较器主要在排序方法中使用

      1 import java.util.Comparator;
      2 import edu.princeton.cs.algs4.StdDraw;
      3 
      4 public class Point implements Comparable<Point> {
      5 
      6     private final int x; // x-coordinate of this point
      7     private final int y; // y-coordinate of this point
      8 
      9     /**
     10      * Initializes a new point.
     11      *
     12      * @param x
     13      *            the <em>x</em>-coordinate of the point
     14      * @param y
     15      *            the <em>y</em>-coordinate of the point
     16      */
     17     public Point(int x, int y) {
     18         /* DO NOT MODIFY */
     19         this.x = x;
     20         this.y = y;
     21     }
     22 
     23     /**
     24      * Draws this point to standard draw.
     25      */
     26     public void draw() {
     27         /* DO NOT MODIFY */
     28         StdDraw.point(x, y);
     29     }
     30 
     31     /**
     32      * Draws the line segment between this point and the specified point to
     33      * standard draw.
     34      *
     35      * @param that
     36      *            the other point
     37      */
     38     public void drawTo(Point that) {
     39         /* DO NOT MODIFY */
     40         StdDraw.line(this.x, this.y, that.x, that.y);
     41     }
     42 
     43     /**
     44      * Returns the slope between this point and the specified point. Formally,
     45      * if the two points are (x0, y0) and (x1, y1), then the slope is (y1 - y0)
     46      * / (x1 - x0). For completeness, the slope is defined to be +0.0 if the
     47      * line segment connecting the two points is horizontal;
     48      * Double.POSITIVE_INFINITY if the line segment is vertical; and
     49      * Double.NEGATIVE_INFINITY if (x0, y0) and (x1, y1) are equal.
     50      *
     51      * @param that
     52      *            the other point
     53      * @return the slope between this point and the specified point
     54      */
     55     public double slopeTo(Point that) {
     56         /* YOUR CODE HERE */
     57         int x0 = this.x;
     58         int y0 = this.y;
     59         int x1 = that.x;
     60         int y1 = that.y;
     61         if (x0 == x1 && y0 == y1)
     62             return Double.NEGATIVE_INFINITY;
     63         else if (x0 == x1)
     64             return Double.POSITIVE_INFINITY;
     65         else if (y0 == y1)
     66             return +0.0;
     67         else
     68             return (y1 - y0) / (double)(x1 - x0);
     69     }
     70 
     71     /**
     72      * Compares two points by y-coordinate, breaking ties by x-coordinate.
     73      * Formally, the invoking point (x0, y0) is less than the argument point
     74      * (x1, y1) if and only if either y0 < y1 or if y0 = y1 and x0 < x1.
     75      *
     76      * @param that
     77      *            the other point
     78      * @return the value <tt>0</tt> if this point is equal to the argument point
     79      *         (x0 = x1 and y0 = y1); a negative integer if this point is less
     80      *         than the argument point; and a positive integer if this point is
     81      *         greater than the argument point
     82      */
     83     public int compareTo(Point that) {
     84         /* YOUR CODE HERE */
     85         int x0 = this.x;
     86         int y0 = this.y;
     87         int x1 = that.x;
     88         int y1 = that.y;
     89         if (y0 == y1) {
     90             if (x0 == x1)
     91                 return 0;
     92             else if (x0 > x1)
     93                 return 1;
     94             else
     95                 return -1;
     96         } else if (y0 > y1)
     97             return 1;
     98         else
     99             return -1;
    100     }
    101 
    102     /**
    103      * Compares two points by the slope they make with this point. The slope is
    104      * defined as in the slopeTo() method.
    105      *
    106      * @return the Comparator that defines this ordering on points
    107      */
    108     public Comparator<Point> slopeOrder() {
    109         /* YOUR CODE HERE */
    110         return new SlopeOrder(this);
    111     }
    112     /**
    113      * 此comparator提供两个点关于参照点的比较方法,主要供排序方法使用
    114      * invokePoint就是参照点,两个待比较的Point的大小,这就是排序方法中要用的排序依据
    115      * @author evasean www.cnblogs.com/evasean/
    116      *
    117      */
    118     private class SlopeOrder implements Comparator<Point>{
    119         private final Point p0;
    120         public SlopeOrder(Point invokePoint){
    121             this.p0 = invokePoint;
    122         }
    123         @Override
    124         public int compare(Point o1, Point o2) {
    125             // TODO Auto-generated method stub
    126             double slope1 = p0.slopeTo(o1);
    127             double slope2 = p0.slopeTo(o2);
    128             return Double.compare(slope1, slope2); //double不推荐用==直接比大小,采用这种方式比较好
    129         }
    130     }
    131 
    132     /**
    133      * Returns a string representation of this point. This method is provide for
    134      * debugging; your program should not rely on the format of the string
    135      * representation.
    136      *
    137      * @return a string representation of this point
    138      */
    139     public String toString() {
    140         /* DO NOT MODIFY */
    141         return "(" + x + ", " + y + ")";
    142     }
    143 
    144     /**
    145      * Unit tests the Point data type.
    146      */
    147     public static void main(String[] args) {
    148         /* YOUR CODE HERE */
    149         Point p1 = new Point(0, 0);
    150         Point p2 = new Point(1, 1);
    151         System.out.println("p1.compareTo(p2)=" + p1.compareTo(p2));
    152         System.out.println("p1.slopeTo(p2)=" + p1.slopeTo(p2));
    153         Point p3 = new Point(0, 4);
    154         System.out.println("p1.slopeTo(p3)=" + p1.slopeTo(p3));
    155         Point p4 = new Point(4, 4);
    156         System.out.println("p3.compareTo(p4)=" + p3.compareTo(p4));
    157         System.out.println("p3.slopeTo(p4)=" + p3.slopeTo(p4));
    158         Point p5 = new Point(0, 0);
    159         System.out.println("p1.slopeTo(p5)=" + p1.slopeTo(p5));
    160     }
    161 }

    二、Brute force

     

    这个类很简单,直接看代码

      1 import java.util.ArrayList;
      2 import java.util.Arrays;
      3 import java.util.Comparator;
      4 
      5 import edu.princeton.cs.algs4.In;
      6 import edu.princeton.cs.algs4.StdDraw;
      7 import edu.princeton.cs.algs4.StdOut;
      8 /**
      9  * @author evasean www.cnblogs.com/evasean/
     10  */
     11 public class BruteCollinearPoints {
     12     private int segNum;
     13     private Point[] points; //提交作业时提示输入的给构造函数的数组内容不能发生改变,故类中加个数组将输入参数存起来
     14     private ArrayList<LineSegment> segmentList= new ArrayList<LineSegment>();
     15 
     16     public BruteCollinearPoints(Point[] inpoints) {
     17         if (inpoints == null)
     18             throw new IllegalArgumentException("Constructor argument Point[] is null!");
     19         // finds all line segments containing 4 points
     20         for (int i=0;i<inpoints.length;i++) {
     21             if (inpoints[i] == null)
     22                 throw new IllegalArgumentException("there is null in constructor argument");
     23         }
     24         points = new Point[inpoints.length];
     25         for (int i=0;i<inpoints.length;i++) {
     26             points[i] = inpoints[i];
     27         }
     28         Arrays.sort(points); //对本对象的私有数组进行排序
     29         for (int i=0;i<points.length-1;i++) {
     30             if (points[i].compareTo(points[i+1]) == 0) // 与前一个元素相等
     31                 throw new IllegalArgumentException("there exists repeated points!");
     32         }
     33         //作业提交时提示随机穿插顺序调用numberOfSegments()和segment()方法返回结果要求稳定
     34         //那么构造函数中就要把LineSegment找好
     35         findLineSegment(points); 
     36     }
     37 
     38     /**
     39      * 按照作业要求用四层循环来做
     40      * @param points
     41      */
     42     private void findLineSegment(Point[] points) {
     43         int pNum = points.length;
     44         for (int i = 0; i < pNum - 3; i++) { // i不需要遍历最后三个节点,因为至少四个节点才能组成LineSegment
     45             // 每个comparator需要占据额外空间,总共需要n-4个Comparator<Point>的额外空间
     46             Comparator<Point> comparator = points[i].slopeOrder();
     47             for (int j = i + 1; j < pNum - 2; j++) {
     48                 if (points[j].compareTo(points[i]) == 0)
     49                     continue; // 相同point直接跳过
     50                 for (int l = j + 1; l < pNum - 1; l++) {
     51                     if (points[l].compareTo(points[i]) == 0)
     52                         continue;
     53                     if (points[l].compareTo(points[j]) == 0)
     54                         continue;
     55                     if (comparator.compare(points[j], points[l]) == 0) { // point[j]和point[l]相对于point[i]的斜率相等
     56                         for (int m = l + 1; m < pNum; m++) {
     57                             if (points[m].compareTo(points[i]) == 0)
     58                                 continue;
     59                             if (points[m].compareTo(points[j]) == 0)
     60                                 continue;
     61                             if (points[m].compareTo(points[l]) == 0)
     62                                 continue;
     63                             if (comparator.compare(points[l], points[m]) == 0) {
     64                                 // point[l]和point[m]相对于point[i]的斜率相等时,i、j、l、m 四点可以组成一个linesegment
     65                                 // 每个LineSegment需要占据一份额外空间
     66                                 LineSegment seg = new LineSegment(points[i], points[m]);
     67                                 segmentList.add(seg);
     68                             }
     69                         }
     70                     }
     71                 }
     72             }
     73         }
     74         segNum = segmentList.size();
     75         
     76     }
     77     
     78     public int numberOfSegments() {
     79         // the number of line segments
     80         return segNum;
     81     }
     82     
     83     public LineSegment[] segments() {
     84         // the line segments
     85         //作业提交时,提示要求多次调用segments()方法返回的应该是不同的对象
     86         LineSegment[] segments = new LineSegment[segNum];
     87         int i = 0;
     88         for(LineSegment seg : segmentList){
     89             segments[i++] = seg;
     90         }
     91         return segments;
     92     }
     93 
     94     public static void main(String[] args) {
     95         // read the n points from a file
     96         In in = new In(args[0]);
     97         //In in = new In("collinear/input8.txt"); //本地测试使用
     98         int n = in.readInt();
     99         Point[] points = new Point[n];
    100         for (int i = 0; i < n; i++) {
    101             int x = in.readInt();
    102             int y = in.readInt();
    103             points[i] = new Point(x, y);
    104         }
    105         
    106         // draw the points
    107         StdDraw.enableDoubleBuffering();
    108         StdDraw.setXscale(0, 32768);
    109         StdDraw.setYscale(0, 32768);
    110         StdDraw.setPenColor(StdDraw.RED);
    111         StdDraw.setPenRadius(0.01);
    112         for (Point p : points) {
    113             p.draw();
    114         }
    115         StdDraw.show();
    116 
    117         // print and draw the line segments
    118         BruteCollinearPoints collinear = new BruteCollinearPoints(points);
    119         for (LineSegment segment : collinear.segments()) {
    120             StdOut.println(segment);
    121             segment.draw();
    122         }
    123         StdDraw.show();
    124     }
    125 }

    三、A faster, sorting-based solution

     

    这个类的设计思想题目中已经描述的很详细了,主要就是以下四点:

    1. Think of p as the origin. 
    2. For each other point q, determine the slope it makes with p
    3. Sort the points according to the slopes they makes with p
    4. Check if any 3 (or more) adjacent points in the sorted order have equal slopes with respect to p. If so, these points, together with p, are collinear.

    这个算法的性能瓶颈就在于第三点的排序,更详细的设计思路我直接写在代码注释里。

      1 import java.util.ArrayList;
      2 import java.util.Arrays;
      3 import java.util.Comparator;
      4 import java.util.HashMap;
      5 import java.util.List;
      6 import java.util.Map;
      7 
      8 import edu.princeton.cs.algs4.In;
      9 import edu.princeton.cs.algs4.StdDraw;
     10 import edu.princeton.cs.algs4.StdOut;
     11 /**
     12  * @author evasean www.cnblogs.com/evasean/
     13  */
     14 public class FastCollinearPoints {
     15     
     16     private Point[] points; //提交作业时提示输入的给构造函数的数组内容不能发生改变,故类中加个数组将输入参数存起来
     17     private final LineSegment[] segments;
     18     private int segNum;
     19     
     20     private List<PointPair> pointPairList; //存储构成LineSegment的起点和终点Point对
     21     /**
     22      * LineSegment类不允许变动,但是可使用灵活度受限,自己新加个内部类使用
     23      * 本类用来存储可构成LineSegment的起点和终点point对
     24      * 由于在遍历过程中会存在包含关系的起点和终点point对,仅仅靠LineSegment类识别包含关系的效率会很低
     25      * 此类中加了slope来记录就可以很大的提高效率了,因为一个点和一个斜率就确定了一条直线
     26      * 不需要再进行额外比较和计算
     27      * 因为由于PointPair是对points从前到后遍历产生的,所以如果两个PointPair存在包含关系,那么
     28      * 这两个PointPair中largePoint和slope一定相等
     29      * 但smallPoint不相等,smallPoint更小的那个PointPair包含了另一个PointPair
     30      * 这是LineSegment去重的关键
     31      * @author evasean www.cnblogs.com/evasean/
     32      */
     33     private class PointPair{
     34         private final Point smallPoint;
     35         private final Point largePoint;
     36         private final double slope; 
     37         public PointPair(Point smallPoint, Point largePoint){
     38             this.smallPoint = smallPoint;
     39             this.largePoint = largePoint;
     40             this.slope = largePoint.slopeTo(smallPoint);
     41         }
     42         public Point getLargePoint(){
     43             return this.largePoint;
     44         }
     45         public Point getSmallPoint(){
     46             return this.smallPoint;
     47         }
     48         public double getSlope(){
     49             return this.slope;
     50         }
     51         public int compareTo(PointPair that) {
     52             Point l1 = this.getLargePoint();
     53             Point l2 = that.getLargePoint();
     54             double s1 = this.getSlope();
     55             double s2 = that.getSlope();
     56             if(l1.compareTo(l2) > 0) return 1;
     57             else if(l1.compareTo(l2) < 0) return -1;
     58             else{
     59                 if(s1>s2) return 1;
     60                 else if(s1<s2) return -1;
     61                 else return 0;
     62             }
     63         }
     64         /**
     65          * 判断PointPair中的包含关系时需要用到比较器
     66          * 此比较器是以largePoint为比较的主要元素,slope为次要元素
     67          * smallPoint不参比较大小的考核,仅仅在两个PointPair相等时用作判断包含关系之用
     68          * 两个PointPair pp1 和 pp2中
     69          * if pp1.largePoint > pp2.largePoint --> pp1 > pp2
     70          * else if pp1.largePoint < pp2.largePoint --> pp1 < pp2
     71          * if pp1.largePoint == pp2.largePoint && pp1.slope > pp2.slope --> pp1 > pp2
     72          * if pp1.largePoint == pp2.largePoint && pp1.slope < pp2.slope --> pp1 < pp2
     73          * if pp1.largePoint == pp2.largePoint && pp1.slope == pp2.slope --> pp1 == pp2
     74          * @return
     75          */
     76         public Comparator<PointPair> pointPairComparator() {
     77             return new PointPairComparator();
     78         }
     79         private class PointPairComparator implements Comparator<PointPair>{
     80             @Override
     81             public int compare(PointPair pp1, PointPair pp2) {
     82                 // TODO Auto-generated method stub
     83                 Point l1 = pp1.getLargePoint();
     84                 Point l2 = pp2.getLargePoint();
     85                 double s1 = pp1.getSlope();
     86                 double s2 = pp2.getSlope();
     87                 if(l1.compareTo(l2) > 0) return 1;
     88                 else if(l1.compareTo(l2) < 0) return -1;
     89                 else{
     90                     return Double.compare(s1, s2); //double元素用Double.compare进行比较更精确
     91                 }
     92             }
     93         }
     94     }
     95     
     96     public FastCollinearPoints(Point[] inpoints) {
     97         // finds all line segments containing 4 or more points
     98         if (inpoints == null)
     99             throw new IllegalArgumentException("Constructor argument Point[] is null!");
    100         // finds all line segments containing 4 points
    101         for (int i=0;i<inpoints.length;i++) {
    102             if (inpoints[i] == null)
    103                 throw new IllegalArgumentException("there is null in constructor argument");
    104         }
    105         points = new Point[inpoints.length];
    106         for (int i=0;i<inpoints.length;i++) {
    107             points[i] = inpoints[i];
    108         }
    109         Arrays.sort(points); //对本对象的私有数组进行排序
    110         for (int i=0;i<points.length-1;i++) {
    111             if (points[i].compareTo(points[i+1]) == 0) // 与前一个元素相等
    112                 throw new IllegalArgumentException("there exists repeated points!");
    113         }
    114         //作业提交时提示随机穿插顺序调用numberOfSegments()和segment()方法返回结果要求稳定
    115         //那么构造函数中就要把LineSegment找好
    116         findPointPairForLineSegment(points);
    117         segments = generateLineSegment();
    118     }
    119 
    120     /**
    121      * 寻找满足LineSegment的PointPair
    122      * @param points
    123      */
    124     private void findPointPairForLineSegment(Point[] points){
    125         int pNum = points.length;
    126         pointPairList = new ArrayList<PointPair>();
    127         for (int i = 0; i < pNum - 3; i++) { //i不需要遍历最后三个节点,因为至少四个节点才能组成LineSegment
    128             if(points[i]==null)
    129                 throw new IllegalArgumentException("there is null in constructor argument");
    130             Point origin = points[i]; //i处节点作为相对原点
    131             Point[] tPoints = new Point[pNum-i-1]; //需要用到额外空间来存储本轮i之后的节点根据它们各自与节点i的相对斜率来排序的结果
    132             int tpNum = 0;
    133             for (int j = i + 1; j < pNum; j++) {
    134                 tPoints[tpNum++] = points[j];
    135             }
    136             //origin.slopeOrder()这个比较器就是告诉Arrays.sort待排序的那些节点tPoints排序的依据是各自与节点i的斜率
    137             Arrays.sort(tPoints,origin.slopeOrder()); 
    138             
    139             int startPostion = 0; //startPosition用来记录slope相同的point位置区间的起始位置
    140             double slope = origin.slopeTo(tPoints[0]);
    141             Map<Integer,Integer> intervalMap = new HashMap<Integer,Integer>(); //记录slope相同的point位置区间
    142             int curPostion = 1;
    143             for(; curPostion<tpNum; curPostion++){
    144                 if(Double.compare(origin.slopeTo(tPoints[curPostion]), slope)==0)
    145                     continue;
    146                 else{ //遍历至slope不与之前相同的位置
    147                     if(curPostion-startPostion >= 3) { //如果大于3,就表示满足了组成LineSegment的条件,记录point位置区间
    148                         intervalMap.put(startPostion, curPostion-1);//curPostion-1就是区间终止节点位置
    149                     }
    150                     slope = origin.slopeTo(tPoints[curPostion]);
    151                     startPostion = curPostion; //重置起始节点
    152                 }
    153             }
    154             if(curPostion-startPostion >= 3) { //tPoints最后一个节点也可能与前一节点有相同的slope
    155                 intervalMap.put(startPostion, curPostion-1);
    156             }
    157             //根据满足条件的区间位置,创建PointPair
    158             for(int key : intervalMap.keySet()){
    159                 int value = intervalMap.get(key);
    160                 Point[] linearPoints = new Point[value-key+2];
    161                 linearPoints[0] = origin;
    162                 int l = 1;
    163                 while(key<=value){
    164                     linearPoints[l++] = tPoints[key++];
    165                 }
    166                 Arrays.sort(linearPoints);
    167                 PointPair pointPair = new PointPair(linearPoints[0], linearPoints[l-1]);
    168                 pointPairList.add(pointPair);
    169             }
    170             //清空临时数据,便于垃圾回收
    171             intervalMap.clear();
    172             intervalMap = null;
    173             for(int t=0;t<tPoints.length;t++){
    174                 tPoints[t] = null;
    175             }
    176             tPoints = null;
    177         }
    178     }
    179     /**
    180      * 生成LineSegment
    181      * @return
    182      */
    183     private LineSegment[]  generateLineSegment(){
    184         int ppsize = pointPairList.size();
    185         if(ppsize==0) return new LineSegment[0];;
    186         PointPair[] pointPairs =  new PointPair[ppsize];
    187         int i = 0;
    188         for(PointPair pp : pointPairList){
    189             pointPairs[i++] = pp;
    190         }
    191         pointPairList.clear();
    192         //根据pointPairComparator比较器所定制的排序依据进行排序,使得存在包含关系的PointPair变成相邻关系
    193         Arrays.sort(pointPairs,pointPairs[0].pointPairComparator());
    194         List<LineSegment> lineSegmentList = new ArrayList<LineSegment>();
    195         
    196         PointPair ppls = pointPairs[0]; 
    197         for(i=1;i<ppsize;i++){
    198             if(ppls.compareTo(pointPairs[i])==0){ //相邻的PointPair相等时,具有更小smallPoint的PointPair区间更大
    199                 Point s = pointPairs[i].getSmallPoint();
    200                 if(ppls.getSmallPoint().compareTo(s) > 0)
    201                     ppls = pointPairs[i];
    202             }else{
    203                 LineSegment seg = new LineSegment(ppls.getSmallPoint(),ppls.getLargePoint());
    204                 lineSegmentList.add(seg);
    205                 ppls = pointPairs[i];
    206             }
    207         }
    208         LineSegment seg = new LineSegment(ppls.getSmallPoint(),ppls.getLargePoint());
    209         lineSegmentList.add(seg);
    210         
    211         LineSegment[] segments = new LineSegment[lineSegmentList.size()];
    212         segNum = 0;
    213         for (LineSegment ls : lineSegmentList) {
    214             segments[segNum++] = ls;
    215         }
    216         return segments;
    217     }
    218     
    219     public int numberOfSegments() {
    220         // the number of line segments
    221         return segNum;
    222     }
    223     
    224     public LineSegment[] segments() {
    225         // the line segments
    226         //作业提交时,提示要求多次调用segments()方法返回的应该是不同的对象
    227         LineSegment[] retseg = new LineSegment[segNum];
    228         for(int i =0 ;i<segNum;i++){
    229             retseg[i] = segments[i];
    230         }
    231         return retseg;
    232     }
    233     
    234     public static void main(String[] args) {
    235         // read the n points from a file
    236         //In in = new In(args[0]);
    237         In in = new In("collinear/rs1423.txt"); //本地测试使用
    238         int n = in.readInt();
    239         Point[] points = new Point[n];
    240         for (int i = 0; i < n; i++) {
    241             int x = in.readInt();
    242             int y = in.readInt();
    243             points[i] = new Point(x, y);
    244         }
    245 
    246         // draw the points
    247         StdDraw.enableDoubleBuffering();
    248         StdDraw.setXscale(0, 32768);
    249         StdDraw.setYscale(0, 32768);
    250 //        StdDraw.setPenColor(StdDraw.RED);
    251 //        StdDraw.setPenRadius(0.01);
    252         for (Point p : points) {
    253             p.draw();
    254         }
    255         StdDraw.show();
    256 
    257         // print and draw the line segments
    258         FastCollinearPoints collinear = new FastCollinearPoints(points);
    259         for (LineSegment segment : collinear.segments()) {
    260             StdOut.println(segment);
    261             segment.draw();
    262         }
    263         StdDraw.show();
    264     }
    265 }
  • 相关阅读:
    windows下postgreSQL安装与启动
    Map、Set、List区别
    责任链模式实战
    Java的URL类(二)
    linux 之分区和文件系统
    linux 之网络命令
    linux 之用户管理
    linux 权限之acl
    我的hadoop学习之路
    Hadoop 学习之Docker
  • 原文地址:https://www.cnblogs.com/evasean/p/7246793.html
Copyright © 2011-2022 走看看