「5つの一般的なアルゴリズム」分岐アルゴリズムとアイデアを図解で紹介

「5つの一般的なアルゴリズム」分岐アルゴリズムとアイデアを図解で紹介

[[355166]]

この記事はWeChatの公開アカウント「bigsai」から転載したもので、著者はbigsaiです。この記事を転載する場合はbigsai公開アカウントまでご連絡ください。

序文

分割統治アルゴリズムは、一般的に使用される 5 つのアルゴリズム (分割統治アルゴリズム、動的計画法アルゴリズム、貪欲アルゴリズム、バックトラッキング法、分割統治境界法) の 1 つです。多くの人は、日常の学習で分割統治アルゴリズムを知っているだけで、体系的に分割統治アルゴリズムを学習したことがないかもしれません。この記事では、分割統治アルゴリズムをより包括的に理解できるようにします。

分割統治アルゴリズムを学ぶ前に、質問させてください。誰もが子供の頃に貯金箱を持った経験があると思います。親や親戚からお金をもらったら、自分の宝物にそのお金を貯め、時々そのお金を数えていました。しかし、お金の山を扱うのは、データが大きすぎて頭で理解できず、間違いを起こしやすいので難しいかもしれません。お金をいくつかの小さな部分に分割し、それらを合計してお金の合計額を算出してもよいでしょう。

もちろん、各部分の金額がまだ多すぎると思われる場合は、分割して結合することもできます。なぜこんなにたくさんあるのかというと、

それぞれの小さなお金の山を計算する方法は、最大のお金の山を計算する方法と同じです(違いはサイズです)

そうすると、大きなお金の山の合計は、実際には小さなお金の山の結果の合計になります。これは実際には一種の分割統治の考え方です。

もちろん、このお金はすべて考え抜かれたものです...

分割統治アルゴリズムの紹介

分割統治アルゴリズムは、分割統治の考え方を使用するアルゴリズムです。分割統治とは何ですか?

分割統治とは、文字通り「分割して統治する」という意味で、複雑な問題を 2 つ以上の同一または類似のサブ問題に分割し、そのサブ問題をさらに小さなサブ問題に分割することを意味します。最後のサブ問題が単純かつ直接的に解決できるまで、分割していき、元の問題の解決策はサブ問題の解決策の組み合わせになります。コンピュータサイエンスにおいて、分割統治法は分割統治の考え方を採用した非常に重要なアルゴリズムです。分割統治法は、ソートアルゴリズム (クイックソート、マージソート)、フーリエ変換 (高速フーリエ変換) など、多くの効率的なアルゴリズムの基礎となります。

親問題をサブ問題に分解し、同じ方法で解決することは再帰の概念と一致しているため、分割統治アルゴリズムは通常、再帰的に実装されます (もちろん、非再帰的な実装もあります)。分割統治アルゴリズムの説明も文字通り理解しやすいです。実際、分割統治にはマージプロセスもあります。

  • 分割: 小さな問題を再帰的に解決する (終了層に到達するか、解決できるようになった時点で停止する)
  • 征服: 問題を再帰的に解決します。問題が十分に小さい場合は、直接解決します。
  • 組み合わせる: サブ問題の解決策から親問題を構築する

一般的に、分割統治アルゴリズムは本文で 2 つ以上の再帰呼び出しに分解され、サブクラスの問題は一般に分離されています (互いに影響を及ぼしません)。問題が大きすぎて直接解決できないが、小規模であれば簡単に解決でき、分割統治アルゴリズムの適用条件を満たす場合は、分割統治アルゴリズムを使用できます。


では、分割統治アルゴリズムを使用して問題を解決するには、どのような条件 (特性) を満たす必要があるのでしょうか?

1. 元の問題は通常、規模が大きく、直接解決するのは困難ですが、問題をある程度の大きさに縮小すればより簡単に解決できます。

2. 問題は、同じ(類似の)解決方法を持ついくつかの小さなサブ問題に分解できます。さらに、サブ問題に対する解決策は独立しており、相互に影響を与えません。

