这本来应该是第三周的作业,但是由于其他作业逼近deadline,暂时推后了一周完成。

这周的assignment大大提高了我对这门课的看法,不得不说,Algorithms这门课的assignment部分设计得很好。为什么好?个人认为有以下几点:

  1. 程序要求解耦
  2. 循序渐进,这周的作业不允许使用hashcode()
  3. 输入变量不允许被程序改变

能做到1、3两点,在工程中也是高质量代码的体现


题目很简单,即给出一个点集,找所有出任意四点或以上共线的线段,将该线段的端点new 一个segment()对象存起来。看起来很简单,实际上到处都是坑TAT

第一部分是用brute force的方法做,由于4个及以上的点才是共线,这里要求中提出为了简化,brute方法不考虑5个及以上的点。所以既是对点集进行4层循环即可,时间复杂度是O(n4)。

但是这里需要注意的是,输入的点集,不能被直接操作,必须被赋值为拷贝才行,否则,外界如果更改了点集中的点,则会影响结果。这一点还说明了,java都是值传递,传进来的是一个数组引用的copy。

另外辩解条件必须要注意,其实挺容易出错的。

 1 public class BruteCollinearPoints {
 2     // finds all line segments containing 4 points
 3     private int N;
 4     private ArrayList<LineSegment> segment = new ArrayList<LineSegment>();
 5
 6     public BruteCollinearPoints(Point[] points) {
 7         if (points == null)
 8             throw new java.lang.NullPointerException();
 9         N = 0;
10         Point[] copy = new Point[points.length];
11         for (int i = 0; i < points.length; i++) {
12             copy[i] = points[i];
13         }
14         Arrays.sort(copy);
15         for (int i = 0; i < copy.length - 1; i++) {
16             if (copy[i].compareTo(copy[i + 1]) == 0) {
17                 throw new java.lang.IllegalArgumentException();
18             }
19         }
20         for (int i = 0; i < copy.length - 3; i++) {
21             for (int j = i + 1; j < copy.length - 2; j++) {
22                 double slope1 = copy[i].slopeTo(copy[j]);
23                 for (int k = j + 1; k < copy.length - 1; k++) {
24                     double slope2 = copy[i].slopeTo(copy[k]);
25                     if (slope1 != slope2)
26                         continue;
27                     int temp = 0;
28                     for (int l = k + 1; l < copy.length; l++) {
29                         double slope3 = copy[i].slopeTo(copy[l]);
30                         if (slope1 == slope3)
31                             temp = l;
32                         if ((l == copy.length - 1) && (temp != 0)) {
33                             N++;
34                             segment.add(new LineSegment(copy[i], copy[temp]));
35                         }
36                     }
37                 }
38             }
39         }
40     }
41
42     // the number of line segments
43     public int numberOfSegments() {
44         return N;
45     }
46
47     // the line segments
48     public LineSegment[] segments() {
49         LineSegment[] results = new LineSegment[N];
50         for (int i = 0; i < N; i++) {
51             results[i] = segment.get(i);
52         }
53         return results;
54     }
55
56     public static void main(String[] args) {
57
58         // read the N points from a file
59         // In in = new In(args[0]);
60         In in = new In("./collinear/rs1423.txt");
61         int N = in.readInt();
62         System.out.println(N);
63         Point[] points = new Point[N];
64         for (int i = 0; i < N; i++) {
65             int x = in.readInt();
66             int y = in.readInt();
67             System.out.println("x:" + x + " y:" + y);
68             points[i] = new Point(x, y);
69         }
70
71         // draw the points
72         StdDraw.show(0);
73         StdDraw.setXscale(0, 32768);
74         StdDraw.setYscale(0, 32768);
75         for (Point p : points) {
76             p.draw();
77         }
78         StdDraw.show();
79
80         // print and draw the line segments
81         BruteCollinearPoints collinear = new BruteCollinearPoints(points);
82         for (LineSegment segment : collinear.segments()) {
83             StdOut.println(segment);
84             segment.draw();
85         }
86     }
87 }

BruteCollinearPoints

由于这个方法时间复杂度太高,于是提出一种N2 log N 的方法,核心思路就是,通过实现一个排序方法,使点始终保持有序性来提高效率。看到N*NlogN,就想到了遍历点后排序,orz。下面是官方给出的算法描述:

  • Think of p as the origin.
  • For each other point q, determine the slope it makes with p.
  • Sort the points according to the slopes they makes with p.
  • 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. 对所有点排序,越靠近左下角的点越小
  2. 遍历每一个点,遍历点P过程中,先将其他点根据与P的斜率进行排序
  3. 对排序后的其他点进行遍历,若有斜率相同超过4个以上的点,即是要找的线段

按照这个思路,很快就实现除了代码,需要注意的除了边界等细节问题,再有就是,如何避免子线段和重复线段。这两个问题把我卡住很久。

