情境管理——-點擊檢測

來源:互聯網
上載者:User

----------------感謝華哥對我的信任,給了這個任務我,讓找回之前寫代碼的感覺。

演算法仍待改進,有興趣的讀者可聯絡

QQ 25887708.

MSN:xueuxan.kr@hotmail.com 

gmail: xuexuan.kr@gmail.com一起交流改進。

 

主要問題是:

解決對((1<<31)-1) * ((1<<31)-1)的情境下超過5000個矩形的滑鼠划過去檢測在哪個矩形上。通俗的想法是採用兩個迴圈。如下所示

var startime: int = getTimer();<br />for (i = 0; i < 10000; ++i)<br />{<br />x = (Math.random() * _randNum[int((Math.random() * 10) % 7)]);<br />y = (Math.random() * _randNum[int((Math.random() * 10) % 7)]);</p><p>var point: Point = new Point(x, y);</p><p>var j: int = 0;</p><p>for each(var item: Rectangle in _pool)<br />{<br />trace("the num is " + j++);<br />if (item.containsPoint(point))<br />{<br />trace(item);<br />break;<br />}<br />}<br />}<br />var findtime: int = getTimer() - startime;<br />trace("inittime...... " + inittime);<br />trace("findtime...... is " + findtime);<br />}

滑鼠划過的訊息響應數量是在最大上限可接近10000的,而矩形個數上限接近10000,這樣兩個迴圈下來,是一定無法讓使用者忍受的。所以一定要在結構上最佳化。

 

解決方案是:

四叉樹。

 

解決模型是:

父節點有四個子節點。

其結構體為:

import flash.geom.Rectangle;</p><p>class MyRectangle extends Rectangle<br />{<br />public var fullname: String = null;</p><p>public function MyRectangle(x: int, y: int, width: int, height: int)<br />{<br />super(x, y, width, height);<br />}<br />}</p><p>class Node<br />{<br />public var nEast: Node;<br />public var nWest: Node;<br />public var sEast: Node;<br />public var sWest: Node;</p><p>public var beginx: int;<br />public var beginy: int;<br />public var endx: int;<br />public var endy: int;</p><p>public var rectnum: int;</p><p>public var list: Vector.<MyRectangle>;</p><p>public function Node()<br />{<br />nEast = null;<br />nWest = null;<br />sEast = null;<br />sWest = null;<br />rectnum = 0;<br />list = new Vector.<MyRectangle>();<br />}</p><p>}

 

其中的rectnum為滿40(一開始是取3個,但是經過多次實驗,取40是對空間和時間的一個折衷選擇)則建立新的節點。

 

其實在考慮四叉樹之前,我採用了一個很欠考慮的方法,就是對這個情境進行網格劃分,單位為一個像素*一個像素。採用矩陣數組方式儲存每個像素方格上的矩形資訊,這樣在尋找的時候覆雜度為O(1),後來和同事交流後,發現一個很幼稚的錯誤,就是當一個3000*3000的矩形,其座標索引是會導致爆記憶體的。所以只能用四叉樹處理。

 

看了很多資料後,發現其實我用的這顆四叉樹並非真正意義上的四叉樹,只不過把地區縮小,可以是上述方法的一種最佳化。這種思想有點類似於我們常說的座標離散化,上述方法我是把矩形分攤在像素矩陣上,而這個方法則是把像素累積到四分之一的情境中。至於每次插入的時間會近似於log4(n),這裡的N取決於某個地區矩形的密集程度。如果過於密集且超過每個節點的rectnum,則會導致插入一個矩形要跑到二十幾層的子節點。對於4 ^n倍節點增長速度的一個顆樹來說是很恐怖的。所以之前的rectnum取3的時候,插入200個矩形就產生十幾萬個結點.而把rectnum提高的40後,產生的節點縮小到幾十個。雖然這樣的空間降低將會提高尋找時間。不過這個折衷是正確的。

 

至於按座標尋找矩形和按矩形資訊修改樹節點資訊,則採用廣搜技術實現,按座標尋找矩形則是遍曆層數,而耗時上限為rectnum * (層數上限)。據測試資料得出,對於一顆具有5000個矩形的樹,10000次尋找耗時4.443秒。相對於兩個迴圈的尋找已經很客觀了。

 

以下為插入一棵樹的代碼

