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

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

最大子序列和问题的描述为:在给定一列整数的情况下,求取其所有子序列的和的最大值。在本文中,我们生成一个整数数量为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;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值