3. 問題分解のサブ問題をマージすると、問題の解決策が得られます。

分割統治アルゴリズムと再帰の間にはどのような関係があるのか​​疑問に思うかもしれません。実際、分割統治は、問題を分割、統治、統合するプロセスに焦点を当てた重要なアイデアです。再帰とは、自分自身を呼び出すことで往復のプロセスを形成する方法(ツール)であり、分割統治法では、このような往復のプロセスを複数利用することがあります。

分割統治アルゴリズムの古典的な問題

分割統治アルゴリズムの古典的な問題では、そのアイデアが重要です。実装には主に再帰を使用するため、コード実装はほとんどが非常に単純であり、この記事でもアイデアの説明に重点を置いています。

分割統治アルゴリズムの典型的な問題ですが、私はそれを 2 つのカテゴリに分類します。サブ問題が完全に独立しているものと、サブ問題が完全に独立していないものです。

1. 元の問題に対する答えがサブ問題の結果から完全に推測できる場合、サブ問題は完全に独立しています。

2. サブ問題は完全に独立しているわけではありません。一部の区間問題や区間をまたぐ問題では、分割統治法の結果が区間をまたぐことがあります。問題を検討する際には、これらを注意深く参照する必要があります。

バイナリ検索

バイナリ検索は分割統治法の一例ですが、バイナリ検索には独自の特殊性があります。

  • シーケンス順序
  • 結果は価値である

通常のバイナリ検索では、完全な区間を 2 つの区間に分割します。2 つの区間で個別に値を検索し、結果を確認する必要があります。ただし、順序付けられた区間では、結果がどの区間にあるかを直接判断できるため、2 つの区間のうちの 1 つだけを計算し、最後まで続行する必要があります。実装方法には再帰的と非再帰的の 2 つがありますが、非再帰的の方が一般的に使用されます。

  1. 公共  int searchInsert( int [] 数値, intターゲット) {
  2. if(nums[0]>=target) return 0; // プルーニング
  3. if(nums[nums.length-1]==target) return nums.length-1; // プルーニング
  4. if(nums[nums.length-1]<target) nums.lengthを返します
  5. 整数 =0、=nums.length-1;
  6. <){
  7. int中央 = (+)/2;
  8. if(nums[mid]==target)
  9. ミッドに戻ります
  10. そうでない場合 (nums[mid]>target) {
  11. = 中央;
  12. }
  13. それ以外{
  14. = 中央 + 1;
  15. }
  16. }
  17. 戻る ;
  18. }

クイックソート

