1. 程式人生 > >普林斯頓演算法課第三週作業

普林斯頓演算法課第三週作業

第三週作業模式識別

Programming Assignment 3: Pattern Recognition


題目地址:http://coursera.cs.princeton.edu/algs4/assignments/collinear.html

------------------------------------------

這一週的問題是在一個給出N個隨即點的平面上,
畫出所有所在路徑超過四個點的直線。
首先還是老老實實的從第一個類開始說起。
public class Point implements Comparable<Point> {
   public final Comparator<Point> SLOPE_ORDER;        // compare points by slope to this point


   public Point(int x, int y)                         // construct the point (x, y)


   public   void draw()                               // draw this point
   public   void drawTo(Point that)                   // draw the line segment from this point to that point
   public String toString()                           // string representation


   public    int compareTo(Point that)                // is this point lexicographically smaller than that point?
   public double slopeTo(Point that)                  // the slope between this point and that point
}




第一個類的主要功能是建立點的分佈,並通過StdDraw類輸出,其中運用到了比較器。
很良心的,其中前四個方法已經給出。
我們只需要把後兩個和比較器寫出。
1. public    int compareTo(Point that)                // is this point lexicographically smaller than that point?
也就是是否這個點是否比那個點小,比較標準給出了,
y0<y1或y0=y1但x0<x1時 表示this.point<that.point


2.public double slopeTo(Point that)                  // the slope between this point and that point


計算兩點間斜率,官方說明上給的很清楚,包括各類情況的返回值,就不多說了。
3.public final Comparator<Point> SLOPE_ORDER; 
用來比較斜率的比較器,第一次用不是很會用。後來摸透了以後感覺很方便。
程式碼如下:

import java.util.Comparator;

public class Point implements Comparable<Point> {

    // compare points by slope
    public final Comparator<Point> SLOPE_ORDER= new SlopeOrder();;       // YOUR DEFINITION HERE

    private final int x;                              // x coordinate
    private final int y;                              // y coordinate
    
    private class SlopeOrder implements Comparator<Point>{  
    	  
        public int compare(Point p1, Point p2) {  
            double slopeP1 = slopeTo(p1);  
            double slopeP2 = slopeTo(p2);  
            if (slopeP1 == slopeP2) return 0;  
            if (slopeP1 < slopeP2) return -1;  
            else return +1;  
        }  
          
    }  

    // create the point (x, y)
    public Point(int x, int y) {
        /* DO NOT MODIFY */
        this.x = x;
        this.y = y;
    }

    // plot this point to standard drawing
    public void draw() {
        /* DO NOT MODIFY */
        StdDraw.point(x, y);
    }

    // draw line between this point and that point to standard drawing
    public void drawTo(Point that) {
        /* DO NOT MODIFY */
        StdDraw.line(this.x, this.y, that.x, that.y);
    }

    // slope between this point and that point
    public double slopeTo(Point that) {
        /* YOUR CODE HERE */
    	if (this.compareTo(that) == 0)  
            return Double.NEGATIVE_INFINITY;  
        else if (this.x == that.x)  
            return Double.POSITIVE_INFINITY;  
        else if (this.y == that.y)  
            return +0;  
        else  
            return (that.y - this.y) * 1.0 / (that.x - this.x);  
    }

    // is this point lexicographically smaller than that one?
    // comparing y-coordinates and breaking ties by x-coordinates
    public int compareTo(Point that) {
        /* YOUR CODE HERE */
    	if (this.y < that.y || (this.y == that.y && this.x < that.x))  
            return -1;  
        else if (this.y == that.y && this.x == that.x)  
            return 0;  
        else  
            return 1;  
    	
    }

    // return string representation of this point
    public String toString() {
        /* DO NOT MODIFY */
        return "(" + x + ", " + y + ")";
    }

    // unit test
    public static void main(String[] args) {

        // rescale coordinates and turn on animation mode
        StdDraw.setXscale(0, 32768);
        StdDraw.setYscale(0, 32768);
        StdDraw.show(0);
        StdDraw.setPenRadius(0.01);  // make the points a bit larger

        // read in the input
        String filename = args[0];
        In in = new In(filename);
        int N = in.readInt();
        for (int i = 0; i < N; i++) {
            int x = in.readInt();
            int y = in.readInt();
            Point p = new Point(x, y);
            p.draw();
        
        }

        // display to screen all at once
        StdDraw.show(0);

        // reset the pen radius
        StdDraw.setPenRadius();
    
    }
}







