算法分析学习日志(二):最大子序列和问题
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;
}
最大子序列和问题的描述为:在给定一列整数的情况下,求取其所有子序列的和的最大值。在本文中,我们生成一个整数数量为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;
}
相关文章推荐
- 算法分析学习日志(一):选择问题与冒泡算法
- 算法学习之最大子序列问题
- 【数据结构和算法分析】最大子序列求和问题及联机算法
- [算法学习笔记]分治法——最大子序列和问题
- HDU1003 Max Sum 最大子序列和的问题【四种算法分析+实现】
- <数据结构与算法分析 C++描述>算法分析之最大子序列和问题
- 数据结构和算法学习系列之最大子序列求和问题的O(N)时间复杂度
- 算法学习之最大子序列算法(java实现)
- 算法导论学习笔记-5.4 概率分析和指示器随机变量的应用-几个有趣的问题
- 【算法与数据结构】最大子序列和问题
- 【转】【算法学习】最大子串问题
- 【算法导论学习-007】最大子数组和问题(Maximum subarray problem)
- 算法导论-分治、最大子序列问题
- 算法笔记1-最大子序列和问题的求解
- 最大子序列和问题 算法
- 连续子序列最大和问题的分析
- 算法学习笔记----最大子数组问题
- 求最大和连续子向量问题的算法分析
- java实现最大子序列问题——————性能最优的算法
- Max Subsequence Sum 最大子序列和问题 O(NlogN)与O(N)算法的对比