部分配列で、その和が最大になるものを求める〜その2その3〜

昨日のアルゴリズムからループ一つ減らしたもの。

#include <iostream>
#include <boost/random.hpp>
#include "autoStopWatch.h"

using namespace std;
using namespace boost;

const int ARRAY_LENGTH = 10000;
float x[ARRAY_LENGTH];

//部分和を総当たりで調べて、和の最大を求める関数その2
float maxSoFar(float x[], size_t length){
	float maxsofar = 0;

	for(int i = 0; i < length; i++){
		float sum = 0;
		for(int j = i; j < length; j++){
			sum += x[j];
			maxsofar = max(maxsofar, sum);
		}
	}

	return maxsofar;
}

int main(){
	//ランダムな配列生成
	{
		mt19937 gen;
		uniform_real<float> dst(-100, 100);
		variate_generator<mt19937, uniform_real<float> > rand(gen, dst);

		//cout << "in " << endl;
		for(int i = 0; i < ARRAY_LENGTH; i++){
			x[i] = rand();
			//cout << x[i] << ", " ;
		}
		//cout << endl;
	}

	{
		AutoStopWatch sw;
		float maxsofar = maxSoFar(x, ARRAY_LENGTH);//計算
		cout << maxsofar << endl;//答え出力
	}

	return 0;
}

そいだけじゃ、なんなので、「分割して統治」アルゴリズム版も

#include <iostream>
#include <boost/random.hpp>
#include "autoStopWatch.h"

using namespace std;
using namespace boost;

const int ARRAY_LENGTH = 100000;
float x[ARRAY_LENGTH];

float max(float f1, float f2, float f3){
	return max(f1, max(f2, f3));
}

//和の最大を求める関数:分割して統治のアルゴリズム
float maxsum3(float x[], int l, int u){
	if(l > u){//要素がない場合
		return 0.0f;
	}

	if(l == u){
		return max(0.0f, x[l]);
	}

	int m = (l + u) / 2;

	//境界から左に伸びる部分配列の和最大値
	float lmax, rmax, sum;
	lmax = sum = 0.0f;
	for(int i = m; i >= l; i--){
		sum += x[i];
		lmax = max(lmax, sum);
	}

	//境界から右に伸びる部分配列の和最大値
	rmax = sum = 0.0f;
	for(int i = m + 1; i <= u; i++){
		sum += x[i];
		rmax = max(rmax, sum);
	}
	
	return max(lmax + rmax, maxsum3(x, l, m), maxsum3(x, m+1, u));
}




int main(){
	//ランダムな配列生成
	{
		mt19937 gen;
		uniform_real<float> dst(-100, 100);
		variate_generator<mt19937, uniform_real<float> > rand(gen, dst);

		for(int i = 0; i < ARRAY_LENGTH; i++){
			x[i] = rand();
		}
	}

	{
		AutoStopWatch sw;
		float maxsofar = maxsum3(x, 0, ARRAY_LENGTH - 1);//計算
		cout << maxsofar << endl;//答え出力
	}

	return 0;
}

配列の長さはアルゴリズムによってテキトーに変えています。これは、後半に紹介するアルゴリズムほど計算が早く終わってしまい、テキトーなストップウォッチでは、計測不能(0秒)になってしまうからです。


分割して統治のアルゴリズムを簡単に説明します。
配列を真ん中から2つに分けます。
すると、元の配列の最大部分和は、頭側の配列の最大部分和か、尾側の配列の最大部分和か、2つにまたがった部分配列の和か、のいずれかになります。
これを再帰的に行うことで、元の配列の最大部分和を求めることができます。


言葉だけだと、わかりにくいですね。すいません。詳しくは「珠玉のプログラミング」を参考にしていただければと思います。