zoukankan      html  css  js  c++  java
  • 数列求和的算法和优化的故事 Anthony

    问题引出:
    某位朋友写到:

    21日那天我被安排在4:30面试,由一位技术人员单独给我面试,在问了一些简单的问题之后
    他给我出了一道编程题目,题目是这样的: 
    (由于具体面试的题目比较烦琐,我将其核心思想提取出来分解成……)

    1) 写一个函数计算当参数为n(n很大)时的值 1-2+3-4+5-6+7......+n 
    哼,我的心里冷笑一声!没想到这么简单,我有点紧张的心情顿时放松起来!于是很快我给出我的解法: 
    long fn(long n) 

      long temp=0; 
      int i,flag=1; 
      if(n<=0) 
      { 
        printf("error: n must > 0); 
        exit(1); 
      } 
      for(i=1;i<=n;i++) 
      { 
        temp=temp+flag*i; 
        flag=(-1)*flag; 
      } 
      return temp; 
    }

    搞定!当我用期待的目光看着面试官的时候,他微笑着跟我说,执行结果肯定是没有问题!
    但当n很大的时候我这个程序执行效率很低,在嵌入式系统的开发中,程序的运行效率很重
    要 ,能让CPU少执行一条指令都是好的,他让我看看这个程序还有什么可以修改的地方,把程
    序 优化一下!听了这些话,我的心情当时变的有点沉重,没想到他的要求很严格,之后我对
    程序进行了严格的分析,给出了改进了的方案! 

    long fn( long n) 

      long temp=0; 
      int j = 1,i = 1,flag = 1; 
      if(n <= 0) 
      {  
        printf("error: n must > 0); 
        exit(1); 
      } 
      while(j <= n) 
      { 
        temp = temp + i; 
        i = -i; 
        i > 0 ? i++ : i--; 
        j++; 
      } 
      return temp; 

    虽然我不敢保证我这个算法是最优的,但是比起上一个程序,我将所有涉及到乘法指令的语
    句改为执行加法指令,既达到要题目的要求而且运算时间上缩短了很多!而代价仅仅是增加了
    一个整型变量!但是我现在的信心已经受了一点打击,我将信将疑的看者面试官,他还是微笑
    着跟我说:“不错,这个程序确实在效率上有的很大的提高!”我心里一阵暗喜!但他接着说
    这个程序仍然不能达到他的要求,要我给出更优的方案!天啊!还有优化!我当时真的有点崩
    溃了,想了一会后,我请求他给出他的方案!然后他很爽快的给出了他的程序! 
    long fn(long n) 

      if(n<=0) 
      { 
        printf("error: n must > 0); 
        exit(1); 
      } 
      if(0 == n % 2) 
        return (n/2)*(-1); 
      else 
        return (n/2)*(-1)+n; 

    搞笑,当时我目瞪口呆,没想到他是这个意思,这么简单的代码我真的不会写吗,但是我为
    什么没有往那方面上想呢!他说的没有错,在n很大很大的时候这三个程序运行时间的差别简
    直是天壤之别!当我刚想开口说点什么的时候,他却先开口了:“不要认为CPU运算速度快就
    把所有的问题都推给它去做,程序员应该将代码优化再优化,我们自己能做的决不要让CPU做
    ,因为CPU是为用户服务的,不是为我们程序员服务的!”多么精辟的语言,我已经不想再说

    -------------------------------------------------------------------------------
    就题而论,我觉得程序还是第一种好.优化只应在必要时才做.题目中并没有要求做出最优化的
    解法(实际上最后也不是最优的).完全没有必要写得如此费神.

    程序最重要的是可读性,能够被人理解.而不是机器.

    光说不练是假把式,练一把吧.

    #include <assert.h>
    #include <stdio.h>

    int fn_readable(int n)
    {
       if(n < 0)
      {
        return 0;
      }
      int sum= 0;

      //奇位数相加
      for(int i = 1; i < n + 1; i += 2)
      {
        sum += i;
      }
      //偶位数相减
      for(int i = 2; i < n + 1; i += 2)
      {
        sum -= i;
      }
      return sum;
    }

    int fn_best(int n)
    {
      assert(n > 0);
      return (n & 0x1) ? (n >> 1) + 1 : - ( n >> 1);
    }

    int main()
    {
      printf("sum is %d, %d n", fn_readable(100001), fn_best(100001));
      printf("sum is %d, %d n", fn_readable(100002), fn_best(100002));
      printf("sum is %d, %d n", fn_readable(100003), fn_best(100003));
      return 0;
    }

    我没有实测,fn_best有多快.不过它的汇编代码是这样的.
    只有最多只有四条指令.应该是比较快吧.

    test al, 1
    je SHORT $L648
    sar eax, 1
    inc eax
    $L648:
    sar eax, 1
    neg eax

    简单说一下思路
    (1-2)+(3-4)...-1^(n)*n 这个公式大家都会
    n为偶数时, fn = -1 * n/2
    n为奇数时, fn = -1 * (n-1)/2 + n
    这就是面试官的解.
    没完呢.

    n为偶数时 设x = n/2, 所以fn =  -1 * x = -x
    n为奇数时 设x = (n-1)/2,所以n = 2x + 1
    fn =  -1 * ((2x + 1) - 1)/2 + (2x + 1) = x + 1

    n/2 和 (n-1)/2 与n >> 2是等价的
    奇偶的判断最简单的方法是看最低位是否为一,即n & 0x1
    所以结果就是
    if(n & 0x1) 奇数
      (n >> 2) + 1
    else        偶数
      -(n >> 2)

  • 相关阅读:
    Soap 教程
    MAC mysql install
    PHP date
    MAC 终端terminal颜色
    MAC 终端颜色设置
    MAC brew软件安装
    PHP iconv函数
    Java----前端验证之验证码额实现
    Java---Ajax在Struts2框架的应用实例
    Java基础—标识符及命名规范
  • 原文地址:https://www.cnblogs.com/ahuangliang/p/5309288.html
Copyright © 2011-2022 走看看