您的位置:首页 > 其它

算法分析学习日志(二):最大子序列和问题

2017-08-17 18:45 363 查看
最大子序列和问题是非常经典的算法问题,它的各种解法也常见于各类算法教材,本文将用一个简单的实验,来展示在处理该问题的过程中,优化算法的重要性。

最大子序列和问题的描述为:在给定一列整数的情况下,求取其所有子序列的和的最大值。在本文中,我们生成一个整数数量为500,取值范围为[-10,10]的随机整数列作为样例,验证不同算法所需要的时间。

生成随机数的代码如下:

#include <iostream>
#include <stdio.h>
#include <fstream>

using namespace std;

/** @主函数 */
int main(int argc, const char** argv)
{
int i, j;
const int n = 20;
ofstream RandInt("int.txt");
//生成[-10,10]的随机整数
for (i = 0; i<n; i++)
{
j = (rand() % 20) - 10;
RandInt << j << ",";
}
cout << endl;
RandInt.close();
return 0;
}

测试使用的代码如下:
#include <iostream>
#include <stdio.h>
#include <Windows.h>

using namespace std;

/** @主函数 */
int main(int argc, const char** argv)
{
const int n = 500;
int Max, m;
const int a[] = { -9, -3, 4, -10, -1, -6, 8, 8, -8, -6, -5, -5, -9, -3, -9, 1, 5, -8, -3, 6, 1, -6, -8, 3, 2, -8, -9, 6, 8, 5, -3, -4, 1, 8, -1, 2, -3, 9, 5, 4, -7, 1, -8, 3, 3, -6, -9, 1, 3, -2, -3, -6, -8, 7, 7, 9, -7, -9, -1, 8, 6, 5, 0, -8, -2, -4, -10, -8, -6, -2, -4, -5, 0, -1, 0, 0, -4, -9, 3, -2, -1, -7, -6, 4, 6, -10, -4, 6, 1, -2, -6, 9, -4, -7, 7, 8, 8, -8, -1, -9, 3, 5, 9, 8, -6, 0, 7, -4, 3, -4, -9, -5, -6, 2, 0, -1, 7, 3, 7, 2, -4, 0, -9, 6, 5, -3, 5, 4, 1, 2, 0, 0, -9, -6, -4, 0, -3, 1, -3, 7, 7, -3, 3, -7, -5, -1, -1, 8, -9, -2, -8, -4, -4, 0, 3, -2, -10, 1, -8, 5, 0, 9, -6, 7, -2, -7, 5, -9, -8, 0, 1, 6, 4, -10, 6, -9, -2, 9, -2, -6, -9, 4, 3, 9, 8, 8, -10, -2, -3, -3, -2, 3, -2, -7, -3, -9, 0, 7, 3, 4, -1, 6, 5, 1, -10, -1, 9, 6, 8, -7, -6, -2, -6, -1, -1, -8, 5, -5, 3, -7, -7, -3, 4, -7, -2, -10, 8, 8, -10, 6, 8, -9, -1, 8, -1, 7, 2, -8, 8, 2, 8, 9, 0, 7, 8, 1, 5, -2, 6, 1, -8, 4, 2, 5, -2, -4, -8, -4, 5, 3, -1, -8, -6, 6, -9, 8, -8, 1, -9, 9, 7, 6, 2, -1, 5, 2, -10, 0, -7, -1, -9, -2, -9, -1, 5, -7, -8, -5, -8, -5, -2, -4, 7, 7, 2, 2, -1, 4, -9, -1, 6, -1, -2, 2, -5, 5, 4, -1, -9, 2, -5, -10, 8, 3, 9, -7, 9, 6, -3, -1, -1, 7, -4, -1, 3, 5, 7, 6, -4, -5, -2, -8, 5, 4, 4, -9, 6, 1, -4, -7, 3, 5, -5, 3, 2, -2, 2, -5, 3, 6, -9, 8, -4, -8, -9, -6, 6, -8, -1, -9, 5, 0, 3, -4, -6, 9, 2, 9, 3, -6, 4, 0, -5, -1, -4, 3, 4, -8, -2, 8, -3, -5, 8, 1, -8, -5, 7, 4, 4, -6, 4, 4, 2, 9, 3, 0, -3, 7, 8, 7, 3, 0, -7, -2, 2, -5, -8, -10, 3, -3, -8, 9, -3, -5, -7, -6, -7, 2, -9, -9, -7, 2, -5, 3, -5, 1, 2, -8, -8, -4, -6, -10, -3, -10, 3, 4, 0, 0, 5, 3, 1, -10, 6, -3, -5, 0, -10, 4, 5, -6, 9, -5, 6, 4, 8, -8, 1, -4, 8, 3, 1, -6, 8, -6, 9, 2, -10, -3, -10, -4, 0, 7, 0, 5, 6, -3, -7, 8, -6, -1, -8, -4, -5, -3, 7, 4, -5, -3, -1, -2, -7, -10, -8, -8, -7, 6, 7, -9, 5, -5 };
for (m = 0; m<n; m++)
{
cout << a[m] << '\t';
}
cout << endl;

DWORD dwTimeBegin, dwTimeEnd;
dwTimeBegin = GetTickCount();

Max = MaxSubsequenceSum(a, n);
dwTimeEnd = GetTickCount();
cout << "Max is " << Max << endl;
cout << "time " << dwTimeEnd - dwTimeBegin << "ms" << endl;
getchar();
return 0;
}

1. 首先,最基本的想法是,将所有的子序列的和遍历一遍,并通过判断语句挑选出最大值。在遍历过程中,我们需要确定子序列的起点和长度,这需要两个for循环进行实现,同时我们还需要一个for循环求和,因此,该算法一共需要3个嵌套的for循环。
代码如下:

int MaxSubsequenceSum(const int a[], int n)
{
int ThisSum, MaxSum, i, j, k;
MaxSum = 0;
for (i = 0; i<n; i++)
{
for (j = i; j<n; j++)
{
ThisSum = 0;
for (k = i; k<j; k++)
{
ThisSum += a[k];
}
if (ThisSum>MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}

运行结果如下:



2.为了对算法1进行改进,我们很容易想到,在使用一个for循环确定了子序列起点以后,子序列的长度和求和/判断可以在一个for循环中实现。这样的操作同样可以遍历所有子序列,具体的代码如下:

int MaxSubsequenceSum(const int a[], int n)
{
int ThisSum, MaxSum, i, j;
MaxSum = 0;
for (i = 0; i<n; i++)
{
ThisSum = 0;
for (j = i; j<n; j++)
{

ThisSum += a[j];

if (ThisSum>MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}

运行结果如下:



可以看到,算法2比算法1只少了一个嵌套的for循环,运行时间却比1快了2个数量级,由此可见,算法的优化对程序的运行非常重要。

3. 最优解法,只使用一个for循环即可解决该问题,该代码的正确性可在其他博客中查得,建议读者自行思考,代码如下:

int MaxSubsequenceSum(const int a[], int n)
{
int ThisSum, MaxSum, i;
ThisSum=MaxSum = 0;
for (i = 0; i<n; i++)
{

ThisSum += a[i];

if (ThisSum>MaxSum)
MaxSum = ThisSum;
else if(ThisSum<0)
ThisSum=0;
}
return MaxSum;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: