2007年3月的归档

  前些日子,部门开会时讨论了一下技术人员如何与业务为员相处的问题。其实自己也一直在思索着这方面的问题,想想这些年来自己做为技术人员与业务人员打交道的过程,真可以说是感触很深了。   搜狐的朋友,曾向我埋怨说,最讨厌什么都不懂的业务人员,来指导技术,开会一开下午,一点效果都没有。他说这话时的心情我可以理解,因为我也遇到过。在稍大一点的公司,技术和业务人员总是分的很泾渭分明,各施其责,但技术人员一般是专业性较强,而业务人员,可能很多都是由销售转过来,或是相关人文,经济专业的知识面广,但并不如技术那样专一某方面,所以难免会有些磨合上的问题。   这些只是表面现象,但究其实质:是在需求描述和需求获取上,技术人员与业务人员并没有找到一个好的切合点。   我想起半年前,百度的一位朋友,曾跟我说过一句,他说:好的技术不应该只顾满足业务提出的需求,还应能对给出的需求提出自己的合理的建议。   关于这一点,我直到现在,才算真正理解了。   在技术工程师的心目中,最理想的业务人员应该是了解技术,能用技术模型去描述需求的超强业务人员。当然了,这类极受欢迎少往往都比较少,要不,我也转去做业务:)   现实中的业务人员,都是专注于所关心的市场领域,他们心目中在勾画一片天空的时候,可能在需求描述时,让开发工程师以为他是想要天空上的那架观光飞机。   另一种情况是,业务人员设计的东西,可能会让流程化,过程化的技术人员所否定,有的可能是对的,比如用户的操作的习惯,现在市场上浏览器的兼容性等,这个时候我们应该给出我们的判断解释给业务人员,并帮他去共同改进这个需求;有的工程师会因为这个需求太过复杂,而否定业务的设计,这个就只能看情况了,对于重要的,且复杂的需求,我们应该去深入细化,对于不重要,且复杂的,最好还是放到下一个版本里,等它的重要度足够高了再开始吧。所以,和业务人员的沟通很重要。按照敏捷开发的思想,业务人员最好是与开发人员在一间办公室里工作,再多的需求,设计文档,也比不上可以运行的系统。   协调业务人员与开发工程师的角色一般是项目经理,这个角色在那种业务人员与技术人员分隔很清楚的公司很重要。因为这时,他就是桥梁。但每个开发工程师都应该把自己也视为桥梁,因为很多伟大的创意是来源于技术,更何况很多中小型公司或创业型的公司并没有项目经理这一职位。   技术人员还是要多一些自己的判断,不要做一个只为外部需求而开发的纯技术,要理解自己所做的这个产品,我始终相信,技术人员是应该去完善业务提出的需求,而不仅仅是实现功能。
    前些日子,部门开会时讨论了一下技术人员如何与业务为员相处的问题。其实自己也一直在思索着这方面的问题,想想这些年来自己做为技术人员与业务人员打交道的过程,真可以说是感触很深了。
  搜狐的朋友,曾向我埋怨说,最讨厌什么都不懂的业务人员,来指导技术,开会一开下午,一点效果都没有。他说这话时的心情我可以理解,因为我也遇到过。在稍大一点的公司,技术和业务人员总是分的很泾渭分明,各施其责,但技术人员一般是专业性较强,而业务人员,可能很多都是由销售转过来,或是相关人文,经济专业的知识面广,但并不如技术那样专一某方面,所以难免会有些磨合上的问题。
  这些只是表面现象,但究其实质:是在需求描述和需求获取上,技术人员与业务人员并没有找到一个好的切合点。
  我想起半年前,百度的一位朋友,曾跟我说过一句,他说:好的技术不应该只顾满足业务提出的需求,还应能对给出的需求提出自己的合理的建议。
  
关于这一点,我直到现在,才算真正理解了。
  在技术工程师的心目中,最理想的业务人员应该是了解技术,能用技术模型去描述需求的超强业务人员。当然了,这类极受欢迎少往往都比较少,要不,我也转去做业务:)
  现实中的业务人员,都是专注于所关心的市场领域,他们心目中在勾画一片天空的时候,可能在需求描述时,让开发工程师以为他是想要天空上的那架观光飞机。
  另一种情况是,业务人员设计的东西,可能会让流程化,过程化的技术人员所否定,有的可能是对的,比如用户的操作的习惯,现在市场上浏览器的兼容性等,这个时候我们应该给出我们的判断解释给业务人员,并帮他去共同改进这个需求;有的工程师会因为这个需求太过复杂,而否定业务的设计,这个就只能看情况了,对于重要的,且复杂的需求,我们应该去深入细化,对于不重要,且复杂的,最好还是放到下一个版本里,等它的重要度足够高了再开始吧。所以,和业务人员的沟通很重要。按照敏捷开发的思想,业务人员最好是与开发人员在一间办公室里工作,再多的需求,设计文档,也比不上可以运行的系统。
  协调业务人员与开发工程师的角色一般是项目经理,这个角色在那种业务人员与技术人员分隔很清楚的公司很重要。因为这时,他就是桥梁。但每个开发工程师都应该把自己也视为桥梁,因为很多伟大的创意是来源于技术,更何况很多中小型公司或创业型的公司并没有项目经理这一职位。
  技术人员还是要多一些自己的判断,不要做一个只为外部需求而开发的纯技术,要理解自己所做的这个产品,我始终相信,技术人员是应该去完善业务提出的需求,而不仅仅是实现功能。

Design pattern

| | Comments(0) | TrackBacks(0)
-
  设计模式Design pattern,是个好东西,因为前人已经为我们解决了很多设计上的难题了,但就看怎么去用了。   一个系统不管你设计和当时运行的有多么的好,随着时间的推移,这个系统注定要不断的接受新的需求变化直到它退休。所以,我们要把握住这个原则:尽量让每一次改动都尽可能少的触动已有的代码.所有的这些模式都提供了一个让各系统独立与其它模块的方法.从而让我们能达到我们想要的设计原则.  设计模式的特点是:program to an interface,not an implementation. ??  模式向我们展示了如何去构架一个良好的面向对象的体系。它本身并不给我们代码,它给我们的是解决问题的方案,或者说是方法。就看我们怎么把它们应用到我们实际的应用中。模式本身一开始并不是在代码里,而是在我们的头脑里。   每当开始设计系统前,都会回味一下设计模式,舒服呀。   

排序研究二

| | Comments(0) | TrackBacks(0)
五.归并排序
  归并排序比前面讲到的排序方法要有效的多.冒泡排序,插入排序和选择排序要用O(N^2)时间,而归并只要O(N*logN).如果N是10000,那么N^2就是100000000,而N*logN只是40000,如果为这么多数据项排序用并归排序的话需要40秒,那么用插入排序则需要将近28个小时.
  但归并排序有个最大的缺点,就是它需要在内存能装下一个大小等于被排序的数据项数目的数组.

  在研究归并排序前,先要说说什么是归并.
  假设有两个有序数组,数组A有4个数据,数组B有6个数据,它们要归并到数组C中,那么我们就要为C初始10个空的存储空间.

  然后,对A的第一个数据,和B的第一个数据进行比较,把较小的数据项被复制到数组C中.再把未复制的数据与另一个数组的第二个数据进行比较,再把比较小的数据复制到C中,依次类推.
Java代码 复制代码
  1.                             // 把数组A和B归并到数组C   
  2. ublic static void merge( int[] arrayA, int sizeA,   
  3.                          int[] arrayB, int sizeB,   
  4.                          int[] arrayC )   
  5. {   
  6. int aDex=0, bDex=0, cDex=0;   
  7.   
  8. while(aDex < sizeA && bDex < sizeB)  // 两个数组均不为空时   
  9.    if( arrayA[aDex] < arrayB[bDex] )   
  10.       arrayC[cDex++] = arrayA[aDex++];   
  11.    else  
  12.       arrayC[cDex++] = arrayB[bDex++];   
  13.   
  14. while(aDex < sizeA)                  // 数组B空了,   
  15.    arrayC[cDex++] = arrayA[aDex++];  // 数组A还没空   
  16.   
  17. while(bDex < sizeB)                  // 数组A空了,   
  18.    arrayC[cDex++] = arrayB[bDex++];  // 数组B还没空   
  19. }  // end merge()  

  现在来看看归并排序的思想,是把一个数组分成两半,排序每一半,然后用上面的归并思想把数组的两半归并成一个有序的数组.但如何对每一部分排序呢,就要用到递归了,把每一半都分成两个1/4,对每个1/4部分排序,然后把它们归并成一个有序的一半,再以此类推不停的分解问题,直到得到了基值条件:只有一个数据项的数组是有序的.

