33,007
社区成员
发帖
与我相关
我的任务
分享
/**
* @author ZhengYesheng
*/
public class QuarteringDemo
{
private int[] data;
public QuarteringDemo(int[] data)
{
this.data = data;
}
public int[] quartering()
{
int mid, left, right;
mid = halve(0, data.length - 1);
if (mid == -1)
{
return null;
}
else
{
System.out.println("mid=" + mid);
left = halve(0, mid - 1);
if (left == -1)
{
return null;
}
else
{
System.out.println("left=" + left);
right = halve(mid + 1, data.length - 1);
if (right == -1)
{
return null;
}
else
{
System.out.println("right=" + right);
int[] res = { left, mid, right };
return res;
}
}
}
}
private int halve(int s, int e)
{
int left = data[s];
int right = data[e];
int i = s, j = e;
while (j - 1 > i + 1)
{
// 优先选取可以使左右两边的和的差值减小的元素
int li = left + data[i + 1];
int di = Math.abs(li - right);
int rj = right + data[j - 1];
int dj = Math.abs(left - rj);
if (di > dj)
{
j--;
right += data[j];
}
else
{
i++;
left += data[i];
}
}
if (left == right)
{
return j - 1;
}
else
{
return -1;
}
}
/**
* @param args
*/
public static void main(String[] args)
{
int count = 15;
for (int i = 0; i < 100; i++)
{
int[] data = new int[count];
for (int j = 0; j < count; j++)
{
int rnd = (int) Math.floor(Math.random() * 10) - 5;
data[j] = rnd;
}
System.out.println("--------- " + i + " --------");
System.out.print("数据:\t");
printArray(data);
QuarteringDemo demo = new QuarteringDemo(data);
int[] res = demo.quartering();
System.out.print("结果:\t");
if (res != null)
{
printArray(res);
}
else
{
System.out.println("不能四等分");
}
}
}
public static void printArray(int[] array)
{
for (int i = 0; i < array.length; i++)
{
System.out.print(array[i] + "\t");
}
System.out.println();
}
}
以上代码做过简单数据的测试,可行;用随机数据,运行多次,生成的数据就没有一次是可以四等分的。procedure TForm2.btnTest2Click(Sender: TObject);
var
idx : Integer;
sStr : string;
begin
ValueCnt := 100; //15 + Random(10);
sStr := '个数:' + IntToStr(ValueCnt) + ',值=' ;
for idx := 1 to ValueCnt do
begin
iArrayValue[idx] := Random(20);
sStr := sStr + IntToStr(iArrayValue[idx])+ ',';
end;
Memo1.Lines.Add(sStr);
btnSearchClick(nil);
end;
测试数据(随机生成)之一,前后两组相等:
数:100,值=14,1,1,1,2,15,16,7,12,8,14,9,6,16,16,2,1,1,1,3,13,4,11,5,7,16,6,6,13,13,18,6,9,5,15,14,11,14,8,10,11,17,13,2,9,8,2,1,10,13,1,18,16,13,18,5,14,14,9,13,3,4,18,7,2,19,6,1,2,17,11,13,2,13,0,6,18,10,6,8,18,6,7,9,1,17,15,3,18,9,2,17,8,4,16,11,2,12,18,10,
b:10,e:94
b:13,e:90
b:23,e:84
b:25,e:82
b:30,e:77
b:33,e:73
b:37,e:69
b:44,e:58
无解!
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
btnSearch: TButton;
Memo1: TMemo;
btnInit: TButton;
btnTest: TButton;
procedure btnSearchClick(Sender: TObject);
procedure btnTestClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
iArrayValue: array[1..2000] of Integer; //待处理的数组
ValueCnt: Integer; //实际元素个数
implementation
{$R *.dfm}
function GetSum(bIdx, eIdx: Integer): Integer;
var
idx: Integer;
tmpSum: Integer;
begin
tmpSum := 0;
for idx := bIdx to eIdx do
tmpSum := tmpSum + iArrayValue[idx];
Result := tmpSum;
end;
procedure TForm2.btnSearchClick(Sender: TObject);
var
idxB: Integer;
idxE: Integer;
idxM: Integer;
SumPre, SumEnd: Integer;
SumM1, SumM2: Integer;
Cnt: Integer;
begin
Cnt := ValueCnt;
for idxB := 2 to Cnt - 5 do
begin
SumPre := GetSum(1, idxB - 1);
for idxE := Cnt - 1 downto idxB + 4 do
begin
SumEnd := GetSum(idxE + 1, Cnt);
if SumPre = SumEnd then
begin
for idxM := idxB + 2 to idxE - 2 do
begin
SumM1 := GetSum(idxB + 1, idxM - 1);
if SumM1 = SumPre then
begin
SumM2 := GetSum(idxM + 1, idxE - 1);
if SumM1 = SumM2 then
begin
Memo1.Lines.Add('位置:' + inttostr(idxB) + ',' + inttostr(idxM) + ',' + inttostr(idxE));
Exit;
end;
end;
end;
end;
end;
end;
end;
procedure TForm2.btnTestClick(Sender: TObject);
begin
ValueCnt := 9;
iArrayValue[1] := 1;
iArrayValue[2] := 1;
iArrayValue[3] := 1;
iArrayValue[4] := 2;
iArrayValue[5] := 7;
iArrayValue[6] := 2;
iArrayValue[7] := 5;
iArrayValue[8] := -1;
iArrayValue[9] := 3;
end;
end.
以下是用PHP实现的方法:复杂度O(M*N)
首先来讲讲思路:比如有如下满足条件的数组$arr =[1,3,1,3,1,3,1,3,4,4],长度为10,默认数组两头之外为0,首先我们把这个数组分成3份(左边,中间,右边),其中,左边 = 右边,比如这样[0], 1, [3,1,3,1,3,1,3,4,],4, [0] 又或者是这样[1,3] ,1,[3,1,3,1,3],4,[4],加粗的数字是从数组抽离的隔离点,是不纳入区间求和范围的,这点要注意。我们分成左中右后呢,接下来就是把中间那部分也分成两部分,比如以上的例子,可以是
[0], 1, [3,1],3,[1,3,]1,[3,4,],4, [0] ,
也可以是这样的:
[1,3] ,1,[3,1,],3,[1,3],4,[4]。其中只有后者才能满足条件。废话不多,首先我们来写一个分割中间那部分的方法,如以下的
div2Pairs(),首先我们得把中间这一段攫取下来,传入左右两个分界点,两个分解点往中间挤一挤,比如上面的
1,[3,1,3,1,3],4 ,挤掉分解点后,就只剩下[3,1,3,1,3]。现在要把[3,1,3,1,3]分成两等份(如果可以等份),因为我们必须保留一个分解点,所以不能通过求总和折半的方式,为了保证尽可能的分成两等份,我们可以分别从两头开始,假设从左边开始的和为lsum,右边开始的为rsum ,那么我们应该确保每一步操作中总有|lsum−rsum|是最小的,以避免错过任何一个可能的lsum==rsum,以下为这个方法具体实现:
<?php
/**
* [div2pairs divide the array into two group ]
* @param int $avg [dynamic average]
* @param int $ldeliIndex [the left delimeter]
* @param array &$array [the array to divide by lsum compared rsum]
* @param int $rdeliIndex [the right delimeter]
* @return [mixed] [if true return the middle delimeter,or false]
*/
public function div2pairs(int $avg,int $ldeliIndex,array&$array,int $rdeliIndex){
$lstart = $ldeliIndex+1; //the index where begin to calculate the sum from left;
$rstart = $rdeliIndex-1; //the index where begin to calculate the sum from right;
$lsum = $array[$lstart]; //init the $lsum
$rsum = $array[$rstart]; //init the $rsum
$gap = $lsum-$rsum;
while($lstart<$rstart){ //although within two 'while' but its time complexity is truely o(n);
while($lsum>$rsum && $lstart<$rstart-2){ //here the number 2 means that there must be
$rsum+=$array[--$rstart]; //a delimeter index can't not put into calculate
if($lsum-$rsum>$gap && $lstart<$rstart-2){ //avoid the gap being bigger;
$lsum+=$array[--$rstart];
}
}
while ($lsum<$rsum && $lstart<$rstart-2) {
$lsum+=$array[++$lstart];
if($lsum-$rsum<$gap && $lstart<$rstart-2){
$rsum+=$array[--$rstart];
}
$gap = $lsum-$rsum;
}
if($rsum==$lsum){
if($rstart ==$lstart+2){
if($rsum ==$avg) //true return the dividing delimeter
return $lstart+($rstart-$lstart)/2;
else
return false;
}else{
++$lstart; //you can also change these two lines like this:
$lsum+=$array[$lstart]; //--$rstart;$rsum+=$array[$rstart]
}
}else{
if($lsum<$rsum){ //here and 'while' is used to make the $lsum and $rsum more close;
++$lstart;
$lsum+=$array[$lstart];
}else{
--$rstart;
$rsum +=$array[$rstart];
}
}
$gap = $lsum-$rsum; //upgrade the gap between $lsum and $rsum;
}
return false;
}
但是在拆分中间那部分时,我们需要先确保,左边 = 右边($lsum ==$rsum),确保动态的可能是最终结果的区间和$avg,我们使用与div2Pairs()里相类似的方式,两头开工,获取$lsum ==$rsum==$avg使他们相对应的两个分解点($ldelimeter 和 $rdelimeter),然后代入div2Pair()就可以了。
但是由于所有的m个可能的div2Pair()都需要用到,所以我们不能每次都去求一边,我们可以先用一个数组$lsumArray 先把$lsum所有下标都村起来,以方便$rsum动态调用。以下的div4Pairs()将数组四等分返回左中右三个分界点,或不能等分返回false;
/**
* [div4pairs divide the array into 4 pairs with delimeter]
* @param array $array [array to divide]
* @return [mixed] [if true then return an array cluding all delimeter]
*/
public function div4pairs(array$array){
$length = count($array); //get the lengt of the array;
if($length<5) return false; //because divide into 4 equals with delimeter
$lsumArray = []; //array to store the lsum which calculate from 0;
$lsumArray[0][] = -1; //$lsumArray[$lsum] = index, if index<0 ,default -1
$end = $length-2; //the 4th pair must be >=0,default $array[$length] = 0 as well as the
//$array[-1] = 0,thought they do not exist
for ($i = 0,$lsum = 0; $i < $end; ++$i) {
$lsum+=$array[$i];
$lsumArray[$lsum][] = $i; //store the index for each lsum;
}
for($j = $length-1,$rsum = 0;$j>3;--$j){ //from right to left ,calculate the rsum;
if(isset($lsumArray[$rsum])){ //
while(false!==current($lsumArray[$rsum])){
$ldeliIndex = current($lsumArray[$rsum])+1;
if($ldeliIndex>$j-4) break; //3 elements between ldelimeter and rdelimeter($j);
if(($mid=self::div2pairs($rsum,$ldeliIndex,$array,$j))) //try to divide array into 4 equals
return [$ldeliIndex,$mid,$j]; //true then return these three delimeters index;
next($lsumArray[$rsum]);
}
}
$rsum+=$array[$j];
}
return false;
}
以下是用PHP实现的方法:复杂度O(M*N)
首先来讲讲思路:比如有如下满足条件的数组$arr =[1,3,1,3,1,3,1,3,4,4],长度为10,默认数组两头之外为0,首先我们把这个数组分成3份(左边,中间,右边),其中,左边 = 右边,比如这样[0], 1, [3,1,3,1,3,1,3,4,],4, [0] 又或者是这样[1,3] ,1,[3,1,3,1,3],4,[4],加粗的数字是从数组抽离的隔离点,是不纳入区间求和范围的,这点要注意。我们分成左中右后呢,接下来就是把中间那部分也分成两部分,比如以上的例子,可以是
[0], 1, [3,1],3,[1,3,]1,[3,4,],4, [0] ,
也可以是这样的:
[1,3] ,1,[3,1,],3,[1,3],4,[4]。其中只有后者才能满足条件。废话不多,首先我们来写一个分割中间那部分的方法,如以下的
div2Pairs(),首先我们得把中间这一段攫取下来,传入左右两个分界点,两个分解点往中间挤一挤,比如上面的
1,[3,1,3,1,3],4 ,挤掉分解点后,就只剩下[3,1,3,1,3]。现在要把[3,1,3,1,3]分成两等份(如果可以等份),因为我们必须保留一个分解点,所以不能通过求总和折半的方式,为了保证尽可能的分成两等份,我们可以分别从两头开始,假设从左边开始的和为lsum,右边开始的为rsum ,那么我们应该确保每一步操作中总有|lsum−rsum|是最小的,以避免错过任何一个可能的lsum==rsum,以下为这个方法具体实现:
<?php
/**
* [div2pairs divide the array into two group ]
* @param int $avg [dynamic average]
* @param int $ldeliIndex [the left delimeter]
* @param array &$array [the array to divide by lsum compared rsum]
* @param int $rdeliIndex [the right delimeter]
* @return [mixed] [if true return the middle delimeter,or false]
*/
public function div2pairs(int $avg,int $ldeliIndex,array&$array,int $rdeliIndex){
$lstart = $ldeliIndex+1; //the index where begin to calculate the sum from left;
$rstart = $rdeliIndex-1; //the index where begin to calculate the sum from right;
$lsum = $array[$lstart]; //init the $lsum
$rsum = $array[$rstart]; //init the $rsum
$gap = $lsum-$rsum;
while($lstart<$rstart){ //although within two 'while' but its time complexity is truely o(n);
while($lsum>$rsum && $lstart<$rstart-2){ //here the number 2 means that there must be
$rsum+=$array[--$rstart]; //a delimeter index can't not put into calculate
if($lsum-$rsum>$gap && $lstart<$rstart-2){ //avoid the gap being bigger;
$lsum+=$array[++$lstart];
}
$gap = $lsum-$rsum;
}
while ($lsum<$rsum && $lstart<$rstart-2) {
$lsum+=$array[++$lstart];
if($lsum-$rsum<$gap && $lstart<$rstart-2){
$rsum+=$array[--$rstart];
}
$gap = $lsum-$rsum;
}
if($rsum==$lsum){
if($rstart ==$lstart+2){
if($rsum ==$avg) //true return the dividing delimeter
return $lstart+($rstart-$lstart)/2;
else
return false;
}else{
++$lstart; //you can also change these two lines like this:
$lsum+=$array[$lstart]; //--$rstart;$rsum+=$array[$rstart]
}
}else{
if($lsum<$rsum){ //here and 'while' is used to make the $lsum and $rsum more close;
++$lstart;
$lsum+=$array[$lstart];
}else{
--$rstart;
$rsum +=$array[$rstart];
}
}
$gap = $lsum-$rsum; //upgrade the gap between $lsum and $rsum;
}
return false;
}
但是在拆分中间那部分时,我们需要先确保,左边 = 右边($lsum ==$rsum),确保动态的可能是最终结果的区间和$avg,我们使用与div2Pairs()里相类似的方式,两头开工,获取$lsum ==$rsum==$avg使他们相对应的两个分解点($ldelimeter 和 $rdelimeter),然后代入div2Pair()就可以了。
但是由于所有的m个可能的div2Pair()都需要用到,所以我们不能每次都去求一边,我们可以先用一个数组$lsumArray 先把$lsum所有下标都村起来,以方便$rsum动态调用。以下的div4Pairs()将数组四等分返回左中右三个分界点,或不能等分返回false;
/**
* [div4pairs divide the array into 4 pairs with delimeter]
* @param array $array [array to divide]
* @return [mixed] [if true then return an array cluding all delimeter]
*/
public function div4pairs(array$array){
$length = count($array); //get the lengt of the array;
if($length<5) return false; //because divide into 4 equals with delimeter
$lsumArray = []; //array to store the lsum which calculate from 0;
$lsumArray[0][] = -1; //$lsumArray[$lsum] = index, if index<0 ,default -1
$end = $length-2; //the 4th pair must be >=0,default $array[$length] = 0 as well as the
//$array[-1] = 0,thought they do not exist
for ($i = 0,$lsum = 0; $i < $end; ++$i) {
$lsum+=$array[$i];
$lsumArray[$lsum][] = $i; //store the index for each lsum;
}
for($j = $length-1,$rsum = 0;$j>3;--$j){ //from right to left ,calculate the rsum;
if(isset($lsumArray[$rsum])){ //
while(false!==current($lsumArray[$rsum])){
$ldeliIndex = current($lsumArray[$rsum])+1;
if($ldeliIndex>$j-4) break; //3 elements between ldelimeter and rdelimeter($j);
if(($mid=self::div2pairs($rsum,$ldeliIndex,$array,$j))) //try to divide array into 4 equals
return [$ldeliIndex,$mid,$j]; //true then return these three delimeters index;
next($lsumArray[$rsum]);
}
}
$rsum+=$array[$j];
}
return false;
}
以下这个是最常规的方法,简单一目了然。空间复杂度O(N) 时间复杂度O(N^2)
/**
* [div4pairsF another simple method to divide array into 4 equals with excluding delimeter index]
* @param array $array [array to divide]
* @return [mixed] [if true return delimeters index,or false]
*/
public function div4pairsF(array$array){
$length = count($array);
$ilength = $length-4;
$jlength = $length-2;
for ($i=0,$isum = 0;$i < $ilength; ++$i) {
for ($j=$i+1,$jsum = 0; $j < $jlength; ++$j) {
$jsum+=$array[$j];
if($jsum==$isum){
++$j;
for($k = $j+1,$ksum = 0;$k<$length;++$k){
$ksum+=$array[$k];
if($ksum==$isum){
++$k;
if($k+1<$length){
for ($n=$k+1,$nsum=0; $n < $length; ++$n) {
$nsum+=$array[$n];
}
if($nsum==$isum)
return [$i,$j,$k];
}
}
}
}
}
$isum+=$array[$i];
}
return false;
}
public function createTestingData(int $m){
for ($i=0,$data=[]; $i < $m; ++$i) {
$in = mt_rand(-$m,$m);
$data[] = $in;
}
return $data;
}
check:
$all = [1,2,5,4,-1]; // false
$all = [1,-1,2,-1,4,-3,4,2,1]; //array(3) { [0]=> int(1) [1]=> int(4) [2]=> int(7) }
$all = [1,3,1,3,1,3,1,3,4,4]; //Array ( [0] => 2 [1] => 5 [2] => 8 )
// $res = AlgorithmicTest::div4pairsF($all);
$res = AlgorithmicTest::div4pairs($all);
print_r($res);
测试两种方法
$n = 100;//1000,10000,100000,1000000
$all = AlgorithmicTest::createTestingData($n);
echo memory_get_usage(),'<br>';
$s = microtime(true);
$res = AlgorithmicTest::div4pairs($all);
// $res = AlgorithmicTest::div4pairsF($all);
echo 'times: ',microtime(true)-$s,'<br>';
echo memory_get_peak_usage(),'<br>';
结果:
memory:byte times:s
div4pairsF($all) n=100
memory used before processing:447216
times: 0.00018191337585449
memory used_peak in processin: 612104
div4pairs($all)n=100
memory used before processing:447176
times: 6.9141387939453E-5
memory used_peak in processin: 612104
div4pairsF($all) n=1000
memory used before processing:475888
times: 0.010547161102295
memory used_peak in processin: 612104
div4pairs($all) n=1000
memory used before processing:475848
times: 0.00043797492980957
memory used_peak in processin: 880960
div4pairsF($all) n=10000
memory used before processing:967408
times: 0.97418403625488
memory used_peak in processin: 967664
div4pairs($all)n=10000
memory used before processing:967368
times: 0.0051088333129883
memory used_peak in processin: 5293064
div4pairsF($all) n=100000 (已经阵亡!)
memory used before processing:4637448
(30s内无法完成)
Fatal error: Maximum execution time of 30 seconds exceeded in /usr/local/nginx/html/algorithmic/algorithmic_test/AlgorithmicTest.php on line 741
div4pairs($all) n=100000
memory used before processing:4637408
times: 0.089447975158691
memory used_peak in processin: 46877568
div4pairs($all) n=1000000
memory used before processing:33997536
times: 3.2110240459442
memory used_peak in processin: 447408504
private static HashSet<Integer> getSum(int[] array, int start, int end) {
if (start > end) {
return new HashSet<>();
}
HashSet<Integer> result = new HashSet<>();
int sum = 0;
for (int i = start; i <= end; i++) {
sum = sum + array[i];
}
result.add(sum);
result.add(array[start]);
result.addAll(getSum(array, start + 1, end));
result.add(array[end]);
result.addAll(getSum(array, start, end - 1));
return result;
}
private static boolean testArray(int[] array) {
for (int i = 0; i <= array.length - 4; i++) {
// 0到i内的集合
HashSet<Integer> result1 = getSum(array, 0, i);
for (int j = i + 1; j <= array.length - 3; j++) {
// i+1到j的集合
HashSet<Integer> result2 = getSum(array, i+1, j);
for (int m = j + 1; m <= array.length - 2; m++) {
// j+1到m的集合
HashSet<Integer> result3 = getSum(array, j+1, m);
for (int n = m + 1; n <= array.length - 1; n++) {
// m+1到n的集合
HashSet<Integer> result4 = getSum(array, m+1, n);
//判断result1 2 3 4是否都有相同结果 是返回true
}
}
}
}
return false;
}