题目原文详见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。比较器主要在排序方法中使用

 import java.util.Comparator;
import edu.princeton.cs.algs4.StdDraw; public class Point implements Comparable<Point> { private final int x; // x-coordinate of this point
private final int y; // y-coordinate of this point /**
* Initializes a new point.
*
* @param x
* the <em>x</em>-coordinate of the point
* @param y
* the <em>y</em>-coordinate of the point
*/
public Point(int x, int y) {
/* DO NOT MODIFY */
this.x = x;
this.y = y;
} /**
* Draws this point to standard draw.
*/
public void draw() {
/* DO NOT MODIFY */
StdDraw.point(x, y);
} /**
* Draws the line segment between this point and the specified point to
* standard draw.
*
* @param that
* the other point
*/
public void drawTo(Point that) {
/* DO NOT MODIFY */
StdDraw.line(this.x, this.y, that.x, that.y);
} /**
* Returns the slope between this point and the specified point. Formally,
* if the two points are (x0, y0) and (x1, y1), then the slope is (y1 - y0)
* / (x1 - x0). For completeness, the slope is defined to be +0.0 if the
* line segment connecting the two points is horizontal;
* Double.POSITIVE_INFINITY if the line segment is vertical; and
* Double.NEGATIVE_INFINITY if (x0, y0) and (x1, y1) are equal.
*
* @param that
* the other point
* @return the slope between this point and the specified point
*/
public double slopeTo(Point that) {
/* YOUR CODE HERE */
int x0 = this.x;
int y0 = this.y;
int x1 = that.x;
int y1 = that.y;
if (x0 == x1 && y0 == y1)
return Double.NEGATIVE_INFINITY;
else if (x0 == x1)
return Double.POSITIVE_INFINITY;
else if (y0 == y1)
return +0.0;
else
return (y1 - y0) / (double)(x1 - x0);
} /**
* Compares two points by y-coordinate, breaking ties by x-coordinate.
* Formally, the invoking point (x0, y0) is less than the argument point
* (x1, y1) if and only if either y0 < y1 or if y0 = y1 and x0 < x1.
*
* @param that
* the other point
* @return the value <tt>0</tt> if this point is equal to the argument point
* (x0 = x1 and y0 = y1); a negative integer if this point is less
* than the argument point; and a positive integer if this point is
* greater than the argument point
*/
public int compareTo(Point that) {
/* YOUR CODE HERE */
int x0 = this.x;
int y0 = this.y;
int x1 = that.x;
int y1 = that.y;
if (y0 == y1) {
if (x0 == x1)
return 0;
else if (x0 > x1)
return 1;
else
return -1;
} else if (y0 > y1)
return 1;
else
return -1;
} /**
* Compares two points by the slope they make with this point. The slope is
* defined as in the slopeTo() method.
*
* @return the Comparator that defines this ordering on points
*/
public Comparator<Point> slopeOrder() {
/* YOUR CODE HERE */
return new SlopeOrder(this);
}
/**
* 此comparator提供两个点关于参照点的比较方法,主要供排序方法使用
* invokePoint就是参照点,两个待比较的Point的大小,这就是排序方法中要用的排序依据
* @author evasean www.cnblogs.com/evasean/
*
*/
private class SlopeOrder implements Comparator<Point>{
private final Point p0;
public SlopeOrder(Point invokePoint){
this.p0 = invokePoint;
}
@Override
public int compare(Point o1, Point o2) {
// TODO Auto-generated method stub
double slope1 = p0.slopeTo(o1);
double slope2 = p0.slopeTo(o2);
return Double.compare(slope1, slope2); //double不推荐用==直接比大小,采用这种方式比较好
}
} /**
* Returns a string representation of this point. This method is provide for
* debugging; your program should not rely on the format of the string
* representation.
*
* @return a string representation of this point
*/
public String toString() {
/* DO NOT MODIFY */
return "(" + x + ", " + y + ")";
} /**
* Unit tests the Point data type.
*/
public static void main(String[] args) {
/* YOUR CODE HERE */
Point p1 = new Point(0, 0);
Point p2 = new Point(1, 1);
System.out.println("p1.compareTo(p2)=" + p1.compareTo(p2));
System.out.println("p1.slopeTo(p2)=" + p1.slopeTo(p2));
Point p3 = new Point(0, 4);
System.out.println("p1.slopeTo(p3)=" + p1.slopeTo(p3));
Point p4 = new Point(4, 4);
System.out.println("p3.compareTo(p4)=" + p3.compareTo(p4));
System.out.println("p3.slopeTo(p4)=" + p3.slopeTo(p4));
Point p5 = new Point(0, 0);
System.out.println("p1.slopeTo(p5)=" + p1.slopeTo(p5));
}
}