Java代码 复制代码
  1. public void mergeSort()          // 归并排序组程序,theArray是待排序数组,nElems是其长度   
  2. {                              // 建一个存放有序结果的初始数组   
  3.       long[] workSpace = new long[nElems];   
  4.       recMergeSort(workSpace, 0, nElems-1);   
  5.       }   
  6.   
  7.   
  8.  private void recMergeSort(long[] workSpace, int lowerBound,   
  9.                                                int upperBound)   
  10.       {   
  11.       if(lowerBound == upperBound)            // 如果就一个了,没   
  12.          return;                              // 必要排序了   
  13.       else  
  14.          {                                    // 找到中间点   
  15.          int mid = (lowerBound+upperBound) / 2;   
  16.                                               // 对左边的一半排序   
  17.          recMergeSort(workSpace, lowerBound, mid);   
  18.                                               // 对右边的一半排序   
  19.          recMergeSort(workSpace, mid+1, upperBound);   
  20.                                              // 合并两个排好序的一半   
  21.          merge(workSpace, lowerBound, mid+1, upperBound);    
  22.          }     
  23.       }     
  24.   
  25.  //-----------------------------------------------------------   
  26.    private void merge(long[] workSpace, int lowPtr,     //lowPtr:前半部分的子数组的开始;   
  27.                            int highPtr, int upperBound) //highPtr:后半部分子数组的开始位置;   
  28.       {                                                 //upperBound:后半部分子数组的上界   
  29.       int j = 0;                                
  30.       int lowerBound = lowPtr;   
  31.       int mid = highPtr-1;   
  32.       int n = upperBound-lowerBound+1;    
  33.   
  34.       while(lowPtr <= mid && highPtr <= upperBound) //把theArray数组中前半部分和后半部分   
  35.          if( theArray[lowPtr] < theArray[highPtr] ) //中小的数据复制到workSpace中   
  36.             workSpace[j++] = theArray[lowPtr++];   
  37.          else  
  38.             workSpace[j++] = theArray[highPtr++];   
  39.   
  40.       while(lowPtr <= mid)   
  41.          workSpace[j++] = theArray[lowPtr++];   
  42.   
  43.       while(highPtr <= upperBound)   
  44.          workSpace[j++] = theArray[highPtr++];   
  45.   
  46.       for(j=0; j<n; j++)   
  47.          theArray[lowerBound+j] = workSpace[j];   
  48.       }  // end merge()   
  49.    //-----------------------------------------------------------  

  workspace数组的创建在mergeSort()中实现,是因为如果在recMergeSort()中创建数组会在每一次递归调用中都创建新数组,这是没有效率的.

  但是一个算法作为一个递归的方法通常从概念上很容易理解,但是,在实际的运用中证明递归算法的效率不太高,有时可以用一个基于栈的方法来替代它,有兴趣的朋友可以继续研究.

六,希尔排序
  希尔排序不像快速排序和其他时间复杂度为O(N*logN)的排序算法那么快,因此对非常大的文件排序,它不是最优的.但是希尔排序比选择排序和插入排序这种时间复杂度为O(N^2)的排序算法还是要快得多.另外 ,希尔排序算法的代码既短又简单.
  我们先看看插入排序带来的问题.假设一个很小的数据项在很靠近右端的位置上,这里本来应该是值比较大的数据项所在的位置.把这个小数据项移动到在左边的正确位置上,所有的中间数据都必须向右移一位.这个步骤对每一个数据项都执行了将近N次的复制.

  希尔排序是通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项能大跨度地移动.
  进行希尔排序时数据项之间的间隔被称为增量,并且习惯上用字母h来表示.
  如对包含10个数据项的数组进行排序,我们设h=4,我们则先对0,4,8位置上的元素排序,然后再对1,5,9号数据项进行排序.这个排序过程持续进行,直到所有的数据项都已经完成了4增量排序,也就是说所有间隔为4的数据项之间都已经有序.
  然后我们就可以进行普通的插入排序,即1-增量排序.
  在完成以h为增量的希尔排序后,所有元素离它在最终有序序列中的位置都比较近,这就是数据"基本有序"的含义.这也正是希尔排序的奥秘所在.
  接着上面的例子4-增量排序和1-增量排序结合起来应用,比前面只用普通的插入排序要快得多.
  那对于希尔排序中的h间隔如何取呢,一般我们选用Knuth序列:h初值为1,然后应用h=h*3+1生成序列:1,4,13,40,121,364,...当间隔大于数组大小时,这个过程停止.
