【アルゴリズムの解析と設計】貪欲なアルゴリズム(前編)

目次


1. 学習のポイント

  貪欲アルゴリズムの概念を理解します。
  グリーディアルゴリズムの基本要素をマスターする
  (1)最適部分構造特性
  (2)グリーディ選択特性
  グリーディアルゴリズムと動的計画法アルゴリズムの違いを理解する
  グリーディアルゴリズムの一般理論を理解し
  、応用例を通じてグリーディ設計戦略を学ぶ
  (1)アクティビティのスケジューリング問題
  (2)最適ローディング問題
  (3)ハフマン符号化
  (4)単一ソースの最短パス
  (5)最小スパニング ツリー
  (6)マルチマシンのスケジューリング問題


2. コインを見つける問題

  硬貨にはクォーター、ダイム、ニッケル、セントの4種類があり、63セントを見つけるときに使用する硬貨が最も少ない方法を探します。
  当然、2 クォーター、1 ダイム、3 セントを考えることになりますが、現在の状況でコインが最も少ないオプションを常に使用すると、結果は通常最適な方法になります。
  硬貨には10セント、ニッケル、セントの3種類があり、10セントを探すときは使用する硬貨が最も少ない方法を探します。


2.1 概要

  名前が示すように、貪欲なアルゴリズムは常に現時点で最良の選択を行いますつまり貪欲アルゴリズムは全体的な最適な選択を考慮せず、ある意味での局所的な最適な選択にすぎません。もちろん、欲張りアルゴリズムによって得られる最終結果も全体的に最適なものになることを期待します貪欲アルゴリズムは、すべての問題に対して全体的な最適解を取得することはできませんが、多くの問題に対して全体的な最適な解を生成できます単一ソース最短パス問題、最小スパニングツリー問題など。場合によっては、貪欲アルゴリズムが全体的な最適解を取得できない場合でも、その最終結果は最適解の適切な近似になります。


3. イベント手配の問題

  イベントの手配の問題は指定されたアクティビティのセットから最大の一貫したアクティビティのサブセットを選択しますは、貪欲アルゴリズムを使用して効果的に解決できる良い例です。この問題では、共通のリソースを求めて競合する一連のアクティビティを効率的に調整する必要があります貪欲アルゴリズムは、共通リソースの使用と互換性のあるできるだけ多くのアクティビティを作成するためのシンプルかつエレガントな方法を提供します。

  n 個のアクティビティ E={1,2,…,n} のセットがあり、それぞれが同じリソース(講義会場など) の使用を必要とするものとします。このリソースを使用できるアクティビティは一度に 1 つだけです各アクティビティ i には、リソースの使用を必要とする開始時刻 si と終了時刻 fi があり、si < fi です。アクティビティ i が選択されると、ハーフオープン時間間隔 [si, fi) 中にリソースを占有します。間隔 [si, fi) と間隔 [sj, fj) が交差しない場合、アクティビティ i とアクティビティ j は互換性があると言われます。つまり、sj ≥ fi の場合、アクティビティ i はアクティビティ j と互換性があります

  例: 配置する 11 個のアクティビティの開始時刻と終了時刻を、次のように終了時刻の降順に並べるとします。
ここに画像の説明を挿入します


3.1 戦略の選択

  戦略 1: 開始時間が早いものを優先する; この方法が不可能であることを証明する
  戦略 2: 時間がかからないものを優先する; この戦略を覆す反例を示す
  戦略 3: 時間のかかるものを優先する早めの終了時間; 貪欲なアルゴリズムを使用して最適なソリューションを取得します。

  なぜなら完了時間の降順でないアクティビティを入力します。したがって、アルゴリズムのgreedySelectorは常に、最も早い完了時間を持つ互換性のあるアクティビティを選択してセットAに参加しますこの方法で互換性のあるアクティビティを選択すると、直感的に、スケジュールされていないアクティビティにできるだけ多くの時間が確保されます。言い換えれば、アルゴリズムの貪欲な選択の重要性は、可能な限り多くの互換性のあるアクティビティをスケジュールするために、残りの調整可能な期間を最大化することです
  アルゴリズムgreedySelectorは非常に効率的です。入力アクティビティが終了時間の降順に並べられている場合、アルゴリズムは n 個のアクティビティをスケジュールするのに O(n) 時間だけを必要とするため、ほとんどのアクティビティは相互に互換性のあるパブリック リソースを使用できます指定されたアクティビティが非降順で配置されていない場合、O(nlogn) 時間で再配置できます。
