【问题标题】:Calculating the approximate run time of a for loop计算 for 循环的大致运行时间
【发布时间】:2017-12-15 15:06:39
【问题描述】:

我的 C# Windows 窗体应用程序中有一段代码如下所示:

List<string> RESULT_LIST = new List<string>();
int[] arr = My_LIST.ToArray();

string s = "";

Stopwatch sw = new Stopwatch();
sw.Start();

for (int i = 0; i < arr.Length; i++)
{
  int counter = i;
  for (int j = 1; j <= arr.Length; j++)
  {
    counter++;
    if (counter == arr.Length)
    {
      counter = 0;
    }
    s += arr[counter].ToString();
    RESULT_LIST.Add(s);
  }
  s = "";
}
sw.Stop();
TimeSpan ts = sw.Elapsed;
string elapsedTime = String.Format("{0:00}", ts.TotalMilliseconds * 1000);
MessageBox.Show(elapsedTime);

我使用此代码来获取我的列表中数字的任意组合。我对 My_LIST 的行为就像 recursive 一样。下图非常清楚地展示了我的目的:

我需要做的就是:

制作一个公式来计算这两者的大致运行时间 嵌套的 for 循环猜测任何长度的运行时间并帮助 用户知道他/她必须等待的大概时间。

我使用了这样的 C# 秒表Stopwatch sw = new Stopwatch(); 显示运行时间,下面是结果(注意,为了减少出错的机会,我重复了计算三每个长度的时间和数字分别表示第一次、第二次和第三次尝试的时间,以 纳秒 为单位。):

  1. arr.Length = 400;127838 - 107251 - 100898
  2. arr.Length = 800;751282 - 750574 - 739869
  3. arr.Length = 1200;2320517 - 2136107 - 2146099
  4. arr.Length = 2000;8502631 - 7554743 - 7635173

请注意,My_LIST 中只有 一位数 数字来计时 将数字添加到列表中大致相等。

如何找出arr.Length 与运行时间之间的关系?

【问题讨论】:

  • 我只知道你的代码复杂度是 O(N^2),但是运行时间取决于你的系统资源。最后我认为应该可以找出这种关系。
  • 由于您的嵌套循环都使用相同的计数 (arr.Length),因此时间将与该数字的平方成正比。
  • @DavidG 我知道,但想象一下arr.Length 是 2500 运行这段代码需要多少纳秒?
  • 您是否注意到第一次尝试总是比其他两次要长?这个模式成立吗?多试几次,收集更多数据。如果是这样,那么你有理由相信第一次尝试是非典型的。现在你必须决定我在这里实际测量的是什么。如果您的用户可能会运行一次例程,那么您想要测量首次运行时间。如果他们调用它数千次,那么您可能希望丢弃第一次运行时间。那么它是什么?
  • @fariba.yazdani 是的,我知道,但我不能用它来猜测时间。

标签: c# for-loop time


【解决方案1】:

首先,假设您检查了算法并注意到它在数组长度上似乎是二次方的。这向我们表明,运行所花费的时间应该是形式的函数

t = A + B n + C n2

您已经通过使用不同的 n 值和测量 t 多次运行代码来收集一些观察结果。这是一个很好的方法。

现在的问题是:A、B 和 C 的最佳值是什么,以使它们与您的观察结果紧密匹配?

这个问题可以通过多种方式解决;我建议您从回归的最小二乘法开始,看看您是否能得到好的结果。这里有一个页面:

www.efunda.com/math/leastsquares/lstsqr2dcurve.cfm


更新:我刚刚再次查看了您的算法并意识到它是三次的,因为您在内循环中有一个二次字符串 concat。因此,这种技术可能效果不佳。我建议您使用StringBuilder 使您的算法成为二次方。


现在,假设您事先知道问题是二次的。那么你将如何确定公式?一个好的开始是在对数刻度纸上绘制你的点;如果它们大致形成一条直线,那么直线的斜率可以为您提供有关多项式幂的线索。如果它们没有形成一条直线——那么,当你走到桥上时,就穿过那座桥。

【讨论】:

  • 您的意思是每次将一个字符添加到我的字符串末尾时,它就像一个 for 循环来查找我的字符串中的最后一个字符?你能解释一下我如何计算A、B和C吗?我对回归不太熟悉。
  • @Naser.Sadeghi:C# 中的字符串连接分配了一个新字符串。所以如果你有“A”+“B”,它会分配“AB”。然后,如果您添加“C”,它会分配“ABC”。然后,如果您添加“D”,它会分配“ABCD”。所以我们正在分配大小为 2 + 3 + 4 + 5 + 6 + .... 的字符串,它会很快变大。 StringBuilder 没有这个问题。
  • @Naser.Sadeghi:这不是一个教程网站。如果您在编写回归代码时遇到困难,那么做一些研究,或者观看一些视频或找导师,或者参加一些课程,或者使用 Excel 之类的工具或内置回归的工具。如果您有关于代码的具体问题,请提出一个新问题并提出。
【解决方案2】:

你会在这里做一些数学运算。

由于总运行次数正好是 n^2,不是 O(n^2),而是正好 n^2 次。

那么你可以做的是为处理的项目数量保留一个计数器变量,并使用数学来找出一个估计值

int numItemProcessed;
int timeElapsed;//read from stop watch
int totalItems = n * n;

int remainingEstimate = ((float) totalItems - numItemProcessed) / numItemProcessed) * timeElapsed

【讨论】:

  • 我一开始也是这么想的,但是有一个令人困惑的问题。字符串连接是二次的,所以我们在内循环中有二次运算。目前的算法是立方的。
  • @EricLippert 好点。使用字符串生成器应该可以解决这个问题
【解决方案3】:

不要假设算法的时间复杂度一定是 N^2。

取数字的平均值,并在对数图上绘制最佳拟合,然后测量梯度。这将使您了解多项式中的最大项。 (参见维基百科log-log plot

一旦你有了这个,你可以做一个最小二乘回归来计算出正确顺序的多项式的系数。这将允许从数据中估算出一个看不见的问题所花费的时间。

注意:正如 Eric Lippert 所说,这取决于您要测量的内容 - 根据您的用例,平均可能不合适 - 第一次运行时间可能更正确。

此方法适用于任何多项式算法。它还会告诉您算法是否为多项式(非多项式运行时间不会在对数图上给出直线)。

【讨论】:

  • 如何计算 A、B 和在这个回归中:A+ B*n + C*(n^2)?
猜你喜欢
  • 1970-01-01
  • 2011-06-27
  • 1970-01-01
  • 2021-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-17
相关资源
最近更新 更多