Java代码 复制代码
  1. public void shellSort(int []a)   
  2.      {   
  3.      int inner, outer;   
  4.      long temp;   
  5.   
  6.      int h = 1;                     // 间隔h的初始值是1   
  7.      while(h <= a.length/3)   
  8.         h = h*3 + 1;                // (1, 4, 13, 40, 121, 等等)   
  9.   
  10.      while(h>0)                     // h递减,直到h=1   
  11.         {   
  12.                                     // h-增量排序   
  13.         for(outer=h; outer<a.length; outer++)  //即插入排序   
  14.            {   
  15.            temp = a[outer];   
  16.            inner = outer;   
  17.            while(inner > h-1 && a[inner-h] >=  temp)   
  18.               {   
  19.               a[inner] = a[inner-h];   
  20.               inner -= h;   
  21.               }   
  22.            a[inner] = temp;   
  23.            }  // end for   
  24.         h = (h-1) / 3;              // h递减   
  25.         }  // end while(h>0)   
  26.      }  // end shellSort()  

  迄今为止,除了在一些特殊情况下,还没有人能够从理论上分析希尔排序的效率,有各种各样基于试验的评估,估计它的时间级从O(N^3/2)到O(N^7/6).

七,快速排序
  讲快速排序,就要先说一下划分,因为划分是快速排序的根本机制.
  划分就是把数组分为两组,使所有关键字大于特定值的数据项在一组,使所有关键字小于特定值的数据项在另一组.
  完成划分,数据还不能称为有序,也只是比没有划分前更接近有序了.同时划分也是不稳定的,因为它往往会颠倒数组中一些数据的顺序.
Java代码 复制代码
  1. public int partitionIt(int left, int right, long pivot)   
  2.       {   
  3.       int leftPtr = left - 1;           // 左边   
  4.       int rightPtr = right + 1;         // 右边   
  5.       while(true)   
  6.          {   
  7.          while(leftPtr < right &&       // 找出左边比pivot大的元素时停止   
  8.                theArray[++leftPtr] < pivot)   
  9.             ;  // (nop)   
  10.   
  11.          while(rightPtr > left &&       // 找出右边比pivot小的元素时停止   
  12.                theArray[--rightPtr] > pivot)   
  13.             ;  // (nop)   
  14.          if(leftPtr >= rightPtr)        // 左右相逢   
  15.             break;                      // 划分结束   
  16.          else                           // 左右未逢   
  17.             swap(leftPtr, rightPtr);    // 交换元素   
  18.          }  // end while(true)   
  19.   swap(leftPtr, rightPtr);        //将pivot放到正确点   
  20.       return leftPtr;                   //返回相逢点   
  21.       }   

  划分算法主要是两个指针,分别指向数组的两头.在左边的指针,leftPtr向右移动,而在右边的rightPtr则向左移动.实际上,leftPtr初始化时是在第一数据的左边一位,rightPtr是在最后一个数据项的右边一位,这是因为它们在执行前都要分别的加一和减一.
  当leftPtr遇到比待比较数据小的数据项时,它继续右移,但遇到比待比较数大时,它就停止.类似的,当rightPtr遇到大于待比较数的数据项时,它继续左移,但是当遇到比待比较数小时,也停止.两个内层while循环,第一个用于leftPtr,第二个用于rightPtr,控制左右两部分的扫描过程.
  当这两个循环都退出之后,也就是leftPtr与rightPtr都指向左右两边位置不对的数据项上,所以交换这两个数据.交换结束之后,继续移动两个指针,再停止,再交换.这一切都包含在一个外部循环中.

  快速排序是最流行的算法,在大多数情况下,快速排序都是最快的,执行时间为O(N*logN)级.这也只是对内部排序而言,对于在磁盘文件中的数据进行排序,其他的排序算法可能更好.
  快速排序算法本质上是通过把一个数组划分为两个子数组,然后递归地调用自身为每一个子数组进行快速排序.
Java代码 复制代码
  1. public void recQuickSort(int left, int right)   
  2.      {   
  3.      if(right-left <= 0)              // if size <= 1,   
  4.          return;                      //    already sorted   
  5.      else                             // size is 2 or larger   
  6.         {   
  7.         long pivot = theArray[right];      // 选择最右端的元素为待比较数   
  8.                                            // 划分   
  9.         int partition = partitionIt(left, right, pivot);   
  10.         recQuickSort(left, partition-1);   // 排序左边   
  11.         recQuickSort(partition+1, right);  // 排序右边   
  12.         }   
  13.      }  // end recQuickSort() 

最近的留言

John发表于2008北京车展: 你是要看车,还是看美
bannoorse发表于2008北京车展: 太好看了啊啊啊 希
易风行发表于宇宙大爆炸(四):宇宙的模样: 这片子真的不错,很值
John发表于宇宙大爆炸(四):宇宙的模样: 自此为止,《宇宙大爆
星迷发表于周星驰的下一个梦想是什么?: 人因梦想而伟大
John发表于数据恢复中....: 呵,谢谢了,已发过的
风南发表于数据恢复中....: 我rss里还有一些片
风南发表于数据恢复中....: 唉。早知道我把那几篇