Python の高度なアルゴリズムとデータ構造: treap を使用してデュアル インデックスを実装する (パート 1)

Python の高度なアルゴリズムとデータ構造: treap を使用してデュアル インデックスを実装する (パート 1)

\

上記で紹介したヒープ構造では、データを部分的にしかソートできません。つまり、一部の要素のソートしか知ることができません。たとえば、ルート ノードから開始して左の子または右の子に沿って移動すると、トラバースされた要素が増加 (小さなヒープ) または減少 (大きなヒープ) 関係にあることがわかりますが、左のサブツリーと右のサブツリーのノード間のソート関係を知ることはできません。

多くのアプリケーション シナリオでは、データの最大値や最小値をすばやく知るなどのヒープ特性だけでなく、要素のソート情報も知る必要があります。したがって、このセクションでは、両方の長所を実現する方法について説明します。要素が 2 つの部分から構成される一連のデータがあるとします。1 つの部分は製品名に対応し、その型は文字列です。もう 1 つの部分は製品の在庫数に対応し、その型は整数です。製品を名前で並べ替える必要があり、同時に、現在の在庫が最も少ない製品をすばやく照会する必要があります。このような特性を満たすために、対応するアルゴリズムとデータ構造をどのように設計すればよいでしょうか。

たとえば、次のようになります。

上図から、対応する要素の文字列がソートされたバイナリツリーであることがわかります。したがって、ルートノードの左側のサブツリーの要素に対応する文字列はルート文字列よりも小さく、右側のサブツリーに対応する文字列はルートノードの文字列よりも大きくなります。同時に、各要素は対応する製品の在庫数にも対応しています。在庫が最も少ない製品を追跡して、在庫切れになる前に迅速に補充できるようにする必要があります。しかし、上図からわかるように、文字列の順序性を保証するためには、商品の数量の小ヒープ特性を犠牲にしなければなりません。例えば、上図の水に対応する在庫とワインに対応する在庫は、小ヒープ特性に違反しています。ここで問題となるのは、文字列の順序性を確保しながら、数量が小ヒープ特性を満たすようにするにはどうすればよいかということです。

まず、データ構造を定義しましょう。

  1. クラス ノード:
  2. def __init__(self,キー: str, 優先度: float ):
  3. self._key =キー 
  4. self._priority = 優先度
  5. self._left: ノード = なし
  6. self._right: ノード = なし
  7. self._parent: ノード = なし
  8.  
  9. @財産
  10. def left (自分):
  11. self._leftを返す
  12.  
  13. @財産
  14. def right (自分):
  15. self._rightを返す
  16.  
  17. @財産
  18. 親(自分)を定義します。
  19. self._parentを返す
  20.  
  21. @left .setter
  22. def left (self, ノード):
  23. self._left = ノード
  24. ノード なしではない:
  25. ノード.親 = 自分
  26.  
  27. @right .setter
  28. def right (self, ノード):
  29. self._right = ノード
  30. ノード なしではない:
  31. ノード.親 = 自分
  32.  
  33. @親セッター
  34. 親を定義します(自分自身、ノード):
  35. self._parent = ノード
  36.  
  37. def is_root(self) -> bool:
  38. self.parentNone の場合:
  39. 戻る 真実 
  40. 戻る 間違い 
  41.  
  42. __repr__(self)を定義します:
  43. 戻る  "({}, {})" .format(self._key, self._priority)
  44.  
  45. __str__(自分)を定義します:
  46. repr_str: str = ""  
  47. repr_str += repr(自分自身)
  48. self.parent なしではない:
  49. repr_str += " 親: " + repr(self.parent)
  50. それ以外
  51. repr_str += " 親: なし "  
  52.  
  53. 自分自身が左の場合  なしではない:
  54. repr_str += " 左: " + repr ( self.left )
  55. それ以外
  56. repr_str += " 左: なし "  
  57.  
  58. 自己が正しい場合  なしではない:
  59. repr_str += " 右: " + repr ( self.right )
  60. それ以外
  61. repr_str += " 右: なし"  
  62.  
  63. repr_strを返す
  64.  
  65. クラスTreap:
  66. __init__(self)を定義します。
  67. self.root: ノード = なし

現在の問題は、上図のような矛盾が発生した場合に、文字列がソート特性を維持し、インベントリ値が小さなヒープ特性を満たすようにどのように調整するかということです。いくつかの状況に応じて異なるアクションを取る必要があります。以下に示すように、最初のものを見てみましょう。

