4

更新:我已经做了一些测试,Jonas 的解决方案对于一系列不同大小的输入向量来说是最快的。特别是,正如 angainor 指出的那样,该解决方案可以非常好地扩展到大尺寸 - 这是一项重要的测试,因为通常是大尺寸问题促使我们在 SO 上提出此类问题。感谢 Jonas 和 tmpearce 的解决方案——基于解决大型问题的效率,我将答案打勾给 Jonas。

我的问题:我有这个列向量:

Vec = [0; 1; 2; -1; -3; 0; 0; 2; 1; -1];

我想将每个大于一个的元素转换为长度等于元素值的序列。同样,我想将每个小于负一的元素转换为一系列负数。因此我的输出向量应该是这样的:

VecLong = [0; 1; 1; 1; -1; -1; -1; -1; 0; 0; 1; 1; 1; -1];

请注意,每个 2 已更改为两个 1,而 -3 已更改为三个 -1。目前,我解决了这样的问题:

VecTemp = Vec;
VecTemp(VecTemp == 0) = 1;
VecLong = NaN(sum(abs(VecTemp)), 1);
c = 1;
for n = 1:length(Vec)
    if abs(Vec(n)) <= 1
        VecLong(c) = Vec(n);
        c = c + 1;
    else
        VecLong(c:c + abs(Vec(n))) = sign(Vec(n));
        c = c + abs(Vec(n));
    end    
end

这感觉不是很优雅。任何人都可以提出更好的方法吗?注意:您可以假设Vec它将仅包含整数值。提前感谢所有建议。

4

2 回答 2

3

编辑:我想到了另一种(有点晦涩)但更短的方法来做到这一点,它比你拥有的循环更快。

for rep=1:100000
    #% original loop-based solution
end
toc
Elapsed time is 2.768822 seconds.

#% bsxfun-based indexing alternative
tic;
for rep=1:100000
TempVec=abs(Vec);TempVec(Vec==0)=1;
LongVec = sign(Vec(sum(bsxfun(@gt,1:sum(TempVec),cumsum(TempVec)))+1))
end
toc
Elapsed time is 1.798339 seconds.

与原始答案相比,这个答案的扩展性也很好 - 至少在一定程度上。有一个表演的甜蜜点。

Vec = repmat(OrigVec,10,1);
#% test with 100,000 loops
#% loop-based solution:
Elapsed time is 19.005226 seconds.
#% bsxfun-based solution:
Elapsed time is 4.411316 seconds.

Vec = repmat(OrigVer,1000,1);
#% test with 1,000 loops - 100,000 would be horribly slow
#% loop-based solution:
Elapsed time is 18.105728 seconds.
#% bsxfun-based solution:
Elapsed time is 98.699396 seconds.

bsxfun 将向量扩展为矩阵,然后用 sum 折叠它。对于非常大的向量,与循环相比,这不必要地占用大量内存,因此最终会丢失。不过在那之前,它做得很好。


原始的,缓慢的答案:

这是一个单行:

out=cell2mat(arrayfun(@(x) repmat(((x>0)*2)-1+(x==0),max(1,abs(x)),1),Vec,'uni',0));
out' =

     0   1   1   1  -1  -1  -1  -1   0   0   1   1   1  -1

这是怎么回事:

((x>0)*2)-1 + (x==0) #% if an integer is >0, make it a 1, <0 becomes -1, 0 stays 0 

max(1,abs(x)) #% figure out how many times to replicate the value  

arrayfun(@(x) (the above stuff), Vec, 'uni', 0) #% apply the function  
 #% to each element in the array, generating a cell array output

cell2mat( (the above stuff) ) #% convert back to a matrix 
于 2012-10-27T04:18:47.527 回答
3

您可以使用良好的旧 cumsum-approach 正确重复条目。请注意,如果您想将所有内容放在一行中,我将分配一些您可以摆脱的临时变量。

%# create a list of values to repeat
signVec = sign(Vec);

%# create a list of corresponding indices that repeat
%# as often as the value in signVec has to be repeated

tmp = max(abs(Vec),1); %# max: zeros have to be repeated once
index = zeros(sum(tmp),1);
index([1;cumsum(tmp(1:end-1))+1])=1; %# assign ones a pivots for cumsum
index = cumsum(index); %# create repeating indices

%# repeat
out = signVec(index);
out'
out =

     0     1     1     1    -1    -1    -1    -1     0     0     1     1     1    -1
于 2012-10-27T07:24:09.243 回答