有没有一种更有效的方法可以省略for循环实例

本文关键字:for 实例 循环 方法 一种 有效 有没有 | 更新日期: 2023-09-27 18:29:37

如果我有一个标准的for循环,有没有更有效的方法可以省略某些出现?

例如:

A:

        for (int i = 0; i < n; i++)
        {
            if (i != n / 2 && i != n / 3 && i != n / 4)
            {
                val += DoWork(i);
            }
            else
            {
                continue;
            }
        }

B:

        for (int i = 0; i < n / 4; i++)
        {
            val += DoWork(i); ;
        }
        for (int i = n / 4 + 1; i < n / 3; i++)
        {
            val += DoWork(i);
        }
        for (int i = n / 3 + 1; i < n / 2; i++)
        {
            val += DoWork(i);
        }
        for (int i = n / 2 + 1; i < n; i++)
        {
            val += DoWork(i);
        }

C:

        for (int i = 0; i < n; i++)
        {
            if (i == n / 2 || i == n / 3 || i == n / 4)
            {
                continue;
            }
            else
            {
                val += DoWork(i);
            }
        }

对于n = int.MaxValue,结果如下:A结果:57498毫秒。B结果:42204毫秒。C结果:57643毫秒。

根据@Churk的回答进行编辑。我添加了另一个测试用例方法D如下:

D:

        for (int i = 0; i < n; i =  (i != n / 2 -1 && i != n / 3 -1  && i != n / 4-1) ? i+1: i+2)
        {
            val += DoWork(i);
        }

得到以下结果:

A结果:56355毫秒。B结果:40612毫秒。C结果:56214毫秒。D结果:51810毫秒。

第2版:在预先计算循环外n/2 n/3和n/4的值后得到:

A结果:50873毫秒。B结果:39514毫秒。C结果:51167毫秒。D结果:42808毫秒。

D循环似乎再次接近B的"展开",A和C仍然需要更多的时间。

就每种方法之间的比较而言,这些都是我所期望的。

我的问题是,有没有更有效的方法来做到这一点?

有没有一种更有效的方法可以省略for循环实例

这在一定程度上取决于上下文。在许多情况下,一种可能性是作弊。因此,与其省略数字,不如将它们包括在内,然后从你不想要的数字中反转结果:

    for (int i = 0; i < n; i++) 
    { 
        val += DoWork(i); 
    } 
    val -= DoWork(i/2);
    val -= DoWork(i/3);
    val -= DoWork(i/4);

根据DoWork操作的成本,您从比较中节省的时间可能会超过两次计算某些数字的结果。

为循环嵌入次要条件

未测试:

for (int i = 0; i < n || (i != n / 2 && i != n / 3 && i != n / 4); i++)
  val += DoWork(i);
}

我认为只要其中一个条件成立,它就会继续

首先,只需在40-60秒内暂停几次,就可以大致了解DoWork中的时间比例。如果你甚至可以让你的循环花一点时间,你仍然需要花掉这部分时间。

现在看看你们的比较。每个人都在问一个问题。如果一个问题的答案几乎总是正确或几乎总是错误,那么这就是一个加速的机会。(所有的log(n)和n*log(n)算法都是通过使它们的决策点更像公平硬币来工作的。)

所以你可以看到为什么你的B更快。平均而言,它在每个循环中提出的问题更少。通过展开循环(提问次数更少),你可以让它变得更快。

(我知道,我知道,编译器可以展开循环。嗯,也许。自己做吧,你不必在上面下注。)

您可以计算循环外的值,然后跳过它们:

  int skip1 = n/2;
  int skip2 = n/3;
  int skip3 = n/4;
  for (int i = 0; i < n; i++)
  {
      if (i != skip1 && i != skip2 && i != skip3)
      {
         val += DoWork(i);
      }
  }

不过,不确定这是否能节省足够的毫秒。

更新:我刚刚注意到@surfen在评论中建议了这一点。

如果DoWork()不是瓶颈,那么它是一个足够小的方法,可以嵌入到循环中,因此您不需要单独花费时间的调用。如果DoWork()完成了大部分工作,那么您就是在浪费时间:)

您可以计数操作。。。

选项A:
n在for循环中检查i,然后对每个不是该值的i进行3次检查。。。所以只有4n个操作用于检查。

选项B:
您只是在间隔中循环,所以您正在执行n-3操作。

选项C:
与选项A相同,4n操作。

我不会麻烦用这样的修改来复杂化代码(因为很多人已经对你的问题发表了评论)

相反,您可能希望使用Pararell.ForEach在多个线程中同时运行DoWork。如果您的DoWork()做任何事情,这将对性能产生更大的影响。