上の図から、親ノードと左の子ノードが数値的にヒープの性質に違反している状況が 1 つあることがわかります。このとき、右回転操作を実行します。手順は次のとおりです。1. Beer ノードが反時計回りに回転して、親ノードを置き換えます。2. 親ノード Cabbage が時計回りに回転して、Beer の右の子ノードになります。3. Beer の元の右の子ノードが Cabbage の左の子ノードに変換されます。完了後の結果は、次の図のようになります。

このとき文字列はソートされた二分木の性質を維持しており、数値に対応する小さなヒープの性質も満たされていることがわかります。コードの実装を見てみましょう:

  1. クラスTreap:
  2. __init__(self)を定義します。
  3. self._root: ノード = なし
  4.  
  5. def right_rotate(self, x: ノード):
  6. xNoneまたはx.is_root() 真実
  7. 戻る 
  8.  
  9. y = x.親
  10. if y.left != x: # 右に回転するには左の子である必要があります
  11. 戻る 
  12.  
  13. p = y.親
  14. p  not None: # 右回転を実行する
  15. p.left == yの場合:
  16. p.左= x
  17. それ以外
  18. p.右= x
  19. それ以外
  20. 自己._root = x
  21.  
  22. y.左= x.右 
  23. x.右= y

次に、上記の実装が正しいかどうかをテストするためにいくつかのデータを構築します。

  1. def setup_right_rotate():
  2. 小麦粉: Node = Node( "小麦粉" , 10)
  3. キャベツ: Node = Node( "キャベツ" , 77)
  4. ビール: Node = Node( "ビール" , 76)
  5. ベーコン: Node = Node( "ベーコン" , 95)
  6. バター: Node = Node( "バター" , 86)
  7.  
  8. flour.parent = なし
  9. 小麦粉。=キャベツ
  10. flour.right = なし
  11. キャベツ.左= ビール
  12.  
  13.  
  14. ビール.左=ベーコン
  15. ビール。はバター
  16.  
  17. 小麦粉、ビールを返す
  18.  
  19. def print_treap(n: ノード):
  20. nNone の場合:
  21. 戻る 
  22.  
  23. 印刷(n)
  24. print_treap( n.left )
  25. print_treap(名詞)
  26.  
  27. トレップ = トレップ()
  28. ルート、x、キャベツ = setup_right_rotate()
  29. print( "---------右回転前---------:" )
  30. print_treap(ルート)
  31. treap.right_rotate(x)
  32. print( "------右回転後-------" )
  33. print_treap(ルート)

上記のコードを実行した後の出力は次のようになります。

  1. ---------右回転前---------:  
  2. (小麦粉、10) 親: なし: (キャベツ、77): なし
  3. (キャベツ、77) 親: (小麦粉、10): (ビール、76): (卵、129)
  4. (ビール、76) 親: (キャベツ、77): (ベーコン、95): (バター、86)
  5. (ベーコン、95) 親: (ビール、76): なし: なし
  6. (バター、86) 親: (ビール、76): なし: なし
  7. (卵、129) 親: (キャベツ、77): なし: なし
  8. -------右回転後-------  
  9. (小麦粉、10) 親: なし: (ビール、76): なし
  10. (ビール、76) 親: (小麦粉、10): (ベーコン、95): (キャベツ、77)
  11. (ベーコン、95) 親: (ビール、76): なし: なし
  12. (キャベツ、77) 親: (ビール、76): (バター、86): (卵、129)
  13. (バター、86) 親: (キャベツ、77): なし: なし
  14. (卵、129) 親: (キャベツ、77): なし: なし

右回転の前後のバイナリ ツリーの出力を比較すると、回転したバイナリ ツリーによって印刷される情報は、上記の回転後の対応する画像と確かに一致しています。次に、左回転を実装します。まず、上図のキャベツノードに対応する値を 75 に変更して、そのノードと親ノードが小さいヒープ プロパティに違反するようにします。

必要な作業は次のとおりです。1. キャベツ ノードをビールの位置に「左」に回転します。2. ビールの親ノードをキャベツに設定します。3. ビールの右の子をキャベツの左の子に設定します。4. キャベツの左の子がビールになります。左回転後、バイナリ ツリーは次のようになります。

上の図から、左回転後も文字列はバイナリツリーソートを維持し、数値出力もスモールヒープ原則に準拠していることがわかります。対応するコード実装を見てみましょう。

  1. クラスTreap:
  2. ...
  3.  
  4. def left_rotate(self, x:ノード):
  5. xNoneまたはx.is_root() 真実
  6. 戻る 
  7.  
  8. y = x.親
  9. yが正しい場合   not x: # 右の子だけが左に回転できる
  10. 戻る 
  11.  
  12. p = y.親
  13. p なしではない:
  14. p.leftの場合  y:
  15. p.左= x
  16. それ以外
  17. p.右= x
  18. それ以外
  19. 自己._root = x
  20.  
  21. y.右= x.左 
  22. x.左= y

