最大子数组问题算法比较


title: 最大子数组问题算法比较
date: 2019-02-08 17:23:34
tags: Algorithms

《算导》4.1-3。将暴力法和分治法进行比较。

首先定义结构体:

typedef struct {
	int left;
	int right;
	int max;
}ans;

产生随机数组以供测试

#include <time.h>
#define random(x) (rand()%x)

srand((int)time(0));
int A[NUM];
for (int i = 0; i < NUM; i++) {
	A[i] = random(100) - 50;	
}

暴力求解法

代码

ans MaxSubarray_violent(int A[], int low, int high) {
	ans a {low, high, A[low]};
	if (low == high) return a;//假设low<=high
	for (int i = low; i <= high; i++) {
		int sum = 0;
		for (int j = i; j <= high; j++) {
			sum += A[j];
			if (sum > a.max) {
				a.max = sum;
				a.left = i;
				a.right = j;
			}
		}
	}
	return a;
}

分析

运行时间f(n) = c1n²+c2n+c3

时间复杂度 O(n²)

分治法

思路

分治法首先将问题分成三种情况:最大子数组在左半部分、在右半部分、跨越中间点。前两种称为递归情况,需要继续递归下去;最后一种情况称为基本情况,可以直接求解。

而求解跨越中点的情况,只需要从中点向左、向右找最大子数组,合并即可。

代码

ans MaxSubarray_divide(int A[], int low, int high) {
	ans a = { low,high,A[low] };
	if (low == high) return a;
	ans left, right, cross;
	int mid = (low + high) / 2;
	left = MaxSubarray_divide(A, low, mid);
	right = MaxSubarray_divide(A, mid + 1, high);
	cross = MaxSubarray_divide_crossing(A, low, mid, high);
	if (left.max > right.max&&left.max > cross.max) return left;
	else if (right.max > left.max&&right.max > cross.max) return right;
	else return cross;
}
ans MaxSubarray_divide_crossing(int A[], int low, int mid, int high) {
	int sum = 0, left_max = INT_MIN, right_max = INT_MIN;
	ans a;
	for (int i = mid; i >= low; i--) {
		sum += A[i];
		if (sum > left_max) {
			left_max = sum;
			a.left = i;
		}
	}
	sum = 0;
	for (int i = mid + 1; i <= high; i++) {
		sum += A[i];
		if (sum > right_max) {
			right_max = sum;
			a.right = i;
		}
	}
	a.max = left_max + right_max;
	return a;
}

分析

分治法时间复杂度为nlgn。

交叉点

题目要求求出这两种算法的性能交叉点。用数学的角度思考也就是解方程:c1n+c2n+c3 = nlgn。由函数图像知n很小时,左边<右边;n很大时,左边>右边。在VS2017,x64平台上,我通过clock函数计时来确定交叉点:

问题规模(N)运行次数暴力法运行时长(ms)分治法运行时长(ms)
12010000208174
11010000178157
10010000147141
9510000133131
9410000138142
9010000124142
8010000101136
701000079109

观察发现,N = 94 时暴力法运行时长开始大于分治法,也就是说该方程的解n0——性能交叉点为94。
(这里我其实有担心VS做编译优化,导致运行时间不准确)

混合法

题目提到,当问题规模小于n0时采用暴力算法,大于n0时采用分治算法,再来看性能交叉点。

ans MaxSubarray_mix(int A[], int low, int high) {
	if (high < CROSS) {
		return MaxSubarray_violent(A, low, high);
	}
	return MaxSubarray_divide(A, low, high);
}

我的分析:问题规模小于n0时,混合法运行时间等于暴力法;大于n0时,运行时间等于分治法。从函数图像上看只不过是两函数的最小值,所以性能交叉点不会变化。这种方法我觉得是比较好的。

实际:发现混合法的运行时长没有规律,推测是因为我把混合法放在最后一个来测试,编译器对重复运行的函数会进行优化。需要将随机数组改成固定数组再来测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值