4

我对矢量化有点陌生。自己试过但做不到。有人可以帮我对这段代码进行矢量化处理,并简要说明你是如何做到的,这样我也可以调整思维过程。谢谢。

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;
4

1 回答 1

7

给定Polygon二维信息,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 内置程序性能相匹配的代码。

回到主题...

首先要做的是获取和行向量linDiff之间的线性差异(在代码示例中)。要以矢量化方式执行此操作,两个变量的维度必须相同。实现此目的的一种方法是使用复制每一行以使其大小与. 但是,通常是 repmat 的更好替代方案(如最近的 SO 问题中所述),使代码......PolygonpointrepmatpointPolygonbsxfun

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,2sum命令中添加)。然后我更进一步,通过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语句的重复会变得有点难看。为了保持干燥,将带有条件函数的测试放在一个函数中似乎是明智的,就像您在原始帖子中所做的那样。但是,该函数的效用将非常狭窄——它只会测试该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);

...它的美妙之处在于您可以使用任何测试、默认值和昂贵的功能。这个简单的例子可能有点矫枉过正,但当我提出使用函数句柄的想法时,这就是我的想法。

于 2012-10-18T08:37:00.407 回答