「貪欲アルゴリズムについてのすべて | 初心者ガイド」

美とファッションの魅力 | 初心者ガイド

新しい目的地への旅行を想像してみてください、おそらく最も速いルートを見つけるためにGPSナビゲーションを使用します。まるで見知らぬ道路で効率的な時間を過ごすように、貪欲法は常に最も明らかで即座の利益がある次のステップを選択します。貪欲法は常に各ステップで最良の選択肢を選び、時間効率の良いアプローチで解決策を得る方法を提供してくれます。

さて、貪欲法についてもっと学ぶために例で考えてみましょう — コインのおつり問題

次に、おつりとして31枚のコインを与える必要があり、利用できる額面は20、10、5、2、1です。

コインのおつり問題は、貪欲アルゴリズムのアプローチを使います —

  • まず、総合金額よりも小さな額面の最大のコインを見つけます。
  • その額面を結果に加え、総合金額から差し引きます。
  • もし総合金額がゼロになったら、額面を表示します。
  • それ以外の場合は、ステップ1と2を繰り返します。

貪欲アルゴリズム:定義

貪欲アルゴリズムはアルゴリズム問題解決で使用される最適化アルゴリズムのセットです。貪欲アプローチの中心思想は、各段階でローカルに最適な選択をすることで、グローバルな最適解を見つけることを期待することです。動的計画法とは異なり、サブ問題を解決し、計算を繰り返さないために解答を保存するのではなく、貪欲アルゴリズムはより良い選択肢を時点で選ぶという決定をします。

いつ貪欲アルゴリズムを選ぶべきか?

貪欲アルゴリズムは、次のような特性を持つ問題に適しています:

  1. 貪欲選択:貪欲アルゴリズムは常に最善の意思決定をし、最適なグローバル解にたどり着くために各ステップで行動します。このプロセスでは、過去の意思決定を見直したり変更したりすることは考慮しません。もし、意思決定を見直す必要や変更する必要がない場合は、貪欲アルゴリズムを選択できます。
  2. 最適な部分構造:問題を最良の意思決定が問題となる一連のサブ問題に分割することで、総合的な解法に取り組むことができる場合、貪欲アルゴリズムを使用することができます。
  3. 時間効率:貪欲アルゴリズムはシンプルで時間効率が高いです。グローバル最適解に迅速にアプローチし、過去の意思決定を考慮しないかもしれません。

今、貪欲アルゴリズムを使う3つの問題に取り組んでみましょう。

活動選択問題 — 重ならない区間

intervalsという区間の配列が与えられ、残りの区間が重ならないようにするために削除する必要のある最小の区間数を返します。

例1:

入力: intervals = [[1,2],[2,3],[3,4],[1,3]] 出力: 1 説明: [1,3]を削除すれば、残りの区間は重なりません。

例2:

入力: intervals = [[1,2],[1,2],[1,2]] 出力: 2 説明: [1,2]を2つ削除する必要があります。

例3:

入力: intervals = [[1,2],[2,3]] 出力: 0 説明: 既に区間は重なっていないため、削除する必要はありません。

コード:

class Solution:    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:        intervals.sort(key=lambda x: x[1])        last_end = intervals[0][1]        count = 0        for s, e in intervals[1:]:          if last_end>s:            count += 1          else:            last_end = e        return(count)

上記のコードは貪欲アルゴリズムのアプローチに従っています。最適な部分構造の特性を追うために、入力リストをソートして、それを小さな問題に分割します。次に、ネストされたリスト内の各リストが前のリストと重ならないようにし、それに基づいて私たちの答えを増やします。

ハフマン符号化

ハフマン符号化は、非可逆データ圧縮のためのアルゴリズムです。ハフマン符号化の目標は、最も頻繁に発生する文字には短いバイナリコードを割り当て、出現頻度が低い文字には長い文字を割り当てることによって、データを表現することです。

アプローチ:

  • データの各文字の出現頻度を計算します。
  • より頻繁なシンボルには短いバイナリコードが割り当てられ、より頻度の低いシンボルにはより長いバイナリコードが割り当てられるようにツリーが構築されます。
  • 各文字にバイナリコードが割り当てられます。ツリーのルートから特定の文字へのパスは、そのバイナリコードを一意に定義します。
  • これらのシンボルに割り当てられたバイナリコードは、ハフマン符号として形成されます。元のデータはハフマン符号で置き換えられます。
  • 同じハフマンツリーを使用してデータを復元します。ルートから開始して、バイナリコードを走査し、元のシンボルを再構築します。

例 — ツリーの構築:

テキスト「ABRACADABRA」を考えてみましょう。ここでは、「A」が5回、「B」と「R」が2回、「C」と「D」が1回出現します。これらの出現頻度に基づいて、ハフマンツリーが構築され、以下のようになる可能性があります。

この例では、「A」にはコード「0」が割り当てられるかもしれません。「B」にはコード「110」、「R」にはコード「111」、「C」にはコード「10」、「D」にはコード「1110」が割り当てられるかもしれません。ハフマン符号化は、ZIPファイルの圧縮、JPEGのイメージ圧縮、ネットワークプロトコルなど、さまざまなアプリケーションで広く使用されています。頻繁なシンボルには短いコードが割り当てられるため、データ全体の圧縮が行われます。

コード:

