在通过分而治之的方法查找数组中的反转次数的过程中我遇到了实现merge-step的问题:我们有两个排序的数组,任务是计算一个元素的数量第一个数组大于第二个数组中的元素。
例如,如果数组是v1 = [1,2,4], v2 = [0,3,5]
,我们应该计算4次反转。
所以,我在Matlab中实现了合并步骤,但我坚持如何快速实现它的问题。
首先,我用
尝试了蛮力方法tempArray = arrayfun(@(x) length(find(v2>x)), v1)
它和下一个片段
的效果太慢了l = 1;
s = 0;
for ii = 1:n1
while(l <= n2 && p1(ii)>p2(l))
l = l + 1;
end
s = s + l - 1;
end
有没有一种方法可以让它更快?
修改
感谢您的回答和方法!我为进一步的工作找到了有趣的东西。
这是片段,应该是我尝试过的最快
n1 = length(vec1); n2 = length(vec2);
uniteOne = [vec1, vec2];
[~, d1] = sort(uniteOne);
[~, d2] = sort(d1); % Find ind-s IX such that B(IX) = A, where B = sort(A)
vec1Slice = d2(1:n1);
finalVecToSolve = vec1Slice - (1:n1);
sum(finalVecToSolve)
答案 0 :(得分:6)
另一种使用bsxfun
的蛮力方法 -
sum(reshape(bsxfun(@gt,v1(:),v2(:).'),[],1))
或者,正如@thewaywewalk在评论中提到的那样,使用nnz
替换summing
-
nnz(bsxfun(@gt,v1(:),v2(:).'))
答案 1 :(得分:4)
n = numel(v1);
[~, ind_sort] = sort([v1 v2]);
ind_v = ind_sort<=n;
result = sum(find(ind_v))-n*(n+1)/2;
n
表示输入向量的长度。 ind_v
是一个长度为2*n
的向量,表示排在一起的v1
和v2
的值,其中一个表示{{1}的值}和零表示来自v1
的值。对于你的例子,
v2
我们有
v1 = [1,2,4];
v2 = [0,3,5];
ind_v =
0 1 1 0 1 0
的第一个条目零。这意味着ind_v
和v1
(v2
)的最低值属于0
。然后有一个一个,因为第二低的值(v2
)属于1
。 v1
的最后一项是零,因为输入向量的最大值(ind_v
)属于5
。
从这个v2
可以很容易地计算结果。也就是说,我们只需要计算每个一个左侧有多少零,并将所有这些数量相加。
我们甚至不需要做那些计数;我们可以从每个一个 的位置推断它们。第一个一个左侧的零的数量是一个减去ind_v
的位置。第二个 one 左侧的零的数量是其位置减去1
。等等。因此2
将给出期望的结果。但sum(find(ind_v)-(1:n))
仅为sum(1:n)
,因此结果可简化为n*(n+1)/2
。
对向量进行排序是限制操作,需要sum(find(ind_v))-n*(n+1)/2
算术比较。相反,蛮力需要O(2*n*log(2*n))
比较。
答案 2 :(得分:3)
一种明确的方法可能是减去你的元素,看看它们在哪里是否定的:
v1 = [1,2,4];
v2 = [0,3,5];
mydiffs = zeros(length(v1), length(v2));
for ii = 1:length(v1)
mydiffs(ii,:) = v2 - v1(ii);
end
test = sum(reshape(mydiffs,[],length(v1)*length(v2)) < 0)
返回:
test =
4
这不是最漂亮的方法,而且肯定有改进的余地。我也怀疑它比bsxfun
方法更快。
Edit1:arrayfun
方法,看起来更整洁,但不一定比循环更快。
test = arrayfun(@(x) (v2 - x) < 0, v1, 'UniformOutput', false);
inversions = sum([test{:}]);
Edit2:repmat
方法
inversions = nnz(repmat(v2, length(v2), 1) - repmat(v1', 1, length(v1)) < 0)