最大子列和問題(分治法和在線處理)


    時間複雜度太大,該算法不可取。


    

    該時間複雜度爲O(N),一般遇到爲次複雜度的可以考慮改進算法,使其複雜度爲O(logN),將會有大的改善。




分治法在每一層遞歸上都有三個步驟:
分解:將原問題分解爲若干個規模較小,相互獨立,與原問題形式相同的子問題;
解決:若子問題規模較小而容易被解決則直接解,否則遞歸地解各個子問題;
合併:將各個子問題的解合併爲原問題的解。
針對此問題,分析方法如下:
1、先一分爲二:左邊爲4,-3,5,-2;右邊爲:-1,2,6,-2
2、對左邊再一分爲二:左邊爲4,-3;右邊爲5,-2。計算左邊子序列大於0的值分別爲4和1,取4;計算右邊子序列大於0的值分別爲5和3,取5
3、對左邊(4,-3,5,-2)再進行合:連續子序列跨越分界線計算的大於0的值爲4-3+5=6最大,再與4和5相比較,得出6爲最大值。
4、對右邊再一分爲二:左邊爲-1,2;右邊爲6,-2。計算左邊子序列大於0的值分別爲2和1,取2;計算右邊子序列大於0的值分別爲6和4,取6
5、對右邊(-1,2,6,-2)再進行合:連續子序列跨越分界線計算的大於0的值爲2+6=8最大,再與2和6比較,得出8爲最大值。
6、對整個序列(4,-3,5,-2,-1,2,6,-2)進行合:連續子序列跨越分界線計算的大於0的值爲4-3+5-2-1+2+6=11最大,再與6和8比較,得出11最大。
7、整個序列採用先分再合逐步遞歸的方法得出最終結果爲11
時間複雜度爲上圖已經給出的O(NlogN),針對本題,分治法依然不是最理想的算法。

在線處理的算法時間複雜度僅爲O(N),是最好的算法。

上圖是以上四種算法運行時間進行比較,明顯可以看出第四種算法最爲高效。


