當前位置:首頁 » 操作系統 » 對象的演算法

對象的演算法

發布時間: 2023-04-09 00:28:02

1. 面向對象編程需要演算法

面向過程 是將問題作為出發點的,以解決問題的過程和方式為主要編程思路,因此面向過程都是一些程序或者說函數的集合,數據和程序緊耦合在一起。面向對象:是以客觀事物作為出發點的,將一個事物的屬性和這個事物的行為結合在一起,也可以說是將一個事物的動態行為和靜態行為相結合聯系在一起。面向過程中重要的部分是演算法,面向對象的重要的過程是模式,所謂模式,就是類與類的組織結構,如經常說的設計模式,但是面向對象中的具體對象的靜態方法,就是面向過程的函數,只不過這種函數在使用數據,和作用域上有了對象領域特徵,即作用域的限制。所以從這個角度講,面向過程中的演算法在面向對象中是一種具體的表現,也需要演算法的支撐,而面向對象是從一個高層次的體系結構,組織對象之間關系,保證對象之間的耦合度(依賴程度)降低,以實現復用的目的。

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.修改某個點坐標之後,根據你的鏈表結構,我感覺不是刪除、插入節點這么簡單的,能不能具體點說明。

熱點內容
內置存儲卡可以拆嗎 發布:2025-05-18 04:16:35 瀏覽:336
編譯原理課時設置 發布:2025-05-18 04:13:28 瀏覽:378
linux中進入ip地址伺服器 發布:2025-05-18 04:11:21 瀏覽:612
java用什麼軟體寫 發布:2025-05-18 03:56:19 瀏覽:32
linux配置vim編譯c 發布:2025-05-18 03:55:07 瀏覽:107
砸百鬼腳本 發布:2025-05-18 03:53:34 瀏覽:944
安卓手機如何拍視頻和蘋果一樣 發布:2025-05-18 03:40:47 瀏覽:741
為什麼安卓手機連不上蘋果7熱點 發布:2025-05-18 03:40:13 瀏覽:803
網卡訪問 發布:2025-05-18 03:35:04 瀏覽:511
接收和發送伺服器地址 發布:2025-05-18 03:33:48 瀏覽:372