zoukankan      html  css  js  c++  java
  • n = 5x+2y+z,程序优化:unroll loop

    求 n = 5x + 2y + z的全部非负整数解.例如n = 5时,有4组解:(0, 0, 5)、(0, 1, 3)、(0, 2, 1)、(1, 0, 0).

    1.最普通的解法,三层循环遍历:

    int SolutionLoop(int n) {
      int x = n / 5;
      int y = n / 2;
      int z = n;
      int res = 0;
      for (int i = 0; i <= x; i++) {
        for (int j = 0; j <= y; j++) {
          for (int k = 0; k <= z; k++) {
            if (n == 5*i + 2*j + k) res++;
          }
        }
      }
      return res;
    }

    优化1:unroll 3rd loop(展开最里层的循环)

    Replace
    for (int k = 0; k <= z; k++) {
      if (n == 5*i + 2*j + k) res++;
    }
    by the following:
    if (n - 5*i - 2*j >= 0) res++;

    优化2:remove if(移除if判断)

    Replace
    for (int j = 0; j <= y; j++) {
      if (n - 5*i - 2*j >= 0) res++;
    }
    by the following:
    for (int j = 0; j <= (n - 5*i) / 2; j++) {
      res++;
    }

    优化3:unroll  second loop(展开第二层循环)

    step 3 : unroll 2nd loop
    Replace
    for (int j = 0; j <= (n - 5*i) / 2; j++) {
      res++;
    }
    by the following:
    res += (n - 5*i) / 2 + 1;

    至此已经去掉了两层循环了.代码变成了如下所示:

    int unrollLoop(int n) {
      int x = n / 5;
      int res = 0;
      for (int i = 0; i <= x; i++) {
        res += (n - 5*i) / 2 + 1;
      }
      return res;
    }

    那么怎么展开最后一层循环呢?

    可以看出:这个x的循环相当于公式:

    根据等差数列求和公式:可以轻松求出结果:

    如果按照这个公式写出代码,速度自然是最快的,但是如果n=100时,你发现正确结果是541,但是用上面公式算出来是546,那结果到底差在哪里呢?

     举个例子:如果计算7/2*6按顺序计算结果是18,但是如果打乱顺序7*6/2,那么结果就是21,所以用上面的公式算出来的数偏大的原因就是:把本该损失的精度给加进去了,因为原求和公式中有一个除以2,如果(n-5i)时偶数,那么没有任何问题,但是如果(n-5i)是奇数,就不能随意的调整顺序了.比如:7/2 + 5/2 = 5,但是(7+5)/2 = 6,本来每部分会损失1/2的精度,但是因为调整了计算顺序导致结果偏大了.

    知道了产生问题的原因,那么就很好解决了,只需要按照奇数偶数分开算就好了。将(n-5i)为奇数的划分一组,偶数的划分一组.但是对于任意的n,n-5i奇偶性都不定,所以按照n的奇偶性分两种情况:

    1)n为奇数时:

      ①当i为奇数时,n-5i为偶数,(n-5i)/2没有精度损失

      ②当i为偶数时n-5i为基数,那么(n-5i)/2,将转换为(n-5i-1)/2

    2)n为偶数时:

      ①当i为奇数时,n-5i为奇数

      ②当i为偶数时n-5i为偶数

    优化4:展开最后一层循环:

    int unrollAllLoop(int n) {
      int res = 0;
      int even = n / 10 + 1;
      int odd = n / 5 + 1 - even;
      if (n % 2) {
        //res +=  (n - 1 + n - (2*even - 2)*5) / 2 * even / 2;
        res += (n + 4 - 5*even) * even / 2;
        //res +=  (n - 5 + n - (2*odd - 1)*5) / 2 * odd / 2;
        res += (n - 5*odd) * odd / 2;
      } else {
        //res += ( n/2 + (n - (2*even - 2)*5)/2) * even / 2;
        res += (n + 5 - 5*even) * even / 2;
        //res += ( (n - 6)/2 + (n - 1 - (2*odd - 1)*5)/2) * odd / 2;
        res += (n - 1 - 5*odd) * odd / 2;
      }
      res += n / 5 + 1;
      return res;
    }

    代码即为上面按照奇偶性区分的实现,其中even是i为偶数的个数,odd是i为奇数的个数,等价于等差数列中的项数.

    然后按照n的奇偶性分组,利用等差数列求和公式,即可求得两组和,然后相加及得到结果。

    注意:因为已经确保求和时n-5i (或者n-5i-1)必为偶数,因此第一个除以2可以直接约掉,但是最后的除以2要始终保持在最后,原因之前解释过了.

  • 相关阅读:
    bzoj2428 [HAOI2006]均分数据 模拟退火
    Jersey入门三:创建一个JavaEE的Web项目
    Jersey入门二:运行项目
    Jersey入门一:从Maven Archetype创建jersey项目
    Bootstrap进阶七:LESS语法详解
    Bootstrap进阶六:动态样式语言LESS简介
    Bootstrap进阶五:Web开发中很实用的交互效果积累
    Bootstrap进阶四:jQuery插件详解
    Bootstrap进阶三:jQuery插件概览
    Bootstrap进阶二:基本组件
  • 原文地址:https://www.cnblogs.com/deepllz/p/11490167.html
Copyright © 2011-2022 走看看