クイックソートも分割統治法の一例です。クイックソートの各ラウンドでは、数字が選択され、この数字より小さい数字は左側に配置され、この数字より大きい数字は右側に配置されます。次に、2つのサブ区間は再帰的な分割統治法によって解決されます。もちろん、クイックソートは分割時に多くの作業を行い、すべての数字が最下層に分割されると、シーケンスの値がソートされた値になります。これは分割統治の現れです。


  1. パブリックvoidクイックソート( int []a, int   int  
  2. {
  3. int低 =;
  4. int高さ =;
  5. // 次の 2 つの文の順序を混在させることはできません。混在させると、配列が範囲外になります。 ! !とても重要です! ! !
  6. if(low>high)//カットオフかどうかを判断する条件として
  7. 戻る;
  8. int k=a[low]; //余分なスペースk、左端のものを基準として、最終的に左側がそれより小さく、右側がそれより大きいことを要求します。
  9. while(low<high) //このラウンドでは、左側がa[low]より小さく、右側がa[low]より大きい必要があります。
  10. {
  11. while(low<high&&a[high]>=k)//右側にkより小さい最初のものが見つかったら停止します
  12. {
  13. 高い- ;  
  14. }
  15. //これはそれより小さい最初のものを見つけます
  16. a[low]=a[high]; // 低い位置にする
  17. while(low<high&&a[low]<=k)//lowの右側でkより大きい最初のものを探し、右側のa[high]に配置します。
  18. {
  19. 低++;
  20. }
  21. a[高]=a[低];
  22. }
  23. a[low]=k; //値を割り当て、左と右の再帰で分割統治する
  24. クイックソート(a,, 下位1);
  25. クイックソート(a, low+1, right );
  26. }

マージソート(逆順)

クイックソートは分割時に多くの作業を行いますが、マージはその逆です。分割の場合、マージは数に応じて均等に分割し、マージの場合は、2 つずつ順番にマージします。これは、2 つの順序付けられたシーケンスに対して O(n) レベルの複雑さで必要な結果が得られるためです。マージソートに基づく逆数の変換も分割統治の考え方によって解決されます。

  1. プライベート静的voidマージソート( int []配列、 int   int  ) {
  2. int中央 = (+)/2;
  3. if(<)
  4. {
  5. マージソート(配列、、中央);
  6. マージソート(配列、中央+1、);
  7. 配列をマージします。、中央、
  8. }
  9. }
  10.  
  11. プライベート静的voidマージ( int []配列、 int l、 int mid、 int r) {
  12. int lindex=l; int rindex=mid+1;
  13. intチーム[] = 新しいint [r-l+1];
  14. intチームインデックス = 0;
  15. while (lindex<=mid&&rindex<=r){//まず左と右を比較する
  16. if(配列[lindex]<=配列[rindex])
  17. {
  18. チーム[チームインデックス++]=配列[lindex++];
  19. }
  20. それ以外{
  21. チーム[チームインデックス++]=配列[rindex++];
  22. }
  23. }
  24. while(lindex<=mid) // 1つが制限を超えたら、残りを順番に追加できます
  25. {
  26. チーム[チームインデックス++]=配列[lindex++];
  27.  
  28. }
  29. while(rindex<=r)
  30. {
  31. チーム[チームインデックス++]=配列[rindex++];
  32. }
  33. ( int i=0;i<teamindex;i++)の場合
  34. {
  35. 配列[l+i]=チーム[i];
  36. }
  37. }

最大部分列合計

最大部分列合計の問題を解決するには、動的プログラミングを使用できますが、分割統治アルゴリズムを使用して問題を解決することもできます。ただし、最大部分列合計は、部分列合計に長さの問題が関係するため、マージ時に単純なマージではありません。そのため、正しい結果がすべて左端または右端にあるとは限らず、結果が表示される可能性のある領域は次のとおりです。

  • 完全に中央より左
  • 完全に中央より右
  • 中央の左側と右側に2つのノードを含むシーケンス

これはグラフで表すことができます:


そのため、具体的に考えると、再帰的に求めることができない真ん中の最大値の文字列の結果を計算し、それを左右と比較する必要があります。

53. 最大部分列の合計は次のコードで実装されています。

  1. パブリックint maxSubArray( int [] nums) {
  2. 整数  max =maxsub(nums,0,nums.length-1);
  3. 戻る 最大;
  4. }
  5. int maxsub( int数値[], int   int  
  6. {
  7. if(==)
  8. nums[ left ]を返します
  9. int中央 = (+)/2;
  10. int leftmax=maxsub(nums, left ,mid); // 左の最大値
  11. int rightmax=maxsub(nums,mid+1, right );//右側の最大値
  12.  
  13. int midleft=nums[mid]; //中央から左
  14. int midright=nums[mid+1]; //右中央
  15. チーム=0;
  16. ( int i=mid;i>= left ;i --)の場合 
  17. {
  18. チーム+=数値[i];
  19. if(チーム>ミッドレフト)
  20. 左中=チーム;
  21. }
  22. チーム=0;
  23. ( int i=mid+1;i<= right ;i++)の場合
  24. {
  25. チーム+=数値[i];
  26. if(チーム>ミッドライト)
  27. 中央右=チーム;
  28. }
  29. 整数  max = midleft+midright; // 中央の最大値
  30. if(最大値< 左最大値 )
  31. 最大=左最大;
  32. if(最大値< 右最大値 )
  33. 最大=右最大;
  34. 戻る  最大;
  35. }

最も近い点

最も近いペアは、分割統治の最も成功した応用例の 1 つです。 2 次元の座標軸上には複数の点座標があり、最も近い 2 点間の距離を求める必要があります。直接求める場合、列挙型ブルート フォース法では非常に多くの計算が必要になります。通常、この問題を最適化するために分割統治法を使用します。


計算を直接 2 つの部分に分割すると、最も短い部分が左側にあり、もう 1 つが右側にある場合、問題が発生することが確実にわかります。最適化できます。

具体的な最適化計画では、x または y の次元を考慮し、データを 2 つの領域に分割し、最初に (同じ方法を使用して) 左側と右側の領域でそれぞれ最短のポイント ペアを計算します。次に、2 つの距離のうち短い方に基づいて、左と右にカバーし、カバーした左と右のポイント間の距離を計算し、最小の距離を見つけて、現在の最短距離と比較します。


このように、この 1 つの操作だけで (状況を考慮せずに)、左側の赤い点は右側のほとんどの赤い点との距離計算を回避できることがわかります (時間計算量は O(n2))。実際、左右の区間内で計算を行う際も、分割統治の計算を何度も再帰的に実行します。図に示すように:

この方法により、多くの計算を節約できます。

ただし、この分割統治法には、2 次元座標がすべて特定の軸に沿って密集する可能性があるため、効果が明らかでない可能性がある (ポイントがすべて x=2 の近くにある場合、x 分割の効果は小さくなる) という問題があるため、この点に注意してください。

杭州Dianzi 1007はどなたにもお勧めです。エアコンのコードは次のとおりです。

  1. java.io.BufferedReader をインポートします。
  2. java.io.IOException をインポートします。
  3. java.io.InputStreamReader をインポートします。
  4. java.io.OutputStreamWriter をインポートします。
  5. java.io.PrintWriter をインポートします。
  6. java.io.StreamTokenizer をインポートします。
  7. java.util.ArrayList をインポートします。
  8. java.util.Arrays をインポートします。
  9. java.util.Comparator をインポートします。
  10. java.util.List をインポートします。
  11. パブリッククラスMain {
  12. 静的 整数n;
  13. 公共 静的void main(String[] args)はIOExceptionをスローします{
  14. StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System. in )));
  15. PrintWriter出力= 新しい PrintWriter(新しい OutputStreamWriter(System.out ));
  16. //List<node>list=新しいArrayList();
  17. while( .nextToken()!=StreamTokenizer.TT_EOF)
  18. {
  19. n=( int ) in .nval;if(n==0) {break;}
  20. ノード番号[]=新しいノード[n];
  21.              
  22. ( int i=0;i<n;i++ )の場合
  23. {
  24. .nextToken(); .nvaldouble x = ;
  25. .nextToken(); .nval内のdouble y = ;
  26. // list.add (新しいノード(x,y));
  27. [i]=新しいノード(x,y)なし
  28. }
  29. Arrays.sort( no , com );
  30. ダブル  min = search( no ,0,n-1);
  31. 出力.println(String.format( "%.2f" , Math.sqrt( min )/2));出力.flush();
  32. }
  33. }
  34. プライベートスタティック ダブルサーチ(ノード[] no , int   int  ) {
  35. int中央 = (+) / 2;
  36. ダブルminleng=0;
  37. if(==) {戻り値 ダブル.MAX_VALUE;}
  38. そうでない場合、(+1==) {minleng= ( no [].x- no [].x)*( no [].x- no [].x)+( no [].y- no [].y)*( no [].y- no [].y);}
  39. そうでない場合、 minleng = min (search( no , left ,mid),search( no ,mid, right ));
  40. int ll=mid; int rr=mid+1;
  41. while( no [mid].y - no [ll].y <= Math.sqrt(minleng)/2&&ll-1> = left ) {ll --;}  
  42. while( no [rr].y- no [mid].y<=Math.sqrt(minleng)/2&&rr+1<= right ) {rr++;}
  43. ( int i=ll;i<rr;i++ )の場合
  44. {
  45. ( int j=i+1;j<rr+1;j++)の場合
  46. {
  47. ダブルチーム=0;
  48. if( Math.abs (( [i].xなし- [j].xなし)*( [i].xなし- [j].xなし))>minleng) {続行;}
  49. それ以外 
  50. {
  51. チーム=( [i].xなし- [j].xなし)*( [i].xなし- [j].xなし)+( [i].yなし- [j].yなし)*( [i].yなし- [j].yなし);
  52. チーム<minleng)minleng=チーム;
  53. }
  54. }
  55. }
  56. minlengを返します
  57.      
  58. }
  59. プライベートスタティック ダブル  min (ダブルa、ダブルb) {
  60. //TODO 自動生成されたメソッドスタブ
  61. a<b?a:b;を返します
  62. }
  63. 静的コンパレータ<node>com=新しいコンパレータ<node>() {
  64.  
  65. @オーバーライド
  66. 公共  int compare(ノードa1, ノードa2) {
  67. //TODO 自動生成されたメソッドスタブ
  68. a1.y-a2.y>0?1:-1を返します
  69. }};
  70. 静的クラス Node
  71. {
  72. ダブルx;
  73. ダブルy;
  74. パブリックノード( double x, double y)
  75. {
  76. .x = x;
  77. y = y;
  78. }
  79. }
  80. }