ここに画像の説明を挿入します
  アルゴリズムgreedySelectorの計算過程を左に示します。図の各行は、アルゴリズムの反復に対応します。影付きのバーで表されるアクティビティは、セット A に選択されているアクティビティであり、空白のバーで表されるアクティビティは、現在互換性がチェックされているアクティビティです。


3.2 イベントアレンジメントに関するプログラムコードの問題

template<class Type>
void GreedySelector(int n, Type s[], Type f[], bool A[])
{
    
    
       A[1]=true;
       int j=1;
       for (int i=2;i<=n;i++) {
    
    
          if (s[i]>=f[j]) {
    
     A[i]=true; j=i; }
          else A[i]=false;
          }
}

  各アクティビティの開始時刻と終了時刻は配列 s と f に格納され、終了時刻の降順に並べられます。

  チェックされたアクティビティ i の開始時刻 Si が、最近選択されたアクティビティ j の終了時刻 fi より小さい場合、アクティビティ i は選択されません。そうでない場合、アクティビティ i が選択され、セット A に追加されます。
  貪欲なアルゴリズムは、問題に対する全体的な最適な解決策を常に見つけられるとは限りませんただし、アクティビティ配置問題の場合、貪欲アルゴリズムは常に全体的な最適解を見つけることができます。つまり、最終的に最大の互換性のあるアクティビティ セット A を決定します。
  貪欲なアルゴリズムが正しいかどうか、最適解が得られるかどうかを証明する必要があります


3.3 一般に証明するには数学的帰納法を使用します。

  例: 任意の自然数 n について、
  1+2+…+n=n(n+1)/2 であることを
  証明します。 証明: n=1、left=1、right=1(1+1)/2=1 のとき、仮定します
  。自然数 n の方程式が成立すると
  1+2+…+n+(n+1)=n(n+1)/2+(n+1)
  =(n+1)(n/2+ 1)
  =(n +1)2(n/2+1)/2
  =(n+1)(n+2)/2


3.4 アクティビティ選択アルゴリズムの提案

  アルゴリズムが k 番目のステップまで実行され、k 個のアクティビティが選択されます (i1=1,i2,…ik)。すると、アクティビティ i1=1,i2,…ik を含む最適解 A が存在します。上記の命題によれば
  、任意の k について、アルゴリズムの最初の k ステップの組み合わせによって最適な解が得られ、最大で n 番目のステップで問題インスタンスに対する最適な解が得られます。


3.4.1 まず k=1 のときに正しいかどうかを確認する

  アクティビティ 1 を含む最適解が存在することを証明します。
  任意の最適解 A を取得し、A のアクティビティを期限の昇順に並べ替えます。A の最初のアクティビティが j≠1 の場合、A のアクティビティ j を 1 に置き換えて取得します。 A'、つまり A'=(A-{j})∪{1}、
  f1<=fj なので、A' も最適解であり、1 が含まれます。


3.4.2 誘導ステップ、k->k+1

  アルゴリズムを k 番目のステップまで実行し、アクティビティ i1=1,i2, ...   ik を選択すると、
  帰納仮説によれば、i1=1,i2,...ik を含む最適解 A が存在します。
A のアクティビティは集合 S'
  S'={ i|i∈Si>=fk}
  A={i1,i2,…ik}∪Bから選択されます。