上記のコード実装をテストするには、まず cabbage の値を変更してから、上記のコードを呼び出します。

  1. キャベツ._優先度 = 75
  2. print( "-------左回転前--------" )
  3. print_treap(ルート)
  4. treap.left_rotate(キャベツ)
  5. print( "-------左回転後----------" )
  6. print_treap(ルート)

コードを実行した後の出力は次のようになります。

  1. -------左回転前--------  
  2. (小麦粉、10) 親: なし: (ビール、76): なし
  3. (ビール、76) 親: (小麦粉、10): (ベーコン、95): (キャベツ、75)
  4. (ベーコン、95) 親: (ビール、76): なし: なし
  5. (キャベツ、75) 親: (ビール、76): (バター、86): (卵、129)
  6. (バター、86) 親: (キャベツ、75): なし: なし
  7. (卵、129) 親: (キャベツ、75): なし: なし
  8. -------左回転後---------  
  9. (小麦粉、10) 親: なし: (キャベツ、75): なし
  10. (キャベツ、75) 親: (小麦粉、10): (ビール、76): (卵、129)
  11. (ビール、76) 親: (キャベツ、75): (ベーコン、95): (バター、86)
  12. (ベーコン、95) 親: (ビール、76): なし: なし
  13. (バター、86) 親: (ビール、76): なし: なし
  14. (卵、129) 親: (キャベツ、75): なし: なし

出力結果の説明は、上図の左回転後の結果と一致しています。 Treap は要素のキーを基準にソートされたバイナリ ツリーであるため、文字列を指定すると、その文字列が Treap 内にあるかどうかを簡単に照会できます。その本質はソートされたバイナリ ツリーの検索であり、ここではその実装については無視します。

クエリはシンプルですが、挿入後に新しいノードとその親ノードが小さいヒープのプロパティに違反する可能性があるため、ノードの挿入は少し面倒です。したがって、挿入が完了した後も、上記で実装した左回転または右回転を使用して調整する必要があります。

<<:  人工知能時代の機械の未来

>>:  Microsoft TensorFlow-DirectML 正式版リリース: WSL での GPU による機械学習の高速化

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

推薦する

Yixue EducationのCui Wei氏:将来、教育分野での授業はロボットに置き換えられるでしょう

[原文は51CTO.comより] 教育業界と人工知能が出会うと、どんな火花が散るでしょうか?国内外の...

スマートホームデバイスにおける自然言語生成の応用

スマートホームデバイスへの自然言語生成 (NLG) の統合により、テクノロジーとのやり取りの方法に革...

エッジAIとは何ですか?

エッジ AI は、今日のデジタル変革の時代に台頭している 2 つのテクノロジー、エッジ コンピューテ...

AI基盤を強化し、業界の実践に注力する---WOTグローバル人工知能技術サミット機械学習実践フォーラムの記録

[51CTO.comよりオリジナル記事] 6月21日、51CTO主催のWOT2019グローバル人工知...

Qi Lu: 人工知能の時代では、チップと基盤となるソフトウェアは基本的に作り直す必要がある

2019年5月18日、YC Chinaが開催したYC China起業家会議において、YC China...

...

JD Cityが新しいブランドアイデンティティを発表、スマートシティがJDグループの主要戦略に

3月21日、北京でiCityスマートシティカンファレンスが開催され、JD CityがJDグループの第...

OpenAI が深夜に 5 つのモデル アップデートを割引価格でリリースします。

編集者 | ヤン・ジェン現地時間1月25日、OpenAIは新モデルをリリースし、GPT-3.5 Tu...

トップエキスパートが語る: 生成型AIとロボット工学の未来

ビッグデータダイジェスト制作最近、カーネギーメロン大学、カリフォルニア大学バークレー校、Meta、N...

中国のこの場所で:人工知能の新たな革命が起こる - 中国におけるAIの現状分析

中国はなぜ米国と同じくらい多くの人工知能研究者を育成しているにもかかわらず、機械学習などの主要分野で...

人工知能の現状と今後の発展はどのようなものでしょうか?

まず、人工知能の現在の開発状況を理解しましょう。人工知能技術は現在、急速な発展期にあります。雨後の筍...

米国の専門家:中国のロボット優位性が懸念される

フォーブスは10月2日、寄稿者ティム・バジャリン氏による記事を掲載し、中国ロボットの利点と、中国と米...

20世紀の最も偉大なアルゴリズム10選

参考: 20 世紀のベスト: 編集者が選ぶトップ 10 アルゴリズム。著者:バリー・A・シプラ。アド...

...