結論

分割統治アルゴリズムについて私が言いたいことはこれだけです。分割統治アルゴリズムで重要なのは、そのアイデアを理解することだからです。また、大きな整数の乗算、シュトラッセン行列の乗算、チェス盤のカバー、線形時間選択、ラウンドロビンスケジュール、ハノイの塔など、分割統治アルゴリズムによって解決される典型的な問題もいくつかあります。分割統治アルゴリズムのアイデアと原理を自分で研究することができます。

オリジナルリンク: https://mp.weixin.qq.com/s/jHiBOjfyMvuvGzSecsdNPw

<<:  AIを使って新薬を「発見」し、研究開発を加速させる

>>:  業界規模のナレッジグラフ:経験と課題

ブログ    
ブログ    
ブログ    
ブログ    
ブログ    

推薦する

今日の AI 開発者にとって必須のローコード ツール 22 選

翻訳者 |陳俊レビュー | Chonglou今日、人工知能ツール (AI) は非常に強力です。開発チ...

ChatGPT-4 に基づく IDEA スマート アシスタントの使い方を教えます

遅れて気づいて申し訳ありません。この記事を読んでいる友人の中には、すでにこのプラグインをインストール...

トレンド | 今後 10 年間の機械学習研究のホットスポット

[[248597]]人工知能が注目されています。技術革新は経済成長の根本的な原動力です。これらの技術...

...

ビジネスにおける人工知能のリスクと限界

ビジネスにおいては、人工知能のリスクと限界を考慮する必要があります。 AI のリスクと限界には、プラ...

...

...

LangChain と DeepInfra を使用してカスタマー サポート チャットボットを構築するためのガイド

翻訳者 |ブガッティレビュー | Chonglou日常のオンラインのやり取りの中でチャットボットを目...

AIとIoTが交通管理に及ぼす6つの影響

物流と輸送は世界貿易とサプライチェーン管理にとって極めて重要であり、テクノロジーの急速な発展により、...

...

...

人工知能は目覚めたのか?アマゾンのAIは人間の命令を聞かず不気味な笑い声を上げる

人類が人工知能の開発に熱心に取り組み始めて以来、著名な科学者ホーキング博士をはじめ、疑問や反対の声が...

データ構造フレームワークの考え方を理解すると、すべてのアルゴリズムは単なる張り子の虎に過ぎない

1. データ構造の保存方法データ構造を保存する方法は、配列 (順次ストレージ) とリンク リスト (...