使元素向量小于另一个向量的每个元素

时间:2019-08-06 03:09:06

标签: matlab

我有一个向量v,由N个正整数组成,其值我事先都不知道。我想构造另一个向量a,其中新向量中的值由v中的值根据以下规则确定: -a中的元素都是整数,直到v中的每个元素的值都包括在内 -0个条目仅包含一次,但正整数连续出现两次

例如,如果v[1,0,2],则a应该是:[0,1,1,0,0,1,1,2,2]

有没有一种方法,而不仅仅是对大量if语句进行for循环?

我已经以循环格式编写了代码,但希望使用矢量化函数来处理它。

3 个答案:

答案 0 :(得分:1)

问题的经典版本是创建带有a的向量1:n(i),其中n(i)是向量b中的第i个条目,例如

b = [1,4,2];

给出一个向量a

a = [1,1,2,3,4,1,2];

在向量cumsum上使用ones(1,sum(b))解决了这个问题,但在与下一个序列开始的点1+cumsum(b(1:end-1))处重置了总和。

要解决您的特定问题,我们可以做类似的事情。由于每个步骤需要两个条目,因此我们将向量0.5 * ones(1,sum(b*2+1))floor一起使用。另外,由于您只希望条目0发生一次,因此我们只需要从0.5开始每个序列,而不是从0开始(这将产生floor([0,0.5,...]) = [0,0,...])。

总的来说,我们有类似的东西

% construct the list of 0.5s
a = 0.5*ones(1,sum(b*2+1))

% Reset the sum where a new sequence should start
a(cumsum(b(1:end-1)*2+1)+1) =a(cumsum(b(1:end-1)*2+1)+1)*2 -(b(1:end-1)+1)

% Cumulate it and find the floor
a = floor(cumsum(a)) 

请注意,此处所有操作 都是矢量化的!

基准:

您可以使用以下代码进行基准测试

function SO()
b =randi([0,100],[1,1000]);

t1 = timeit(@() Nicky(b));
t2 = timeit(@() Recursive(b));
t3 = timeit(@() oneliner(b));

if all(Nicky(b) == Recursive(b)) && all(Recursive(b) == oneliner(b))
    disp("All methods give the same result")
else
    disp("Something wrong!")
end

disp("Vectorised time: "+t1+"s")
disp("Recursive time: "+t2+"s")
disp("One-Liner time: "+t3+"s")
end

function [a] = Nicky(b)
a = 0.5*ones(1,sum(b*2+1));
a(cumsum(b(1:end-1)*2+1)+1) =a(cumsum(b(1:end-1)*2+1)+1)*2 -(b(1:end-1)+1);
a = floor(cumsum(a));
end

function out=Recursive(arr)
    out=myfun(arr);
    function local_out=myfun(arr)
        if isscalar(arr)
            if arr
                local_out=sort([0,1:arr,1:arr]); % this is faster
            else
                local_out=0;
            end
        else
            local_out=[myfun(arr(1:end-1)),myfun(arr(end))];
        end
    end
end

function b = oneliner(a)
b = cell2mat(arrayfun(@(x)sort([0,1:x,1:x]),a,'UniformOutput',false));
end

哪个给我

All methods give the same result
Vectorised time: 0.00083574s
Recursive time: 0.0074404s
One-Liner time: 0.0099933s

因此矢量化的确实是最快的,大约是10倍。

答案 1 :(得分:0)

这可以通过使用eval的单线来完成:

a = eval(['[' sprintf('sort([0 1:%i 1:%i]) ',[v(:) v(:)]') ']']);

这是另一种不使用eval的解决方案。不确定“向量化函数”的意图是什么,但是以下代码很紧凑,可以轻松地制成函数:

a = [];
for i = 1:numel(v)
    a = [a sort([0 1:v(i) 1:v(i)])];
end

答案 2 :(得分:0)

  

有没有一种方法,而不仅仅是对许多if语句进行for循环?

好的。递归呢?当然,我们不能保证Matlab具有尾部调用优化功能。

例如,在名为filename.m的文件中

function out=filename(arr)
    out=myfun(in);
    function local_out=myfun(arr)
        if isscalar(arr)
            if arr
                local_out=sort([0,1:arr,1:arr]); % this is faster
            else
                local_out=0;
            end
        else
            local_out=[myfun(arr(1:end-1)),myfun(arr(end))];
        end
    end
end

在cmd中,键入

input=[1,0,2];
filename(input);

您可以取消父功能。我添加它只是希望Matlab可以发现filename.m中的递归并对其进行优化。

  

想要一个矢量化函数来处理它。

好的。尽管我看不到矢量化的意义,但这种独特的难题无法推广到其他应用程序。我也没有预见到性能会提高。

例如,假设输入为1乘N。在cmd中,键入

input=[1,0,2];
cell2mat(arrayfun(@(x)sort([0,1:x,1:x]),input,'UniformOutput',false)

基准

在R2018a中

>> clear all
>> in=randi([0,100],[1,100]); N=10000;

>> T=zeros(N,1);tic; for i=1:N; filename(in) ;T(i)=toc;end; mean(T),
ans =
    1.5647

>> T=zeros(N,1);tic; for i=1:N; cell2mat(arrayfun(@(x)sort([0,1:x,1:x]),in,'UniformOutput',false)); T(i)=toc;end; mean(T),
ans =
    3.8699

Ofc,我测试了另外一些不同的输入。 “向量化”方法总是大约两倍长。

结论:递归更快。