二、Brute force

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

 import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;
/**
* @author evasean www.cnblogs.com/evasean/
*/
public class BruteCollinearPoints {
private int segNum;
private Point[] points; //提交作业时提示输入的给构造函数的数组内容不能发生改变,故类中加个数组将输入参数存起来
private ArrayList<LineSegment> segmentList= new ArrayList<LineSegment>(); public BruteCollinearPoints(Point[] inpoints) {
if (inpoints == null)
throw new IllegalArgumentException("Constructor argument Point[] is null!");
// finds all line segments containing 4 points
for (int i=0;i<inpoints.length;i++) {
if (inpoints[i] == null)
throw new IllegalArgumentException("there is null in constructor argument");
}
points = new Point[inpoints.length];
for (int i=0;i<inpoints.length;i++) {
points[i] = inpoints[i];
}
Arrays.sort(points); //对本对象的私有数组进行排序
for (int i=0;i<points.length-1;i++) {
if (points[i].compareTo(points[i+1]) == 0) // 与前一个元素相等
throw new IllegalArgumentException("there exists repeated points!");
}
//作业提交时提示随机穿插顺序调用numberOfSegments()和segment()方法返回结果要求稳定
//那么构造函数中就要把LineSegment找好
findLineSegment(points);
} /**
* 按照作业要求用四层循环来做
* @param points
*/
private void findLineSegment(Point[] points) {
int pNum = points.length;
for (int i = 0; i < pNum - 3; i++) { // i不需要遍历最后三个节点,因为至少四个节点才能组成LineSegment
// 每个comparator需要占据额外空间,总共需要n-4个Comparator<Point>的额外空间
Comparator<Point> comparator = points[i].slopeOrder();
for (int j = i + 1; j < pNum - 2; j++) {
if (points[j].compareTo(points[i]) == 0)
continue; // 相同point直接跳过
for (int l = j + 1; l < pNum - 1; l++) {
if (points[l].compareTo(points[i]) == 0)
continue;
if (points[l].compareTo(points[j]) == 0)
continue;
if (comparator.compare(points[j], points[l]) == 0) { // point[j]和point[l]相对于point[i]的斜率相等
for (int m = l + 1; m < pNum; m++) {
if (points[m].compareTo(points[i]) == 0)
continue;
if (points[m].compareTo(points[j]) == 0)
continue;
if (points[m].compareTo(points[l]) == 0)
continue;
if (comparator.compare(points[l], points[m]) == 0) {
// point[l]和point[m]相对于point[i]的斜率相等时,i、j、l、m 四点可以组成一个linesegment
// 每个LineSegment需要占据一份额外空间
LineSegment seg = new LineSegment(points[i], points[m]);
segmentList.add(seg);
}
}
}
}
}
}
segNum = segmentList.size(); } public int numberOfSegments() {
// the number of line segments
return segNum;
} public LineSegment[] segments() {
// the line segments
//作业提交时,提示要求多次调用segments()方法返回的应该是不同的对象
LineSegment[] segments = new LineSegment[segNum];
int i = 0;
for(LineSegment seg : segmentList){
segments[i++] = seg;
}
return segments;
} public static void main(String[] args) {
// read the n points from a file
In in = new In(args[0]);
//In in = new In("collinear/input8.txt"); //本地测试使用
int n = in.readInt();
Point[] points = new Point[n];
for (int i = 0; i < n; i++) {
int x = in.readInt();
int y = in.readInt();
points[i] = new Point(x, y);
} // draw the points
StdDraw.enableDoubleBuffering();
StdDraw.setXscale(0, 32768);
StdDraw.setYscale(0, 32768);
StdDraw.setPenColor(StdDraw.RED);
StdDraw.setPenRadius(0.01);
for (Point p : points) {
p.draw();
}
StdDraw.show(); // print and draw the line segments
BruteCollinearPoints collinear = new BruteCollinearPoints(points);
for (LineSegment segment : collinear.segments()) {
StdOut.println(segment);
segment.draw();
}
StdDraw.show();
}
}

