普林斯頓演算法課第三週作業
阿新 • • 發佈:2019-01-10
第三週作業模式識別
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);
}
}