【问题标题】:Make vector of elements less than each element of another vector使元素向量小于另一个向量的每个元素
【发布时间】:2019-08-06 03:09:06
【问题描述】:

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

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

有没有一种方法可以做到这一点,而不仅仅是使用大量 if 语句进行 for 循环?

我以循环格式编写了代码,但想要一个矢量化函数来处理它。

【问题讨论】:

  • 仅供参考,`text` 标记要在code 标记中显示的一段文本。包括您尝试解决您提出的问题也是一个好主意。在这种情况下,它包括您以循环格式编写的内容。当然很明显。但是没有人愿意花额外的时间来形象化它。反正你已经写好了。

标签: matlab


【解决方案1】:

您的问题的经典版本是创建一个向量a1:n(i) 的串联,其中n(i) 是向量b 中的第i 个条目,例如

b = [1,4,2];

给出一个向量a

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

这个问题可以在向量ones(1,sum(b)) 上使用cumsum 来解决,但是在与下一个序列开始的位置相对应的点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 倍。

【讨论】:

    【解决方案2】:

    这可以通过使用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
    

    【讨论】:

    • eval =/= 矢量化。不用说,避免它。
    【解决方案3】:

    有没有一种方法可以做到这一点,而不仅仅是使用包含大量 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,我用更多不同的输入进行了测试。 “矢量化”方法的长度总是大约两倍。

    结论:递归更快。

    【讨论】:

    • arrayfun 不是矢量化。这是一个伪装的 for 循环,只会增加一些开销。实际上,显式编写循环比让 MATLAB 弄清楚如何执行它要快。
    • 如果这是一项执行了数千或数百万次的任务,并且如果矢量化提高了性能,那么矢量化就会很有用。 MATLAB 中的循环不像以前那么慢了,因此即使可能,矢量化也并非总是如此。
    • @StewieGriffin:我过去测试过arrayfun 至少要快一个常数。你有测试来证明你声称的吗?至于参数再向量化,我对哲学讨论不感兴趣,除非实际问题需要对效率进行认真思考。
    • 它可能在较新的 MATLAB 版本中有所改进,但它曾经更快。当然,您必须以最佳方式编写循环,记住初始化向量/数组等。如果正确完成,向量化通常比循环/arrayfun 快很多。通常,如果您使用bsxfun(现已过时)、permutereshape 的组合解决了问题,那么您已经对其进行了矢量化处理。而且会很有趣。
    • @StewieGriffin 对我来说,这只是意味着您已将“矢量化”定义为使用 bsxfun 等。测试中有趣的部分是,Matlab 可能毕竟实现了尾调用优化。 (哦,让我编辑我的版本号。)如果您尝试弄乱代码以使其某些输出不会写入local_out,则代码仍然有效。例如使 else 案例写入父函数中的变量。但是Matlab会报错。
    猜你喜欢
    • 1970-01-01
    • 2014-05-21
    • 2018-02-01
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    相关资源
    最近更新 更多