先说如何解决子线,所谓子线段就是原本线段:a->b->c->d->e是符合条件的,我将其取出,但是我又把b->c->d->e也当作新的线段取出来了。一开始的思路是,每次遍历一个点,都将其所有在一条线段上的点找出来,然后将这些点集sort一下,取最大最小。即在搜寻b的时候,将a,c,d,e和其本身b进行排序,取最小a和最大e。这样问题就解决了,但是同样还有一个问题是,由于每个点都进行了这样的操作,会导致有很多重复的线段。通过a得到线段(a,e),通过b又得到一边(a,e),所以结果是需要去重的。非常自然的想到了hashset,保证了时间复杂度不高。

然而使用hashcode的思路都无法实现...原因是poin和segment两个class都继承了Object的hashcode()方法,但是仅仅是在调用时抛出异常...至于为何会这样,原因是第三周还没有讲到hashtable这个数据结构orz

这条路不行,退一步,用toString()方法,将其toString后,存入hashset,来检查是否有重复的线段。我太机智了。不过这样依然是不是100%的通过,看了下testcase,竟然有一条fail是说我不应该依赖于toString()方法...

为什么不让?我陷入了沉思,comment中提到,这个方法仅供debug用,不要用于实现算法...不过细想其实是非常有道理的。我的算法本身实际上是依赖于comparable接口以及camparator的实现。而不是依赖于point和segment这两个具体的类。所以本身我就不应该去依赖于这两个类其他的方法,用了toString()只能说是非常tricky的方法。这样的做法,做到了解耦。看到这个testcase,感觉自己和名校的学生差距就是在这些细节上拉开的。不做这样的题,意识不到interface到底有啥用,不就是定义了几个未实现的方法吗?可是正是通过约定好的接口,做到了解耦。以后如果有人要修改point toString(),依然不会影响到我的程序的正确性。这就无形中提高了效率。

好了,回来说说我是怎么解决这个问题的。注意到,我在遍历之前,就先sort了点,这个sort有啥用呢?这个sort意味着,等会对于每个点找共线线段时,是按照从左下往右上的顺序来的,看图说话:

排过序的点是按照1,2,3,4,5的顺序来遍历的。当选取点1时,找到共线的5个点{1,2,3,4,5},因此我们选择最大最小作为线段(1,5)。

当选取点2时,找到了共线的5个点{1,2,3,4,5},这里也这里发现2<1,由于有序性,我们可以确认在此之前就有(1,5)被选取过了,所以这次的选择可以直接跳过。

这意味着,只要是当前遍历的点P不是共线点集中最小的点,我们都可以直接抛弃。这样就完美避免了重复的线段。

至此,根据这个方案,这个assignment圆满解决了。

一下是fast方法的实现,其他的代码就不贴了。