三、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.

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

 import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;
/**
* @author evasean www.cnblogs.com/evasean/
*/
public class FastCollinearPoints { private Point[] points; //提交作业时提示输入的给构造函数的数组内容不能发生改变,故类中加个数组将输入参数存起来
private final LineSegment[] segments;
private int segNum; private List<PointPair> pointPairList; //存储构成LineSegment的起点和终点Point对
/**
* LineSegment类不允许变动,但是可使用灵活度受限,自己新加个内部类使用
* 本类用来存储可构成LineSegment的起点和终点point对
* 由于在遍历过程中会存在包含关系的起点和终点point对,仅仅靠LineSegment类识别包含关系的效率会很低
* 此类中加了slope来记录就可以很大的提高效率了,因为一个点和一个斜率就确定了一条直线
* 不需要再进行额外比较和计算
* 因为由于PointPair是对points从前到后遍历产生的,所以如果两个PointPair存在包含关系,那么
* 这两个PointPair中largePoint和slope一定相等
* 但smallPoint不相等,smallPoint更小的那个PointPair包含了另一个PointPair
* 这是LineSegment去重的关键
* @author evasean www.cnblogs.com/evasean/
*/
private class PointPair{
private final Point smallPoint;
private final Point largePoint;
private final double slope;
public PointPair(Point smallPoint, Point largePoint){
this.smallPoint = smallPoint;
this.largePoint = largePoint;
this.slope = largePoint.slopeTo(smallPoint);
}
public Point getLargePoint(){
return this.largePoint;
}
public Point getSmallPoint(){
return this.smallPoint;
}
public double getSlope(){
return this.slope;
}
public int compareTo(PointPair that) {
Point l1 = this.getLargePoint();
Point l2 = that.getLargePoint();
double s1 = this.getSlope();
double s2 = that.getSlope();
if(l1.compareTo(l2) > 0) return 1;
else if(l1.compareTo(l2) < 0) return -1;
else{
if(s1>s2) return 1;
else if(s1<s2) return -1;
else return 0;
}
}
/**
* 判断PointPair中的包含关系时需要用到比较器
* 此比较器是以largePoint为比较的主要元素,slope为次要元素
* smallPoint不参比较大小的考核,仅仅在两个PointPair相等时用作判断包含关系之用
* 两个PointPair pp1 和 pp2中
* if pp1.largePoint > pp2.largePoint --> pp1 > pp2
* else if pp1.largePoint < pp2.largePoint --> pp1 < pp2
* if pp1.largePoint == pp2.largePoint && pp1.slope > pp2.slope --> pp1 > pp2
* if pp1.largePoint == pp2.largePoint && pp1.slope < pp2.slope --> pp1 < pp2
* if pp1.largePoint == pp2.largePoint && pp1.slope == pp2.slope --> pp1 == pp2
* @return
*/
public Comparator<PointPair> pointPairComparator() {
return new PointPairComparator();
}
private class PointPairComparator implements Comparator<PointPair>{
@Override
public int compare(PointPair pp1, PointPair pp2) {
// TODO Auto-generated method stub
Point l1 = pp1.getLargePoint();
Point l2 = pp2.getLargePoint();
double s1 = pp1.getSlope();
double s2 = pp2.getSlope();
if(l1.compareTo(l2) > 0) return 1;
else if(l1.compareTo(l2) < 0) return -1;
else{
return Double.compare(s1, s2); //double元素用Double.compare进行比较更精确
}
}
}
} public FastCollinearPoints(Point[] inpoints) {
// finds all line segments containing 4 or more points
if (inpoints == null)
throw new IllegalArgumentException("Constructor argument Point[] is null!");
// finds all line segments containing 4 points
for (int i=0;i<inpoints.length;i++) {
if (inpoints[i] == null)
throw new IllegalArgumentException("there is null in constructor argument");
}
points = new Point[inpoints.length];
for (int i=0;i<inpoints.length;i++) {
points[i] = inpoints[i];
}
Arrays.sort(points); //对本对象的私有数组进行排序
for (int i=0;i<points.length-1;i++) {
if (points[i].compareTo(points[i+1]) == 0) // 与前一个元素相等
throw new IllegalArgumentException("there exists repeated points!");
}
//作业提交时提示随机穿插顺序调用numberOfSegments()和segment()方法返回结果要求稳定
//那么构造函数中就要把LineSegment找好
findPointPairForLineSegment(points);
segments = generateLineSegment();
} /**
* 寻找满足LineSegment的PointPair
* @param points
*/
private void findPointPairForLineSegment(Point[] points){
int pNum = points.length;
pointPairList = new ArrayList<PointPair>();
for (int i = 0; i < pNum - 3; i++) { //i不需要遍历最后三个节点,因为至少四个节点才能组成LineSegment
if(points[i]==null)
throw new IllegalArgumentException("there is null in constructor argument");
Point origin = points[i]; //i处节点作为相对原点
Point[] tPoints = new Point[pNum-i-1]; //需要用到额外空间来存储本轮i之后的节点根据它们各自与节点i的相对斜率来排序的结果
int tpNum = 0;
for (int j = i + 1; j < pNum; j++) {
tPoints[tpNum++] = points[j];
}
//origin.slopeOrder()这个比较器就是告诉Arrays.sort待排序的那些节点tPoints排序的依据是各自与节点i的斜率
Arrays.sort(tPoints,origin.slopeOrder()); int startPostion = 0; //startPosition用来记录slope相同的point位置区间的起始位置
double slope = origin.slopeTo(tPoints[0]);
Map<Integer,Integer> intervalMap = new HashMap<Integer,Integer>(); //记录slope相同的point位置区间
int curPostion = 1;
for(; curPostion<tpNum; curPostion++){
if(Double.compare(origin.slopeTo(tPoints[curPostion]), slope)==0)
continue;
else{ //遍历至slope不与之前相同的位置
if(curPostion-startPostion >= 3) { //如果大于3,就表示满足了组成LineSegment的条件,记录point位置区间
intervalMap.put(startPostion, curPostion-1);//curPostion-1就是区间终止节点位置
}
slope = origin.slopeTo(tPoints[curPostion]);
startPostion = curPostion; //重置起始节点
}
}
if(curPostion-startPostion >= 3) { //tPoints最后一个节点也可能与前一节点有相同的slope
intervalMap.put(startPostion, curPostion-1);
}
//根据满足条件的区间位置,创建PointPair
for(int key : intervalMap.keySet()){
int value = intervalMap.get(key);
Point[] linearPoints = new Point[value-key+2];
linearPoints[0] = origin;
int l = 1;
while(key<=value){
linearPoints[l++] = tPoints[key++];
}
Arrays.sort(linearPoints);
PointPair pointPair = new PointPair(linearPoints[0], linearPoints[l-1]);
pointPairList.add(pointPair);
}
//清空临时数据,便于垃圾回收
intervalMap.clear();
intervalMap = null;
for(int t=0;t<tPoints.length;t++){
tPoints[t] = null;
}
tPoints = null;
}
}
/**
* 生成LineSegment
* @return
*/
private LineSegment[] generateLineSegment(){
int ppsize = pointPairList.size();
if(ppsize==0) return new LineSegment[0];;
PointPair[] pointPairs = new PointPair[ppsize];
int i = 0;
for(PointPair pp : pointPairList){
pointPairs[i++] = pp;
}
pointPairList.clear();
//根据pointPairComparator比较器所定制的排序依据进行排序,使得存在包含关系的PointPair变成相邻关系
Arrays.sort(pointPairs,pointPairs[0].pointPairComparator());
List<LineSegment> lineSegmentList = new ArrayList<LineSegment>(); PointPair ppls = pointPairs[0];
for(i=1;i<ppsize;i++){
if(ppls.compareTo(pointPairs[i])==0){ //相邻的PointPair相等时,具有更小smallPoint的PointPair区间更大
Point s = pointPairs[i].getSmallPoint();
if(ppls.getSmallPoint().compareTo(s) > 0)
ppls = pointPairs[i];
}else{
LineSegment seg = new LineSegment(ppls.getSmallPoint(),ppls.getLargePoint());
lineSegmentList.add(seg);
ppls = pointPairs[i];
}
}
LineSegment seg = new LineSegment(ppls.getSmallPoint(),ppls.getLargePoint());
lineSegmentList.add(seg); LineSegment[] segments = new LineSegment[lineSegmentList.size()];
segNum = 0;
for (LineSegment ls : lineSegmentList) {
segments[segNum++] = ls;
}
return segments;
} public int numberOfSegments() {
// the number of line segments
return segNum;
} public LineSegment[] segments() {
// the line segments
//作业提交时,提示要求多次调用segments()方法返回的应该是不同的对象
LineSegment[] retseg = new LineSegment[segNum];
for(int i =0 ;i<segNum;i++){
retseg[i] = segments[i];
}
return retseg;
} public static void main(String[] args) {
// read the n points from a file
//In in = new In(args[0]);
In in = new In("collinear/rs1423.txt"); //本地测试使用
int n = in.readInt();
Point[] points = new Point[n];
for (int i = 0; i < n; i++) {
int x = in.readInt();
int y = in.readInt();
points[i] = new Point(x, y);
} // draw the points
StdDraw.enableDoubleBuffering();
StdDraw.setXscale(0, 32768);
StdDraw.setYscale(0, 32768);
// StdDraw.setPenColor(StdDraw.RED);
// StdDraw.setPenRadius(0.01);
for (Point p : points) {
p.draw();
}
StdDraw.show(); // print and draw the line segments
FastCollinearPoints collinear = new FastCollinearPoints(points);
for (LineSegment segment : collinear.segments()) {
StdOut.println(segment);
segment.draw();
}
StdDraw.show();
}
}

