zoukankan      html  css  js  c++  java
  • 使用堆查找前K个最大值兼谈程序优化(中)


             上篇谈到, 之前的程序使用堆查找前K个最大值的效率并不理想,本篇尝试对程序进行优化,以提高程序效率。


             一、 算法设计方面

            要提高程序效率, 首先从算法设计方面,即时间复杂度方面考虑。 由于查找前K个最大值总要遍历整个列表,因此,其效率必定不小于线性的,而前面已经谈到,使用堆查找其效率平均情况下可以达到线性, 因此, 整体的算法复杂度恰好为线性的,无法在量级上有重大提升。 不过,通过仔细改进和优化,是可以将原来的效率提升若干倍的。


            二、  使用性能分析工具

           进行性能优化的第二步骤是使用性能分析工具分析“热点区域”, 也就是耗费时间非常多的地方。我是在DEV C++ IDE 上开发的, 使用自带的Profile 分析结果如下: 

           

            显然, maxHeapify 所耗费的时间是最多的, 也就是为了保持最大堆性质而进行的操作。 要改进这一操作, 首先想到的是将递归改为非递归。

      

            三、  递归转化为非递归

            maxHeapify 的非递归程序如下:  

    void fastMaxHeapify(Elem list[], long i, long heapsize)
    {
        Elem temp;
        temp.num = list[i].num;   
        long curr_largest = i;
        long last_largest = i;
        
        while (curr_largest <= heapsize) {
           long lch = LEFT(curr_largest);
           long rch = RIGHT(curr_largest);
           if (lch <= heapsize && list[lch].num > list[curr_largest].num) {
                curr_largest = lch;
           }
           if (rch <= heapsize && list[rch].num > list[curr_largest].num) {
                curr_largest = rch;
           }
           if (curr_largest == last_largest) {
               break;
           }
           list[last_largest].num = list[curr_largest].num;
           last_largest = curr_largest;
        }     
        list[curr_largest].num = temp.num;
    }
    
    

          使用 fastMaxHeapify 替代原来的 maxHeapify 后 , 其 Profile 分析结果如下:

           

            可以很明显地看出, 其运行时间降低到原来的大约一半。这是因为减少了很多交换操作及系统调用时间。 使用堆查找N个数中的前K个最大值(不包括初始化N个数)时间降为2.5s 左右, 首战告捷。 

      

            四、 将下标索引操作替换为指针操作

                    改进很不明显, 也许是编译器已经优化的缘故。

     

            五、 去掉 RandNum 函数

            RandNum短 函数在这里只是为了可读性, 可以直接去掉, 用里面的内容代替其调用以减少系统调用开销。 当然,这只是小幅度减少了创建随机元素的时间,并没有影响查找效率。

             

           六、  停下来! 建立正确性的回归测试

            做程序优化很容易头脑发昏,一味地盯着性能数字变化,忘记一件很重要的事情: 那就是每次优化的改动中,必须总是保持程序的正确性。 这不, 出问题了! 使用fastMaxHeapify 后对于有些情况不能正常工作。 现在建立正确性的回归测试还不算晚,要是等待程序已经垒得有点“规模”了,再来测试, 准得更头疼。 此外,需要对代码组织进行调整下,以使整个结构更加清晰。经过仔细的测试和调试, fastMaxHeapify 确实有个重要的错误. 读者不妨找找看。

            建立回归测试后,最重要的是, 在之后的优化工作中, 可以保证改动总是在满足正确性的范围内。 值得注意的是,用于测试和验证的函数一定要正确,否则会起误导的作用。 其程序主要如下:

         

    /* 
     * HeapUtil.c
     * 实现建堆过程的函数以及堆排序,求解前K个最大值 
     *
     * NOTE: 数组 list 的 0 号单位未用,元素应放置于[1:num]而不是[0:num-1]
     *       num 是应用中的数据数目, 因此必须给数组分配至少 num+1 个元素空间,
     */
    
    #include "common.c"
    
    #define LEFT(i)     (2*(i))
    #define RIGHT(i)    (2*(i)+1)
    
    #define FAST  1
    
    /* 
     * maxHeapify: 使以结点i为根的子树为最大堆. 
     * 前置条件:结点i的左右子树都满足最大堆性质 
     */
    void maxHeapify(Elem list[], long i, long heapsize);
    void maxHeapifyRec(Elem list[], long i, long heapsize);  
    void fastMaxHeapify(Elem list[], long i, long heapsize);
    
    /* buildInitMaxHeap: 构造初始最大堆 */
    void buildInitMaxHeap(Elem list[], long num);   
    void swap(Elem *e1, Elem *e2);
    
     /* 计算 list 的 n 个数中前 k 个最大值 */
    Elem* findkthMax(Elem *list, int k, long n); 
    
    /* 验证以 index 为结点的子树确实满足最大堆性质 */
    void  validMaxHeap(Elem *list, long index, long heapsize);
    /* 验证 建立初始最大堆的正确性 */ 
    void validBuildInitMaxHeap(Elem list[], long num);
    /* 验证 kthmax 确实是 list 中的前 k 个最大值 */
    void validkthMax(Elem* list, int num, Elem* kthmax, int k);
    
     
     /* 堆排序实现 */ 
    void heapSort(Elem list[], long num); 
    
    
    void maxHeapify(Elem list[], long i, long heapsize)
    {
         #if FAST == 0
             maxHeapifyRec(list, i, heapsize);
         #else
             fastMaxHeapify(list, i, heapsize);
         #endif 
    }
    
    void maxHeapifyRec(Elem list[], long i, long heapsize)
    {
        long largest = i;     // 结点i与其左右孩子节点中关键词最大的那个结点的下标 
        long lch = LEFT(i);
        long rch = RIGHT(i);
          
          if (lch <= heapsize && list[lch].num > list[largest].num) {
                largest = lch;
          }
          if (rch <= heapsize && list[rch].num > list[largest].num) {
                   largest = rch;
          }
            if (largest != i) {
                   swap(&list[largest], &list[i]);
                   maxHeapify(list, largest, heapsize);
          }   
    }
    
    
    void fastMaxHeapify(Elem list[], long i, long heapsize)
    {
        Elem temp;
        temp.num = list[i].num;   
        long curr_largest = i;
        long last_largest = i;
        
        while (curr_largest <= heapsize) {
           long lch = LEFT(curr_largest);
           long rch = RIGHT(curr_largest);
           
           if (lch <= heapsize && (*(list+lch)).num > temp.num) {
                curr_largest = lch;
           }
    
           if (rch <= heapsize && (*(list+rch)).num > (*(list+curr_largest)).num) {
               curr_largest = rch;
           }
           if (curr_largest == last_largest) {
               break;
           }
           (*(list+last_largest)).num = (*(list+curr_largest)).num;
           last_largest = curr_largest;
        }     
        (*(list+curr_largest)).num = temp.num;
    }
    
    /* 
     * buildInitMaxHeap: 构造初始最大堆 
     */
    void buildInitMaxHeap(Elem list[], long num)
    {
         long i;
         for (i = (num+1)/2; i >= 1; i--)
            maxHeapify(list, i, num);
    }
    
    /* 验证 建立初始最大堆的正确性 */ 
    void validBuildInitMaxHeap(Elem list[], long num)
    {
         long i ;
         if (num % 2 == 0) {
             i = num / 2;
             assert(list[i].num >= list[2*i].num);
             i--;
         }
         else {
             i = num / 2;
         }
         for (; i >= 1; i--) {
             assert(list[i].num >= list[i*2].num);
             assert(list[i].num >= list[i*2+1].num);
         }
    }
    
    /* 验证以 index 为结点的子树确实满足最大堆性质 */
    void  validMaxHeap(Elem *list, long index, long heapsize)
    {
          long lch, rch;
          if (index > heapsize) {
                return ;
          }
          lch = 2 * index; 
          if (lch <= heapsize) {
              assert(list[index].num >= list[lch].num);
              validMaxHeap(list, lch, heapsize);
          }
          rch = 2 * index + 1;
          if (rch <= heapsize) {
              assert(list[index].num >= list[rch].num);
              validMaxHeap(list, rch, heapsize);
          }
    }
    
    void swap(Elem *e1, Elem *e2)
    {
          Elem e;
          e = *e1;
          *e1 = *e2;
          *e2 = e;
    }
    
    void heapSort(Elem list[], long num)
    {
         long i, heapsize = num;
         buildInitMaxHeap(list, num);
         
         for (i = num; i >= 1; i--) {
            swap(&list[1], &list[i]);
            heapsize--;
            maxHeapify(list, 1, heapsize);
         }
    } 
    
     /* 计算 list 的 n 个数中前 k 个最大值 */
    Elem* findkthMax(Elem *list, int k, long n)
    {
         long i;
         long heapsize = n;
         Elem *kthmax = myalloc(k+1);
         Elem *p = kthmax+1;
         buildInitMaxHeap(list, n);
         for (i = n; i > n-k; i--) {
               (p++)->num = (*(list+1)).num;        
                swap(list+1, list+i);
                heapsize--;
                maxHeapify(list, 1, heapsize);
         }
         return kthmax;
    }
    
    /* 
     * 验证 kthmax[1:k] 确实是 list[1:num] 中的前 k 个最大值  
     */
    void validkthMax(Elem* list, int num, Elem* kthmax, int k)
    {
         int i;
         Elem *p, *q;
         heapSort(kthmax, k);  /* 对列表 kthmax 进行排序,使之从小到大排序 */
         
         for (i = 1; i <= k; i++) {
             p = kthmax + k-i+1;   // 从大到小依次取 kthmax 中的最大值 
             for ( q = list+i; q <= list+num; q++) {
                assert((*q).num <= (*p).num);  
                if ((*q).num == (*p).num) {
                   swap(list+i, q);
                }
             }
          }
            
    }
    /* 
     * common.c 存放公共结构与例程 
     */
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <limits.h>
    #include <assert.h>
    
    #define MOD 101
    #define DEBUG 0
     
     typedef struct {
          long  num;   /* 设为关键词 */
     } Elem;
    
    void creatListInternal(Elem *list, long offset, long num, long len);
    void printListInternal(Elem *list, long offset, long num, long len);
    void creatList(Elem *list, long num);
    void creatList2(Elem* list, long num);
    void printList(Elem *list, long num);
    void validSort(Elem *list, long size);
    Elem *myalloc(long size);
    
    /* 
     * 随机生成列表元素,并存入从offset开始的 num 个元素, len 是为列表长度 
     * 若 len < offset + num 则只存入 len-offset个元素 
     */
    void creatListInternal(Elem *list, long offset, long num, long len)
    {
           long i, endIndex = offset+num;
           srand(time(NULL));
           if (offset < 0 || offset >= len) {
                return ;
           }
           if (endIndex > len) {
              endIndex = len ;
           }
           for (i = offset; i < endIndex; i++)
              (*(list+i)).num = ((1 + rand()) % MOD) * ((1 + rand()) % MOD);
    }
    
    /* 
     *  打印从offset开始的num个元素, len 是为列表长度
     *  若 len < offset + num 则只打印 len-offset个元素 
     */ 
    void printListInternal(Elem *list, long offset, long num, long len)
    {
           long i, endIndex = offset+num;
           if (offset < 0 || offset >= len) {
                return ;
           }
           if (endIndex > len) {
              endIndex = len ;
           }
           for (i = offset; i < endIndex; i++)
               printf("%6d%c", (*(list+i)).num, ((i-offset+1)%10 == 0) ? '\n': ' ');
           printf("\n");
    }
    
    void creatList(Elem *list, long num)
    {
         creatListInternal(list, 1, num, num+1);
    }
    
    void creatList2(Elem* list, long num)
    {
         int i ;
         for (i = 1; i <= num; i++) {
             list[i].num = i; 
         }
    }
    
    void printList(Elem *list, long num)
    {
         printListInternal(list, 1, num, num+1);
    }
    
    void validSort(Elem *list, long size)
    {
         long i;
         for (i=1; i < size; i++) {
             assert(list[i].num <= list[i+1].num);
         }
    }
    
    Elem *myalloc(long size)
    {
         Elem* list = (Elem *)malloc(size*sizeof(Elem));
         if (!list) {
              fprintf(stderr, "fail to allocate memory.");
              exit(1);
          }
          return list;
    }
    /* 
     * KthMaxTest.c 使用堆排序求解前K个最大值问题的测试 
     *
     */ 
     
    #include "HeapUtil.c"
    #include "MaxHeapifyTest.c"
    #include "HeapSortTest.c"
    
    #define CHOOSE 2
            
    void testkthMax(long NUM, int K);
    void measure(long NUM , int K);
    void testValid();
    void testPerf();
    
    int main()
    {
          srand(time(NULL));
          #if CHOOSE == 1
             printf("\n*********** maxHeapify test begins. ********\n");
             maintestMaxHeapify(); 
             printf("\n*********** maxHeapify test completed. ********\n");
             printf("\n*********** building initial maxheap test begins. ********\n");
             maintestBIMP();
             printf("\n*********** building initial maxheap test completed. ********\n");
             printf("\n*********** heap sort test begins. ********\n");
             maintestHeapSort();
             printf("\n*********** heap sort test completed. ********\n");
         test(" Test Valid ", testValid);      // 用于保证正确性的回归测试 
         printf("Successful Passed.");
          #elif CHOOSE == 2
              measure(100000000, 100);            // 用于程序优化的实例 
          #else  
              test(" Measure Performace ", testPerf);  // 用于测量性能 
          #endif
          
          getchar();
          return 0;
    }
    
    void test(char *msg, void (*test)())
    {
         printf("\n----------- %s ----------\n", msg);
         (*test)();
         printf("\n\n");
    }
    
    void testValid()
    {
         long num;
         int k;
         for (num = 0; num <= 8; num ++) {
            for (k = 0; k <= num; k++) {
               testkthMax(num, k);
            } 
         }
    }
    
    /* 测试找出前K个最大值 */
    void testkthMax(long NUM, int K)
    {
          Elem *kthmax;
          Elem* list = myalloc(NUM+1);
          creatList(list, NUM);
          
          #if DEBUG == 1
              printf("\nThe original list:\n");
              printList(list, NUM);
          #endif
          
          kthmax = findkthMax(list, K, NUM);
          validkthMax(list, NUM, kthmax, K);
          
          #if DEBUG == 1
              printListInternal(kthmax, 1, K, K+1);  
          #endif
          
          free(kthmax);
          free(list);
    }
    
    void testPerf()
    {
         long num ; 
         int k;
         for (num = 1; num <= 100000000; num*=10) {
            for (k = 1; k <= 1000 && k <= num ; k*=10) {
               measure(num, k);
            } 
         }
    }
    
    void measure(long NUM , int K)
    {
          clock_t start, end;
          Elem *kthmax;
          Elem* list = myalloc(NUM+1);
          creatList(list, NUM);
          
          start = clock();
          kthmax = findkthMax(list, K, NUM);
          end = clock();
          
          free(kthmax);
          free(list); 
          printf("\nNUM = %-10ld, K = %-10d, Eclipsed time: %6.3f\n", NUM, K, ((double)(end-start))/CLOCKS_PER_SEC);
    }
    /**
     * MaxHeapifyTest.c  构建并保持最大堆性质函数的测试 
     *
     */ 
    
    void maintestMaxHeapify();
    void testMaxHeapify(void (*creatList)(Elem* list, long size));
    void testmh(Elem *list, long heapsize);
    
    void testbimp(Elem *list, long heapsize);
    void testBuildInitMaxHeap(void (*creatList)(Elem* list, long size));
    void maintestBIMP();
    
    void maintestMaxHeapify()
    {
        testMaxHeapify(creatList2);
        testMaxHeapify(creatList); 
    }
    
    void testMaxHeapify(void (*creatList)(Elem* list, long size))
    {    
         long heapsize;
         for (heapsize = 0; heapsize <= 8; heapsize++) {
            Elem list[heapsize+1];
            creatList(list, heapsize);
            testmh(list, heapsize);
         }
    }
    
    void testmh(Elem *list, long heapsize)
    {
        long index;
        
        #if DEBUG == 1  
            printf("\n----------- heapsize: %d ----------\n", heapsize);  
            printList(list, heapsize);
        #endif
        
        for (index = heapsize; index >= 1; index--) {  
           long lch, rch, curr;   
           maxHeapify(list, index, heapsize);
           validMaxHeap(list, index, heapsize);  
           
           #if DEBUG == 1
               printf("index = %d\t", index);
               printList(list,heapsize);  
           #endif  
        }          
    }
    
    
    
    void maintestBIMP()
    {
         testBuildInitMaxHeap(creatList2);
         testBuildInitMaxHeap(creatList);
    }
    
    void testBuildInitMaxHeap(void (*creatList)(Elem* list, long size))
    {    
         long heapsize;
         for (heapsize = 0; heapsize <= 8; heapsize++) {
            Elem list[heapsize+1];
            creatList(list, heapsize);
            testbimp(list, heapsize);
         }
    }
    
    void testbimp(Elem *list, long heapsize)
    {
        long index;  
        
        #if DEBUG == 1
           printf("\n----------- heapsize: %d ----------\n", heapsize);  
           printList(list, heapsize);
        #endif
        
        buildInitMaxHeap(list, heapsize);
        validBuildInitMaxHeap(list, heapsize);  
          
        #if DEBUG == 1
           printList(list,heapsize);
        #endif
          
    }
    
    
    
    
    
    
    /*
     * HeapSortTest.c 堆排序测试 
     */ 
    
     
    void testHeapSort(void (*creatList)(Elem* list, long size)); 
    void maintestHeapSort(); 
     
     /* 测试堆排序 */
    void maintestHeapSort()
    {  
          testHeapSort(creatList2);
          testHeapSort(creatList);
    }
    
    void testHeapSort(void (*creatList)(Elem* list, long size))
    {    
         long heapsize;
         for (heapsize = 0; heapsize <= 8; heapsize++) {
            Elem list[heapsize+1];
            creatList(list, heapsize);
            
            #if DEBUG == 1
                printf("%-21s", "\nThe original list: ");
                printList(list, heapsize);
            #endif
            
            heapSort(list, heapsize); 
            validSort(list, heapsize);
            
            #if DEBUG == 1
                printf("%-20s", "The sorted list: ");
                printList(list, heapsize);
            #endif
         }
    }
    
    
  • 相关阅读:
    leetcode每日一题:836. 矩形重叠
    单链表之删除头结点,查找等于定值x的结点数,单链表的逆置
    拼数,零幺串
    最大公约数/最小公倍数
    寻找二叉树双亲结点
    Object类的派生-c++
    牛客小白月赛22
    二叉树的基本操作
    字符串的反转,替换,删除
    [2011山东ACM省赛] Identifiers(模拟)
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/4037819.html
Copyright © 2011-2022 走看看