Algorithm I assignment Collinear

這本來應該是第三周的作業,但是由于其他作業逼近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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/397434.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/397434.shtml
英文地址,請注明出處:http://en.pswp.cn/news/397434.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vc c語言坐標圖,VC++6.0下C語言畫圖編程問題

復制內容到剪貼板代碼:#include#includevoid CSinusoidView::OnDraw(CDC* pDC){CSinusoidDoc* pDoc GetDocument();ASSERT_VALID(pDoc);// TODO: add draw code for native data here//建立畫筆CPen cpen,pen;pen.CreatePen(PS_SOLID,4,RGB(0,0,0));cpen.CreatePen(PS_SOLID,2…

Java BigDecimal詳解

1.引言 float和double類型的主要設計目標是為了科學計算和工程計算。他們執行二進制浮點運算&#xff0c;這是為了在廣域數值范圍上提供較為精確的快速近似計算而精心設計的。然而&#xff0c;它們沒有提供完全精確的結果&#xff0c;所以不應該被用于要求精確結果的場合。但是…

Erlang庫 -- 有意思的庫匯總

抄自這里 首先&#xff0c;庫存在的目的大致可分為&#xff1a;1、提供便利2、盡可能解決一些痛點首先&#xff0c;我們先明確一下Erlang編程語言的一些痛點&#xff08;偽痛點&#xff09;&#xff1a;1&#xff0c;單進程問題Erlang虛擬機屬于搶占式調度&#xff0c;搶占式調…

windows 串口編程 c語言,windows下C語言版串口發送程序(基于VS2017)

#include "tchar.h"#include int main(){/*****************************打開串口*************************************/HANDLE hCom;//全局變量&#xff0c;串口句柄hCom CreateFile(_T("COM3"),//COM3口GENERIC_READ | GENERIC_WRITE,//允許讀和寫0,/…

scikit-learn決策樹算法類庫使用小結

之前對決策樹的算法原理做了總結&#xff0c;包括決策樹算法原理(上)和決策樹算法原理(下)。今天就從實踐的角度來介紹決策樹算法&#xff0c;主要是講解使用scikit-learn來跑決策樹算法&#xff0c;結果的可視化以及一些參數調參的關鍵點。 1. scikit-learn決策樹算法類庫介紹…

3.js模式-策略模式

1. 策略模式 策略模式定義一系列的算法&#xff0c;把它們封裝起來&#xff0c;并且可以互相替換。 var strategies { isNonEmpty: function(value,errMsg){ if(value ){ return errMsg; } }, minLength:function(value,length,errMsg){ if(value.length < length){ retur…

c語言編寫程序求8,使用c語言編寫程式,實現計算1*2*3+4*5*6+7*8*9+……+28*29*30的值...

使用c語言編寫程式&#xff0c;實現計算1*2*34*5*67*8*9……28*29*30的值以下文字資料是由(歷史新知網www.lishixinzhi.com)小編為大家搜集整理后發布的內容&#xff0c;讓我們趕快一起來看一下吧&#xff01;使用c語言編寫程式&#xff0c;實現計算1*2*34*5*67*8*9……28*29*3…

PHP 正則表達式分割 preg_split 與 split 函數

為什么80%的碼農都做不了架構師&#xff1f;>>> preg_split() preg_ split() 函數用于正則表達式分割字符串。 語法&#xff1a; array preg_split( string pattern, string subject [, int limit [, int flags]] ) 返回一個數組&#xff0c;包含 subject 中沿著與…

簡單學C——第五天

結構體 首先明確&#xff0c;結構體是一種構造的數據類型&#xff0c;是一種由多個數據類型如 int&#xff0c;char&#xff0c;double&#xff0c;數組或者結構體......組成的類型,現在告訴大家如何定義一個結構體。在定義int整型變量時&#xff0c;大家肯定都知道 int a; 即…

C語言二叉樹實驗報告流程圖,二叉樹的建立與遍歷實驗報告(c語言編寫,附源代碼).doc...

二叉樹的建立與遍歷實驗報告(c語言編寫,附源代碼).doc第 1 頁&#xff0c;共 9 頁二叉樹的建立與遍歷實驗報告級 班 年 月 日 姓名 學號_ 1實驗題目建立一棵二叉樹&#xff0c;并對其進行遍歷(先序、中序、后序)&#xff0c;打印輸出遍歷結果。2需求分析本程序用 VC 編寫&#…