Coursera Algorithms Programming Assignment 3: Pattern Recognition (100分)的更多相关文章

  1. Coursera Algorithms Programming Assignment 1: Percolation(100分)

    题目来源http://coursera.cs.princeton.edu/algs4/assignments/percolation.html 作业分为两部分:建立模型和仿真实验. 最关键的部分就是建 ...

  2. Algorithms : Programming Assignment 3: Pattern Recognition

    Programming Assignment 3: Pattern Recognition 1.题目重述 原题目:Programming Assignment 3: Pattern Recogniti ...

  3. Coursera Algorithms Programming Assignment 5: Kd-Trees (98分)

    题目地址:http://coursera.cs.princeton.edu/algs4/assignments/kdtree.html 分析: Brute-force implementation. ...

  4. Programming Assignment 3: Pattern Recognition

    编程作业三 作业链接:Pattern Recognition & Checklist 我的代码:BruteCollinearPoints.java & FastCollinearPoi ...

  5. Coursera Algorithms Programming Assignment 2: Deque and Randomized Queue (100分)

    作业原文:http://coursera.cs.princeton.edu/algs4/assignments/queues.html 这次作业与第一周作业相比,稍微简单一些.有三个编程练习:双端队列 ...

  6. Coursera Algorithms Programming Assignment 4: 8 Puzzle (100分)

    题目原文:http://coursera.cs.princeton.edu/algs4/assignments/8puzzle.html 题目要求:设计一个程序解决8 puzzle问题以及该问题的推广 ...

  7. Algorithms: Design and Analysis, Part 1 - Programming Assignment #1

    自我总结: 1.编程的思维不够,虽然分析有哪些需要的函数,但是不能比较好的汇总整合 2.写代码能力,容易挫败感,经常有bug,很烦心,耐心不够好 题目: In this programming ass ...

  8. Coursera课程 Programming Languages, Part A 总结

    Coursera CSE341: Programming Languages 感谢华盛顿大学 Dan Grossman 老师 以及 Coursera . 碎言碎语 这只是 Programming La ...

  9. Pattern Recognition and Machine Learning-02-1.0-Introduction

    Introduction The problem of searching for patterns in data is a fundamental one and has a long and s ...

