Matlab:将大于(小于)1(-1)的元素转换为1(-1)的序列

时间:2012-10-27 03:42:28

标签: matlab vector vectorization

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

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

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

我想将大于1的每个元素转换为长度等于元素值的序列。同样,我想将每个小于-1的元素转换为减1的序列。因此我的输出向量应如下所示:

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

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

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仅包含整数值。提前感谢所有建议。

2 个答案:

答案 0 :(得分: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 

答案 1 :(得分:3)

您可以使用旧的cumsum方法正确重复输入。请注意,如果您想将所有内容放在一行中,我将分配一些您可以删除的临时变量。

%# 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