当未明确知道输入参数的数量时,使用可变数量的输入参数调用函数
Function call with variable number of input arguments when number of input arguments is not explicitly known
我有一个变量 pth
,它是维度 1xn
的元胞数组,其中 n
是用户输入。 pth
中的每个元素本身就是一个元胞数组,length(pth{k})
对于 k=1:n
是变量(另一个函数的结果)。每个元素 pth{k}{kk}
其中 k=1:n
和 kk=1:length(pth{k})
是 integers/node 数字的一维向量,其长度又是可变的。总而言之,我有可变数量的可变长度向量,它们组织在可变数量的元胞数组中。
当你从 pth{1}
、pth{2}
、{pth{3}
等中随机取一个向量时,我想尝试找到所有可能的交点...似乎这样做的文件交换,例如 this one or this one。我遇到的问题是你需要这样调用函数:
mintersect(v1,v2,v3,...)
而且我不能写出一般情况下的所有输入,因为我不知道具体有多少(这将是上面的 n
)。理想情况下,我想做这样的事情;
mintersect(pth{1}{1},pth{2}{1},pth{3}{1},...,pth{n}{1})
mintersect(pth{1}{1},pth{2}{2},pth{3}{1},...,pth{n}{1})
mintersect(pth{1}{1},pth{2}{3},pth{3}{1},...,pth{n}{1})
etc...
mintersect(pth{1}{1},pth{2}{length(pth{2})},pth{3}{1},...,pth{n}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{2},...,pth{n}{1})
etc...
继续尝试所有可能的组合,但我无法用代码编写。来自 File Exchange 的 function 看起来是找到所有可能组合的好方法,但我再次遇到具有可变输入数量的函数调用的相同问题:
allcomb(1:length(pth{1}),1:length(pth{2}),...,1:length(pth{n}))
当您无法物理指定所有输入参数(因为输入参数的数量是可变的)时,有人知道如何解决输入参数数量可变的函数调用问题吗?这同样适用于 MATLAB 和 Octave,因此有两个标签。关于从每个 pth{k}
随机抽取向量时如何找到所有可能 combinations/intersections 的任何其他建议欢迎!
编辑 2020 年 5 月 27 日
感谢疯狂物理学家的回答,我最终使用了以下有效的方法:
disp('Computing intersections for all possible paths...')
grids = cellfun(@(x) 1:numel(x), pth, 'UniformOutput', false);
idx = cell(1, numel(pth));
[idx{:}] = ndgrid(grids{:});
idx = cellfun(@(x) x(:), idx, 'UniformOutput', false);
idx = cat(2, idx{:});
valid_comb = [];
k = 1;
for ii = idx'
indices = reshape(num2cell(ii), size(pth));
selection = cellfun(@(p,k) p{k}, pth, indices, 'UniformOutput', false);
if my_intersect(selection{:})
valid_comb = [valid_comb k];
endif
k = k+1;
end
我自己的版本类似,但使用 for
循环而不是逗号分隔列表:
disp('Computing intersections for all possible paths...')
grids = cellfun(@(x) 1:numel(x), pth, 'UniformOutput', false);
idx = cell(1, numel(pth));
[idx{:}] = ndgrid(grids{:});
idx = cellfun(@(x) x(:), idx, 'UniformOutput', false);
idx = cat(2, idx{:});
[n_comb,~] = size(idx);
temp = cell(n_pipes,1);
valid_comb = [];
k = 1;
for k = 1:n_comb
for kk = 1:n_pipes
temp{kk} = pth{kk}{idx(k,kk)};
end
if my_intersect(temp{:})
valid_comb = [valid_comb k];
end
end
在这两种情况下,valid_comb
都有有效组合的索引,然后我可以使用类似的方法检索它们:
valid_idx = idx(valid_comb(1),:);
for k = 1:n_pipes
pth{k}{valid_idx(k)} % do something with this
end
当我用一些样本数据对这两种方法进行基准测试时(pth
是 4x1
,pth
的 4 个元素是 2x1
,9x1
, 8x1
和 69x1
),我得到了以下结果:
>> benchmark
Elapsed time is 51.9075 seconds.
valid_comb = 7112
Elapsed time is 66.6693 seconds.
valid_comb = 7112
所以疯狂物理学家的方法快了大约 15 秒。
我也误解了mintersect
所做的,这不是我想要的。我想找到一个组合,其中两个或多个向量中不存在任何元素,所以我结束了编写 mintersect
:
的版本
function valid_comb = my_intersect(varargin)
% Returns true if a valid combination i.e. no combination of any 2 vectors
% have any elements in common
comb_idx = combnk(1:nargin,2);
[nr,nc] = size(comb_idx);
valid_comb = true;
k = 1;
% Use a while loop so that as soon as an intersection is found, the execution stops
while valid_comb && (k<=nr)
temp = intersect(varargin{comb_idx(k,1)},varargin{comb_idx(k,2)});
valid_comb = isempty(temp) && valid_comb;
k = k+1;
end
end
我相信这可以解决问题。为 k=1:n
和 kk=1:length(pth{k})
.
对 pth{k}{kk}
中所有可能的向量组合调用 mintersect
使用 eval
并稍微修改 sprintf
/compose
。请注意,通常使用 eval
是 very much discouraged。如果这是您的需要,可以添加更多评论。
% generate some data
n = 5;
pth = cell(1,n);
for k = 1:n
pth{k} = cell(1,randi([1 10]));
for kk = 1:numel(pth{k})
pth{k}{kk} = randi([1 100], randi([1 10]), 1);
end
end
% get all combs
str_to_eval = compose('1:length(pth{%i})', 1:numel(pth));
str_to_eval = strjoin(str_to_eval,',');
str_to_eval = sprintf('allcomb(%s)',str_to_eval);
% use eval to get all combinations for a given pth
all_combs = eval(str_to_eval);
% and make strings to eval in intersect
comp = num2cell(1:numel(pth));
comp = [comp ;repmat({'%i'}, 1, numel(pth))];
str_pattern = sprintf('pth{%i}{%s},', comp{:});
str_pattern = str_pattern(1:end-1); % get rid of last ,
strings_to_eval = cell(length(all_combs),1);
for k = 1:size(all_combs,1)
strings_to_eval{k} = sprintf(str_pattern, all_combs(k,:));
end
% and run eval on all those strings
result = cell(length(all_combs),1);
for k = 1:size(all_combs,1)
result{k} = eval(['mintersect(' strings_to_eval{k} ')']);
%fprintf(['mintersect(' strings_to_eval{k} ')\n']); % for debugging
end
对于随机生成的 pth
,代码会生成以下要评估的字符串(其中一些 pth{k}
只有一个单元格用于说明):
mintersect(pth{1}{1},pth{2}{1},pth{3}{1},pth{4}{1},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{1},pth{4}{2},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{1},pth{4}{3},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{2},pth{4}{1},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{2},pth{4}{2},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{2},pth{4}{3},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{1},pth{4}{1},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{1},pth{4}{2},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{1},pth{4}{3},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{2},pth{4}{1},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{2},pth{4}{2},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{2},pth{4}{3},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{1},pth{4}{1},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{1},pth{4}{2},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{1},pth{4}{3},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{2},pth{4}{1},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{2},pth{4}{2},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{2},pth{4}{3},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{1},pth{4}{1},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{1},pth{4}{2},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{1},pth{4}{3},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{2},pth{4}{1},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{2},pth{4}{2},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{2},pth{4}{3},pth{5}{1})
几个有助于构建解决方案的要点:
- This post shows you how to construct a Cartesian product between arbitrary arrays using
ndgrid
.
cellfun
同时接受多个元胞数组,您可以使用它们来索引特定元素。
- 您可以使用元胞数组从函数中捕获可变数量的参数,如图所示here。
所以让我们从最外层的数组中获取 ndgrid
的输入:
grids = cellfun(@(x) 1:numel(x), pth, 'UniformOutput', false);
现在您可以创建一个包含网格乘积的索引:
index = cell(1, numel(pth));
[index{:}] = ndgrid(grids{:});
你想把所有的网格都做成列向量,横向拼接起来。该矩阵的行将表示笛卡尔索引 select 每次迭代时 pth
的元素:
index = cellfun(@(x) x(:), index, 'UniformOutput', false);
index = cat(2, index{:});
如果将 index
的一行转换为元胞数组,则可以 运行 将其 pth
锁定为 select 正确的元素并调用 mintersect
在结果上。
for i = index'
indices = num2cell(i');
selection = cellfun(@(p, i) p{i}, pth, indices, 'UniformOutput', false);
mintersect(selection{:});
end
这是在假设pth
是一个行数组的情况下写的。如果不是这种情况,对于一般情况,您可以将循环的第一行更改为 indices = reshape(num2cell(i), size(pth));
,对于列情况,只需将其更改为 indices = num2cell(i);
。关键是 indices
的单元格必须与 pth
的形状相同才能同步迭代它。它已经生成为具有相同数量的元素。
正如 Madphysicist 所指出的,我误解了您的初始元胞数组的初始结构,但这一点是成立的。将未知数量的参数传递给函数的方法是通过 comma-separated-list generation, and your function needs to support it by being declared with varargin。更新了下面的示例。
创建一个辅助函数以从每个主单元格中收集随机子单元格:
% in getRandomVectors.m
function Out = getRandomVectors(C) % C: a double-jagged array, as described
N = length(C);
Out = cell(1, N);
for i = 1 : length(C)
Out{i} = C{i}{randi( length(C{i}) )};
end
end
然后假设你已经有一个 mintersect 函数定义如下:
% in mintersect.m
function Intersections = mintersect( varargin )
Vectors = varargin;
N = length( Vectors );
for i = 1 : N; for j = 1 : N
Intersections{i,j} = intersect( Vectors{i}, Vectors{j} );
end; end
end
然后这样称呼:
C = { { 1:5, 2:4, 3:7 }, {1:8}, {2:4, 3:9, 2:8} }; % example double-jagged array
In = getRandomVectors(C); % In is a cell array of randomly selected vectors
Out = mintersect( In{:} ); % Note the csl-generator syntax
PS。我注意到您对 mintersect 的定义与链接的定义不同。可能只是你没有很好地描述你想要什么,在这种情况下我的 mintersect 函数不是你想要的。我所做的是为提供的向量生成所有可能的交集。您链接到的那个产生一个单一的交叉点,这是所有提供的向量所共有的。使用最适合您的那个。不过,使用它的基本原理是相同的。
PS。从您的描述中也不完全清楚您所追求的是每个 n 的随机向量 k,还是所有 n 和 k 上可能向量的整个 space。上述解决方案是前者。如果您想要后者,请参阅 MadPhysicist 关于如何创建所有可能索引的笛卡尔积的解决方案。
我有一个变量 pth
,它是维度 1xn
的元胞数组,其中 n
是用户输入。 pth
中的每个元素本身就是一个元胞数组,length(pth{k})
对于 k=1:n
是变量(另一个函数的结果)。每个元素 pth{k}{kk}
其中 k=1:n
和 kk=1:length(pth{k})
是 integers/node 数字的一维向量,其长度又是可变的。总而言之,我有可变数量的可变长度向量,它们组织在可变数量的元胞数组中。
当你从 pth{1}
、pth{2}
、{pth{3}
等中随机取一个向量时,我想尝试找到所有可能的交点...似乎这样做的文件交换,例如 this one or this one。我遇到的问题是你需要这样调用函数:
mintersect(v1,v2,v3,...)
而且我不能写出一般情况下的所有输入,因为我不知道具体有多少(这将是上面的 n
)。理想情况下,我想做这样的事情;
mintersect(pth{1}{1},pth{2}{1},pth{3}{1},...,pth{n}{1})
mintersect(pth{1}{1},pth{2}{2},pth{3}{1},...,pth{n}{1})
mintersect(pth{1}{1},pth{2}{3},pth{3}{1},...,pth{n}{1})
etc...
mintersect(pth{1}{1},pth{2}{length(pth{2})},pth{3}{1},...,pth{n}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{2},...,pth{n}{1})
etc...
继续尝试所有可能的组合,但我无法用代码编写。来自 File Exchange 的 function 看起来是找到所有可能组合的好方法,但我再次遇到具有可变输入数量的函数调用的相同问题:
allcomb(1:length(pth{1}),1:length(pth{2}),...,1:length(pth{n}))
当您无法物理指定所有输入参数(因为输入参数的数量是可变的)时,有人知道如何解决输入参数数量可变的函数调用问题吗?这同样适用于 MATLAB 和 Octave,因此有两个标签。关于从每个 pth{k}
随机抽取向量时如何找到所有可能 combinations/intersections 的任何其他建议欢迎!
编辑 2020 年 5 月 27 日
感谢疯狂物理学家的回答,我最终使用了以下有效的方法:
disp('Computing intersections for all possible paths...')
grids = cellfun(@(x) 1:numel(x), pth, 'UniformOutput', false);
idx = cell(1, numel(pth));
[idx{:}] = ndgrid(grids{:});
idx = cellfun(@(x) x(:), idx, 'UniformOutput', false);
idx = cat(2, idx{:});
valid_comb = [];
k = 1;
for ii = idx'
indices = reshape(num2cell(ii), size(pth));
selection = cellfun(@(p,k) p{k}, pth, indices, 'UniformOutput', false);
if my_intersect(selection{:})
valid_comb = [valid_comb k];
endif
k = k+1;
end
我自己的版本类似,但使用 for
循环而不是逗号分隔列表:
disp('Computing intersections for all possible paths...')
grids = cellfun(@(x) 1:numel(x), pth, 'UniformOutput', false);
idx = cell(1, numel(pth));
[idx{:}] = ndgrid(grids{:});
idx = cellfun(@(x) x(:), idx, 'UniformOutput', false);
idx = cat(2, idx{:});
[n_comb,~] = size(idx);
temp = cell(n_pipes,1);
valid_comb = [];
k = 1;
for k = 1:n_comb
for kk = 1:n_pipes
temp{kk} = pth{kk}{idx(k,kk)};
end
if my_intersect(temp{:})
valid_comb = [valid_comb k];
end
end
在这两种情况下,valid_comb
都有有效组合的索引,然后我可以使用类似的方法检索它们:
valid_idx = idx(valid_comb(1),:);
for k = 1:n_pipes
pth{k}{valid_idx(k)} % do something with this
end
当我用一些样本数据对这两种方法进行基准测试时(pth
是 4x1
,pth
的 4 个元素是 2x1
,9x1
, 8x1
和 69x1
),我得到了以下结果:
>> benchmark
Elapsed time is 51.9075 seconds.
valid_comb = 7112
Elapsed time is 66.6693 seconds.
valid_comb = 7112
所以疯狂物理学家的方法快了大约 15 秒。
我也误解了mintersect
所做的,这不是我想要的。我想找到一个组合,其中两个或多个向量中不存在任何元素,所以我结束了编写 mintersect
:
function valid_comb = my_intersect(varargin)
% Returns true if a valid combination i.e. no combination of any 2 vectors
% have any elements in common
comb_idx = combnk(1:nargin,2);
[nr,nc] = size(comb_idx);
valid_comb = true;
k = 1;
% Use a while loop so that as soon as an intersection is found, the execution stops
while valid_comb && (k<=nr)
temp = intersect(varargin{comb_idx(k,1)},varargin{comb_idx(k,2)});
valid_comb = isempty(temp) && valid_comb;
k = k+1;
end
end
我相信这可以解决问题。为 k=1:n
和 kk=1:length(pth{k})
.
pth{k}{kk}
中所有可能的向量组合调用 mintersect
使用 eval
并稍微修改 sprintf
/compose
。请注意,通常使用 eval
是 very much discouraged。如果这是您的需要,可以添加更多评论。
% generate some data
n = 5;
pth = cell(1,n);
for k = 1:n
pth{k} = cell(1,randi([1 10]));
for kk = 1:numel(pth{k})
pth{k}{kk} = randi([1 100], randi([1 10]), 1);
end
end
% get all combs
str_to_eval = compose('1:length(pth{%i})', 1:numel(pth));
str_to_eval = strjoin(str_to_eval,',');
str_to_eval = sprintf('allcomb(%s)',str_to_eval);
% use eval to get all combinations for a given pth
all_combs = eval(str_to_eval);
% and make strings to eval in intersect
comp = num2cell(1:numel(pth));
comp = [comp ;repmat({'%i'}, 1, numel(pth))];
str_pattern = sprintf('pth{%i}{%s},', comp{:});
str_pattern = str_pattern(1:end-1); % get rid of last ,
strings_to_eval = cell(length(all_combs),1);
for k = 1:size(all_combs,1)
strings_to_eval{k} = sprintf(str_pattern, all_combs(k,:));
end
% and run eval on all those strings
result = cell(length(all_combs),1);
for k = 1:size(all_combs,1)
result{k} = eval(['mintersect(' strings_to_eval{k} ')']);
%fprintf(['mintersect(' strings_to_eval{k} ')\n']); % for debugging
end
对于随机生成的 pth
,代码会生成以下要评估的字符串(其中一些 pth{k}
只有一个单元格用于说明):
mintersect(pth{1}{1},pth{2}{1},pth{3}{1},pth{4}{1},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{1},pth{4}{2},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{1},pth{4}{3},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{2},pth{4}{1},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{2},pth{4}{2},pth{5}{1})
mintersect(pth{1}{1},pth{2}{1},pth{3}{2},pth{4}{3},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{1},pth{4}{1},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{1},pth{4}{2},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{1},pth{4}{3},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{2},pth{4}{1},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{2},pth{4}{2},pth{5}{1})
mintersect(pth{1}{2},pth{2}{1},pth{3}{2},pth{4}{3},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{1},pth{4}{1},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{1},pth{4}{2},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{1},pth{4}{3},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{2},pth{4}{1},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{2},pth{4}{2},pth{5}{1})
mintersect(pth{1}{3},pth{2}{1},pth{3}{2},pth{4}{3},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{1},pth{4}{1},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{1},pth{4}{2},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{1},pth{4}{3},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{2},pth{4}{1},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{2},pth{4}{2},pth{5}{1})
mintersect(pth{1}{4},pth{2}{1},pth{3}{2},pth{4}{3},pth{5}{1})
几个有助于构建解决方案的要点:
- This post shows you how to construct a Cartesian product between arbitrary arrays using
ndgrid
. cellfun
同时接受多个元胞数组,您可以使用它们来索引特定元素。- 您可以使用元胞数组从函数中捕获可变数量的参数,如图所示here。
所以让我们从最外层的数组中获取 ndgrid
的输入:
grids = cellfun(@(x) 1:numel(x), pth, 'UniformOutput', false);
现在您可以创建一个包含网格乘积的索引:
index = cell(1, numel(pth));
[index{:}] = ndgrid(grids{:});
你想把所有的网格都做成列向量,横向拼接起来。该矩阵的行将表示笛卡尔索引 select 每次迭代时 pth
的元素:
index = cellfun(@(x) x(:), index, 'UniformOutput', false);
index = cat(2, index{:});
如果将 index
的一行转换为元胞数组,则可以 运行 将其 pth
锁定为 select 正确的元素并调用 mintersect
在结果上。
for i = index'
indices = num2cell(i');
selection = cellfun(@(p, i) p{i}, pth, indices, 'UniformOutput', false);
mintersect(selection{:});
end
这是在假设pth
是一个行数组的情况下写的。如果不是这种情况,对于一般情况,您可以将循环的第一行更改为 indices = reshape(num2cell(i), size(pth));
,对于列情况,只需将其更改为 indices = num2cell(i);
。关键是 indices
的单元格必须与 pth
的形状相同才能同步迭代它。它已经生成为具有相同数量的元素。
正如 Madphysicist 所指出的,我误解了您的初始元胞数组的初始结构,但这一点是成立的。将未知数量的参数传递给函数的方法是通过 comma-separated-list generation, and your function needs to support it by being declared with varargin。更新了下面的示例。
创建一个辅助函数以从每个主单元格中收集随机子单元格:
% in getRandomVectors.m
function Out = getRandomVectors(C) % C: a double-jagged array, as described
N = length(C);
Out = cell(1, N);
for i = 1 : length(C)
Out{i} = C{i}{randi( length(C{i}) )};
end
end
然后假设你已经有一个 mintersect 函数定义如下:
% in mintersect.m
function Intersections = mintersect( varargin )
Vectors = varargin;
N = length( Vectors );
for i = 1 : N; for j = 1 : N
Intersections{i,j} = intersect( Vectors{i}, Vectors{j} );
end; end
end
然后这样称呼:
C = { { 1:5, 2:4, 3:7 }, {1:8}, {2:4, 3:9, 2:8} }; % example double-jagged array
In = getRandomVectors(C); % In is a cell array of randomly selected vectors
Out = mintersect( In{:} ); % Note the csl-generator syntax
PS。我注意到您对 mintersect 的定义与链接的定义不同。可能只是你没有很好地描述你想要什么,在这种情况下我的 mintersect 函数不是你想要的。我所做的是为提供的向量生成所有可能的交集。您链接到的那个产生一个单一的交叉点,这是所有提供的向量所共有的。使用最适合您的那个。不过,使用它的基本原理是相同的。
PS。从您的描述中也不完全清楚您所追求的是每个 n 的随机向量 k,还是所有 n 和 k 上可能向量的整个 space。上述解决方案是前者。如果您想要后者,请参阅 MadPhysicist 关于如何创建所有可能索引的笛卡尔积的解决方案。