zoukankan      html  css  js  c++  java
  • 编程珠玑第八章 算法设计艺术

    问题描述:

    一个具有n个浮点数字的数组x,目标是要找到之中连在一起的数组元素中找到最大和。例如如果输入的数组是以下这十个元素:

    31  -41  59  26  -53  58  97  -93  -23  84

    那么程序应该返回从59到97的综合,也就是187。第一个算法迭代了所有满足 0 ≤ i ≤ j < n 的 i 和 j 整数对,分别计算总和,最终找到综合最大的组合。

    问题定义: 具有n个浮点数的向量x,求出输入向量的任何连续子向量的最大和。

    最初的算法(算法1)遍历长度为n向量的所有的非空子向量,总共有n + (n - 1) + (n - 2) + ... + 1 = (n^2 + n)/2。所以算法复杂度为O(n^3)。 算法复杂度为O(n^3)。

    算法1 伪代码:

    maxsofar = 0;
    for i = [0,n)
        for j=[i,n)
            sum = 0;
            for k=[i,j]//求子向量x[i...j]之和
               sum += x[k]
               maxsofar = max(maxsofar,sum);

    算法1 c语言源程序:

     1 float max_subvector1(float array[], int length)
     2 {
     3     assert(length > 0);
     4     int i, j, k;
     5     float max = 0, sum; 
     6     for(i = 0; i < length; i ++)
     7     {
     8         for ( j = i; j < length; j ++)
     9         {
    10             sum = 0;
    11             for( k = i; k <= j; k ++)
    12                 sum += array[k];
    13             printf("%d - %d\tsum = %5.2f\tmax=%5.2f\n", i, j, sum, max);
    14             if(max  < sum)
    15                 max = sum;
    16         }
    17         
    18             
    19     }
    20     return max;
    21 }

    算法2:算法基本思想和算法1相同,也是遍历所有的非空子向量。但是子向量x[i..j]的总和:sum(x[i..j]) = x[i] + x[i + 1] + ... + x[j] = sum(x[i...j-1]) + x[j],即子向量x[i..j]的和是基于子向量x[i..j -1]的和算法复杂度为O(n^2)。

    算法2 伪代码:

    maxsofar = 0
    for i = [0, n)
        sum = 0
        for j = [i, n)
            sum += x[k]
            maxsofar = max(maxsofar, sum)

    算法2 c语言源程序:

     1 float max_subvector2(float array[], int length)
     2 {
     3     assert(length > 0);
     4     int i, j;
     5     float max = 0, sum; 
     6     for(i = 0; i < length; i ++ )
     7     {
     8         sum = 0;
     9         for ( j = i; j < length; j ++)
    10         {
    11             sum += array[j];
    12             if(max  < sum)
    13                 max = sum;
    14         }
    15         
    16             
    17     }
    18     return max;
    19 }

    分治算法(算法3):

    分治法的思想是这样的:要解决规模为 n 的问题,可递归解决两个规模近似为 n/2 的子问题,然后将它们的答案进行合并以得到整个问题的答案。

    把整个数组分为两个大约相等的部分,a 和 b。然后递归找出 a 和 b 中元素和最大的子数组,即为 ma 与 mb。最大子向量要么在在整个a中,要么在整个b中,要么跨越a和b之间的边界。跨越边界的最大子向量称为mc。mc在a中的部分是a中包含右边界的最大子向量,mc在b中的部分包含左边界的最大子向量。所以,最大子向量的和为max(ma, mb, mc)。由递归式可得,算法复杂度为O(nlogn)。

    算法3 伪代码:

    float maxsum3(l, u)
    
        if (l > u)    return 0
    
        if (l == u )    return max(0, x[l])
    
        m = (l + u) / 2
    
        lmax = sum = 0
    
        for (i = m; i >= l; i--)
    
             sum += x[i]
    
             lmax = max (lmax, sum)
    
        rmax = sum = 0
    
        for i = (m, u]
    
             sum += x[i]
    
             rmax = max (rmax, sum)
    
        return max(lmax+rmax, maxsum(l, m), maxsum3(m+1, u))

    算法3 c语言源程序:

     1 float max_subvector4(float array[], int l, int u)
     2 {
     3     int i;
     4     float lmax, lsum, rmax, rsum, max1, max2, max = 0; 
     5     if( l > u)
     6         return 0;
     7         
     8         
     9     if( l == u)
    10         return array[u] > 0 ? array[u] : 0;
    11     int m;
    12     
    13     m = (u + l) / 2;
    14     printf("%d-%d ", l, u);
    15     rsum = rmax = 0;
    16     for(i = m + 1; i <= u; i ++)
    17     {
    18         rsum += array[i];
    19         rmax = rmax > rsum ? rmax : rsum; 
    20     }
    21     
    22     lsum = lmax = 0;
    23     for(i = m; i >= l; i --)
    24     {
    25         lsum += array[i];
    26         lmax = lmax > lsum ? lmax : lsum; 
    27     }
    28     
    29     
    30     printf("lmax=%5.2f rmax=%5.2f\n", lmax, rmax);
    31     max1 = max_subvector4(array, l, m);
    32     max2 = max_subvector4(array, m + 1, u);
    33     max = max1 > (lmax + rmax) ? max1 : (lmax + rmax);
    34     max = max2 > max? max2 : max;
    35     return max;
    36     
    37 }

    扫描算法(算法4):

    从最左端(元素 x[0])开始,一直扫描到最右端(元素 x[n-1]),记下所碰到过的最大总和子数组。最大值初始为0.假设已经解决了针对 x[0..i-1] 的问题,现在需要拓展到 x[i] 中。可以使用类似分治法中的道理,前 i 个元素中,最大总和子数组要么在 i-1 个元素中(存储在 maxsofar 中),要么截止到位置 i(存储在 maxendinghere中)。算法代码更加简短,但是运行起来是最快的,运行时间是O(n),已经是线性算法。

    算法4 伪代码:

    maxsofar = 0
    
    maxendinghere = 0
    
    for i = [0, n)
    
        maxendinghere = max(maxendinghere + x[i], 0)
    
        maxsofar = max(maxsofar, maxendinghere)

    算法4 c语言源程序

     1 float max_subvector5(float array[], int length)
     2 {
     3     int i;
     4     float maxsofar = 0, maxendinghere = 0; 
     5     for(i = 0; i < length; i ++)
     6     {
     7         maxendinghere = (maxendinghere + x[i]) > 0 ? maxendinghere : 0;
     8         maxsofar = maxsofar > maxendinghere ? maxsofar : maxendinghere;
     9     }
    10     return maxsofar;
    11     
    12 }

     本章故事中的这些算法给出了几个重要的算法设计技术:
    1.保存状态,避免重复计算。通过使用一些空间来保存中间计算结果,我们避免了花时间来对其重复计算。
    2.将信息预处理到数据结构中。
    3.分治算法。
    4.扫描算法。与数组相关的问题经常可以通过思考“如何将x[0...i-1]的解扩展为x[0...i]地解来解决。
    5.累积。
    6.下界。确定相匹配的下界。

     

     习题13. 在最大子数组问题中,给定m*n的实数数组,我们需要求出矩形子数组的最大总和。该问题的复杂度如何?

     问题解析:可以在长度为m的维度上使用算法2,而在长度为n的维度上使用算法4。可以在O(m^2*n)的时间复杂度内解决m*n问题。 

     算法 c程序源码:

     1 float max_subarr(float **array, int row, int col)
     2 {
     3     int i, j, k;
     4     float *vector, max = 0, maxsofar, maxendinghere;
     5     assert(row > 0 && col > 0);
     6     vector = (float *) malloc(col * sizeof(float));
     7     assert(vector != NULL);
     8     printf("the array :\n");
     9     for( i = 0; i < row; i ++)
    10     {
    11         memset(vector, 0, col * sizeof(float));//初始化为0
    12         for( j = i; j < row; j ++)
    13         {
              //vector[k]的值为行i至行j的第k列元素之和,把二维问题转化为一维问题。 
    14 for(k = 0; k < col; k ++) 15 vector[k] += array[j][k];//vector[k] = array[i][k] + array[i + 1][k] + ... + array[j][k],
    16 maxsofar = vector[0]; 17 maxendinghere = 0; 18 for(k = 0; k < col; k ++) 19 { 20 maxendinghere = (maxendinghere + vector[k]) > 0 ? (maxendinghere + vector[k]) : 0; 21 maxsofar = maxsofar > maxendinghere ? maxsofar : maxendinghere; 22 } 23 24 max = max > maxsofar ? max : maxsofar; 25 26 } 27 } 28 return max; 29 }

     

     

  • 相关阅读:
    Centos 7.X 安装JDK1.8
    CentOS 7.X 搭建时间服务器 --- chrony
    CentOS 7.X 设置系统时间
    CentOS 7.X 关闭SELinux
    C11工具类:时间处理
    C11内存管理之道:智能指针
    C11性能之道:标准库优化
    C11性能之道:转移和转发
    C11性能之道:右值引用
    C11简洁之道:tupe元祖
  • 原文地址:https://www.cnblogs.com/bigrabbit/p/2666850.html
Copyright © 2011-2022 走看看