三角函數泰勒展開C語言,第六章-函數作業 ---三角函數泰勒級數展開式計算正弦函數值...

E201_06_02_正弦函數題目要求&#xff1a;按照三角函數泰勒級數展開式計算正弦函數值&#xff1a;,直到最后一項的絕對值小于106解題思路&#xff1a;1. 輸入弧度2. 確定初始化值3. 求階梯函數代碼&#xff1a;public class E201_06_02_正弦函數 {public static void main(Stri…

Codeforces Round #325 (Div. 2) B. Laurenty and Shop 前綴和

B. Laurenty and Shop Time Limit: 1 Sec Memory Limit: 256 MB 題目連接 http://codeforces.com/contest/586/problem/BDescription A little boy Laurenty has been playing his favourite game Nota for quite a while and is now very hungry. The boy wants to make sau…

python學習感悟第3節

在繼列表的學習之后&#xff0c;進行了元組的學習。元組和列表功能相似&#xff0c;只是元組不能進行修改&#xff0c;所以元組又叫只讀列表。 下面列舉的是一系列的字符串操作&#xff1a; name.capitalize() #首字母大寫 name.count("a") #數列表中有幾個a name…

MyBatis_ibatis和mybatis的區別【轉】

1. ibatis3.*版本以后正式改名為mybaits&#xff0c;它也從apache轉到了google code下&#xff1b;也就是說ibatis2.*&#xff0c;mybatis3.*。2. 映射文件的不同ibatis的配置文件如下<?xml version"1.0" encoding"UTF-8" ?><!DOCTYPE sqlMapCo…

android gallery自動播放,可循環顯示圖像的Android Gallery組件

類型&#xff1a;源碼相關大小&#xff1a;23.6M語言&#xff1a;中文 評分&#xff1a;9.1標簽&#xff1a;立即下載第 4 頁 實現循環顯示圖像的Gallery組件實現循環顯示圖像的Gallery組件在本節將組出與循環顯示圖像相關的ImageAdapter類的完整代碼。讀者可以從中看到上一節介…

docker內程序如何讀取dockerfile和compose.yml中設置的環境變量

docker內程序如何讀取dockerfile和compose.yml中設置的環境變量 背景 compose文件中配置了服務A和服務B&#xff0c;其中B服務調用了A服務的接口&#xff0c;那么B的實現代碼中該如何調用A的服務呢&#xff1f; 解決 compose文件中&#xff0c;服務B的配置加入A的接口&#xff…

2015年10月13日

關于掙錢&#xff0c;我覺得&#xff0c;只要興趣所在&#xff0c;能把事做好&#xff0c;錢自己就會來。收入上不去&#xff0c;往往是做的事情就不在高收入的那個區間&#xff0c;寫程序很難出富翁。說實話&#xff0c;外圍一天的消費可能就是你工資的好幾倍&#xff0c;不用…

Spring Boot Servlet

上一篇我們對如何創建Controller 來響應JSON 以及如何顯示數據到頁面中&#xff0c;已經有了初步的了解。 Web開發使用 Controller 基本上可以完成大部分需求&#xff0c;但是我們還可能會用到 Servlet、Filter、Listener、Interceptor 等等。 當使用spring-Boot時&#xff0c;…

基于相關性分析系統性能瓶頸

測試的過程中&#xff0c;難免需要會遇到一些性能瓶頸&#xff0c;那么就要求我們能夠分析出性能瓶頸&#xff0c;并給出解決方案。性能瓶頸很抽象&#xff0c;我們可以通過數據使其具象。以我工作內容為例&#xff0c;服務器處理數據的能力是有限的&#xff0c;那么其處理的邊…

curl網站開發指南

curl網站開發指南 作者&#xff1a; 阮一峰 日期&#xff1a; 2011年9月 4日 我一向以為&#xff0c;curl只是一個編程用的函數庫。 最近才發現&#xff0c;這個命令本身&#xff0c;就是一個無比有用的網站開發工具&#xff0c;請看我整理的它的用法。 curl網站開發指南 阮一…