import heapqfrom collections import defaultdictclass Node:    def __init__(self, symbol=None, frequency=None):        self.symbol = symbol        self.frequency = frequency        self.left = None        self.right = None    def __lt__(self, other):        return self.frequency < other.frequencydef build_huffman_tree(frequencies):    heap = [Node(symbol=s, frequency=f) for s, f in frequencies.items()]    heapq.heapify(heap)    while len(heap) > 1:        left = heapq.heappop(heap)        right = heapq.heappop(heap)        merged = Node(frequency=left.frequency + right.frequency)        merged.left = left        merged.right = right        heapq.heappush(heap, merged)    return heap[0]def generate_huffman_codes(node, code="", mapping=None):    if mapping is None:        mapping = {}    if node.symbol:        mapping[node.symbol] = code    if node.left:        generate_huffman_codes(node.left, code + "0", mapping)    if node.right:        generate_huffman_codes(node.right, code + "1", mapping)    return mappingdef huffman_encode(text):    frequencies = defaultdict(int)    for symbol in text:        frequencies[symbol] += 1    root = build_huffman_tree(frequencies)    codes = generate_huffman_codes(root)    encoded_text = "".join(codes[symbol] for symbol in text)    return encoded_text, roottext_to_encode = "ABRACADABRA"encoded_text, huffman_tree_root = huffman_encode(text_to_encode)print("Original Text:", text_to_encode)print("Encoded Text:", encoded_text)

上記のコードで使用されているコレクションについて詳しくは、次の記事を参照してください。

PythonのCollectionsモジュールについてすべて

私たちは皆、Pythonには独自の通常のデータ型のヒーロー – リスト、タプル、ディクショナリ、そして悪名高いセットがあることを知っています。しかし、それとともに…

pub.towardsai.net

ダイクストラのアルゴリズム

ダイクストラのアルゴリズムは、グラフ理論で使用されるアルゴリズムであり、主に正のエッジの重みを持つグラフにおいてノード間の最短パスを見つけるために使用されます。

アプローチ:

  • ノードまでの距離を追跡するために優先度キューを初期化します。ソースへの距離を0に設定し、他のすべてのノードに対してはinfとします。
  • 距離が最小のノードから、他の隣接ノードへの距離を更新します。選択されたノードから他のノードへの現在の距離の和を計算することで行います。
  • 距離を調査した後、そのノードを訪問済みとしてマークし、再び訪問しないようにします。すべてのノードが訪問されるまでこのプロセスを繰り返します。

例:

ノード: A、B、C、Dエッジ: (A、B、4)、(A、C、5)、(A、D、2)、(B、C、1)、(B、D、3)、(C、D、5)

Dijkstraのアルゴリズムを使用して、ノードAから他のすべてのノードへの最短経路を見つけましょう:

  • ノードAから開始します。Aへの距離は0で、他のすべてのノードへの距離はinfとします:(A、0)、(B、∞)、(C、∞)、(D、∞)
  • これらの距離で優先度キューを初期化します。
  • 距離が0のAを選択します。隣接ノードB(重み4)、C(重み5)、D(重み2)を調査します。
  • 距離を更新します:(A、0)、(B、4)、(C、5)、(D、2)
  • Aを訪問済みとしてマークします。すべてのノードが調査されるまで繰り返します。
  • 距離が2のDを選択します。隣接ノードB(重み3)とC(重み5)を調査します。
  • 距離を更新します:(A、0)、(B、4)、(C、5)、(D、2)(Dに変更なし)。Dを訪問済みとしてマークします。
  • 他のすべてのノードに対してプロセスを繰り返します。
  • Aから他のノードへの最短経路は次のとおりです:
  • AからBへ:[A、D、B](総距離:4 + 3 = 7)
  • AからCへ:[A、D、C](総距離:2 + 5 = 7)
  • AからDへ:[A、D](総距離:2)

コード:

import heapqfrom collections import defaultdictdef dijkstra(graph, start):    distances = {node: float('infinity') for node in graph}    distances[start] = 0    priority_queue = [(0, start)]    while priority_queue:        current_distance, current_node = heapq.heappop(priority_queue)        if current_distance > distances[current_node]:            continue        for neighbor, weight in graph[current_node].items():            distance = current_distance + weight            if distance < distances[neighbor]:                distances[neighbor] = distance                heapq.heappush(priority_queue, (distance, neighbor))    return distancesgraph = {    'A': {'B': 4, 'C': 5, 'D': 2},    'B': {'A': 4, 'C': 1, 'D': 3},    'C': {'A': 5, 'B': 1, 'D': 5},    'D': {'A': 2, 'B': 3, 'C': 5}}start_node = 'A'shortest_distances = dijkstra(graph, start_node)print(f"{start_node}からの最短距離:")for node, distance in shortest_distances.items():    print(f"{node}へ:{distance}")

このリンクで貪欲アルゴリズムに関連する問題を探索できます。

注意:問題に対して最適なアプローチを選択することが重要です。貪欲アルゴリズムは常に最適な解を提供するわけではありません。たとえば、コインの両替の問題では、デノミネーションが[3,5]であり、金額が9の場合、貪欲アルゴリズムは失敗します。ただし、それ以外にも機能する多くの他の解法があります。したがって、アプローチを賢く選択しましょう。

以下の記事で、データ構造と動的計画法についてもっと学ぶことができます:

バックトラックを理解する:Python入門ガイド

バックトラックは、問題のすべての可能な解を検索するために使用されるアルゴリズムです。このテクニックでは、…

pub.towardsai.net

Pythonコードの強化:メモ化 vs タブレーション

メモ化は、コンピュータプログラミングとアルゴリズム設計で使用される特定の最適化テクニックで…

blog.devgenius.io

この記事が役に立つことを願っています… 学びを楽しんでください!

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

AI研究

黄さんの法則に留意する:エンジニアたちがどのように速度向上を進めているかを示すビデオ

話の中で、NVIDIAのチーフサイエンティストであるビル・ダリー氏が、モーアの法則時代後のコンピュータパフォーマンスの提供...