上述各程序代碼如下:
  1. #include <stdio.h>
  2. #include <time.h>
  3. //算法1
  4. int MaxSubseqSum1(int A[], int N, int * s, int* e)
  5. {
  6. int ThisSum, MaxSum = 0;
  7. int i, j, k;
  8. for (i = 0; i < N; i++) /* i是子列左端位置 */
  9. for (j = i; j < N; j++) /* j是子列右端位置 */
  10. {
  11. ThisSum = 0; /* ThisSum是從A[i]到A[j]的子列和 */
  12. for (k = i; k <= j; k++) 
  13. {
  14. ThisSum += A[k];
  15. }
  16. if (ThisSum > MaxSum) /* 如果剛得到的這個子列和更大 */
  17. {
  18. MaxSum = ThisSum; /* 則更新結果 */
  19. *e = j;
  20. *s = i;
  21. }
  22. } /* j循環結束 */
  23. } /* i循環結束 */
  24. return MaxSum;
  25. }
  26. //算法2
  27. int MaxSubseqSum2(int A[], int N,int * s, int* e)
  28. {
  29. int ThisSum, MaxSum = 0;
  30. int i, j;
  31. *s = 0;
  32. *e = N;
  33. for (i = 0; i < N; i++) /* i是子列左端位置 */
  34. ThisSum = 0; /* ThisSum是從A[i]到A[j]的子列和 */
  35. for (j = i; j < N; j++) /* j是子列右端位置 */
  36. ThisSum += A[j];        /*對於相同的i,不同的j,只要在j-1次循環的基礎上累加1項即可*/
  37. if (ThisSum > MaxSum)   /* 如果剛得到的這個子列和更大 */
  38. {
  39. MaxSum = ThisSum;   /* 則更新結果 */
  40. *e = j;
  41. *s = i;
  42. }
  43. } /* j循環結束 */
  44. } /* i循環結束 */
  45. return MaxSum;
  46. }
  47. //算法3
  48. int MaxSubseqSum3(int A[], int left, int right, int *s, int *e)
  49. {
  50. int i = 0, j = 0, sum = 0;
  51. int s1 = 0, s2 = 0, lefts = 0, rights = 0;
  52. int center, leftsum, rightsum;
  53. if (left == right)
  54. {
  55. if (A[left]>0)
  56. sum = A[left];
  57. else
  58. sum = 0;
  59. *s = left; *e = right;
  60. }
  61. else
  62. {
  63. center = (left + right) / 2;               //劃分
  64. leftsum = MaxSubseqSum3(A, left, center, s, e);       //對應情況1,遞歸求解
  65. rightsum = MaxSubseqSum3(A, center + 1, right, s, e);      //對應情況2,遞歸求解
  66. //求解s1
  67. for (i = center; i >= left; i--)
  68. {
  69. lefts +=A[i];
  70. if (lefts > s1) 
  71. {
  72. *s = i;
  73. *e = center/2*2;   //不清楚爲啥是這樣的
  74. s1 = lefts;
  75. }
  76. }
  77. //再求解s2
  78. for (j = center + 1; j <= right; j++)
  79. {
  80. rights = rights + A[j];
  81. if (rights > s2) 
  82. {
  83. s2 = rights; 
  84. *e = j;
  85. *s = (center+1)/2*2; //依然不清楚
  86. }
  87. }
  88. sum = s1 + s2;                //計算情況3的最大子段和
  89. if (sum<leftsum)
  90. sum = leftsum;
  91. if (sum<rightsum)
  92. sum = rightsum;
  93. }
  94. return sum;
  95. }


  96. int MaxSubseqSum4(int A[], int N, int * s, int* e)
  97. {
  98. int ThisSum, MaxSum;
  99. int i;
  100. int ss = 0;
  101. ThisSum = MaxSum = 0;
  102. for (i = 0; i < N; i++) 
  103. {
  104. ThisSum += A[i]; /* 向右累加 */
  105. if (ThisSum > MaxSum)
  106. {
  107. MaxSum = ThisSum; /* 發現更大和則更新當前結果 */
  108. *s = ss;
  109. *e = i;
  110. }
  111. else if (ThisSum < 0) /* 如果當前子列和爲負 */
  112. {
  113. ThisSum = 0; /* 則不可能使後面的部分和增大,拋棄之 */
  114. ss = i+1;
  115. }
  116. }
  117. return MaxSum;
  118. }
  119. int main() {
  120. int a[] = { -10 ,1, 2, 3, 4, - 5, - 23, 3, 7, - 21 };
  121. int result,s,e,i;
  122. clock_t start, ended;
  123. double duration;
  124. const int COUNT = 100000;
  125. start = clock();
  126. for ( i = 0; i< COUNT; i++)
  127. result = MaxSubseqSum1(a, sizeof(a) / sizeof(a[0]), &s, &e);
  128. ended = clock();
  129. duration = (double)(ended - start) / CLK_TCK;
  130. printf("ticks1    = %lf\n",(double)(ended - start));
  131. printf("duration1 = %lf\n", duration);
  132. printf("start1=%d,ended1=%d result1 = %d\n", s,e,result);
  133. start = clock();
  134. for ( i = 0; i< COUNT; i++)
  135. result = MaxSubseqSum2(a, sizeof(a) / sizeof(a[0]), &s, &e);
  136. ended = clock();
  137. duration = (double)(ended - start) / CLK_TCK;
  138. printf("ticks2    = %lf\n", (double)(ended - start));
  139. printf("duration2 = %lf\n", duration);
  140. printf("start2=%d,ended2=%d result2 = %d\n", s, e, result);
  141. start = clock();
  142. for ( i = 0; i< COUNT; i++)
  143. result = MaxSubseqSum3(a, 0, sizeof(a) / sizeof(a[0]), &s, &e);
  144. ended = clock();
  145. duration = (double)(ended - start) / CLK_TCK;
  146. printf("ticks3    = %lf\n", (double)(ended - start));
  147. printf("duration3 = %lf\n", duration);
  148. printf("start3=%d,ended3=%d result3 = %d\n", s, e, result);

  149. start = clock();
  150. for ( i = 0; i< COUNT; i++)
  151. result = MaxSubseqSum4(a, sizeof(a) / sizeof(a[0]), &s, &e);
  152. ended = clock();
  153. duration = (double)(ended - start) / CLK_TCK;
  154. printf("ticks4    = %lf\n", (double)(ended - start));
  155. printf("duration4 = %lf\n", duration);
  156. printf("start4=%d,ended4=%d result4 = %d\n", s, e, result);
  157. return 0;
  158. }
程序運行結果如下(在VS2010環境下):

可以看出算法1最差,算法4在線處理最好。
下圖所示是在VC++6.0下的運行結果:

其中算法3因爲採用遞歸算法,空間複雜度太大,運行結果出現了錯誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章