3.4.3 導入ステップ (続き)

  B は S' の最適解です (そうでない場合、S' の最適解は B* で、B のアクティビティが B よりも多い場合、B ∪{1,i2,…ik} が S の最適解であり、より優れています) A よりも多くのアクティビティがあり、A の最適性に矛盾します
  ) S' を部分問題として扱います 帰納法によれば、S' には最適解 B' があり、S には最初のアクティビティ ik+1 があります'、および | B' |=| B| の場合、{i1,i2,…ik}∪B'={i1,i2,…ik,ik+1}∪(B'-{ik+1}) も同様です元の問題の最適化を解く


4. 貪欲アルゴリズムの基本要素

  このセクションでは、議論に焦点を当てます。貪欲なアルゴリズムを使用して解決できる問題の一般的な特徴
  特定の問題について、貪欲アルゴリズムを使用して問題を解決できるかどうか、また問題に対する最適な解決策が得られるかどうかをどのようにして知ることができますか? この質問に対して肯定的な答えを出すのは困難です。しかし、貪欲なアルゴリズムによって解決できる多くの問題から、そのような問題には一般に 2 つの重要な特性、つまり貪欲な選択特性最適な部分構造特性
がある   ことがわかります


4.1 貪欲な選択の性質

  いわゆる貪欲な選択特性とは、次のことを指します。問題に対する全体的な最適な解決策は、一連の局所的な最適な選択、つまり貪欲な選択を通じて達成できます。これは、グリーディ アルゴリズムを実現可能にする最初の基本要素であり、グリーディ アルゴリズムと動的計画法アルゴリズムの主な違いでもあります
  動的プログラミング アルゴリズムは通常、ボトムアップ方式で各部分問題を解決します。一方、貪欲アルゴリズムは通常トップダウン方式で実行され、連続した貪欲な選択を繰り返し行い、貪欲な選択が行われるたびに、目的の問題はより小さな部分問題に単純化されます
  特定の問題について、貪欲な選択特性があるかどうかを判断するには、それを証明する必要があります。各ステップで行われる貪欲な選択は、最終的に問題に対する全体的な最適な解決策につながります。


4.2 最適な下部構造特性

  いつ問題の最適解には、その下位問題の最適解が含まれます。この問題は最適な部分構造特性を持っていると言われます。問題の最適な部分構造特性は、動的プログラミングまたは貪欲アルゴリズムを使用して解決できる問題の重要な特徴です。


4.3 貪欲アルゴリズムと動的計画法アルゴリズムの違い

  貪欲アルゴリズムと動的計画法アルゴリズムはどちらも、問題に最適な部分構造プロパティがあることが必要です。これは、2 つのタイプのアルゴリズムの共通の特徴です。ただし、最適な部分構造を持つ問題を解決するには、グリーディ アルゴリズムと動的計画アルゴリズムのどちらを使用する必要がありますか? 動的計画アルゴリズムで解決できる問題は、グリーディ アルゴリズムでも解決できますか? 2 つの古典的な組み合わせ最適化問題を調べて、それらを図解してみましょう。貪欲アルゴリズムと動的計画アルゴリズムの主な違い


4.4 0-1 ナップザック問題 (動的計画法)

  n 個のアイテムとバックパックが与えられます。アイテム i の重量は Wi、その価値は Vi、バックパックの容量は C です。バックパックに入れるアイテムの合計価値が最大になるように、バックパックに
  入れるアイテムをどのように選べばよいでしょうか?バックパックに入れるか入れないか。アイテム i をバックパックに複数回ロードしたり、アイテム i の一部だけをバックパックにロードしたりすることはできません。


4.5 ナップサック問題(貪欲な選択)

  0-1 ナップザック問題と似ていますが、異なる点は、ナップザックに入れるアイテム i を選択するときに、すべてをバックパックに入れる必要はなく、項目 i の一部を選択することもできます。1≤i≤n
  これら 2 種類の問題は両方とも最適な部分構造特性を持ち、非常によく似ていますが、ナップサック問題は貪欲アルゴリズムで解決できますが、0-1 ナップサック問題は貪欲アルゴリズムでは解決できません


4.6 貪欲アルゴリズムを使用してナップザック問題を解決するための基本手順

まず、各アイテムの単位重量あたりの値 Vi/Wi を計算し、次に貪欲な選択戦略  に従って、単位重量の値が最も高いアイテムをできるだけ多くバックパックに入れますこれらのアイテムをすべてバックパックに入れても、バックパック内のアイテムの総重量は C を超えません。単位重量あたりの値が次に高いアイテムを選択し、できるだけ多くバックパックに入れます。バックパックがいっぱいになるまでこの戦略を続けます。

void Knapsack(int n,float M,float v[],float w[],float x[])
{
    
    
       Sort(n,v,w);
       int i;
       for (i=1;i<=n;i++) x[i]=0;
       float c=M;
       for (i=1;i<=n;i++) {
    
    
          if (w[i]>c) break;
          x[i]=1;
          c-=w[i];
          }
       if (i<=n) x[i]=c/w[i];
}

  アルゴリズムナップザック主な計算時間これは、単位重量あたりの価値に応じて、さまざまなアイテムを大きいものから小さいものまで分類することで構成されます。したがって、アルゴリズムの計算時間の上限は
O(nlogn) です。
  アルゴリズムの正しさを証明するには、ナップサック問題が貪欲な選択特性を持っていることも証明する必要があります。

  0-1 ナップザック問題において、貪欲選択では最適解が得られない理由は次のとおりです。この場合、バックパックが最終的にいっぱいになるという保証はありませんバックパックのスペースが部分的に使用されていないと、バックパックのスペースの 1 キログラム当たりの価値が減少します実際、0-1 ナップザック問題を考えるときは、アイテムを選択した場合と選択しなかった場合の最終的な解決策を比較し、最適な選択を行う必要があります。これは多くのことにつながります重複する部分問題これは、動的プログラミング アルゴリズムを使用してこの問題を解決できるようにするもう 1 つの重要な機能です。
  実際、これも当てはまり、動的計画法アルゴリズムは 0-1 ナップザック問題を効果的に解決できます


5. 最適な積載の問題

  コンテナのバッチは、積載能力のある船に積み込まれます。 c. コンテナ i の重量は Wi です。最適な積載の問題では、積載量を制限せずにできるだけ多くのコンテナを船に積み込む方法を決定する必要があります


5.1 アルゴリズムの説明

  最適な負荷の問題は、貪欲なアルゴリズムを使用して解決できます。最も軽い重量を最初にロードするという貪欲な選択戦略を採用すると、最適なロードの問題に対する最適なソリューションを生成できます
  数学的モデリング(省略)。

template<class Type>
void Loading(int x[],  Type w[], Type c, int n)
{
    
    
        int *t = new int [n+1];
        Sort(w, t, n);
        for (int i = 1; i <= n; i++) x[i] = 0;
        for (int i = 1; i <= n && w[t[i]] <= c; i++) {
    
    
	x[t[i]] = 1; 
	c -= w[t[i]];
	}
}

  質問は0-1 ナップザック問題の部分問題コンテナはアイテムに相当し、アイテムの重量は wi で、値 vi は 1 に等しい。船の積載制限 C はバックパックの積載制限 b に相当する
  。0 ~ 1 現在、バックパックの問題もそうですが、この特別な副問題でも解決できるのです。


5.2 貪欲な選択のプロパティ

最適荷重問題には貪欲な選択特性がある  ことが証明できます


5.3 最適な下部構造特性

  最適荷重問題には、最適な下部構造特性があります。
  による最適荷重問題の貪欲な選択特性そして最適な下部構造特性、アルゴリズムの正しさを証明するのは簡単です。
  このアルゴリズムの主な計算量は、重量に応じてコンテナを小さいものから大きいものに並べ替えることであるため、アルゴリズムに必要な計算時間は O(nlogn) です


5.4 最適荷重問題の正しさを証明するためのアイデア

  命題:荷重問題に対するサイズ n の任意の入力インスタンスに対して、アルゴリズムは最適解を取得します。軽いものから重いものまでのコンテナが 1, 2,...n として記録されていると仮定します
  。帰納的根拠: ボックスが 1 つだけ含まれる入力インスタンスに対して、貪欲な方法が (当然のことながら) 最適解を取得することを証明します。
  帰納ステップ: n 個のボックスの入力インスタンスに対して貪欲法が最適解を取得し、次に n+1 ボックスの入力インスタンスに対しても貪欲法が最適解を取得すると仮定します。


5.5 正しさの証明

  n 個のコンテナの入力に対して、貪欲な方法で最適解を取得できると仮定します。N={1,2,…n,n+1} ただし、w1<=w2<=…<=wn<=wn+1。帰納的仮説によれば、N'={2,3,…n,n +1 }、C'=C-w1、貪欲法は最適解 I' を取得します。I=I'∪{1} とします。I が元の問題 N={1 の最適解であることを証明する必要があります。 ,2,...n,n+1} 素晴らしいソリューションです。
  それ以外の場合は、1 を含む N についての最適解 I* が存在します (I* に 1 が存在しない場合は、I* の最初の要素を 1 に置き換えた解も最適解になります)、|I*|> | I| の場合、I*-{1} は N' と C' の解であり、|I* -{1} |>|I -{1} |=|I'|、I' は N' についてです。そして C の最適解は矛盾しているため、I* は N の最適解ではありません。N の最適解は I だけです。


6. ハフマン符号化

  ハフマンコーディングは、データファイルの圧縮に広く使用されている非常に効果的なコーディング方法です。圧縮率は通常 20% ~ 90% です。ハフマン コーディング アルゴリズムは、ファイル内に出現する文字の頻度テーブルを使用して、0 と 1 の文字列を使用して各文字の最適な表現を作成します。
  頻度の高い文字には短いコードを与え、頻度の低い文字には長いコードを与えると、コード全体の長さを大幅に短縮できます。


6.1 プレフィックスコード

  各文字には 0,1 の文字列がコードとして指定され、どの文字のコードも他の文字コードの接頭語であってはなりません。このエンコーディングはプレフィックス コードと呼ばれます。
  非プレフィックス コードの例は、a:001、b:00、c:010、d:01 です。
  1:01,00,001 d、b、a をデコードします。
  デコード 2: 010、00、01 c、b、d。

  プレフィックスコードの二分木表現:
  プレフィックスコード: {00000,00001,0001,001,01,100,101,11}
  頻度: {5%,5%,10%,15%,25%,10%,10%,20%}
  構築ツリー:
  0-leftサブツリー1 -
  右のサブツリーコードの
  リーフに対応する最大桁数が
  ツリーの深さになります。

  エンコードのプレフィックスの性質により、デコード方法が非常に簡単になります。
  最適なプレフィックス コードを表すバイナリ ツリーは常に完全なバイナリ ツリーです。つまり、ツリー内のどのノードにも 2 つの子ノードがあります
  平均コード長これは次のように定義されます。
ここに画像の説明を挿入します
  平均コード長を最小にするプレフィックス コード エンコーディング スキームは、特定のコード化文字セットCに対する最適なプレフィックス コードと呼ばれます。


6.2 ハフマンコードの構築

  ハフマンは、最適なプレフィックス コードを構築するための貪欲なアルゴリズムを提案し、その結果得られるコーディング スキームは ハフマン コーディングと呼ばれます
  ハフマン アルゴリズムは、ボトムアップ方式で最適なプレフィックス コードを表すバイナリ ツリー T を構築します
  アルゴリズムは |C| リーフ ノードから開始し、|C|-1 の「マージ」操作を実行して、最終的に必要なツリー T を生成します。

  例:
  入力: a:45,b:13,c:12,d:16,e:9,f:5、これからハフマン ツリーを構築し、各文字   a:1   b:011   c
  のエンコーディングを見つけます。 010   d:001   e:0001   f:0000





  この本で説明されているアルゴリズム huffmanTree では、エンコードされた文字セット内の各文字 c の頻度は f© です。f をキー値とする優先キュー Q は、アルゴリズムが現在マージしたい最小頻度の 2 つのツリーを効率的に決定するために貪欲な選択で使用されます。最小の頻度を持つ 2 つのツリーがマージされると、マージされた 2 つのツリーの頻度の合計を頻度とする新しいツリーが生成され、新しいツリーが優先キュー Q に挿入されます。n-1 個のマージの後、優先キューには 1 つのツリーだけが残り、それが必要なツリー T になります。アルゴリズムhuffmanTree は
  、最小ヒープを使用して優先キュー Q を実装します。優先キューの初期化には O(n) の計算時間が必要です。最小ヒープの RemoveMin 操作と put 操作には両方とも O(logn) 時間が必要なため、n-1 個のマージには合計 O(nlogn) の計算時間が必要ですしたがって、n 文字に対するハフマン アルゴリズムの計算時間は O(nlogn) です。


6.3 ハフマンアルゴリズムの正しさ

  ハフマン アルゴリズムの正しさを証明するには、最適プレフィックス コード問題に貪欲な選択特性と最適な部分構造特性があることを証明するだけで済みます。
  (1)貪欲な選択特性
  (2)最適な部分構造特性


6.4 最適なプレフィックスコードの性質 (補題 1)

  補助定理 1: C は文字セット、∨c∈C、f© は周波数、x、y ∈C、f(x)、f(y) が最小の周波数を持つ場合、x、 y コードワードなどは長く、最後の桁のみが異なります


6.5 最適なプレフィックスコードの性質(補題2)

  補題 2: T をプレフィックス コードの 2 分木、∨x,y ∈T、x,y を葉の兄弟、z を x,y の父、T'=T-{x,y} とします。 z の周波数 f(z)=f(x)+f(y)、T は接頭辞コード C'=(C-{x,y})∪{z} に対応する二分木、その後 B(T) =B(T') +f(x)+f(y)


6.6 アルゴリズムの正しさを証明するためのアイデア

  定理:ハフマン アルゴリズムは、サイズ n (>=2) の任意の文字セット C に対して、C の最適なプレフィックス コードのバイナリ ツリーを取得します。
  帰納的基本証明: n=2 の文字セットに対して、ハフマン アルゴリズムは最適なプレフィックス コードを取得します
  帰納法ステップ証明:ハフマン アルゴリズムがサイズ k の文字セットの最適なプレフィックス コードを取得すると仮定すると、サイズ k+1 の文字セットの最適なプレフィックス コードも取得します


6.7 帰納法の基礎

  n=2、文字セット C={x1,x2}、
  任意のコードの文字には少なくとも 1 つの 2 進数が必要です。ハフマンアルゴリズムによって得られるコードは 1 と 0 であり、これらが最適なプレフィックスコードです
  ハフマン アルゴリズムがサイズ k の文字セットに最適なプレフィックス コードを取得すると仮定します。サイズ k+1 C={x1,x2,…xk+1} の文字セットを考えます。ここで、x1,x2 ∈ C は最小頻度です。 2 つの文字の。C'=(C-{x,y})∪{z}、f(z)=f(x)+f(y) とします
  帰納的仮説に従って、アルゴリズムは文字セット C'、周波数 f(z) および f(xi) (i=3,4,...,k+1) に最適なプレフィックス コードのバイナリ ツリー T' を取得します。 )。
  x1 と x2 を z の子として T' に接続して、ツリー T を取得します。このとき、T は、C=(C'-{z})∪{x1,x2} の最適なプレフィックス コードのバイナリ ツリーになります。
  そうでない場合、より良い木 T*、B(T*)<B(T) が存在し、補題 1 によれば、その葉の兄弟は x1、x2 です。
  T* から x1 と x2 を削除し、T*' を取得します。補題 2 によると、B(T*') = B(T*)-(f(x1)+f(x2))<B(T)-( f(x1)+f(x2))=B(T')。
  これは、T' が C' に関する最適なプレフィックス コード バイナリ ツリーであることと矛盾します。


6.8 アプリケーション: ファイル結合

  質問: 異なる長さの並べ替えられたファイルのセット S={f1, f2,...fn} があるとします。ここで、fi は i 番目のファイルに含まれる項目の数を表します。バイナリ マージを使用して、これらのファイルを 1 つのソートされたファイルにマージします
  マージ プロセスはバイナリ ツリーに対応し、ファイルは葉になりますfi と fj によってマージされたファイルはその親ノードです


6.9 ペアごとの順次マージ

  例: S={21,10,32,41,18,70}
  合併コスト: 最悪の場合のワークロード
  (1) すべての計算 = 483。
  (2) (21+10+32+41)*3+(18+70)*2-5=483。
  (3) ハフマンマージ=456。

おすすめ

転載: blog.csdn.net/m0_65748531/article/details/133420143