对象的算法
面向过程 是将问题作为出发点的,以解决问题的过程和方式为主要编程思路,因此面向过程都是一些程序或者说函数的集合,数据和程序紧耦合在一起。面向对象:是以客观事物作为出发点的,将一个事物的属性和这个事物的行为结合在一起,也可以说是将一个事物的动态行为和静态行为相结合联系在一起。面向过程中重要的部分是算法,面向对象的重要的过程是模式,所谓模式,就是类与类的组织结构,如经常说的设计模式,但是面向对象中的具体对象的静态方法,就是面向过程的函数,只不过这种函数在使用数据,和作用域上有了对象领域特征,即作用域的限制。所以从这个角度讲,面向过程中的算法在面向对象中是一种具体的表现,也需要算法的支撑,而面向对象是从一个高层次的体系结构,组织对象之间关系,保证对象之间的耦合度(依赖程度)降低,以实现复用的目的。
2. 高大上的YOLOV3对象检测算法,使用python也可轻松实现
继续我们的目标检测算法的分享,前期我们介绍了SSD目标检测算法的python实现以及Faster-RCNN目标检测算法的python实现以及yolo目标检测算法的darknet的window环境安装,本期我们简单介绍一下如何使用python来进行YOLOV3的对象检测算法
YOLOV3的基础知识大家可以参考往期文章,本期重点介绍如何使用python来实现
1、初始化模型
14-16 行:
模型的初始化依然使用cv下的DNN模型来加载模型,需要注意的是CV的版本需要大于3.4.2
5-8行:
初始化模型在coco上的label以便后期图片识别使用
10-12行:
初始化图片显示方框的颜色
2、加载图片,进行图片识别
输入识别的图片进行图片识别,这部分代码跟往期的SSD 以及RCNN目标检测算法类似
19-20行:输入图片,获取图片的长度与宽度
25-29行:计算图片的blob值,输入神经网络,进行前向反馈预测图片
只不过net.forward里面是ln, 神经网络的所有out层
3、遍历所有的out层,获取检测图片的label与置信度
遍历out层,获取检测到的label值以及置信度,检测到这里YOLOV3以及把所有的检测计算完成,但是由于yolov3对重叠图片或者靠的比较近的图片检测存在一定的问题,使用YOLOV3使用非最大值抑制来抑制弱的重叠边界
竟然把墨镜识别了手机,体现了YOLOV3在重叠图片识别的缺点
4、应用非最大值抑制来抑制弱的重叠边界,显示图片
56: 使用 非最大值抑制来抑制弱的重叠边界
58-59行:遍历所有图片
61-62行:提取检测图片的BOX
64-68行:显示图片信息
70-71行:显示图片
利用python来实现YOLOV3,与SSD 以及RCNN代码有很多类似的地方,大家可以参考往期的文章进行对比学习,把代码执行一遍
进行视频识别的思路:从视频中提取图片,进行图片识别,识别完成后,再把识别的结果实时体现在视频中,这部分代码结合前期的视频识别,大家可以参考多进程视频实时识别篇,因为没有多进程,检测速度很慢,视频看着比较卡
1、初始化模型以及视频流
2、从视频中提取图片,进行图片的blob值计算,进行神经网络的预测
3、提取检测到图片的置信度以及ID值
4、 应用非最大值抑制来抑制弱的重叠边界,显示图片
5、关闭资源,显示图片处理信息
每个目标检测算法都有自己的优缺点,个人感觉,在精度要求不是太高的情况下SSD检测算法可以实现较快的速度实现,毕竟精度差不多的情况下,我们希望速度越快越好
3. yolo算法是什么
YOLO 是一种使用神经网络提供实时对象检测的算法。该算法因其速度和准确性而广受欢迎。它已在各种应用中用于检测交通信号、人员、停车计时器和动物。
YOLO 是“You Only Look Once”一词的缩写。这是一种算法,可以(实时)检测和识别图片中的各种对象。YOLO 中的对象检测是作为回归问题完成的,并提供检测到的图像的类别概率。
YOLO 算法采用卷积神经网络 (CNN) 实时检测物体。顾名思义,该算法只需要通过神经网络进行一次前向传播来检测物体。氏笑
这意味着整个图像中的预测是在单个算法运行中完成的。CNN 用于同时预测各种类别概率和边界框。
YOLO 算法由各种变体组成。
优点
1、速度:该余轮算法提高了检测速度,因为它可以实时预测物体。
2、高歼毁含精度:YOLO 是一种预测技术,可提供准确的结果且背景误差最小。
3、学习能力:该算法具有出色的学习能力,使其能够学习对象的表示并将其应用于对象检测。
4. 【面向对象】Python面向对象之多继承算法
Python的类分为经典类和新式类:
官方推荐使用新式类替换经典类,因为经典类对于多重继承采用的从左到右深度优先匹配算法存在一些问题。也就是如果方法同名,有的时候会绕过一些想要访问的方法,只指向一个方法。
2.x版本中使用的是深度优先算法,而3.x版本使用的是c3算法,和广度优先算法在某些情况下是不一样的
以顶点为起始点,从左到右开始遍历,当遍历到一个节点的时候,判断是否有左右两个顶点,优先选择左边的作为顶点,再继续遍历下去,当遍历完成后,选择未曾访问的顶点重新遍历
如图:根据深度优先算法,就是v1-v2-v4-v5-v3-v6
以顶点为起始点,从左到右开始遍历,一层一层地向下遍历,从左到右
如图:根据广度优先算法:就是v1-v2-v3-v4-v6-v5
C3 算法:MRO是一个有序列表L,在类被创建时就计算出来。
L(Child(Base1,Base2)) = [ Child + merge( L(Base1) , L(Base2) , Base1Base2 )]
L(object) = [ object ]
L的性质:结果为列表,列表中至少有一个元素即类自己。
“+” : 添加到列表的末尾,即 [ A + B ] = [ A,B ]
merge: ① 如果列表空则结束,非空 读merge中第一个列表的表头,
② 查看该表头是否在 merge中所有列表的表尾中,或者不在表尾的第一个字母
②-->③ 不在,则 放入 最终的L中,并从merge中的所有列表中删除,然后 回到①中
②-->④ 在,查看 当前列表是否是merge中的最后一个列表
④-->⑤ 不是 ,跳过当前列表,读merge中下一个列表的表头,然后 回到 ②中
④-->⑥ 是,异常。类定义失败。
表头: 列表的第一个元素 (列表:ABC,那么表头就是A,B和C就是表尾)
表尾: 列表中表头以外的元素集合(可以为空)
merge 简单的说即寻找合法表头(也就是不在表尾中的表头),如果所有表中都未找到合法表头则异常。
例如:
L(D) = L(D(O))
= D + merge(L(O))
= D + O
= [D,O]
L(B) = L(B(D,E))
= B + merge(L(D) , L(E))
= B + merge(DO , EO) # 第一个列表DO的表头D,其他列表比如EO的表尾都不含有D,所以可以将D提出来,即D是合法表头
= B + D + merge(O , EO) #从第一个开始表头是O,但是后面的列表EO的表尾中含有O所以O是不合法的,所以跳到下一个列表EO
= B + D + E + merge(O , O)
= [B,D,E,O]
同理:
L(C) = [C,E,F,O]
L(A(B,C)) = A + merge(L(B),L(C),BC)
= A + merge(BDEO,CEFO,BC)#B是合法表头
= A + B + merge(DEO,CEFO,C)#D是合法表头
= A + B + D + merge(EO,CEFO,C)#E不是合法表头,跳到下一个列表CEFO,此时C是合法表头
= A + B + D + C + merge(EO,EFO)#由于第三个列表中的C被删除,为空,所以不存在第三个表,只剩下两个表;此时E是合法表头
= A + B + D + C + E + merge(O,FO)#O不是合法表头,跳到下一个列表FO,F是合法表头,
= A + B + D + C + E + F + merge(O,O)#O是合法表头
= A + B + D + C + E + F + O
= [A,B,D,C,E,F,O]
L(D)
= L(D(B,C)) 取出D
= D + merge(BA+CA+BC) 查看merge的表头,取出B,去除C,剩下A
= D + B + C + A
5. 目标检测算法图解:一文看懂RCNN系列算法
姓名:王咫毅
学号:19021211150
【嵌牛导读】CNN如此风靡,其衍生算法也是层出不穷,各种衍生算法也可以应用于各种应用场景,各类场合。本文则是了解每个衍生算法的各个使用场景、原理及方法。
【嵌牛鼻子】RCNN 目标检测
【嵌牛提问】RCNN系列算法有何区别和联系?
【嵌牛正文】
在生活中,经常会遇到这样的一种情况,上班要出门的时候,突然找不到一件东西了,比如钥匙、手机或者手表等。这个时候一般在房间翻一遍各个角落来寻找不见的物品,最后突然一拍大脑,想到在某一个地方,在整个过程中有时候是很着急的,并且越着急越找不到,真是令人沮丧。但是,如果一个简单的计算机算法可以在几毫秒内就找到你要找的物品,你的感受如何?是不是很惊奇!这就是对象检测算法(object detection)的力量。虽然上述举的生活例子只是一个很简单的例子,但对象检测的应用范围很广,跨越多个不同的行业,从全天候监控到智能城市的实时车辆检qian测等。简而言之,物体检测是强大的深度学习算法中的一个分支。
在本文中,我们将深入探讨可以用于对象检测的各种算法。首先从属于RCNN系列算法开始,即RCNN、 Fast RCNN和 Faster RCNN。在之后的文章中,将介绍更多高级算法,如YOLO、SSD等。
1.解决对象检测任务的简单方法(使用深度学习)
下图说明了对象检测算法是如何工作。图像中的每个对象,从人到风筝都以一定的精度进行了定位和识别。
下面从最简单的深度学习方法开始,一种广泛用于检测图像中的方法——卷积神经网络(CNN)。如果读者对CNN算法有点生疏,建议 阅读此文 。
这里仅简要总结一下CNN的内部运作方式:
首先将图像作为输入传递到网络,然后通过各种卷积和池化层处理,最后以对象类别的形式获得输出。
对于每个输入图像,会得到一个相应的类别作为输出。因此可以使用这种技术来检测图像中的各种对象。
1.首先,将图像作为输入;
2.然后,将图像分成不同的区域;
3.然后,将每个区域视为单独的图像;
4.将所有这些区域传递给CNN并将它们分类为各种类别;
5.一旦将每个区域划分为相应的类后,就可以组合所有这些区域来获取具有检测到的对象的原始图像:
使用这种方法会面临的问题在于,图像中的对象可以具有不同的宽高比和空间位置。例如,在某些情况下,对象可能覆盖了大部分图像,而在其他情况下,对象可能只覆盖图像的一小部分,并且对象的形状也可能不同。
基于此,需要划分大量的区域,这会花费大量的计算时间。因此,为了解决这个问题并减少区域数量,可以使用基于区域的CNN,它使用提议方法选择区域。
2.基于区域的卷积神经网络
2.1 RCNN的思想
RCNN算法不是在大量区域上工作,而是在图像中提出了一堆方框,并检查这些方框中是否包含任何对象。RCNN 使用选择性搜索从图像中提取这些框。
下面介绍选择性搜索以及它如何识别不同的区域。基本上四个区域形成一个对象:不同的比例、颜色、纹理和形状。选择性搜索在图像中识别这些模式,并基于此提出各种区域。以下是选择性搜索如何工作的简要概述:
首先, 将图像作为输入:
然后,它生成初始子分段,以便获得多个区域:
之后,该技术组合相似区域以形成更大的区域(基于颜色相似性、纹理相似性、尺寸相似性和形状兼容性):
最后,这些区域产生最终的对象位置(感兴趣的区域);
下面是RCNN检测对象所遵循的步骤的简要总结:
1.首先采用预先训练的卷积神经网络;
2.重新训练该模型模型——根据需要检测的类别数量来训练网络的最后一层(迁移学习);
3.第三步是获取每个图像的感兴趣区域。然后,对这些区域调整尺寸,以便其可以匹配CNN输入大小;
4.获取区域后,使用SVM算法对对象和背景进行分类。对于每个类,都训练一个二分类SVM;
最后,训练线性回归模型,为图像中每个识别出的对象生成更严格的边界框;
[对上述步骤进行图解分析]( http://www.robots.ox.ac.uk/~tvg/publications/talks/Fast-rcnn-slides.pdf ):
首先,将图像作为输入:
然后,使用一些提议方法获得感兴趣区域(ROI)(例如,选择性搜索):
之后,对所有这些区域调整尺寸,并将每个区域传递给卷积神经网络:
然后,CNN为每个区域提取特征,SVM用于将这些区域划分为不同的类别:
最后,边界框回归(Bbox reg)用于预测每个已识别区域的边界框:
以上就是RCNN检测物体的全部流程。
2.2 RCNN的问题
从上节内容可以了解到RCNN是如何进行对象检测的,但这种技术有其自身的局限性。以下原因使得训练RCNN模型既昂贵又缓慢:
基于选择性搜索算法为每个图像提取2,000个候选区域;
使用CNN为每个图像区域提取特征;
RCNN整个物体检测过程用到三种模型:
CNN模型用于特征提取;
线性svm分类器用于识别对象的的类别;
回归模型用于收紧边界框;
这些过程相结合使得RCNN非常慢,对每个新图像进行预测需要大约40-50秒,这实际上使得模型在面对巨大的数据集时变得复杂且几乎不可能应用。
好消息是存在另一种物体检测技术,它解决了RCNN中大部分问题。
3.了解Fast RCNN
3.1Fast RCNN的思想
RCNN的提出者Ross Girshick提出了这样的想法,即每个图像只运行一次CNN,然后找到一种在2,000个区域内共享该计算的方法。在Fast RCNN中,将输入图像馈送到CNN,CNN生成卷积特征映射。使用这些特征图提取候选区域。然后,使用RoI池化层将所有建议的区域重新整形为固定大小,以便将其馈送到全连接网络中。
下面将其分解为简化概念的步骤:
1.首先将图像作为输入;
2.将图像传递给卷积神经网络,生成感兴趣的区域;
3.在所有的感兴趣的区域上应用RoI池化层,并调整区域的尺寸。然后,每个区域被传递到全连接层的网络中;
4.softmax层用于全连接网以输出类别。与softmax层一起,也并行使用线性回归层,以输出预测类的边界框坐标。
因此,Fast RCNN算法中没有使用三个不同的模型,而使用单个模型从区域中提取特征,将它们分成不同的类,并同时返回所标识类的边界框。
对上述过程进行可视化讲解:
将图像作为输入:
将图像传递给卷积神经网络t,后者相应地返回感兴趣的区域:
然后,在提取的感兴趣区域上应用RoI池层,以确保所有区域具有相同的大小:
最后,这些区域被传递到一个全连接网络,对其进行分类,并同时使用softmax和线性回归层返回边界框:
上述过程说明了Fast RCNN是如何解决RCNN的两个主要问题,即将每个图像中的1个而不是2,000个区域传递给卷积神经网络,并使用一个模型来实现提取特征、分类和生成边界框。
3.2Fast RCNN的问题
Fast RCNN也存在一定的问题,它仍然使用选择性搜索作为查找感兴趣区域的提议方法,这是一个缓慢且耗时的过程,每个图像检测对象大约需要2秒钟。
因此,又开发了另一种物体检测算法——Faster RCNN。
4.了解Faster RCNN
4.1. Faster RCNN的思想
Faster RCNN是Fast RCNN的修改版本,二者之间的主要区别在于,Fast RCNN使用选择性搜索来生成感兴趣区域,而Faster RCNN使用“区域提议网络”,即RPN。RPN将图像特征映射作为输入,并生成一组提议对象,每个对象提议都以对象分数作为输出。
以下步骤通常采用Faster RCNN方法:
1.将图像作为输入并将其传递给卷积神经网络,后者返回该图像的特征图;
2.在这些特征图上应用RPN,返回提议对象及其分数;
3.在这些提议对象上应用RoI池层,以将所有提案降低到相同的大小;
4.最后,将提议传递到全连接层,该层在其顶部具有softmax层和线性回归层,以对对象的边界框进行分类和输出;
这里简要解释一下RPN是如何运作的:
首先,Faster RCNN从CNN获取特征图并将它们传递到区域提议网络。RPN在这些特征图上使用滑动窗口,每个窗口生成不同形状和大小的k个方框( Anchor boxe):
方框是固定尺寸的边界箱,具有不同的形状和尺寸。对于每个方框,RPN预测两件事:
预测锚是对象的概率;
用于边界框回归器调整锚点以更好地适合物体的形状;
在有了不同形状和大小的边界框后,将其传递到RoI池层。对每个提案并对其进行裁剪,以便每个提案都包含一个对象。这就是RoI池层所做的事情,它为每个方框提取固定大小的特征图:
然后将这些特征图传递到全连接层,该层具有softmax和线性回归层,最终对对象进行分类并预测已识别对象的边界框。
4.2Faster RCNN的问题
上述讨论过的所有对象检测算法都使用区域来识别对象,且网络不会一次查看完整图像,而是按顺序关注图像的某些部分,这样会带来两个复杂性的问题:
该算法需要多次通过单个图像来提取到所有对象;
由于不是端到端的算法,不同的系统一个接一个地工作,整体系统的性能进一步取决于先前系统的表现效果。
链接: https://www.jianshu.com/p/51fc039ae7a4
6. 纯滞后对象的控制算法常用的有哪两种
PID控制: 比例控制能迅速反应误差,从而减小稳态误差。但是,孙升哗比例控制不能消除稳态误差。比例放大系数则行的加大,会引起系统的不稳定。积分控制的作用是,只要系统有误差存在,积分控制器就不断地积累,输出控制量,以消除误差。因而,只要有足够的时间,积分控制将能完全消除误差,使系统误差为零,从而消除稳态误差。积分作用太强会使系统超调加大,甚至使系统出现振荡。微分控制可以减小超调量,克服振荡,使系统的稳定性提高,同时加快系统的动态响应速度,减小调整时间,从而改善系统的动态性能。 应用PID控制,必须适当地调整订怠斥干俪妨筹施船渐比例放大系数KP,积分时间TI和微分时间TD,使整个控制系统得到良好的性能。纯滞后控制: 对给定的水位及温度对象设计相应的监控系统,可以对被控过程进行全面的监测及控制。主要内容有水位及温度对象的在线实验建模,并以HJ-2型高级过程控制实验装置中的水位对象及温度对象为被控对象,进行试验研究;同时以试验求得的数学模型为对象,在MATLAB仿真环境下进行设计。试验结果表明,由于对象存在较大的纯滞后,采用单回路PID控制效果不佳。但常规单回路PID控制对一般对象控制效笑滑果较为理想,是生产过程中常用的一种控制方法。
7. 编程中的“方法”是指对象的功能吗“算法”是指实现功能的方法吗
“方法”可以认为是对象的功能,它一般表示类的对象能够进行的行为;算法在广义上就是解决问题的办法,比如要排序有冒泡算法、折半插入算法等等,无论哪种算法最终实现的都是排序的功能。
8. 如何准确计算java对象的大小
首先,我们先写一段大家可能不怎么写或者穗清认为不可能的代码:一个类中,几个类型都橡族颂是private类型,没有public方法,如何对这些属性进行读写操作,看似不可能哦,为什么,这违背了面向对象的封装,其实在必要的时候,留一道后门可以使得语言的生产力更加强大,对象的序列化不会因为没有public方法就无法保存成功吧,OK,我们简单写段代码开个头,逐步引入到怎么样去测试对象的大小,一下代码非常简单,相信不用我解释什么:
import java.lang.reflect.Field;
class NodeTest1 {
private int a = 13;
private int b = 21;
}
public class Test001 {
public static void main(String []args) {
NodeTest1 node = new NodeTest1();
Field []fields = NodeTest1.class.getDeclaredFields();
for(Field field : fields) {
field.setAccessible(true);
try {
int i = field.getInt(node);
field.setInt(node, i * 2);
System.out.println(field.getInt(node));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
代码最基本的意思就是:实例化一个NodeTest1这个类的实例,然后取出两个属性,分别乘以2,然后再输出,相信大家会认为这怎么可能,NodeTest1根本没有public方法,代码就在这里,将代码拷贝回去运行下就OK了,OK,现在不说这些了,运行结果为:
26
42
为什么可以取到,是每个属性都留了一道门,主要是为了自己或者外部接入的方便,相信看代码自己仔细的朋友,应该知道门就在:field.setAccessible(true);代表这个域的访问被打开,好比是一道后门打开了,呵呵,上面的方法如果不设置这个,就直接报错。
看似和对象大小没啥关系,不过这只是抛砖引玉,因为我们首先要拿到对象的属性,才能知道对象的大小,对象梁郑如果没有提供public方法我们也要知道它有哪些属性,所以我们后面多半会用到这段类似的代码哦!
对象测量大小的方法关键为java提供的(1.5过后才有):java.lang.instrument.Instrumentation,它提供了丰富的对结构的等各方面的跟踪和对象大小的测量的API(本文只阐述对象大小的测量方法),于是乎我心喜了,不过比较恶心的是它是实例化类:sun.instrument.IntrumentationImpl是sun开头的,这个鬼东西有点不好搞,翻开源码构造方法是private类型,没有任何getInstance的方法,写这个类干嘛?看来这个只能被JVM自己给初始化了,那么怎么将它自己初始化的东西取出来用呢,唯一能想到的就是agent代理,那么我们先抛开代理,首先来写一个简单的对象测量方法:
步骤1:(先创建一个用于测试对象大小的处理类)
import java.lang.instrument.Instrumentation;
public class MySizeOf {
private static Instrumentation inst;
/**
*这个方法必须写,在agent调用时会被启用
*/
public static void premain(String agentArgs, Instrumentation instP) {
inst = instP;
}
/**
* 直接计算当前对象占用空间大小,包括:当前类及超类的基本类型实例字段大小
* 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小
* 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小
* 用来测量java对象的大小(这里先理解这个大小是正确的,后面再深化)
*/
public static long sizeOf(Object o) {
if(inst == null) {
throw new IllegalStateException("Can not access instrumentation environment.\n" +
"Please check if jar file containing SizeOfAgent class is \n" +
"specified in the java's \"-javaagent\" command line argument.");
}
return inst.getObjectSize(o);
}
}
步骤2:上面我们写好了agent的代码,此时我们要将上面这个类编译后打包为一个jar文件,并且在其包内部的META-INF/MANIFEST.MF文件中增加一行:Premain-Class: MySizeOf代表执行代理的全名,这里的类名称是没有package的,如果你有package,那么就写全名,我们这里假设打包完的jar包名称为agent.jar(打包过程这里简单阐述,就不细说了),OK,继续向下走:
步骤3:编写测试类,测试类中写:
public class TestSize {
public static void main(String []args) {
System.out.println(MySizeOf.sizeOf(new Integer(1)));
System.out.println(MySizeOf.sizeOf(new String("a")));
System.out.println(MySizeOf.sizeOf(new char[1]));
}
}
下一步准备运行,运行前我们准备初步估算下结果是什么,目前我是在32bit模式下运行jvm(注意,不同位数的JVM参数设置不一样,对象大小也不一样大)。
(1) 首先看Integer对象,在32bit模式下,class区域占用4byte,mark区域占用最少4byte,所以最少8byte头部,Integer内部有一个int类型的数据,占4个byte,所以此时为8+4=12,java默认要求按照8byte对象对其,所以对其到16byte,所以我们理论结果第一个应该是16;
(2) 再看String,长度为1,String对象内部本身有4个非静态属性(静态属性我们不计算空间,因为所有对象都是共享一块空间的),4个非静态属性中,有offset、count、hash为int类型,分别占用4个byte,char value[]为一个指针,指针的大小在bit模式下或64bit开启指针压缩下默认为4byte,所以属性占用了16byte,String本身有8byte头部,所以占用了24byte;其次,一个String包含了子对象char数组,数组对象和普通对象的区别是需要用一个字段来保存数组的长度,所以头部变成12byte,java中一个char采用UTF-16编码,占用2个byte,所以是14byte,对其到16byte,24+16=40byte;
(3) 第三个在第二个基础上已经分析,就是16byte大小;
也就是理论结果是:16、40、16;
步骤4:现在开始运行代码:运行代码前需要保证classpath把刚才的agent.jar包含进去:
D:>javac TestSize.java
D:>java -javaagent:agent.jar TestSize
16
24
16
第一个和第三个结果一致了,不过奇怪了,第二个怎么是24,不是40,怎么和理论结果偏差这么大,再回到理论结果中,有一个24曾经出现过,24是指String而不包含char数组的空间大小,那么这么算还真是对的,可见,java默认提供的方法只能测量对象当前的大小,如果要测量这个对象实际的大小(也就是包含了子对象,那么就需要自己写算法来计算了,最简单的方法就是递归,不过递归一项是我不喜欢用的,无意中在一个地方看到有人用栈写了一个代码写得还不错,自己稍微改了下,就是下面这种了)。
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Stack;
public class MySizeOf {
static Instrumentation inst;
public static void premain(String agentArgs, Instrumentation instP) {
inst = instP;
}
public static long sizeOf(Object o) {
if(inst == null) {
throw new IllegalStateException("Can not access instrumentation environment.\n" +
"Please check if jar file containing SizeOfAgent class is \n" +
"specified in the java's \"-javaagent\" command line argument.");
}
return inst.getObjectSize(o);
}
/**
* 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小
*/
public static long fullSizeOf(Object obj) {//深入检索对象,并计算大小
Map<Object, Object> visited = new IdentityHashMap<Object, Object>();
Stack<Object> stack = new Stack<Object>();
long result = internalSizeOf(obj, stack, visited);
while (!stack.isEmpty()) {//通过栈进行遍历
result += internalSizeOf(stack.pop(), stack, visited);
}
visited.clear();
return result;
}
//判定哪些是需要跳过的
private static boolean skipObject(Object obj, Map<Object, Object> visited) {
if (obj instanceof String) {
if (obj == ((String) obj).intern()) {
return true;
}
}
return (obj == null) || visited.containsKey(obj);
}
private static long internalSizeOf(Object obj, Stack<Object> stack, Map<Object, Object> visited) {
if (skipObject(obj, visited)) {//跳过常量池对象、跳过已经访问过的对象
return 0;
}
visited.put(obj, null);//将当前对象放入栈中
long result = 0;
result += sizeOf(obj);
Class <?>clazz = obj.getClass();
if (clazz.isArray()) {//如果数组
if(clazz.getName().length() != 2) {// skip primitive type array
int length = Array.getLength(obj);
for (int i = 0; i < length; i++) {
stack.add(Array.get(obj, i));
}
}
return result;
}
return getNodeSize(clazz , result , obj , stack);
}
//这个方法获取非数组对象自身的大小,并且可以向父类进行向上搜索
private static long getNodeSize(Class <?>clazz , long result , Object obj , Stack<Object> stack) {
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers())) {//这里抛开静态属性
if (field.getType().isPrimitive()) {//这里抛开基本关键字(因为基本关键字在调用java默认提供的方法就已经计算过了)
continue;
}else {
field.setAccessible(true);
try {
Object objectToAdd = field.get(obj);
if (objectToAdd != null) {
stack.add(objectToAdd);//将对象放入栈中,一遍弹出后继续检索
}
} catch (IllegalAccessException ex) {
assert false;
}
}
}
}
clazz = clazz.getSuperclass();//找父类class,直到没有父类
}
return result;
}
}
9. 高分求算法:寻找与特定对象距离最近的对象
用现在的ID,X,Y三个元素来找最近点的话无论什么办法,都至少要与每个点进行一次距离的判断,比较X或者Y也好,计算距离(当然这个距离是不用开平方的,与其他所有点的距离都不开平方也能比较相对的距离长短)也好,所以如果只有这三个元素的话,其实再怎么改也不会有太大的优化的。
最好还是添加一些辅助信息,比较常用的就是以划分网格的方法,将所有点分派在不同的网格中,然后以网格为基础找最近点,这样的话就要加一个网格的结构(以空间换时间),里面存放的就是属于这个网格的点的ID,通过编号规则可以很快的找最近的网格,然后再找里面的点,这样可以提高点查找速度。
呵呵,不好意思,没想清楚就写了:P,改一下,改一下,再加一步就好了
1.给点添加一个所属网格的信息:
Class A
{
public int ID;
public int X;
public int Y;
publci int Index;//所属网格编号
}
2.构造一个点链表,这是为了减少空间和方便处理而加的,后面的算法里会感觉到用处
(为每个点对象建立一个点链表节点)
Class I
{
public A *pAObject; //指向一个点对象
publci I *pNextA; //指向下一个
}
3.构件一个网格信息结构
Class G
{
public int ID; //网格编号
public I *pAObjects; //指向一个点链表(至于这个是带头节点还是不带头节点的都一样啦)
//这里用链表比较好
//第一,因为点可移动,那么点对象个数可以随意,可以发挥链表的扩展性
//第二,不需要取中间节点,也就没有用到数组的长处
}
4.构建网格,比如以1000为长度(长度可以自己定义,关键是平衡空间和速度关系),建立正方形的网格,那么(0,0)到(1000000,1000000)就是有1000*1000个网格(在给网格编号的时候最好有规律,那么就能通过List或者其他数组容器的下标来获得网格信息)
5.添加点的时候,先判断属于哪个网格,然后在点信息中添加网格编号,同时构建对应的点链表节点,并添加到所属网格的链表中
6.最后就是查询点了,首先获得出发点中的所属网格信息,看这个网格中是否有其他点:有,则一个个的判断距离(距离的平方),那么最近点一定在这些点里面(如果点还是太多,那么就考虑缩小网格的范围);没有,那就找所属网格周边8个网格中是否有点,有,则再找最近点,没有就再往外扩(如果感觉网格太多,可以加大网格的范围)
7.以找到的最近点到出发点的距离为基准,看看出发点到周边网格在这个距离内会接触到的(没有在6中遍历过的)有几个网格,把这些网格中的点再查看有没有更近的就行了
注:
1.如果还要优化就是加大网格的层次,可以用多层网格,这就会更复杂一点
2.在网格信息结构(G)中,因为点会移动,那么删点链表节点会有一个查找过程,如果觉得这个慢,那么就在点对象结构体中加一个所属点链表的指针:
class I;
Class A
{
public int ID;
public int X;
public int Y;
publci int Index;//所属网格编号
publci I *pI;
}
....
呵呵,看了lipai006的回答,想了下似乎也是可以实现的,只要多花点内存就可以达到比较好的速度了,而且也不需要真的从X坐标出发这样慢慢的以扇形扩展了啦,通过做一些辅助结构,就直接可以从出发点的X坐标出发,找同X不同Y中Y坐标与出发点最近的就行啦,循环结束条件就是X的扩展距离已经大于当前最小距离,因为再往外也肯定比当前最小距离大了。这个方法也就是要更复杂一些的辅助结构做索引,添加点的时候也要多做些事情,而且实现上的代码相对网格方法复杂一些,但查找速度应该比网格会快一点,因为毕竟是直接找点去了,其实网格方法就是把一批点的X,Y坐标看成是一样的,这样先过滤一批而已,是个速度与复杂度的折中。
xx_lzj:划分区域的目的就是为了使每个区域内的点不能太多,根据我的结构,每个区域有没有点,一个bool判断就够了,不会存在太稀疏影响效率的事情,不过最坏的情况的确会退化到遍历整个点空间,所以这个方法的时间复杂度仍然是O(n)。
你的方法其实和lipai006说的原理是差不多的(如果我对你们链表结构的猜想准确的话),无非就是通过X,Y坐标形成一个二维双向链表,在形成这个链表的过程会比网格相对复杂一点,而且也不是像你想的只要判断8个点就够的,当只有一个点在中间,其他点分布成以这个点为圆心的圆周上时,按照贴主的要求,难道只有8个最近点吗??在这个情况下,你的最坏复杂度还是O(n),但就如我说过的,这个方法的平均时间复杂度在参数上是会比网格的低一点,但是算法本身的代码复杂度上会高一点,而且在插入点的过程中的时间消耗会大一点而已。我觉得这是一个整体的过程,不能为了查找的快速牺牲太多其他的时间。
*************
xx_lzj:不好意思,你的链表我还有些不明白的地方:1.二维双向链表每个节点有4个指针,你能否把这4个指针如何获得的说一下,最好不要取边界线上的点,取中间的一个点进行介绍。2.对于初始化和修改点坐标的时候,现有数据如果是链表结构(不是数组),如何能不依靠其他辅助数据进行折半查找?3.修改某个点坐标之后,根据你的链表结构,我感觉不是删除、插入节点这么简单的,能不能具体点说明。