Programming Assignment 5: Kd-Trees
用2d-tree数据结构实现在2维矩形区域内的高效的range search 和 nearest neighbor search。2d-tree有许多的应用,在天体分类、计算机动画、神经网络加速、数据挖掘、图像检索。
range search: 返回所有在query rectangle里的所有点
nearest neighbor search: 返回query point的最近点
下图显示这两种search操作
Geometric Primitives. 在assignment给定了几何图元应该如何表示,如下图
其中关于Point和Rectangle的表示已经定义在了Point2D.java和RectHV.java中,API都已经提供了,这个都不用自己实现。
Point2D的API主要是点的坐标、平方距离、欧几里得距离、点的比较、绘制等,
RectHV主要是一个2维的包围盒,记录矩形的左下角和右上角点的信息,主要API是contains(Point2D)判断点是否在矩形内,intersects(RectHV)是否与另一个矩形相交,以及矩形到点的平方距离和距离,绘制等。源码都可以找到来进行分析。
下面就是要完成的两个任务:Brute-force 实现 和 2d-tree 实现。
需要实现的API是一样的,这里以PointSET为例子,2d-tree也一样:
public class PointSET {
public PointSET() // construct an empty set of points
public boolean isEmpty() // is the set empty?
public int size() // number of points in the set
public void insert(Point2D p) // add the point p to the set (if it is not already in the set)
public boolean contains(Point2D p) // does the set contain the point p?
public void draw() // draw all of the points to standard draw
public Iterable<Point2D> range(RectHV rect) // all points in the set that are inside the rectangle
public Point2D nearest(Point2D p) // a nearest neighbor in the set to p; null if set is empty
}
Brute-force:暴力的实现需要insert()和contains()是在O(logn)的复杂度,nearest()和range()是O(N)的复杂度。
这里用algs4.jar的SET来实现,代码很简单。
range(RectHV): 遍历SET中所有Point与当前RectHV进行包含关系判断
nearest(Point2D):遍历SET中的所有Point与当前Point进行距离判断,不断更新最小距离和最小距离的点,在进行距离判断的时候,用平方距离,开方会影响计算速度。
public class PointSET {
private SET<Point2D> set; // construct an empty set of points
public PointSET() {
set = new SET<Point2D>();
} // is the set empty?
public boolean isEmpty() {
return set.isEmpty();
} // number of points in the set
public int size() {
return set.size();
} // add the point p to the set (if it is not already in the set)
public void insert(Point2D p) {
set.add(p);
} // does the set contain the point p?
public boolean contains(Point2D p) {
return set.contains(p);
} // draw all of the points to standard draw
public void draw() {
for (Point2D p : set) {
StdDraw.point(p.x(), p.y());
}
} // all points in the set that are inside the rectangle
public Iterable<Point2D> range(RectHV rect) {
Queue<Point2D> q = new Queue<Point2D>();
for (Point2D p : set) {
if (rect.contains(p))
q.enqueue(p);
}
return q;
} // a nearest neighbor in the set to p; null if set is empty
public Point2D nearest(Point2D p) {
double mindis = Double.MAX_VALUE;
Point2D ret = null;
for (Point2D s : set) {
double dis = s.distanceSquaredTo(p);
if (dis < mindis) {
mindis = dis;
ret = s;
}
}
return ret;
}
}
2d-tree:这里是使用BST为结构对节点进行组织,每个节点记录下面的相关属性,这个在Possible Progress Step中有提示。通过assignment中的描述和图可以对它有很清晰的认识。
Node节点定义如下:
p记录当前点,rect记录当前点的“包围盒”(轴平行矩阵),lb记录左边或者下边的区域节点,rt记录右边或者上边的区域节点。
private static class Node {
private Point2D p; // the point
// the axis-aligned rectangle corresponding to this node
// the max rectangle include this node, aabb
private RectHV rect;
private Node lb; // the left/bottom subtree
private Node rt; // the right/top subtree
public Node(Point2D p, RectHV rect) {
this.p = p;
this.rect = rect;
lb = null;
rt = null;
}
}
2d-Tree的具体实现只要参考BST的写法就很好实现,insert的时候原本写的是new RectHV,不断进行递归进行构造,但是new的太多,fail test了。后面在insert中直接把RectHV的4个坐标作为参数在Insert中进行递归。
还有一个比较重要的问题是,在insert,get,draw中,要把方向orientation作为参数,用来标示当前应该是左右分还是上下分,draw,insert和get都参照BST的写法,递归实现是十分简洁的。
range()和nearest()都采用BFS广度搜索的方法,遍历这个2d-tree,进行相交和包含的判断,维护有效的节点信息。nearest()也记得使用平方距离,开方影响运行时间。
代码实现如下:
public class KdTree { private Node root;
private int N;
private static class Node {
private Point2D p; // the point
// the axis-aligned rectangle corresponding to this node
// the max rectangle include this node, aabb
private RectHV rect;
private Node lb; // the left/bottom subtree
private Node rt; // the right/top subtree
public Node(Point2D p, RectHV rect) {
this.p = p;
this.rect = rect;
lb = null;
rt = null;
}
} private final RectHV CANVAS = new RectHV(0, 0, 1, 1); // construct an empty set of points
public KdTree() {
root = null;
N = 0;
} // is the set empty?
public boolean isEmpty() {
return N == 0;
} // number of points in the set
public int size() {
return N;
} /**************************************
* less
* compare two Point2D with orientation
*************************************/
private int compareTo(Point2D v, Point2D w, int ori) {
if (v.equals(w)) return 0; // same point
else {
if (ori == 0) {
// vertical line
if (v.x() < w.x()) return -1;
else return 1;
} else {
// horizontal line
if (v.y() < w.y()) return -1;
else return 1;
}
}
} /***********************************************
* Insert
**********************************************/ private Node insert(Node x, Point2D p,
double xmin, double ymin, double xmax, double ymax,
int ori) {
if (x == null) {
N++;
return new Node(p, new RectHV(xmin, ymin, xmax, ymax));
}
int cmp = compareTo(p, x.p, ori);
double x0 = xmin, y0 = ymin, x1 = xmax, y1 = ymax;
if (cmp < 0) {
if (ori == 0) x1 = x.p.x();
else y1 = x.p.y();
x.lb = insert(x.lb, p, x0, y0, x1, y1, 1-ori);
}
else if (cmp > 0) {
if (ori == 0) x0 = x.p.x();
else y0 = x.p.y();
x.rt = insert(x.rt, p, x0, y0, x1, y1, 1-ori);
}
return x;
} // add the point p to the set (if it is not already in the set)
public void insert(Point2D p) {
// 0 for vertical, 1 for horizontal
root = insert(root, p,
CANVAS.xmin(), CANVAS.ymin(),
CANVAS.xmax(), CANVAS.ymax(), 0);
} /*******************************************
* contains
*****************************************/
private boolean get(Node x, Point2D p, int ori) {
if (x == null) return false;
int cmp = compareTo(p, x.p, ori);
if (cmp < 0) return get(x.lb, p, 1-ori);
else if (cmp > 0) return get(x.rt, p, 1-ori);
return true;
} // does the set contain the point p?
public boolean contains(Point2D p) {
// 0 for vertical, 1 for horizontal
return get(root, p, 0);
} /***************************************
* Draw()
*************************************/
private void draw(Node x, int ori) {
if (x == null) return;
// draw point
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.setPenRadius(.01);
StdDraw.point(x.p.x(), x.p.y());
// draw line
if (ori == 0) {
// vertical
StdDraw.setPenColor(StdDraw.RED);
StdDraw.setPenRadius();
StdDraw.line(x.p.x(), x.rect.ymin(), x.p.x(), x.rect.ymax());
} else {
// horizontal
StdDraw.setPenColor(StdDraw.BLUE);
StdDraw.setPenRadius();
StdDraw.line(x.rect.xmin(), x.p.y(), x.rect.xmax(), x.p.y());
}
draw(x.lb, 1-ori);
draw(x.rt, 1-ori);
} // draw all of the points to standard draw
public void draw() {
StdDraw.setScale(0, 1);
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.setPenRadius();
CANVAS.draw();
draw(root, 0);
} // all points in the set that are inside the rectangle
public Iterable<Point2D> range(RectHV rect) {
Queue<Point2D> points = new Queue<Point2D>();
Queue<Node> queue = new Queue<Node>();
if (root == null) return points;
queue.enqueue(root);
while (!queue.isEmpty()) {
Node x = queue.dequeue();
if (x == null) continue;
if (rect.contains(x.p)) points.enqueue(x.p);
if (x.lb != null && rect.intersects(x.lb.rect)) queue.enqueue(x.lb);
if (x.rt != null && rect.intersects(x.rt.rect)) queue.enqueue(x.rt);
}
return points;
} // a nearest neighbor in the set to p; null if set is empty
public Point2D nearest(Point2D p) {
if (root == null) return null;
Point2D retp = null;
double mindis = Double.MAX_VALUE;
Queue<Node> queue = new Queue<Node>();
queue.enqueue(root);
while (!queue.isEmpty()) {
Node x = queue.dequeue();
double dis = p.distanceSquaredTo(x.p);
if (dis < mindis) {
retp = x.p;
mindis = dis;
}
if (x.lb != null && x.lb.rect.distanceSquaredTo(p) < mindis)
queue.enqueue(x.lb);
if (x.rt != null && x.rt.rect.distanceSquaredTo(p) < mindis)
queue.enqueue(x.rt);
}
return retp;
} }
总结:Last words, 这应该是第一门坚持上完的公开课吧,原来Andrew Ng的ML上了一半后,由于事情太多就把课给荒废了(现在又重新开始新一轮了,fighting!)。
可能这几个Assignment写的都不咋地,但记录回顾一下,还是觉得很有收获。特别感谢Prof.Sedgewick和Coursera平台,给予了一段精彩的旅程。后面的Part II到时候继续跟上。
不得不感叹,国外的MOOC平台做的相当的完美,提供了这么多好的资源,国内估计也有类似的吧,没去具体了解过。一定程度上真是把大学搬进了家里,不过感觉仅凭MOOC上几周课程来对领域或者部分的知识,作为一个较为(较为深入?)了解比较恰当,如果要熟练运用和掌握,还需要很长的路要走,Study hungry! Study foolish!
Programming Assignment 5: Kd-Trees的更多相关文章
- 课程一(Neural Networks and Deep Learning),第三周(Shallow neural networks)—— 3.Programming Assignment : Planar data classification with a hidden layer
Planar data classification with a hidden layer Welcome to the second programming exercise of the dee ...
- Algorithms: Design and Analysis, Part 1 - Programming Assignment #1
自我总结: 1.编程的思维不够,虽然分析有哪些需要的函数,但是不能比较好的汇总整合 2.写代码能力,容易挫败感,经常有bug,很烦心,耐心不够好 题目: In this programming ass ...
- Algorithms : Programming Assignment 3: Pattern Recognition
Programming Assignment 3: Pattern Recognition 1.题目重述 原题目:Programming Assignment 3: Pattern Recogniti ...
- Programming Assignment 2: Randomized Queues and Deques
实现一个泛型的双端队列和随机化队列,用数组和链表的方式实现基本数据结构,主要介绍了泛型和迭代器. Dequeue. 实现一个双端队列,它是栈和队列的升级版,支持首尾两端的插入和删除.Deque的API ...
- 课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 2、编程作业常见问题与答案(Programming Assignment FAQ)
Please note that when you are working on the programming exercise you will find comments that say &q ...
- Programming Assignment 4: 8 Puzzle
The Problem. 求解8数码问题.用最少的移动次数能使8数码还原. Best-first search.使用A*算法来解决,我们定义一个Seach Node,它是当前搜索局面的一种状态,记录了 ...
- coursera普林斯顿算法课part1里Programming Assignment 2最后的extra challenge
先附上challenge要求: 博主最近在刷coursera普林斯顿大学算法课part1部分的作业,Programming Assignment2最后的这个extra challenge当初想了一段时 ...
- Programming Assignment 2: Deques and Randomized Queues
编程作业二 作业链接:Deques and Randomized Queues & Checklist 我的代码:Deque.java & RandomizedQueue.java & ...
- Programming Assignment 4: Boggle
编程作业四 作业链接:Boggle & Checklist 我的代码:BoggleSolver.java 问题简介 Boggle 是一个文字游戏,有 16 个每面都有字母的骰子,开始随机将它们 ...
随机推荐
- Node.js初探之hello world
昨天公司内部培训,主讲人王老板对Node.js评价很高,连用几个“变态”来形容,恰好今天周末,有时间来认识下Node.js,对一门新语言最好的认识,是让其输出“hello world”,今天我就利用N ...
- mysql Can't connet MySQL server to '@localhost'
10063/10060/10038好像都能解决 mysql -nt -remove mysql -nt install
- win 7 IIS 配置
http://jingyan.baidu.com/article/219f4bf723bcb2de442d38ed.html win7旗舰版系统 点击开始→控制面板,然后再点击程序,勿点击卸载程序,否 ...
- Hibernate对象的状态
站在持久化的角度, Hibernate 把对象分为 4 种状态: 1. 持久化状态 2. 临时状态 3. 游离状态 4. 删除状态 Session 的特定方法能使对象从一个状态转换到另一个状态. 下面 ...
- Repeater控件 ---属性(ItemCommand事件)
epeater的Command操作:1.ItemCommand事件 - 在Repeater中所有能触发事件的控件,都会来触发这一个事件 2.CommandName - 判断点击的是什么按钮,e.Com ...
- nginx配置文件注释
#定义Nginx运行的用户和用户组user www www; #nginx进程数,建议设置为等于CPU总核心数.worker_processes 8; #全局错误日志定义类型,[ debug | in ...
- css兼容tooltip提示框方法
最终效果图: 基本原理 先设定一个背景色的普通div盒子,然后使用上篇post得到的三角型图标,把div盒子设置为相对定位模式,三角型图标设置为绝对定位,位置相对于div盒子,调整到合适的位置.这样就 ...
- Xshell
http://baike.baidu.com/link?url=5lc5IxVVauitYSbqlOXJBvrvM3qVfMhzz6x_xu2cOMb108Ln9Wk7iJ3f46vG0kBninKw ...
- [INS-32025] 所选安装与指定 Oracle 主目录中已安装的软件冲突
windows server 2008 r2 enterprise下的解决办法为:删除C:\Program Files (x86)\Oracle\Inventory\ContentsXML目录下的in ...
- 搭建本地MAVEN NEXUS 服务
下载 http://120.192.76.70/cache/www.sonatype.org/downloads/nexus-latest-bundle.zip?ich_args=232fba36ed ...