随机推荐

  1. jQuery.ajax()方法中參数具体解析

    前言 在项目开发中,为了实现异步向服务端发起请求,最常常使用的就是jQuery.ajax方法了.刚開始需求比較简单,调用jQuery.ajax方法时要传的參数也就那几个常见的參数:url/data/d ...

  2. 如何用Python批量发现互联网“开放”摄像头

    现在无论家用还是公司使用摄像头越来越多,但是安全性又如何呐?今天我来说说几款比较常用的摄像头,并且使用python如何批量检查弱口令. 第一个“海康威视”: 前段时间爆出海康威视的摄像头存在默认弱口令 ...

  3. OSX: diskutil命令-转换成自由空间并再对其分区

    声明:本文涉及的操作非常可能会破坏你的系统文件,造成数据丢失.请谨慎模仿,一切后果作者均不承担不论什么责任. 目的: 尽管说比較熟悉diskutil命令和它的GUI前端程序磁盘工具(Disk Util ...

  4. Android PullToRefresh 下拉刷新,上拉很多其它,支持ScrollView,ListView,可方便拓展GridView,WebView等

    在写着东西之前.从网上找到非常多这方面的源代码,可是基本没有找到惬意的.包含在GitHub上的比較有名的Android-PullToRefresh-master.思来想去还是自己写吧.当然当中借鉴了一 ...

  5. 老大写得一个非常高大上的Makefile,包括非常多语法:

    一个非常高大上的Makefile,包括非常多语法: TARGET = api-login INSTALL_PATH = /huishoubao/cgi include ../../implements ...

  6. Sublime Text使用

    安装Sublime Text Sublime 的安装比較简单,我们能够直接去官网http://www.sublimetext.com/,点击Download菜单.进入之后选择自己操作系统的进行下载安装 ...

  7. 细说linux IPC(三):mmap系统调用共享内存

    [版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途]         前面讲到socket的进程间通 ...

  8. 转载:用python爬虫抓站的一些技巧总结

    原文链接:http://www.pythonclub.org/python-network-application/observer-spider 原文的名称虽然用了<用python爬虫抓站的一 ...

  9. const *char p和char const *p

    const *char p和char const *p,const char*p的区别 char*const p——p必须初始化,且不能指向别处,即p是指针常量: char const*p——p指向的 ...

  10. Android 打造随意层级树形控件 考验你的数据结构和设计

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40212367,本文出自:[张鸿洋的博客] 1.概述 大家在项目中或多或少的可能会 ...