2012-10-18 57 views
4

我有點新的矢量化。已經嘗試過自己但不能。有人可以幫助我對這段代碼進行矢量化,並簡要說明如何做,以便我可以調整思維過程。謝謝。向量化matlab代碼

function [result] = newHitTest (point,Polygon,r,tol,stepSize) 
%This function calculates whether a point is allowed. 

%First is a quick test is done by calculating the distance from point to 
%each point of the polygon. If that distance is smaller than range "r", 
%the point is not allowed. This will slow down the algorithm at some 
%points, but will greatly speed it up in others because less calls to the 
%circleTest routine are needed. 
polySize=size(Polygon,1); 
testCounter=0; 

for i=1:polySize 
d = sqrt(sum((Polygon(i,:)-point).^2)); 

if d < tol*r 
    testCounter=1; 
    break 
end 
end 

if testCounter == 0 
circleTestResult = circleTest (point,Polygon,r,tol,stepSize); 
testCounter = circleTestResult; 
end 

result = testCounter; 

回答

7

鑑於信息Polygon是2維的,point是行向量和其他變量是標量,這裏是你的新功能的第一個版本(向下滾動看到,有很多方法對皮膚這個貓):

function [result] = newHitTest (point,Polygon,r,tol,stepSize) 
result = 0; 
linDiff = Polygon-repmat(point,size(Polygon,1),1); 
testLogicals = sqrt(sum((linDiff).^2 ,2)) < tol*r;  
if any(testLogicals); result = circleTest (point,Polygon,r,tol,stepSize); end 

在Matlab矢量化的思想過程涉及嘗試使用單個命令操作儘可能多的數據。大多數基本的Matlab內置函數在多維數據上運行非常高效。使用for循環與此相反,因爲您正在將數據分解爲更小的數據段進行處理,每個數據段都必須單獨解釋。通過使用for循環進行數據分解,您可能會失去與Matlab內置函數背後的高度優化代碼相關的一些大規模性能優勢。

在您的示例中首先要考慮的是主循環中的條件中斷。你無法從矢量化過程中解脫出來。相反,計算所有可能性,爲每行數據創建一個結果數組,然後使用關鍵字any來查看是否有任何行已發出信號表明circleTest函數應該被調用。

注意:在Matlab中有效地有條件地打破計算並不容易。但是,由於您只是在循環中計算歐幾里德距離,所以通過使用矢量化版本和計算所有可能性,您可能會看到性能提升。如果循環中的計算更加昂貴,輸入數據很大,並且一旦遇到特定條件就想要分解,那麼使用編譯語言編寫的matlab擴展可能比矢量化版本快得多你可能正在執行不必要的計算。然而,這是假設您知道如何編寫與編譯爲本地代碼的語言相匹配的Matlab內置函數性能的代碼。

回到主題...

做的第一件事就是把Polygon和你的行向量point之間(在代碼示例linDiff)線性差。要以矢量化的方式進行此操作,2個變量的尺寸必須相同。達到此目的的一種方法是使用repmat複製point的每一行,使其與Polygon的大小相同。然而,bsxfun通常是一個更好的選擇repmat(as described in this recent SO question),使得代碼...

function [result] = newHitTest (point,Polygon,r,tol,stepSize) 
result = 0; 
linDiff = bsxfun(@minus, Polygon, point); 
testLogicals = sqrt(sum((linDiff).^2 ,2)) < tol*r;  
if any(testLogicals); result = circleTest (point,Polygon,r,tol,stepSize); end 

我通過在第二軸求和軋製您d值成d列(注意去除所述陣列的Polygon的索引和在sum命令中增加,2)。然後,我進一步深入並評估邏輯陣列testLogicals與距離測量的計算內嵌。您很快就會發現重矢量化的一個缺點就是它可以讓那些不熟悉Matlab的人對代碼的可讀性降低,但性能提升是值得的。評論是非常必要的。

現在,如果你想完全瘋狂,你可能會認爲測試函數非常簡單,因此它保證使用'匿名函數'或'lambda'而不是完整的函數定義。測試是否值得執行circleTest不需要參數stepSize,這也許是使用匿名函數的另一個原因。您可以將測試轉換爲匿名函數,然後在調用腳本中使用circleTest,從而使代碼在某種程度上自行記錄。 。 。

doCircleTest = @(point,Polygon,r,tol) any(sqrt(sum(bsxfun(@minus, Polygon, point).^2, 2)) < tol*r); 

if doCircleTest(point,Polygon,r,tol) 
    result = circleTest (point,Polygon,r,tol,stepSize); 
else 
    result = 0; 
end 

現在一切都是向量化的,使用函數句柄給了我另一個想法。 。 。

如果您計劃在代碼中的多個點執行此操作,那麼if語句的重複會變得有點難看。要保持dry,將條件函數的測試放入單個函數似乎是明智的,就像您在原始文章中所做的那樣。但是,該函數的實用程序將非常狹窄 - 它只會測試circleTest函數是否應該執行,然後在需要時執行它。

現在想象一下,過了一段時間,您還有其他一些條件函數,就像circleTest一樣,其自身等效於doCircleTest。也許重用條件轉換代碼會很好。對於這一點,讓喜歡你原來的一個函數,它的默認值,計算上廉價的測試功能的布爾結果,以及與其相關的參數的昂貴條件函數的功能手柄...

function result = conditionalFun(default, cheapFunResult, expensiveFun, varargin) 
if cheapFunResult 
    result = expensiveFun(varargin{:}); 
else 
    result = default; 
end 
end %//of function 

你可以使用以下命令從主腳本調用此函數。 。 。

result = conditionalFun(0, doCircleTest(point,Polygon,r,tol), @circleTest, point,Polygon,r,tol,stepSize); 

...它的美麗之處在於您可以使用任何測試,默認值和昂貴的功能。這個簡單的例子可能有點矯枉過正,但是當我提出使用函數句柄的想法時,它就是我思緒徘徊的地方。

+0

感謝您的解釋。它確實有幫助。但還有一個問題..當我運行向量化代碼時,我會得到以下的錯誤信息。 '錯誤使用 - 矩陣尺寸必須一致。' 我們從矩陣中減去一個定標器,錯誤不應該出現。 – Vikram

+0

在哪一行?假設點,r和tol是標量,Polygon是二維數據,那麼我的代碼就完全符合你的想法。 – learnvst

+0

here..'(多邊形點)' – Vikram