第二個類是用暴力方法連線


暴力解法的思想很簡單,用列舉法對所有點進行排序後判斷斜率,三個斜率相等後則畫出直線。
當然時間很慢,複雜度是N^4


程式碼如下:
public class Brute {
	public static void main(String[] args) {

        // rescale coordinates and turn on animation mode
        StdDraw.setXscale(0, 32768);
        StdDraw.setYscale(0, 32768);
       
      

        // read in the input
        String filename = args[0];
        In in = new In(filename);
        int N = in.readInt();
        Point[] pointArray=new Point[N];
        for (int i = 0; i < N; i++) {
            int x = in.readInt();
            int y = in.readInt();
            Point p = new Point(x, y);
            pointArray[i] = p;
            p.draw();
        }
        Quick3way.sort(pointArray);
        BruteForce(pointArray);  
        StdDraw.show(0);  
	}
	private static void BruteForce(Point[] pointArray){  
        int N = pointArray.length;  
        for (int i=0; i<N; i++){  
            for (int j=i+1; j<N; j++){  
                for (int k=j+1; k<N; k++){  
                    for (int l=k+1; l<N; l++){  
                        if (pointArray[i].slopeTo(pointArray[j]) == pointArray[i].slopeTo(pointArray[k]) &&  
                            pointArray[i].slopeTo(pointArray[j]) == pointArray[i].slopeTo(pointArray[l])){  
                            StdOut.println(pointArray[i] + " -> " + pointArray[j] + " -> "  + pointArray[k] + " -> "  + pointArray[l]);  
                            pointArray[i].drawTo(pointArray[l]);  
                        }  
                    }  
                }  
            }  
        }  
    }  
	
}





第三個類是用一個很快的、基於排序的解決方法。
在給定點P上,對所有斜率進行排序,如果存在三個點或以上的斜率相等,則共線。
當然還是要列舉P點,不過排序的話複雜度會下降很多。
同時要避免同一條線段多次輸出,需要用到compareTo。


程式碼如下:

import java.util.Arrays;

public class Fast {
	public static void main(String[] args){
		// rescale coordinates and turn on animation mode
        StdDraw.setXscale(0, 32768);
        StdDraw.setYscale(0, 32768);
        
		String filename = args[0];
        In in = new In(filename);
        int N = in.readInt();
        if (N < 4){
        	return;
        }
        Point[] pointArray = new Point[N];
        for (int i = 0; i < N; i++) {
            int x = in.readInt();
            int y = in.readInt();
            Point p = new Point(x, y);
            pointArray[i] = p;
            p.draw();
        }
        Quick3way.sort(pointArray);
        FastMethod(pointArray);
        StdDraw.show(0);
    }
	
	private static void FastMethod(Point[] pointArray){
		int N = pointArray.length;
		for (int i=0; i<N; i++){
			Point origPoint = pointArray[i];
			Point[] otherPoint = new Point[N-1];
			for (int j=0; j<pointArray.length; j++){
				if (j < i) otherPoint[j] = pointArray[j];
				if (j > i) otherPoint[j-1] = pointArray[j];
			}
			Arrays.sort(otherPoint, origPoint.SLOPE_ORDER);
			
			int count = 0;
			int index = 0;
			double tempSlope = origPoint.slopeTo(otherPoint[0]);
			for (int j=0; j<otherPoint.length;j++){
				if (Double.compare(origPoint.slopeTo(otherPoint[j]),  tempSlope) == 0){
					count++;
					continue;
				}else{
					if (count >=3){
						if (otherPoint[index].compareTo(origPoint) >=0){
							StdOut.print(origPoint + " -> ");
							for (int k=index; k<j-1; k++){
								StdOut.print(otherPoint[k] + " -> ");
							}
							StdOut.println(otherPoint[j-1]);
							origPoint.drawTo(otherPoint[j-1]);
						}
					}
					count = 1;
					index = j;
					tempSlope = origPoint.slopeTo(otherPoint[j]);
				}
			}
			if (count >= 3){
				if (otherPoint[index].compareTo(origPoint) >= 0){
					StdOut.print(origPoint + " -> ");
					for (int k=index; k<otherPoint.length - 1; k++){
						StdOut.print(otherPoint[k] + " -> ");
					}
					StdOut.println(otherPoint[otherPoint.length-1]);
					origPoint.drawTo(otherPoint[otherPoint.length-1]);
				}
			}
		}
		StdDraw.show(0);
	}
}