2016-02-01 16 views
8

for-loopsを設定するときに、未知数のパラメータをループすることができます。MATLABのfor-loopsを動的に変更

入力ファイルを使用して、ユーザーは必要な数のループパラメーターを設定できます。設定したパラメーターの数に関係なく、ループすることができます。ループの入力例:(私の入力は、同様の文字列と番号の組み合わせのリストであることに注意してください)

ケース1:

  • 重量45000:5000:75000
  • 高度10000
  • スピード0.2:0.1:0.9

ケース2:

  • 重30000
  • 標高:1000:1000 10000
  • フラップ10、20
  • 温度-10:1:10

リストの長さが異なることができ、そして含むことができどこでも0-15変数。私は回避策を知っていますが、これを行うためにネストされたfor-loopsを使うのは面倒です。私は、関連するパラメータの数に関係なく、コードが適切なfor-loopシステムをセットアップし、それらの変数を追跡し続ける、再帰を使用してメソッドを設定する方法を探しています。

+2

あなたは[これと同じような](http://stackoverflow.com/a/34840222/2732801)をお探しですか? – Daniel

+2

私は入力ファイルを解析し、その結果を関数の引数として渡します。 – excaza

+0

構文解析を行う必要はありません。ループの最大数(たとえば、ループオーバーウェイト、高度、速度、温度、フラップなど)を使用するコードを記述するだけです。パラメータの1つが定数である場合、ループは効果的に行われず、インデックスを一度設定して次のループに移動します。 –

答えて

1

再帰。

これには速度要件はありません。この回答は遅く、メモリが不十分ですが、この考え方の最も単純な実装です。より複雑で、メモリを大幅に節約し、大幅に高速化された、より多くの実装が存在します。その後

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 
+2

私はしばしばこの問題に直面しましたが、何らかの理由であなたの質問が私にそれについての全く新しい考え方に拍車をかけました。優れた質問。 – John

+2

コードに何か問題があります。 struct( 'a'、[1 2]、 'b'、[3]、 'c'、[4,5]、 'e'、[6,7]) 'を試してみてください。シミュレーションの6回の呼び出しが予想されますが、より多くの呼び出しを生成します。 – Daniel

+1

@ダニエル、ありがとう。あなたは間違いなくバグがあります。一定。 btw。あなたのインプットでは、最終的な答えに8つの結果があると思います。注:シミュレーションへの個別呼び出しの数は8より大きくなります。 – John

1

すべてのパラメータのfor -loopsを入れ子に行うことは簡単な解決策であります定数パラメータは、その特定のループに対してただ1回の繰り返しを行うだけです。

また、あなたのcomputeFunction(...)の各呼び出しのためのパラメータである多次元配列のセットを作成するためにndgridを使用して、あなたが予想される場合、各潜在変数パラメータのループのネストされた建物のngrid's出力上の代わりに反復可能性パラメータの数を変更します。

しかし、matlabのどのバージョンで正確に何をするかに応じて、 '単純な' forループバージョンが得られる組み込み最適化の恩恵を受けない可能性があるため、パフォーマンスコストがかかる可能性があることに注意してください。ここ

+1

パフォーマンスに関して非常に有用な点があります。入力いただきありがとうございます! – arthream

+1

また、私が間違っている場合は私を修正してください、それはndgridが私の状況に役立たない数値のみに限られているようです。私は文字列入力のための索引付き検索を構築することができましたが、それはコードに追加したいと思うほど複雑です。 – arthream

+1

@arthream:このソリューションの主な欠点は、大量のメモリを使用する可能性のある完全なパラメータ空間(各組み合わせ)で行列を構築することです。 – Daniel

1

は、アレイの列ベクトルなどのパラメータのすべての可能なセットを生成する再帰関数は次のとおり

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の列を反復処理する一つのループを使用します。このソリューションのコンセプトは、@ダニエル氏がOPでコメントしたように、this solutionに似ています。

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 

EDIT:文字列と数値入力の両方を処理するための

私は掲載元再帰関数の簡単なMODではなく、実際の要素よりも、それはインデックスを返すことができますので、文字列のセルは大丈夫です:

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 
0123機能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にすべてのインデックスを格納するのがメモリの問題である場合、かなり重いループを実行しているに違いありません。それにもかかわらず、現在のインデックスセットから次の辞書式インデックスセットを決定する関数を作成することができます。つまり、インデックスの1つのセットを格納し、各ループの最後に反復するだけです。

+0

興味深い。私はまた、入力パラメータに文字列と英数字の値を含めることができるように指定しておく必要があります。また、ndgridは文字列入力では最適な方法ではありません。しかし、鍵は複雑です。 将来のユーザーのためにコードを理解できるようにすることが重要です。 – arthream

+0

@arthream私の解決策は、文字列入力を処理するために編集されています。文字列オプションを自分でインデックスする必要はありません。私はそれが助けて欲しい – Geoff

+0

ありがとうたくさん!この編集はうまくいくはずですが、MATLABバージョン2014bを実行しています(2015aにリピートメントが導入されています)。そして、少なくとも私のコードは2012aと少なくとも同じくらい互換性があると思われます。これは間違いなく洞察力があり、私はそれを念頭に置いておきます! – arthream

2

コード生成ソリューション

さて、あなたはすでにかなり良い解決策の数を持っています。私はちょうどそこにコード生成を伴う1つを投げるでしょう。 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 

生成されたコード(thisrun.m):ランニングの

for i1 = 1:numel(s.(vars{1})) 
    i1val = s.(vars{1})(i1) ; 
    for i2 = 1:numel(s.(vars{2})) 
     i2val = s.(vars{2})(i2) ; 
     for i3 = 1:numel(s.(vars{3})) 
      i3val = s.(vars{3}){i3} ; 
      mydisp(i1val,i2val,i3val) ; 
     end 
    end 
end 

結果は、コードを生成:

>> 
    [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' 

コード生成には時間がかかりますが、あなたがする必要がある場合ファイルを何度も実行すると、効率的なソリューションになる可能性があります。

関連する問題