public function Insert(_parent: Node, rect: Rectangle, num: int, name: String):Boolean<br />{<br />if (_parent == null || rect == null)<br />{<br />return null;<br />}</p><p>//trace("num is " + num);<br />if (_parent.rectnum == 3)<br />{<br />var midx: int = (_parent.endx + _parent.beginx) >> 1;<br />var midy: int = (_parent.endy + _parent.beginy) >> 1;</p><p>//NW 第一象限<br />if (rect.x < midx && rect.y < midy)<br />{<br />//trace( "tree NW");<br />if (_parent.nWest == null)<br />{<br />_parent.nWest = new Node();<br />}</p><p>_parent.nWest.beginx = _parent.beginx;<br />_parent.nWest.beginy = _parent.beginy;<br />_parent.nWest.endx = midx;<br />_parent.nWest.endy = midy;<br />Insert(_parent.nWest, rect, num+1, name);<br />}</p><p>//NE 第二象限<br />if ((rect.x + rect.width) > midx && rect.y < midy)<br />{</p><p>//trace("tree NE");<br />if (_parent.nEast == null)<br />{<br />_parent.nEast = new Node();<br />_parent.nEast.beginx = midx;<br />_parent.nEast.beginy = _parent.beginy;<br />_parent.nEast.endx = _parent.endx;<br />_parent.nEast.endy = midy;<br />}<br />//trace("nEast is" + _parent.nEast);</p><p>Insert(_parent.nEast, rect, num+1, name);<br />}</p><p>//SE 第四象限<br />if ((rect.x + rect.width) > midx && (rect.y + rect.height) > midy)<br />{<br />//trace("tree SE ");<br />if (_parent.sEast == null)<br />{<br />_parent.sEast = new Node();<br />_parent.sEast.beginx = midx;<br />_parent.sEast.beginy = midy;<br />_parent.sEast.endx = _parent.endx;<br />_parent.sEast.endy = _parent.endy;<br />}</p><p>Insert(_parent.sEast, rect, num+1, name);<br />}</p><p>//SW 第三象限<br />if (rect.x < midx && (rect.y + rect.height) > midy)<br />{<br />//trace("tree SW");<br />if (_parent.sWest == null)<br />{<br />_parent.sWest = new Node();<br />_parent.sWest.beginx = _parent.beginx;<br />_parent.sWest.beginy = midy;<br />_parent.sWest.endx = _parent.endx;<br />_parent.sWest.endy = _parent.endy;<br />}</p><p>Insert(_parent.sWest, rect, num+1, name);<br />}<br />}</p><p>else<br />{<br />_parent.list.push(rect);<br />_parent.rectnum++;<br />//trace("parent is " + _parent.list);<br />}</p><p>return true;<br />}

 

 

以下為按座標點尋找矩形的代碼

public function findNodeByPoint(_parent: Node, point: Point, num: int): MyRectangle<br />{<br />if (_parent == null || point == null)<br />{<br />return null;<br />}</p><p>var queue: Vector.<Node> = new Vector.<Node>();<br />var tmp: Node = null;<br />var left: int = 0;<br />queue.push(_parent);</p><p>var layer: int = 0;</p><p>while (left != queue.length)<br />{<br />tmp = queue[left];</p><p>trace("這裡是第N層。。。。。" + layer++);</p><p>if (tmp.nWest != null && point.x < tmp.nWest.endx && point.y < tmp.nWest.endy)<br />{<br />queue.push(tmp.nWest);<br />}</p><p>else if (tmp.nEast != null && point.x > tmp.nEast.beginx && point.y < tmp.nEast.endy)<br />{<br />queue.push(tmp.nEast);<br />}</p><p>else if (tmp.sWest != null && point.x < tmp.sWest.endx && point.y > tmp.sWest.beginy)<br />{<br />queue.push(tmp.sWest);<br />}</p><p>else if (tmp.sEast != null && point.x > tmp.sEast.beginx && point.y > tmp.sEast.beginy)<br />{<br />queue.push(tmp.sEast);<br />}</p><p>left++;<br />}</p><p>trace("這裡廣搜出現的結點看看是不是最後一個。。。。" + queue[queue.length - 1].list);<br />return queue[queue.length - 1].list.pop();<br />}

 

以下為按矩形修改樹的資訊

public function findNodeByElem(_parent: Node, rect: MyRectangle): MyRectangle<br />{<br />var left: int = 0;<br />var queue: Vector.<Node> = new Vector.<Node>();</p><p>queue.push(_parent);</p><p>//未添加截斷處理<br />while (left < queue.length)<br />{<br />var rectTmp: Vector.<MyRectangle> = queue[left].list;</p><p>var isexist: Boolean = false; //判斷節點是否存在該地區<br />//trace("開始迴圈。。。。。。。");<br />for each(var tmp: MyRectangle in rectTmp)<br />{<br />if (tmp.fullname == rect.fullname)<br />{<br />//trace("尋找到,這個節點是" + rectTmp);<br />tmp = null;<br />isexist = true;<br />}<br />}<br />//trace("結束迴圈。。。。。。");</p><p>if (isexist)<br />{<br />//trace("得出矩形存在的節點是啥。。。"+ queue[left].list);<br />queue[left].rectnum--;<br />}</p><p>if (queue[left].nWest != null && rect.x < queue[left].nWest.endx && rect.y < queue[left].nWest.endy)<br />{<br />queue.push(queue[left].nWest);<br />}</p><p>if (queue[left].nEast != null && (rect.x + rect.width) > queue[left].nEast.beginx && (rect.y) < queue[left].nEast.endy)<br />{<br />queue.push(queue[left].nEast);<br />}</p><p>if (queue[left].sWest != null && rect.x < queue[left].sWest.endx && (rect.y + rect.height) > queue[left].sWest.beginy)<br />{<br />queue.push(queue[left].sWest);<br />}</p><p>if (queue[left].sEast != null && (rect.x + rect.width) > queue[left].sEast.beginx && (rect.y + rect.height) > queue[left].sEast.beginy)<br />{<br />queue.push(queue[left].sEast);<br />}</p><p>left++;<br />}</p><p>trace("看看最後這個隊列能有多長~~~" + queue.length);</p><p>return null;<br />}

 

這個廣搜其實寫很垃圾,沒有模數控制長度。

感覺還是不甚完美。

=================================================================================

 

  今天再次看了下雲風寫的那本編程感悟以及前輩們的總結,編程世界很精彩,但同樣是挑戰多多。不過自從寫了這段代碼後,感覺又找回了之前的那種感覺。對本質的追求,對最佳化的追求。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.