package collinear;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;public class FastCollinearPoints {private int N;private ArrayList<LineSegment> segment = new ArrayList<LineSegment>();private Point[] points;public FastCollinearPoints(Point[] points) {if (points == null)throw new java.lang.NullPointerException();this.points = new Point[points.length];for (int i = 0; i < points.length; i++) {this.points[i] = points[i];}N = 0;Arrays.sort(this.points);// remove repeat pointsfor (int i = 0; i < this.points.length - 1; i++) {if (this.points[i].compareTo(this.points[i + 1]) == 0) {throw new java.lang.IllegalArgumentException();}}Point[] temp = new Point[this.points.length];Point[] copy = new Point[this.points.length];ArrayList<ArrayList<Point>> end_sets = new ArrayList<>();// use a copy to sortfor (int i = 0; i < this.points.length; i++) {temp[i] = copy[i] = this.points[i];end_sets.add(new ArrayList<Point>());}Arrays.sort(copy);// to sort by slopefor (int i = 0; i < copy.length - 1; i++) {Arrays.sort(temp, copy[i].slopeOrder());// find same slope copy then save them as segment in segment// ArrayListint count = 1;double slope0 = copy[i].slopeTo(temp[0]);ArrayList<Point> set = new ArrayList<>();for (int j = 0; j < temp.length; j++) {// record max pointdouble slope1 = copy[i].slopeTo(temp[j]);if (slope1 == slope0) {set.add(temp[j]);count++;if (count > 2 && j == temp.length - 1) {set.add(copy[i]);Collections.sort(set);// System.out.println(set);if (set.get(0).compareTo(copy[i]) < 0) {} else {// no key, no setsegment.add(new LineSegment(set.get(0), set.get(set.size() - 1)));N++;}                        count = 1;}} else {if (count > 2) {set.add(copy[i]);Collections.sort(set);// System.out.println(set);if (set.get(0).compareTo(copy[i]) < 0) {} else {// no key, no setsegment.add(new LineSegment(set.get(0), set.get(set.size() - 1)));N++;}}set = new ArrayList<>();set.add(temp[j]);count = 1;}slope0 = slope1;}}}// the number of line segmentspublic int numberOfSegments() {return N;}// the line segmentspublic LineSegment[] segments() {LineSegment[] results = new LineSegment[N];for (int i = 0; i < N; i++) {results[i] = segment.get(i);}return results;}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();// System.out.println(N);Point[] points = new Point[N];for (int i = 0; i < N; i++) {int x = in.readInt();int y = in.readInt();// System.out.println("x:" + x + " y:" + y);points[i] = new Point(x, y);}// draw the pointsStdDraw.show(0);StdDraw.setXscale(0, 32768);StdDraw.setYscale(0, 32768);for (Point p : points) {p.draw();}StdDraw.show();// // print and draw the line segmentsFastCollinearPoints collinear = new FastCollinearPoints(points);for (LineSegment segment : collinear.segments()) {StdOut.println(segment);segment.draw();}}
}

FastCollinearPoints

老规矩,亮下分数:

转载于:https://www.cnblogs.com/yilujuechen/articles/4856540.html

Algorithm I assignment Collinear相关推荐

  1. Python小白的数学建模课-07.选址问题

    选址问题是要选择设施位置使目标达到最优,是数模竞赛中的常见题型. 小白不一定要掌握所有的选址问题,但要能判断是哪一类问题,用哪个模型. 进一步学习 PuLP工具包中处理复杂问题的字典格式快捷建模方法. ...

  2. Python小白的数学建模课-06.固定费用问题

    Python 实例介绍固定费用问题的建模与求解. 学习 PuLP工具包中处理复杂问题的快捷使用方式. 『Python小白的数学建模课 @ Youcans』带你从数模小白成为国赛达人. 前文讲到几种典型 ...

  3. 多目标跟踪之匈牙利算法

    0 前言 目标跟踪(Object Tracking)是自动驾驶中非常常见的任务,根据跟踪目标数量的不同,目标跟踪可分为: 单目标跟踪(Single Object Tracking,SOT) 多目标跟踪 ...

  4. Pareto-Efficient Hybridization for Multi-Objective Recommender Systems

    ABSTRACT 简介 Performing accurate suggestions is an objective of paramount importance for effective re ...

  5. algorithm第三周作业 Collinear Points

    cousera 上algorithm part I第三周课程讲述的是排序,包括插入排序.选择排序.希尔排序.归并排序和快速排序.其配套作业为Collinear Points,题目大意为给定若干点,求出 ...

  6. MATH6005 Final Assignment MATH6005 2018-19

    MATH6005 2018-19 MATH6005 Final Assignment 1. Instructions Your Assignment 3 should consist of three ...

  7. 2015 Multi-University Training Contest 1 - 1002 Assignment

    Assignment Problem's Link:  http://acm.hdu.edu.cn/showproblem.php?pid=5289 Mean: 给你一个数列和一个k,求连续区间的极值 ...

  8. CISC-235 Fall 2018 Assignment

    代写CISC-235作业.代做Python/Java编程作业.代写C/C++课程设计作业.代做HOTNCU留学生作业 CISC-235 Fall 2018 Assignment 2 A certain ...

  9. python 两阶段聚类_挑子学习笔记:两步聚类算法(TwoStep Cluster Algorithm)——改进的BIRCH算法...

    转载请标明出处:http://www.cnblogs.com/tiaozistudy/p/twostep_cluster_algorithm.html 两步聚类算法是在SPSS Modeler中使用的 ...

最新文章

  1. Python心得基础篇【2】函数
  2. csv注入java怎么解决_CSV Injection(CSV注入)
  3. 【discuz x3】源代码中的sql调用
  4. Laravel 5 4 实现前后台登录
  5. [react] 可以使用TypeScript写React应用吗?怎么操作?
  6. 500 OOPS: vsftpd: refusing to run with writable root inside chroot()
  7. PyTorch 1.0 中文官方教程:词嵌入:编码形式的词汇语义
  8. 布尔操作的“骤死式”(short-circuiting behavior)
  9. 【生物信息学】外显子测序的原理及优缺点
  10. 弗洛伊德算法原理解释(数学证明)
  11. 测试固态硬盘寿命软件,为什么说耐久测试才是检验SSD寿命的关键
  12. 2021-2027全球与中国环卫云平台市场现状及未来发展趋势
  13. 小猫特别聪明的shoow
  14. 给Android新手的六条建议,听说安卓不火了?
  15. win10 关闭自动维护计划任务
  16. 如何合并多个.xlsx文件到一个Excel表格(office16)
  17. 【spark基础】之client模式下--conf读取外部文件
  18. 网页跳转(APP内/浏览器)
  19. 赛门铁克软件恢复服务器系统,网络版赛门铁克杀软服务器重装恢复
  20. 9.16 a.m.小结

热门文章

  1. seafile 部署_Seafile开启webdav及读写性能测试
  2. html生成自定义表格,自定义js的表格插件
  3. linux 进程 释放内存,Linux 释放内存方法和原理
  4. 160 - 48 DueList.3
  5. java 根据类名示例化类_Java即时类| from()方法与示例
  6. getseconds补0_Java Duration类| getSeconds()方法与示例
  7. 数组shift方法_数组shift()方法以及JavaScript中的示例
  8. Python---实验九
  9. FreeRTOS任务创建和删除
  10. 2014_anshan_onsite