我有一个向量v
,由N个正整数组成,其值我事先都不知道。我想构造另一个向量a
,其中新向量中的值由v
中的值根据以下规则确定:
-a
中的元素都是整数,直到v
中的每个元素的值都包括在内
-0个条目仅包含一次,但正整数连续出现两次
例如,如果v
是[1,0,2]
,则a
应该是:[0,1,1,0,0,1,1,2,2]
。
有没有一种方法,而不仅仅是对大量if语句进行for循环?
我已经以循环格式编写了代码,但希望使用矢量化函数来处理它。
答案 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,我测试了另外一些不同的输入。 “向量化”方法总是大约两倍长。
结论:递归更快。