MATLAB中循环的动态变化

MATLAB中循环的动态变化,matlab,for-loop,dynamic,Matlab,For Loop,Dynamic,设置循环时,我希望能够循环未知数量的参数 通过一个输入文件,用户可以设置任意多或任意少的循环参数,我希望无论设置了多少个参数,都可以在这些参数上循环。循环输入示例:(注意,我的输入也可以是字符串和数字组合的列表) 案例1: 重量45000:5000:75000 海拔10000 速度0.2:0.1:0.9 案例2: 重量30000 海拔1000:1000:10000 襟翼10、20 温度-10:1:10 列表的长度可以不同,可以包含0-15个变量。我知道解决方法,但使用一堆嵌套for循环来

设置循环时,我希望能够循环未知数量的参数

通过一个输入文件,用户可以设置任意多或任意少的循环参数,我希望无论设置了多少个参数,都可以在这些参数上循环。循环输入示例:(注意,我的输入也可以是字符串和数字组合的列表)

案例1:

  • 重量45000:5000:75000
  • 海拔10000
  • 速度0.2:0.1:0.9
案例2:

  • 重量30000
  • 海拔1000:1000:10000
  • 襟翼10、20
  • 温度-10:1:10
列表的长度可以不同,可以包含0-15个变量。我知道解决方法,但使用一堆嵌套for循环来实现这一点确实很麻烦。我正在寻找一种方法来建立一个方法,可能是递归,在这种方法中,代码将建立一个适当的for循环系统,而不管涉及的参数有多少,并且仍然跟踪这些变量。

递归

你没有对这个设定任何速度要求。这个答案很慢,使用内存很差,但这是这个想法最简单的可能实现方式。存在许多更好的实现,它们更复杂,使用更少的内存,并且速度更快。取决于你需要的内环有多紧

parms.weight = [45000:5000:75000];
parms.alt    = 10000;
parms.Speed  = 0.2:0.1:0.9;
然后,定义您的模拟器,如下所示:

function result = simulation(parms)
    fieldNames = fieldnames(parms)
    result = [];
    for ix = 1 : numel(fieldNames)
       if 1 < numel(parms.(fieldNames{ix})) 
           list = parms.(fieldNames{ix});
           for jx = 1 : numel(list)
                tmpParms = parms;
                tmpParms.(fieldNames{ix}) = list(jx);
                tmpResult = simulation(tmpParms);
                result = [result; tmpResult];
           end
           return;
        end
     end

     if 0 == numel(result)
         % Do the real simulation here.
     end
函数结果=模拟(parms)
字段名=字段名(parms)
结果=[];
对于ix=1:numel(字段名)
如果1
为所有参数执行嵌套的
-循环是一个简单的解决方案,常量参数只会导致该特定循环的一次迭代

您还可以使用创建一组多维数组,这些数组是
computeFunction(…)
每次调用的参数,如果您希望更改参数的数量,则可以迭代
ngrid的
输出,而不是为每个可能可变的参数构建嵌套for循环


但是,请注意,它可能会以性能为代价,因为这取决于您在哪个matlab版本中具体执行的操作,它可能不会从“简单”for loop版本的内置优化中获益。

这里有一个递归函数,它生成所有可能的参数集作为数组的列向量:

function idx=dynloop(varargin)
    if nargin==1
        idx=varargin{1};
    else
        idx=dynloop(varargin{2:end});
        idx=[repelem(varargin{1},size(idx,2));
             repmat(idx,[1,length(varargin{1})])];
    end
return
示例输出:循环
5:7
,然后
8
,然后
[2,3,5,7]

 >> idx = dynloop(5:7,8,[2,3,5,7])

idx =

     5     5     5     5     6     6     6     6     7     7     7     7
     8     8     8     8     8     8     8     8     8     8     8     8
     2     3     5     7     2     3     5     7     2     3     5     7
输出相当于
ndgrid
,(我认为形状更直观的情况除外)。(此外,一些初步基准测试表明,这可能略快于ndgrid
。?,有待确定)

主循环:现在只需使用一个循环来遍历
idx
的列。注:该解决方案概念与@Daniel在OP上的评论类似

Weight   = 45000:5000:75000;
Altitude = 10000;
Speed    = 0.2:0.1:0.9;

idx = dynloop(Weight,Altitude,Speed);

for ii=1:size(idx,2)
    params = idx(:,ii);

    %// do stuff with params! 
    %// ...

end
编辑:处理字符串和数字输入

我发布的原始递归函数的一个简单修改允许它返回索引,而不是实际元素,因此字符串单元格很好:

function idx=dynloop(varargin)
    if nargin==1
        idx=1:length(varargin{1});
    else
        idx=dynloop(varargin{2:end});
        idx=[repelem(1:length(varargin{1}),size(idx,2));
             repmat(idx,[1,length(varargin{1})]);];
    end
return
总之,你的函数大概是这样运行的:

function yourMainFcn(varargin)
    idx=dynloop(varargin{:});
    for ii=1:size(idx,2)
        params = cellfun(@(x,k) numORcell(x,k),...
                 varargin,num2cell(idx(:,ii).'),... %//'
                 'UniformOutput',0);

        %// do stuff with params! 
        %// ...
        disp(params)

    end
end
其中函数
numORcell
正确解析数值与单元格数据:

function y=numORcell(x,k)
    if iscell(x)
        y=x{k};
    else
        y=x(k);
    end
end
字符串示例:

Weight   = 45000:5000:75000;
Altitude = 10000;
Speed    = 0.2:0.1:0.9;
Names    = {'Foo','Bar'};

>> yourMainFcn(Names,Altitude,Weight)
'Foo'    [10000]    [45000]

'Foo'    [10000]    [50000]

'Foo'    [10000]    [55000]

'Foo'    [10000]    [60000]

'Foo'    [10000]    [65000]

'Foo'    [10000]    [70000]

'Foo'    [10000]    [75000]

'Bar'    [10000]    [45000]

'Bar'    [10000]    [50000]

'Bar'    [10000]    [55000]

'Bar'    [10000]    [60000]

'Bar'    [10000]    [65000]

'Bar'    [10000]    [70000]

'Bar'    [10000]    [75000]
或者:

>> yourMainFcn(Names,Names,Speed,Names)
'Foo'    'Foo'    [0.2]    'Foo'

'Foo'    'Foo'    [0.2]    'Bar'

'Foo'    'Foo'    [0.3]    'Foo'

'Foo'    'Foo'    [0.3]    'Bar'

'Foo'    'Foo'    [0.4]    'Foo'

...

'Foo'    'Foo'    [0.9]    'Bar'

'Foo'    'Bar'    [0.2]    'Foo'

...

'Foo'    'Bar'    [0.9]    'Bar'

'Bar'    'Foo'    [0.2]    'Foo'

...

'Bar'    'Foo'    [0.9]    'Bar'

...

'Bar'    'Bar'    [0.8]    'Foo'

'Bar'    'Bar'    [0.8]    'Bar'

'Bar'    'Bar'    [0.9]    'Foo'

'Bar'    'Bar'    [0.9]    'Bar'

留给读者的练习:如果将所有索引存储在
idx
中是一个内存问题,那么您一定是在做一些相当繁重的循环。尽管如此,您可以创建一个函数,从当前索引集确定下一个词典索引集,这意味着您只需存储一组索引,并在每个循环结束时迭代

代码生成解决方案

你已经有很多很好的解决方案了。我将在这里抛出一个涉及代码生成的代码。MATLAB在这类工具方面并没有太多的功能,但您可以通过几个循环和
fprintf
来伪造它。以下是我的代码生成脚本:

s = struct() ;
s.weight = 45000:5000:75000 ;
s.altitude = 10000 ;
s.engine = {'ge','rolsroyce'} ;

h = fopen('thisrun.m','w+') ;

mydisp = @(varargin)disp(transpose(varargin(:))) ; % dummy function body

vars = fields(s) ;
nv = numel(vars) ;

for ii = 1:nv
    if isnumeric(s.(vars{ii}))
        lb = '(' ;
        rb = ')' ;
    else
        lb = '{' ;
        rb = '}' ;
    end
    fprintf(h,'for i%g = 1:numel(s.(vars{%g})) \n',ii,ii) ;
    fprintf(h,'i%gval = s.(vars{%g})%si%g%s ; \n',ii,ii,lb,ii,rb) ;
end

fprintf(h,'mydisp(') ;
for ii = 1:numel(vars)
    fprintf(h,'i%gval',ii) ;
    if ii<nv
        fprintf(h,',') ;
    end
end
fprintf(h,') ; \n') ;

for ii = 1:nv
    fprintf(h,'end \n') ;
end

fclose(h) ;
run thisrun.m
运行生成代码的结果:

>>
    [45000]    [10000]    'ge'

    [45000]    [10000]    'rolsroyce'

    [50000]    [10000]    'ge'

    [50000]    [10000]    'rolsroyce'

    [55000]    [10000]    'ge'

    [55000]    [10000]    'rolsroyce'

    [60000]    [10000]    'ge'

    [60000]    [10000]    'rolsroyce'

    [65000]    [10000]    'ge'

    [65000]    [10000]    'rolsroyce'

    [70000]    [10000]    'ge'

    [70000]    [10000]    'rolsroyce'

    [75000]    [10000]    'ge'

    [75000]    [10000]    'rolsroyce'

代码生成需要时间,但如果需要多次运行该文件,它可能仍然是一个有效的解决方案

你在找吗?我会解析输入文件并将结果作为参数传递给函数。不需要进行任何解析。只需编写代码以使用最大循环数(例如,循环过载、高度、速度、温度、襟翼等)。在其中一个参数为常量的情况下,它实际上不会循环,只需设置一次索引并移动到下一个循环。@transversality condition我适当地解析数据,并将其作为结构发送到循环中。我当前的设置基本上就是您所描述的,但我正在寻找更优雅、更灵活的设置-如果添加了一个我没有预见到的新变量,该怎么办?我希望代码能够无缝地处理这个问题。@excaza我完全按照你说的做,但这是解析阶段。我希望能够在将任意数量的输入发送到函数进行计算之前对其进行适当的解析。我经常遇到这个问题,但出于某种原因,您的问题促使我以一种全新的方式思考这个问题。很好的问题。你的代码有问题,我得到了很多组合。只要尝试一下struct('a',[12],'b',[3],'c',[4,5],'e',[6,7])。我希望有6个电话
>>
    [45000]    [10000]    'ge'

    [45000]    [10000]    'rolsroyce'

    [50000]    [10000]    'ge'

    [50000]    [10000]    'rolsroyce'

    [55000]    [10000]    'ge'

    [55000]    [10000]    'rolsroyce'

    [60000]    [10000]    'ge'

    [60000]    [10000]    'rolsroyce'

    [65000]    [10000]    'ge'

    [65000]    [10000]    'rolsroyce'

    [70000]    [10000]    'ge'

    [70000]    [10000]    'rolsroyce'

    [75000]    [10000]    'ge'

    [75000]    [10000]    'rolsroyce'