【问题标题】:Is there a more elegant way to write these loops?有没有更优雅的方式来编写这些循环?
【发布时间】:2016-02-03 05:46:12
【问题描述】:

我有一个需要少量参数才能运行的脚本。我有兴趣在参数更改时探索结果,因此我在顶部定义了几个scan 数组,将整个代码包装在多个for loops 中,并将参数值设置为当前扫描值。

这容易出错且不优雅。更改代码的过程是:1)重置顶部的scan变量,2)注释掉例如b = scan2(j2)和3)取消注释b=b0

有什么更好的方法可以将变量设置为数组,然后为所有此类组合运行代码?现在我的代码示例:

close all
clear all

%scan1 = linspace(1,4,10);
scan1 = 0;
scan2 = linspace(0,1,10);
scan3 = linspace(-1,0,10);

for j3 = 1:length(scan3)
  for j2 = 1:length(scan2)
    for j1 = 1:length(scan1)

      a = a0;
      %b = scan2(j2);
      b = b0;
      %c = c0;
      c = scan3(j3);
      d = scan2(j2);

      %(CODE BLOCK THAT DEPENDS ON variables a,b,c,d...)

    end

  end

end

【问题讨论】:

  • 目前没有一般规则。代码的向量化很大程度上取决于循环中到底发生了什么,例如矩阵乘法、求和或其他。
  • 嗯。我实际上并没有试图对此进行矢量化,我只是想要更好的组织。例如,我可以为顶部的每个参数创建一个 1xN 数组,避免注释掉业务,但我有点担心内存使用......而且当我经常只循环时,似乎没有必要使用三重 for 循环1 次扫描。
  • 我尝试理解您的代码,是否有特定原因枚举与您的参数不同的 scan 变量?您不能将它们重命名为 scana scanb 等并将迭代器重命名为 ja jb 等吗?
  • @Daniel 这是一种可能性,但是我需要 n 个嵌套循环,每个 a,b,c...

标签: matlab coding-style


【解决方案1】:

基于this idea 使用一个for循环来模拟多个循环,我尝试将其调整为适合您的情况。在实现良好的内存效率和可用性的同时,该解决方案比使用单独的 for 循环要慢。

%define your parameters
p.a = 1;
p.b = linspace(1,4,4);
p.c = linspace(11,15,5);
p.d = linspace(101,104,4);
p.e = 5;
iterations=structfun(@numel,p);
iterator=cell(1,numel(iterations));
for jx = 1:prod(iterations)
    [iterator{:}]=ind2sub(iterations(:).',jx);%.'
    %This line uses itertor to extract the corresponding elemets of p and creates a struct which only contains scalars.
    q=cell2struct(cellfun(@(a,b)(a(b)),struct2cell(p),iterator(:),'uniform',false),fieldnames(p));
    %__ (CODE THAT DEPENDS ON q.a to q.e here) __

end

对于我测试的场景,它在每次迭代中增加了低于0.0002s 的计算开销,总计0.0002.*prod(iterations)s。

【讨论】:

  • 非常酷!我认为这会因为structure而变慢?就我而言,包装循环的速度不是问题:我的代码内部需要更多时间,所以这是一个选择。
  • 元胞数组、结构体、调用 ind2sub,与每次迭代大约 0.002 秒的纯 for 循环相比,所有这三件事都会增加开销。
  • 你的意思是ind2sub(iterations(:).',j),而不是循环第一行的ind2sub(iterations(:).',3)
【解决方案2】:

一种方法是使用 ndgrid 创建一个包含所有参数组合的向量。对于足够大的参数扫描,这可能会成为内存问题,但至少要干净得多,只需要一个循环并且在代码后面不需要重新分配:

a0vec = 1;
b0vec = linspace(1,4,4);
c0vec = linspace(11,15,5);
d0vec = linspace(101,104,4);
e0vec = 5;

[a0s,b0s,c0s,d0s,e0s] = ndgrid(a0vec,b0vec,c0vec,d0vec,e0vec);    
N = numel(a0s);

for j = 1:N

    a0 = a0s(j);
    b0 = b0s(j);
    c0 = c0s(j);
    d0 = d0s(j);
    e0 = e0s(j);

    %__ (CODE THAT DEPENDS ON a0 - e0 here) __

end

仍然希望看到您的建议!

【讨论】:

  • 既然您的个人资料显示python:如何使用该语言和生成器,以避免将所有这些值存储在内存中?:) 相关说明:循环中发生了哪些类型的事情?一些复杂的东西,或者可能是可矢量化的?
  • @AndrasDeak 好建议!快速浏览一下,matlab 没有类似生成器的语法。至于内存,我只是运行了数字,100x100x100 网格 x 20 双打占用不到 0.2 gig,所以这对我来说是可行的。代码大约 1000 行,这确实是一个良好的编码习惯问题!
  • 确实,我敢肯定 MATLAB 不支持生成器之类的东西。如果您的参数空间总是那么小,那么您的答案似乎很合理。
猜你喜欢
  • 1970-01-01
  • 2019-01-12
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多