title | type | weight | lastmod | |
---|---|---|---|---|
Algorithm |
docs |
1 |
|
-
-
Save showa-93/1ff2c1488c358bb2b924720696116dc2 to your computer and use it in GitHub Desktop.
title | type | weight | lastmod | |
---|---|---|---|---|
動的計画法 |
docs |
1 |
|
title | type | weight | lastmod | |
---|---|---|---|---|
ナップサック問題 |
docs |
1 |
|
TBD
title | type | weight | lastmod | |
---|---|---|---|---|
循環するケース |
docs |
1 |
|
ある順列の1番目とn番目が隣接し、循環している場合のDPに関してまとめる。
- ABC285 E - Work or Rest
- 過去にやってた
- ABC307 E - Distinct Adjacent
- 循環の起点となる1番目だけ注目し、1番目と同じかどうかの2パターンの個数を考えることで簡単にしている。
- i-1番目が1番目と同じ → i番目はm-1とりうる
- i-1番目が1番目と同じでない → 1番目と同じはとれないかつ隣と同じ数字はとれないのでi番目はm-2とりうる
- 循環の起点となる1番目だけ注目し、1番目と同じかどうかの2パターンの個数を考えることで簡単にしている。
title | type | weight | lastmod | |
---|---|---|---|---|
Graph |
docs |
1 |
|
title | type | weight | lastmod | |
---|---|---|---|---|
ダイクストラ法 |
docs |
1 |
|
単一始点最短経路問題(SSSP)を解くためのアルゴリズムのひとつ。
ベルマンフォード法($O(|E|\times|V|)$)よりも高速に動作できる一方、グラフに負の辺がある場合利用できない。(性質上最短経路を確定できなくなる)
始点を始点からの最短経路がゼロと確定した頂点とし、他の頂点を未確定の頂点(始点からの距離が十分に大きい)とする。
最短経路が確定した頂点と最短経路が未確定の頂点との間の辺とその重みを集計し、重みが最小の辺につながる頂点を最短距離が確定した頂点とする。この条件が成立するのは負の辺が存在しない場合である。負の辺がなければ最短経路になるのは最小の重みの辺であることが一意にわかる。
愚直にすべての頂点に対して最短となる辺をすべての頂点で探索した場合、計算量は$O(|V|^2)$となる。
ヒープや優先度付きキューなどを使った場合、最短距離となる辺の探索が$O(log|V|)$で計算でき、これを辺の数だけその探索をおこなう。
このときの計算量は$O(|E|log|V|)$となり、辺の数が小さければ高速に動作する。
- E - Art Gallery on Graph
- ダイクストラの考え方を利用した類問
- ダイクストラ法 - Wikipedia
- うさぎでもわかる離散数学(グラフ理論) 第14羽 ダイクストラ法による最短経路の求め方 | 工業大学生ももやまのうさぎ塾
- ダイクストラ法による単一始点最短経路を求めるアルゴリズム | アルゴリズムロジック
- Dijkstra [いかたこのたこつぼ]
- 単一始点最短経路問題
特定の1つのノードから他の全ノードとの間の最短経路問題。この問題を解くアルゴリズムとしては、ダイクストラ法やベルマン-フォード法がよく知られている。
最短経路問題 - Wikipedia
title | type | weight | lastmod | |
---|---|---|---|---|
トポロジカルソート |
docs |
1 |
|
閉路のない有向グラフの辺が左から右に向くように頂点を左から右に一列に並べるソートのこと。
グラフがトポロジカルソート可能であるということは、そのグラフがDAGであることと等価。
物事の依存関係や因果関係などをモデル化するのに有効。MakefileやJOB Scheduleなどがある。
トポロジカルソートの実装にはBFSまたはDFSを利用して実装できる。どちらの場合でも計算量は$O(|E|+|V|)$(E=辺、V=頂点)となる。
幅優先探索を利用する場合、各頂点の入次数を記録しておきます。
入次数がゼロの頂点がトポロジカルソートの開始位置となる。この頂点からBFSで探索を行います。
頂点から辺を辿って各頂点を見つけるたびに入次数を減らし、入次数がゼロになったものが次にBFSをおこなう頂点となる。
適当な頂点から探索を開始する。末尾まで探索が終わった後に頂点をたどるときの順番(帰りがけ順 postorder)の逆順がトポロジカルソートした結果となる。
前述の探索でまだ未チェックの頂点があれば同様の探索を繰り返すことでグラフのトポロジカルソートができる。
トポロジカルソートはその性質からトポロジカルソートができない=グラフに閉路が存在することを確認できる。
BFSを利用している場合、すべての探索をおこなった場合、入次数がゼロにならない頂点が存在する。
DFSを利用した場合、すでに訪問したアクティブな頂点に再度訪問した場合、そのグラフに閉路が存在することを検出できる。
- DAG → 有向非巡回グラフ - Wikipedia
グラフ理論における閉路のない有向グラフのこと
- トポロジカルソートのアルゴリズム(閉路のない有向グラフDAGのソート) | アルゴリズムロジック
- 有向非巡回グラフ(DAG)とトポロジカルソート - Qiita
- 閉路の検出 - 深さ優先探索 - アルゴリズム (翻訳)
title | type | weight | lastmod | |
---|---|---|---|---|
ベルマン-フォード法 |
docs |
1 |
|
単一始点最短経路問題(SSSP)を解くためのアルゴリズムのひとつ。
ダイクストラ法より計算量は大きいが負の辺が含まれていても利用ができる。また、負の閉路が存在する場合検出することも可能。
いずれかの頂点で最短距離が更新されなくなるか、$|V|$回目の更新が終わるまで繰り返す。
最短距離を更新する処理はすべての辺に対して、辺の終点の$v$の最短距離より辺の始点の$u$の最短距離+辺の重みのほうが小さい場合、最短距離を更新する。
1回の更新で少なくとも1つの頂点の最短距離が決まるため、始点となる頂点を除いた$|V|-1$回の更新で最短距離が求まる。
グラフ上に負の閉路が存在する場合、最短距離の更新が必ず発生するため、$|V|-1$回の更新で終わらない。よって、$|V|$回更新された場合、負の閉路が存在することがわかる。
すべての頂点の最短経路がわかるまですべての辺の走査を$|V|-1$回の更新を繰り返すため、計算量は$O(|V| \times |E|)$となる。
最短距離ではなく最大化したい場合、グラフの辺に$-1$をかけることで最大化を計算できる。
これは負の辺が存在しても計算できるために利用できる方法。ダイクストラではこの方法は適用できない。
- ベルマン–フォード法 - Wikipedia
- ベルマンフォード法による単一始点最短経路を求めるアルゴリズム | アルゴリズムロジック
- 単一始点最短経路問題
特定の1つのノードから他の全ノードとの間の最短経路問題。この問題を解くアルゴリズムとしては、ダイクストラ法やベルマン-フォード法がよく知られている。
最短経路問題 - Wikipedia
title | type | weight | lastmod | |
---|---|---|---|---|
ワーシャルフロイド法 |
docs |
1 |
|
TBD
title | type | weight | lastmod | |
---|---|---|---|---|
巡回セールスマン問題 |
docs |
1 |
|
Traveling salesman problem, TSP
都市の集合と各2都市間の移動コストが与えられたとき、全ての都市をちょうど一度ずつ巡り出発地に戻る巡回路のうちで総移動コストが最小のものを求める組合せ最適化問題である。
wikiより抜粋
NP困難とよばれる問題。多項式時間で解けるアルゴリズムがみつからず計算量的に解くのが困難な問題。
すべての経路を計算する場合、$O(n!)$のため、都市数が20以上の場合、現実的な範囲でなくなる。
集合に対する動的計画法のひとつ。$n$個のものに対して$n!$通りの順序の中から最適なものを求めるときに使える。
巡回セールスマン問題で巡回する集合の部分集合Sを巡回する
頂点uからvまでの距離を
計算量は
- 応用型
- ABC274 E - Booster
- ABC301 E - Pac-Takahashi
- お菓子が18個、スタートとゴールで合わせて20。
- 巡回セールスマン問題 - Wikipedia
- ビットDP(bit DP)の考え方
集合に対する動的計画法| アルゴリズムロジック - 巡回セールスマン問題をいろんな近似解法で解く(その1: 総当たり法とグリーディ法) - Qiita
- NP困難: 多項式時間で解ける任意の問題と比べて同等またはそれ以上に難しい問題のこと。多項式時間とは入力のサイズnに対しての処理時間の上限をnの多項式で表せるアルボリズムを指す
title | type | weight | lastmod | |
---|---|---|---|---|
強連結成分分解 |
docs |
1 |
|
強連結とは、有向グラフ$G$の部分集合$S$の任意の2頂点を選んだとき、頂点同士が互いに行き来可能であることである。
部分集合$S$にほかの頂点集合を付け加えても強連結にならないとき、部分集合$S$は強連結成分(SCC, Storongly Connected Component)である。
有向グラフを強連結成分となる部分集合ごとに分解することを強連結成分分解と呼ぶ。
強連結成分部分を頂点とみなすと非DAGをDAG(有向非巡回グラフ)とみなして走査できるようになる。
有向グラフ$G$をDFSで探索し帰りがけに頂点を番号でラベリングする。これを未探索の頂点がなくなるまで繰り返す。
辺の方向を逆向きにした$G^T$を一番大きい番号の頂点から探索する。探索が完了した頂点の集合が1つのSCCとなる。
未探索の頂点がなくなるまで探索することですべてのSCCをみつけることができる。
2回目のグラフ探索が逆グラフを探索するのは、最大番号の頂点は逆グラフだと最下流になるため、元のグラフにおける下流の連結成分まで誤って連結成分として探索してしまうことがなくなる。
計算量は頂点と辺に線形で
title | type | weight | lastmod | |
---|---|---|---|---|
超頂点 |
docs |
1 |
|
すべての集合をそのまま頂点としたグラフにするとN個の頂点の数だけ変数ができ、$O(N^2)$となるため、コストの計算が間に合わない。
そこで集合の内容の頂点とそれらをまとめる集合をあらわす頂点(超頂点)を用意しグラフを作成する。
超頂点を利用することで各集合間のつながりが超頂点を介するようになるので、辺の数が$\sum A$となり探索可能な範囲まで辺を減らすことができる。
title | type | weight | lastmod | |
---|---|---|---|---|
ハッシュ関数 |
docs |
1 |
|
任意長のメッセージに対して、メッセージを代表する固定長の値を出力する関数。ハッシュ関数の出力する固定長の値をハッシュ値と呼ぶ。
同じハッシュ関数に同じメッセージが入力された場合、同じハッシュ値が出力されなければならない。さらに入力のメッセージのサイズに問わず高速に計算できることが求められます。
ハッシュ関数が単射(→必ず唯一の値に射影される)で正しい入力に対して必ず異なるハッシュ値が一意に定まる場合のハッシュ関数を完全と呼ぶ。1つのハッシュテーブルで目的のデータを直接探すことができる。入力の範囲がわかっており変化しない場合に成立する。
n個のキーに対してn個の連続する整数がハッシュの値域となる場合、最小完全ハッシュ関数と呼ぶ。参照の単純化と無駄なデータ領域が発生しないのでコンパクトになる。
完全ハッシュ関数が有効なケースは
- 更新が無い莫大なkey、valueのデータセット
- keyの探索(lookup)が高頻度
暗号学的ハッシュ関数の安全性は以下の3種類の性質で議論される。
-
原像計算困難性(一方向性)
与えられたハッシュ値の元のメッセージを求めることが困難である性質。逆計算が困難な関数を一方向性関数と呼ぶ。
この一方向性を破ろうとする攻撃のことを原像攻撃と呼ぶ。同じハッシュ値が得られるメッセージを出力することが目的の攻撃です。 -
第2原像計算困難性
メッセージ(第1原像)とそのハッシュ値を与えられたとき、同じハッシュ値を出力するメッセージ(第2原像)を求めることが困難である性質。 -
衝突困難性
同じハッシュ値になるような2つのメッセージを求めることが困難である性質。
ハッシュ関数が出力するハッシュ値は、入力のメッセージより要素数が小さくなるため、同じハッシュ値をもつメッセージが存在する。
この同じハッシュ値が出力される現象をハッシュ値の衝突と呼ぶ。また、衝突した2つのメッセージを衝突ペアと呼ぶ。
第2原像計算困難性を破ることと衝突困難性を破ることを比較した場合、衝突困難性を破る方が実現しやすい。特定のハッシュ値の衝突より任意のハッシュ値の衝突のほうが条件がゆるいため。
攻撃が容易な性質を防ぐ方が安全性が強いため、衝突困難性のほうが安全性が高い。
衝突困難性を持つならば、異なる2つのメッセージが同じハッシュ値を持つことが困難であるため、ハッシュ値から元のメッセージの復元が困難である。
よって、衝突困難性を持つならば原像計算困難性をもつため、衝突困難性のほうが安全性が強いと言える。
アルゴリズムの安全性を表す指標としてセキュリティビットが用いられる。あるアルゴリズムに対して必要な攻撃の計算量が$2^n$のとき、そのアルゴリズムの安全性はnビットセキュリティという。
前述の安全性に対する総当り攻撃による計算量は次の表のようになる。
安全性の種類 | 計算量 | ビットセキュリティ |
---|---|---|
原像計算困難性(一方向性) | ||
第2原像計算困難性 | ||
衝突困難性 |
- 近似衝突困難性
よく似たハッシュ値をもつ2つの異なるメッセージを特定が困難である性質。 - 部分原像計算困難性
一部が不明なメッセージとそのハッシュ値から不明な一部を特定することが困難である性質。
- Miyaguchi-Preneel構造
- Merkle-Damgård構造
- Davies-Meyer構造
バースデーパラドックスを利用した攻撃。
バースデーパラドックスとは、「何人集まれば誕生日の一致するペアが存在するか」という問題(誕生日問題)を考えたときの直感の確率と数学的事実の乖離についてのパラドックです。
n個の箱にq個のボールを入れるとき$q=\sqrt{n}$のときに同じ箱に2個以上入る確率は0.3以上であることが知られている。特に$q=1.18\sqrt{n}$のときの確率は0.5以上となる。
これを誕生日問題についてあてはめると20人$(\sqrt{365})$で0.3という確率で一致するペアが存在する。また、24人$(1.18\sqrt{365})$集まれば$1/2$の確率になる。
前述の内容をハッシュ関数の世界で考えると、ハッシュ値がkビットとすると$2^k$通りの箱が存在する。
先程の数学的事実より$q=\sqrt{2^k}=2^{k/2}$回計算すると0.3以上の確率で衝突ペアが求まる。
既知のハッシュ値とメッセージから未知のSALTを先頭に結合されたメッセージのハッシュ値でも攻撃者が任意のメッセージを付与した新しいハッシュ値を計算することができる。
反復型ハッシュ関数では入力のメッセージをブロックに分けてブロックごとに処理をする。メッセージのビット数がブロックのサイズの倍数でない場合、メッセージの最後にパディングが付け足される。
最初のブロックで初期ベクトルⅣを与えブロックを処理する。計算した結果を順番に並べた結果がハッシュ値となるがブロック数が多い場合、前回計算したハッシュ値を初期ベクトルⅣとして同様の処理をする。
つまり、一つ前のブロックのハッシュ値がわかればメッセージを追加したハッシュ値を計算することが可能であることがわかる。
伸長攻撃では次のような攻撃に利用できる。(By ChatGPT4)
- データ改ざん:既知のハッシュ値とメッセージに追加のデータを付加して、新しいハッシュ値を計算することができる。
- メッセージの偽装:新しいハッシュ値を使用して偽のメッセージを送信することができる。
- 認証の回避;新しいハッシュ値を使用して認証を回避することができます。
- 署名の偽造: 新しいハッシュ値を使用してデジタル署名を偽造することができる。
- Effective DoS attacks against Web Application Plattforms – #hashDoS [UPDATE3] | Cryptanalysis – breaking news
- Webアプリケーションに対する広範なDoS攻撃手法(hashdos)の影響と対策 | 徳丸浩の日記
ハッシュテーブルの脆弱性を利用したWebアプリケーションのCPU資源を枯渇させるサービス妨害攻撃。
ハッシュテーブルのハッシュ値が衝突するような多くの異なるキーがテーブルにマップされているとハッシュテーブルに対するあらゆる操作の計算量が単純なリンクトリストの操作まで落ちる。
これを利用してHTTPのリクエストの処理にハッシュテーブルを利用しているようなサーバーに対して、ハッシュ値がおなじになるキーを大量にリクエストのパラメータに含ませることでⅠ回のPOSTリクエストでCPUを枯渇させます。
- MurmurHash - Wikipedia
- aappleby/smhasher: Automatically exported from code.google.com/p/smhasher
- 著者のmurmurhashの実装
- MurmurHash - nojima
- twmb/murmur3: Fast, fully fledged murmur3 in Go.
ハッシュベースの検索向けのハッシュ関数である。2008年にAustin Applebyによって作成された。名前は内部ループで使用される2つの基本演算、乗算(MU)と回転(R)に由来する。
最新のバージョンはMurmurHash3
である。32bitまたは128bitのハッシュ値を生成する。128bitの場合、プラットフォームごとに最適化されているためx86とx64では同じ値が得られない。MurmurHash2
は32bitまたは64bitのハッシュ値を生成する。多くのバリエーションがあり、オリジナルの修正版のMurmurHash2Aやインクリメンタルに動作するCMurmurHash2A、x64に最適化されたMurmurHash64Aなどがある。MurmurHash1
はLookup3
より高速な関数を作る試みとして作られた。MurmurHash1
ではLookup3
のように64bitのハッシュ値を生成できなかった。
MurmurHash
は衝突攻撃に脆弱な可能性がある。ランダムなシート値をつかった実装でもHashDoS攻撃に対して脆弱であることが示されている。
MurmurHash3の実装は、以下のステップで構成されています。Goによる実装
- 初期化: ハッシュ関数は、初期ハッシュ値としてシード値を受け取ります。このシード値は、異なるハッシュ値を生成するために変更することができます。
- ブロック処理: 入力データは、固定サイズのブロックに分割されます。各ブロックは、ハッシュ関数によって個別に処理されます。ブロックのサイズは、x86アーキテクチャでは4バイト、x64アーキテクチャでは16バイトです。
- ミキシング: 各ブロックは、ビットシフト、ビットXOR、乗算などの操作を使用してミキシングされます。これにより、ハッシュ値の分布が均一になります。
- 結合: ミキシングされたブロックは、初期ハッシュ値と結合されます。これによりハッシュ値が生成されます。
- 最終処理: 最後のブロックが処理された後、ハッシュ値は最終処理ステップを経て、最終的なハッシュ値が生成されます。
title | type | weight | lastmod | |
---|---|---|---|---|
Bloom Filter |
docs |
1 |
|
TBD
title | type | weight | lastmod | |
---|---|---|---|---|
ハッシュテーブル |
docs |
1 |
|
キーと値の組(エントリ)を格納し、キーに対応するあ愛を高速に参照するためのデータ構造。
ハッシュテーブルはキーをもとに生成したハッシュ値を添え字とした配列。キーを要約するハッシュ値を添え字として管理することでデータ量によらず定数時間$O(1)$の検索を実現している。
衝突しやすいハッシュ関数など選んだハッシュ関数によっては最悪の場合$O(n)$となる。
ハッシュテーブルで利用されるハッシュ関数は、高速であり、入力データに対して均等に分散されたハッシュ値を生成することが求められます。(simple uniform hashing)以下は、ハッシュテーブルでよく利用されるハッシュ関数の一部です。(By ChatGPT)
- Division Method
キーをテーブルのサイズで割った余りをハッシュ値として使用します。テーブルのサイズを素数にすると、より均等な分散が得られることが多いです。 - Multiplication Method
キーに定数を掛け、結果の小数部分を取り出して、テーブルのサイズを掛けることでハッシュ値を得ます。 - Universal Hashing
- MurmurHash
- CityHash
- FNV (Fowler-Noll-Vo) Hash
- Jenkins Hash Function
ハッシュテーブルはキーをもとに生成したハッシュ値を添え字として利用するが、ハッシュ値は多くの場合入力となるキー全体より小さいため、異なるキー値に同じハッシュ値が生成される可能性がある。(鳩の巣原理 - Wikipedia)このハッシュ値の衝突がおきた場合、異なるキーで同じハッシュ値のデータを区別して保管する必要がある。
衝突が発生したときの対処法としては、チェイン法(Chaining/連鎖法)とオープンアドレス法(Open Addressing/開番地法)に大別される。
衝突したキーのエントリを連結リストとしてもつ方式。
配列にはエントリそのものではなく、連結リストを格納する。
キーが衝突した場合、テーブルの中の開いている別のスロットを探索する方式。ハッシュ関数とは別の関数で次の候補となるスロットを求める。(再ハッシュ)
ただし、テーブルからキーを削除したときにスロットに格納されたデータを空にすると問題が発生する。削除されたキーよりあとに候補になるスロットにあるキーを検索した場合、削除されたスロットで走査が終了してしまいキーが存在しないことになってしまう。そのために削除を示す状態を保持する必要がある。
メモリ使用効率が高いが、テーブルが満杯に近づくと性能が低下する可能性がある。
ハッシュ値から求めたスロットが衝突していた場合、次のスロットを順番に調べて最初の空きスロットにデータを格納する。パフォーマンスを維持するならload factorが0.5程度になったときresizeするのが目安。
この方法の主な問題点としてPrimary clusteringが知られている。
線形探索によって連続するスロットが埋まることでクラスタ(連続するスロットのグループ)が形成されると、そのクラスタ内の衝突の確率が高くなる。クラスタは時間とともに成長し、ハッシュテーブルの性能が低下する。
次のスロットを任意の実数のi乗(
ハッシュテーブル全体に均一なレコード分布を得られる利点がある。
線形探査と異なり、クラスタが発生しないためPrimary clusteringがおきないようになっている。
しかし、異なるキーが同じスロットにハッシュされた場合、同じ探査シーケンスをもつため、特定のスロット周辺で衝突が頻発する可能性がある。この問題をsecondary clusteringと呼ぶ。
2つの独立したハッシュ関数を使って衝突の解決をおこなう。
1つ目のハッシュ関数$h_1$では最初のスロットを決めるためのハッシュ値を求める。2つ目のハッシュ関数$h_2$で次のスロットをみつけるためのステップの大きさを求める。
利点として、空きスロットを探索するステップの値を別のハッシュ関数を利用しているため、初期位置が同じでも探索範囲がことなるため衝突の発生確率がさがる。問題点としては、2つのハッシュ関数による計算の必要があるため、挿入と探索の計算量が増える可能性がある。また、2つのハッシュ関数が互いに素でなければ衝突確率が高くなる可能性がある。
- Cuckoo hashing - Wikipedia
- Cuckoo Hashing - Radium Software
- Cuckoo Hashing - Worst case O(1) Lookup! - GeeksforGeeks
- A cool and practical alternative to traditional hash tables
- GolangでBucketized Cuckoo Hashingを実装してみた - 逆さまにした
衝突解決のための処理がカッコウの雛が孵化したら巣の鳥の卵を捨ててしまうカッコウの習性と類似していることが名前の由来。
他の手法と異なり、エントリの検索が最悪でも定数時間$O(1)$内で完了する。
Cuckoo hashingでは2種類のハッシュ関数$h_1$,
キー$x$をハッシュテーブルに挿入する場合を考える。$T_1$の$h_1(x)$のスロットにキー$x$のエントリを格納する。このときスロットに既にエントリが存在した場合、古いエントリのキー$y$をもう一方のテーブル$T_2$に格納する。この追い出して格納の処理が完了するまで繰り返すことでエントリを挿入する。作成されたテーブルにはキー$x$が$T_1$または$T_2$のどちらかに入っていることが保証されるため、最悪2回の検索で見つけることができる。
ただし、エントリの挿入時に循環参照になった場合、追い出し処理が完了しないため処理回数に上限をつけテーブルをrehashする必要がある。load factorが0.5を超えない限り挿入コストは$O(1)$が保たれる。0.5を超えた場合、性能低下することがしられている。
load factorとRehash
ハッシュテーブルはエントリの数が配列のサイズに近づくほど衝突の確率が高くなり性能が悪化する。
この比率をload factor(座席利用率)と呼び、$n/m$の形で表す。$n$はエントリの数、$m$は配列のサイズを指す。
チェイン法の場合、load factorの増加に対して線形に性能が悪化する。
オープンアドレス法では衝突したキーが空いた番地に格納されるため、load factorが0.7~0.8付近から急激に性能が悪化する。
この問題を回避するためにload factorが一定の値を超えた場合、より大きいハッシュテーブルを用意して格納しなおすrehashが必要になる。
すべての要素を再計算した場合、必要な計算量は$O(n)$程度となる。
rehashせずにあらかじめ十分なサイズの配列を用意する方法もあるが、エントリ数を事前に想定できない場合は適用できない。
テーブルのサイズに合わせて素直にハッシュ値を計算し直そうとすると$O(n)$だけ時間がかかるため、テーブルのサイズがおおきくなるにつれて時間がかかる。
以下にrehashの速度の向上させる方法について列挙する。
- Incremental Resizing
一度にエントリをrehashするのではなく、各操作(挿入、削除、検索)の後にエントリを古いテーブル(古いテーブルのスロットの位置)から新しいテーブルに移動する。rehashのコストを各操作に分散できる。 - 並列rehash
- ハッシュ関数の計算速度の向上
- テーブルサイズ
- メモリの事前確保
- データの局所性の利用
title | type | weight | lastmod | |
---|---|---|---|---|
HMAC |
docs |
1 |
|
TBD
package murmur3 | |
import ( | |
"encoding/binary" | |
"hash" | |
"math/bits" | |
) | |
const Size = 16 | |
const BlockSize = 16 | |
const ( | |
c1_128 uint64 = 0x87c37b91114253d5 | |
c2_128 uint64 = 0x4cf5ad432745937f | |
n1_128 uint64 = 0x52dce729 | |
n2_128 uint64 = 0x38495ab5 | |
f1_128 uint64 = 0xff51afd7ed558ccd | |
f2_128 uint64 = 0xc4ceb9fe1a85ec53 | |
) | |
var _ Hash128 = (*digest128)(nil) | |
type Hash128 interface { | |
hash.Hash | |
Sum128() (uint64, uint64) | |
} | |
type digest128 struct { | |
tail []byte | |
h1 uint64 | |
h2 uint64 | |
len int | |
seed uint32 | |
} | |
func New128(seed uint32) Hash128 { | |
d := new(digest128) | |
d.seed = seed | |
d.Reset() | |
return d | |
} | |
// BlockSize implements Hash128. | |
func (*digest128) BlockSize() int { return BlockSize } | |
// Reset implements Hash128. | |
func (d *digest128) Reset() { | |
d.len = 0 | |
d.tail = nil | |
d.h1 = uint64(d.seed) | |
d.h2 = uint64(d.seed) | |
} | |
// Size implements Hash128. | |
// 128bitのハッシュ値を返すため16byteを返す | |
func (*digest128) Size() int { return Size } | |
// Write implements Hash128. | |
func (d *digest128) Write(p []byte) (n int, err error) { | |
n = len(p) | |
d.len += n | |
if len(d.tail) > 0 { | |
p = append(d.tail, p...) | |
} | |
d.tail = d.mix(p) | |
return n, nil | |
} | |
func (d *digest128) mix(p []byte) []byte { | |
h1, h2 := d.h1, d.h2 | |
for len(p) >= d.BlockSize() { | |
k1 := binary.LittleEndian.Uint64(p[:8]) | |
k2 := binary.LittleEndian.Uint64(p[8:16]) | |
p = p[16:] | |
k1 *= c1_128 | |
k1 = bits.RotateLeft64(k1, 31) | |
k1 *= c2_128 | |
h1 ^= k1 | |
h1 = bits.RotateLeft64(h1, 27) | |
h1 += h2 | |
h1 = h1*5 + n1_128 | |
k2 *= c2_128 | |
k2 = bits.RotateLeft64(k2, 33) | |
k2 *= c1_128 | |
h2 ^= k2 | |
h2 = bits.RotateLeft64(h2, 31) | |
h2 += h1 | |
h2 = h2*5 + n2_128 | |
} | |
d.h1, d.h2 = h1, h2 | |
return p | |
} | |
// Sum implements Hash128. | |
func (d *digest128) Sum(b []byte) []byte { | |
h1, h2 := d.Sum128() | |
b = binary.BigEndian.AppendUint64(b, h1) | |
b = binary.BigEndian.AppendUint64(b, h2) | |
return b | |
} | |
// Sum128 implements Hash128. | |
func (d *digest128) Sum128() (uint64, uint64) { | |
h1, h2 := d.h1, d.h2 | |
var k1, k2 uint64 | |
switch len(d.tail) & 15 { | |
case 15: | |
k2 ^= uint64(d.tail[14]) << 48 | |
fallthrough | |
case 14: | |
k2 ^= uint64(d.tail[13]) << 40 | |
fallthrough | |
case 13: | |
k2 ^= uint64(d.tail[12]) << 32 | |
fallthrough | |
case 12: | |
k2 ^= uint64(d.tail[11]) << 24 | |
fallthrough | |
case 11: | |
k2 ^= uint64(d.tail[10]) << 16 | |
fallthrough | |
case 10: | |
k2 ^= uint64(d.tail[9]) << 8 | |
fallthrough | |
case 9: | |
k2 ^= uint64(d.tail[8]) << 0 | |
k2 *= c2_128 | |
k2 = bits.RotateLeft64(k2, 33) | |
k2 *= c1_128 | |
h2 ^= k2 | |
fallthrough | |
case 8: | |
k1 ^= uint64(d.tail[7]) << 56 | |
fallthrough | |
case 7: | |
k1 ^= uint64(d.tail[6]) << 48 | |
fallthrough | |
case 6: | |
k1 ^= uint64(d.tail[5]) << 40 | |
fallthrough | |
case 5: | |
k1 ^= uint64(d.tail[4]) << 32 | |
fallthrough | |
case 4: | |
k1 ^= uint64(d.tail[3]) << 24 | |
fallthrough | |
case 3: | |
k1 ^= uint64(d.tail[2]) << 16 | |
fallthrough | |
case 2: | |
k1 ^= uint64(d.tail[1]) << 8 | |
fallthrough | |
case 1: | |
k1 ^= uint64(d.tail[0]) << 0 | |
k1 *= c1_128 | |
k1 = bits.RotateLeft64(k1, 31) | |
k1 *= c2_128 | |
h1 ^= k1 | |
} | |
h1 ^= uint64(d.len) | |
h2 ^= uint64(d.len) | |
h1 += h2 | |
h2 += h1 | |
h1 = fmix64(h1) | |
h2 = fmix64(h2) | |
h1 += h2 | |
h2 += h1 | |
return h1, h2 | |
} | |
func fmix64(h uint64) uint64 { | |
h ^= h >> 33 | |
h *= f1_128 | |
h ^= h >> 33 | |
h *= f2_128 | |
h ^= h >> 33 | |
return h | |
} | |
func Sum128(data []byte, seed uint32) (h1 uint64, h2 uint64) { | |
d := New128(seed) | |
d.Write(data) | |
return d.Sum128() | |
} |
title | type | weight | lastmod | |
---|---|---|---|---|
Math |
docs |
1 |
|
2つの自然数の最大公約数(GCD, Greatest Common Divisor)を求める手法。
という性質が成立するため、この性質を利用してあまりがゼロ(公約数)になるまで再帰的に演算をすれば2つの自然数の最大公約数を求めることができる。
title | type | weight | lastmod | |
---|---|---|---|---|
三角関数 |
docs |
1 |
|
記憶から消えてるのでまとめる TBD
title | type | weight | lastmod | |
---|---|---|---|---|
包除原理 |
docs |
1 |
|
包除原理(ほうじょげんり、英: Inclusion-exclusion principle, principle of inclusion and exclusion, Principle of inclusion-exclusion, PIE)あるいは包含と排除の原理とは、数え上げ組合せ論における基本的な結果のひとつ。
特別な場合には「有限集合 A と B の和集合に属する元の数を計算するには、まずそれぞれに属する元の数 |A| と |B| を足しあわせた後、それらの共通部分に属する元の数 |A ∩ B| を引き去ればよい」というものである。つまり単に数え上げた後で重複を取り除くことに相当する。
包除原理 - Wikipedia
title | type | author | lastmod | waight | |
---|---|---|---|---|---|
tmp |
docs |
showa |
|
1 |
ScrapBoxから移行中
アルゴリズム
- mod
- 割ったあまりをもとめるやつ
- [「1000000007 で割ったあまり」の求め方を総特集! 〜 逆元から離散対数まで 〜 - Qiita https://qiita.com/drken/items/3b4fdf0a78e7a138cd9a]
- 素数判定
- エラトステネスの篩
- 与えられた範囲内のすべての素数を列挙するアルゴリズム
- 計算量:O(N log log N)
- エラトステネスの篩
- 最大公約数
- ユークリッドの互除法
- x>=yのときgcd(x,y)とgcd(y,xをyで割ったあまり)は等しい
- 計算量:O(log y)
- [拡張ユークリッドの互除法 〜 一次不定方程式 ax + by = c の解き方 〜 - Qiita https://qiita.com/drken/items/b97ff231e43bce50199a]
- [ラメの定理]
-
a, b を非負整数とし、小さい方の数を十進法で表したときの桁数を d とする。このとき、ユークリッドの互除法を用いて a と b の最大公約数を求めるときの計算回数は、5d 回以下となる。
-
- 拡張ユークリッドの互除法
- ax + by = gcd(a, b)を満たすx, yを求める
- ユークリッドの互除法
- ![image](べき乗 https://go.dev/play/p/H8EQVbXNwms)
- (x^n/2)^2とすることで半分の計算量で済む
- ![image](Prime Factorize)
- 整数nを素因数分解する
- [最小公倍数(Least common multiple)]
- ![image](Euler's phi function)
- ![image](Extended Euclid Algorithm)
- 離散対数問題
- [Baby-step Giant-step https://go.dev/play/p/BOaKgCmTOpT]
- [Baby-step Giant-stepアルゴリズムを解釈する https://sono-portal.firebaseapp.com/techblog/EZlVBfa4NkgR8T2na53l]
- [Baby-step Giant-step https://go.dev/play/p/BOaKgCmTOpT]
- ニム
- 石の山を取り合うゲーム。排他的論理和による必勝法がある
- [ニム(複数山の石取りゲーム)の必勝法 | 高校数学の美しい物語 https://manabitimes.jp/math/950]
- グランディ数
- [不偏ゲームのグランディ数 (ニム数) https://cympfh.cc/aiura/game-nim#:~:text=グランディ数とは不偏,値のことを言う.&text=ここで%20mex,数のことである.]
- [尺取法]
- https://go.dev/play/p/ZgzoAyFCbzI
- https://go.dev/play/p/rWReZCQPm6z
- 連続する区間の値や数などを求める問題で利用できる
- 前計算をおこない、前計算した範囲の差分で区間を表現することで求める
- ![image](しゃくとり法 (尺取り法) の解説と、それを用いる問題のまとめ - Qiita https://qiita.com/drken/items/ecd1a472d3a0e7db8dce)
- [反転]
- 反転操作に対する問題
- ある範囲で反転したかどうかを0, 1でもつことで、各マスに対して反転結果をもつことなくある位置がどっちを向いているか判定できる
- Face The Right Way POJ No.3276
- Fliptile POJ No.3279
- 最初の行を確定すると次の行以降も確定するので順番に試す
- https://go.dev/play/p/ot3pHv5pVro
- ![image](挿入ソート https://go.dev/play/p/27ZG-NkZNte)
- 1個ずつ全部と順番にくらべていくやつ
- シェルソート
- 一定の間隔で先に大雑把に並び替えて、ある程度整列した状態をつくることで
- ソートの効率をあげたソート
- O(N^1.25)くらいまで性能がでると予測されてる
- ![image](バブルソート https://go.dev/play/p/hj4SYpxFoOi)
- 端っこから隣同士をくらべるやつ
- ![image](選択ソート https://go.dev/play/p/hrsihLuVKwf)
- 配列の最小の要素を探して順番に先頭と交換していくあれ
- 安定ソートとは
- 比較する値に同じ値があった場合、入力と同じ順序で出力されるソート
- バブルソートは安定ソートだが、選択ソートはちがう
- トポロジカルソート
- 参考
- 閉路のない有向グラフを仕事の手順をあらわすデータ構造として利用した場合に、着手すべき順番に列挙するときにおこなうソート
- 線形探索
- O(n)
- 番兵:検索したい対象を配列に配置することで条件を簡略化して定数倍の高速化をおこなえる
- 二分探索
- [$ O(log2n)]
- 全探索
- [半分全列挙]
- N/2 ずつの 2 グループに分けて全列挙し、2グループ同士を組み合わせる際に高速化する
- 半分のグループ A を全列挙
- もう半分のグループ B を全列挙
- Aの全ての要素について以下を行う
- A から選ぶ要素を1つに固定したとき、条件に合う要素をBから高速に探す
- [半分全列挙による全探索の高速化 | アルゴリズムロジック https://algo-logic.info/split-and-list/]
- N/2 ずつの 2 グループに分けて全列挙し、2グループ同士を組み合わせる際に高速化する
- [半分全列挙]
- ハッシュ法
- O(1)※衝突がなければ
- ハッシュテーブルを用いた検索
- ハッシュ関数でキーを生成して一意に求める
- ヒュースティック探索
- ![image](8クイーン問題 https://go.dev/play/p/OFZpirNhGjn)
- チェス盤に8つのクイーンをお互いに襲撃できないようなおき方を解く問題
- バックトラックをつかってとくよん
- 8パズル
- 幅優先探索や深さ優先探索で愚直に計算できる大きさ
- パズルの状態を記憶して、解法になるパターンを順番に見つけていく
- サンプルの実装は幅優先探索で実装
- 15パズル
- [反復深化(Iterative Deepening)]
- IDA*(反復深化A*)
- 推定値(=ヒュースティック)を用いて枝を刈り取るアルゴリズム
- リンクは15パネルを反復深化で解いた
- 解法との距離をマンハッタン距離で推定値をだして、推定値と実際の値がゼロになるまで深さ優先探索を繰り返す
- [A*]
- 反復深化A*に用いた推定値を優先度付きキューを用いたダイクストラをベースとした探索アルゴリズム
- 「始点から現在地までのコスト+現在地からゴールまでの推定値」が最も小さい状態から優先的に状態遷移することで速度があがる
- IDA*(反復深化A*)
- [反復深化(Iterative Deepening)]
- ![image](8クイーン問題 https://go.dev/play/p/OFZpirNhGjn)
- 分割統治法(Divide and Conquer)
- 2つ以上の小さい問題に分割してもとの問題の解を求める
- 全探索
- コッホ曲線
- 出力結果をスプレッドシートとかでグラフをつくるとみれる
- ![image](2次元回転行列使うのが正解 https://go.dev/play/p/LFoDxb0KSJf)
- n=5
- マージソート
- [$ O(nlog2n)]
- 配列のマージを行うことでソートをおこなう
- 対象の配列以外で一次保存のためのメモリが必要
- ai > aj and i < j である組の個数を反転数と呼ぶ
- パーティション
- O(n)
- ある基準より大きいか小さいかを分類するアルゴリズム
- クイックソート
- 平均=O(nlog2n)
- どんなデータに対しても一定のソートをおこなうのでソート済みのデータなどではO(n^2)
- 基準をランダムに選ぶなどで工夫する必要がある
- パーティションを使って離れた要素の交換をおこなうため、安定ソートではない
- マージソートと異なり追加メモリが必要ではない
- 計数ソート
- O(n + k)
- 各要素がk以下の数列に対してソートをおこなう
- ソート時に後ろから取得することで安定ソートになる
- 対象の配列以外で一次保存のためのメモリが必要
- 計算結果をメモリに保持することで効率化をおこなう
- フィボナッチ
- 漸化式でiとi+1しか存在しない場合、偶数と奇数のそれぞれの場合の2つの配列だけでDPテーブルの再利用が可能になるケースがある
- ![image](最長共通部分列(LCS) https://go.dev/play/p/vo0f3weQOMs)
- 二つの列の最長の共通部分列を求める
- 部分問題に分割して漸化式として解くことができる
- 連鎖行列積
- 行列の積の計算順序を変えられるため、最小の計算回数になるような順序を求めるアルゴリズム
- 最小限の組み合わせから順番にコストを求めて、最小になるようなコストを求める
- わかったようなわかってないような感じ
- コイン問題
- j円を支払う時の最小枚数を記録していく
- ナップサック問題
- 個数制限付きナップサック問題
- DPテーブルを1次元に圧縮
- 価値と重さが決まっている複数の品物を容量が一定のナップサックに詰め込むとき、ナップサックに詰め込める品物の価値の和の最大値は何であるか?
- 解法パターンにはメモ化再帰を使う場合と漸化式の場合がある
- 参考
- 個数制限付き部分和ナップサック問題
- 個数に着目して、品物を順番に配置したとき、計算したい値に残りの個数(0以上)が記録されていたかで計算できる
- 個数制限なしナップサック問題
- DPテーブルを1次元に圧縮
- 1次元に圧縮すると個数制限あり、なしの違いはループの方向だけ
- i番目の品物を重さをこえない範囲でk個選んで最大の価値にする
- k個選ぶ=dpテーブルにおいてi番目の品物を選ぶときにi番目の重さを差し引いた残りの重さで最大の価値に加算することで表現できる
- i番目で重さ2、価値3の場合
- j(重さ)=4のとき、i番目の重さを差し引いたj=2のときの最大の価値が3の場合、i番目の価値をたした場合、価値が6になる
- DPテーブルを1次元に圧縮
- 個数制限付きナップサック問題
- ![image](分割数 https://go.dev/play/p/lMtaGqZo1Oj)
- 数え上げ問題の典型的な考え方
- [分割数 - inamori’s diary https://inamori.hateblo.jp/entry/20121216/p1]
- 基本の証明~[オイラーの五角数定理]で高速化までやってる。詳しい
- [重複のある場合の組み合わせ]
- [動的計画法における重複組み合わせの式変形について - ツバサの備忘録 https://emtubasa.hateblo.jp/entry/2018/08/29/161456]
- 最長増加部分列(LIS)
- 2分探索をつかってDPで記録した値を更新していく
- 参考
- 最大正方形
- 最大の正方形を調べる問題
- 動的計画法で書くマスごとに足し上げていく
- 計算量:[$ O(HW)≒O(n^2)]
- 最大長方形
- 最大の長方形を調べる問題
- 列方向の前処理をおこなったDPテーブルを用意して、スタックを使って各行の最大面積を求めていく
- 計算量:O(HW)≒O(n^2)
- ![image](Levenshtein Distance)
- ![image](Traveling Salesman Problem)
- 巡回セールスマン問題
- [ビットDP(bit DP)の考え方
集合に対する動的計画法| アルゴリズムロジック https://algo-logic.info/bit-dp/] - [ABC274 E問題]でbitdpを使った類似問題が出題された
- [ビットDP(bit DP)の考え方
- 巡回セールスマン問題
- ![image](Chinese Postman Problem)
-
節点(node)と節点同士を結ぶ辺(edge)で表されるデータ構造
-
ある節点と辺がつながっており、浅い節点を親(parent)
- 同じ親をもつ節点同士を兄弟(sibling)
-
ある節点と辺がつながっており、深い節点を子(child)
- 節点の子の数を次数(degree)
- 子を持たない節点を外部節点(external node)または葉(leaf)
- 葉でない節点を内部節点(internal node)
-
根(root)と呼ばれる頂点をあらわす節点を持つ木を[根つき木(rooted tree) https://go.dev/play/p/AA5dlyDHdD8]
- 根から節点までの長さを深さ(depth)
- 節点から葉までの最大値を高さ(height)
-
- 木の節点の中で一番遠い節点間の距離のこと
- 参考
-
二分木
- すべての節点の子の数が2以下の木
- ![image](木の巡回 https://go.dev/play/p/0MRkF9Sy5oj)
- 木が深いと再帰が深くなるので注意
- 木の復元
- 先行順巡回で根から順番にたどり、中間順巡回で部分木を抽出し、葉から順番にノードを再構築する
- 二分探索木
- [二分ヒープ(https://go.dev/play/p/4i1fgeL_QjD])
- 1つの配列の各要素に対応した完全二分木であらわされたデータ構造
- max-ヒープ:節点のキーがその親のキー以下である
- min-ヒープ:節点のキーがその親のキー以上である
- 優先度付きキュー
- 挿入、削除:O(logn)
-
全域木
- グラフのすべての頂点を含む部分グラフで、木(閉路を持たないグラフ)である限りできるだけ多くの辺をもつ木
- グラフは複数の全域木をもつ
- 最小全域木(MST):全域木の中で辺の重みが最小のもの
- プリムのアルゴリズム
- ![image](参考1](https://inzkyk.xyz/algorithms/minimum_spanning_trees/jarkins_prims_algorithm/) [参考2 http://www.dais.is.tohoku.ac.jp/~shioura/teaching/ad09/ad09-10.pdf)
- 最小探索木を求めるアルゴリズムの1つ
- 二分ヒープ(優先度付きキュー)で実装
- こっちのほうが効率が良い
- フィボナッチヒープのほうがInsertがO(1)でできるため、より高速
- クラスカル法
- 参考
- Union Find Treeを使って閉路ができないような木をつくる
- 基本的にこっちのほうが早い%3F-,基本的にはクラスカル法が良いようです%E3%80%82,-クラスカル法)
- 辺の重みによるソートの計算次第
- プリムのアルゴリズム
- 最小全域有向木 (Minimum-Cost Arborescence)
- 重み付き有向グラフで指定された頂点を根とする最小有向木を求める
- [Edmondsのアルゴリズム]
- [シュタイナー木問題]
- 与えられたすべての点を通る最小全域木を求めるのに対して、与えられた部分集合をつなぐ木を求める
- 最短経路問題
- 単一始点最短経路
- 与えられた1つの頂点からすべての頂点への最短経路を求める問題
- ダイクストラ
- 最短経路を探しながらコストの計算をおこなう
- 親の頂点のコストと辺のコストを加算しながら距離を計算する
- 負の辺が存在する場合計算ができない
- ベルマンフォード
- 負の重みが存在する場合の単一始点最短経路
- [ベルマンフォード法による単一始点最短経路を求めるアルゴリズム | アルゴリズムロジック https://algo-logic.info/bellman-ford/]
- 全点対間最短経路(All Pairs Shortest Path:APSP)
- 単一始点最短経路
- ![image](Level Ancestor Problem)
- 頂点u の祖先であって深さが d であるものを求める問題
- クエリが事前にわかってるならDFSで探索しながらキューの中身からとれる
- [Jump Pointer Algorithm(ダンブリング) https://go.dev/play/p/mAe1EIOo34G]
- ちゃんと実装できてないで
- [Level Ancestor Problem – 37zigenのHP https://37zigen.com/level-ancestor-problem/]
- 頂点u の祖先であって深さが d であるものを求める問題
- 対象の集合とそれらのつながりを表す集合
- 連結成分
- 連結グラフ:グラフに含まれるすべての頂点がいずれかの頂点とリンクしているグラフ
- 連結成分とは「部分グラフのうち、極大で連結なもの」
- グラフが連結でないとき、その中のうち連結な部分グラフを連結成分と呼ぶ
- [強連結成分(strongly connected component)]
- 有向グラフにおいて、すべての頂点対u, vについて互いに到達可能な連結成分
- 参考
- 連結成分
- 対象を頂点(Vertex)、つながりを辺(edge)であらわす
- G = (V, E)
- 頂点の数:|V|
- 辺の数:|E|
- 辺:e = (u, v)
- 辺の重み:w(u, v)
- 頂点uとvの間に辺(u, v)が存在するとき隣接(adjacent)している
- 隣接している頂点の列をパス(path)という
- 始点と終点が同じパスを閉路(cycle)
- ![image](間接点(Articulation Point) https://go.dev/play/p/kqb_cLGbmAs)
- ![image](取り除くと連結成分が増える頂点(グラフ全体が非連結になる) http://hos.ac/slides/20110504_graph.pdf)
- グラフが連結できなくなる辺を[橋(Bridge)]とよぶ
- 参考
- フローネットワーク
- 辺に容量が設定されている有向グラフ
- [最大フロー問題(Maximum Flow)]
- 始点から終点へ流れる最大の流量を求める
- [Ford-Fulkerson法]
- 実装例はこれ[atcoder/main.go at main · showa-93/atcoder https://github.com/showa-93/atcoder/blob/main/contests/atcoder/tessoku_book/bp/main.go]
- マッチング
- どの各点も2辺以上つながっていない部分グラフ
- 参考
- グラフの実装
- 隣接リスト表現
- メモリ消費量が辺の数しか必要としない
- 頂点間の関係を調べるには隣接する頂点を順番に調べる必要がある(O(n))
- 連結リストに依存するため辺の削除の操作が効率的でない
- ![image](隣接行列表現 https://go.dev/play/p/lRujH0xq7In)
- 頂点間の関係を定数時間で確認できる
- 辺の追加削除を定数時間でおこなえる
- メモリ消費量が頂点の数の2乗
- 頂点間の関係を1つしか記録できない
- 隣接リスト表現
- 探索
- ![image](深さ優先探索(DFS) https://go.dev/play/p/fGn1fUFhYzj)
- 幅優先探索(BFS)
- 01-BFS
- 辺の長さが0または1の有向グラフにおいてある1つの始点から全頂点への最短の長さを求める
- [01-BFSのちょっと丁寧な解説 - ARMERIA https://betrue12.hateblo.jp/entry/2018/12/08/000020]
- 01-BFS
- 2部グラフ
- 各頂点を白または黒でぬったとき、隣接する頂点が異なる色になるグラフのこと
- [二部グラフ判定をUnionFindTreeで行う - noshi91のメモ https://noshi91.hatenablog.com/entry/2018/04/17/183132]
- 頂点Xの色わけをするとき、色Aと色BをXとX+nの2種類のグラフで考える
- 各色を2頂点でわけたUnionFindTreeをつくったときにXとX+nが同じDejointSetに含まれるなら、同じ頂点に両方の色が同時に存在することになり、矛盾するため二部グラフを満たさないことがわかる
- 互いな素な集合
- 1つの要素が複数の集合に属することのない互いに素な集合に分類して管理するためのデータ構造
- Union Find:指定された2つの要素x,yが同じ集合な属するか探索する
[領域探索(kD Tree) https://go.dev/play/p/rm35zu-nrqK]
- 点の集合から二分探索木を生成する
- 二分探索木の根から探索をはじえmて、ノードに関連づいている点が範囲内に存在するかをチェックしていく
- 更にその子のノードに関しても再帰的に探索をおこなう
- これはk次元の空間に拡張可能なため、kD木と呼ばれるデータ構造を構築することで探索をおこなえる
- 木の深さによってポイントのソートの基準を変更することでk次元に対して探索をおこなう
- 要素の値が動的に変化する数列に対して、範囲内の最小の要素を高速に求める[$ O(log(n))]
- 指定の範囲内の最小値を求める
- 数列の![image]($ a_i)を更新する
- RMQを実現する別解として![image](Sparse Table https://ikatakos.com/pot/programming_algorithm/data_structure/sparse_table)による実装がある
- 構築に[$ O(nlog(n))]、クエリに[$ O(log(1))]で求められる
- 値の更新がセグメント木よりも高速におこなえない
列や平面をバケットと呼ばれる任意の単位で分割して、バケットごとにデータを管理することで効率的に計算や操作をおこなう手法
- 平方分割
- n個の要素からなる列を![image]($ \sqrt n)程度ごとのバケットにまとめて管理する手法
- 区間に対する処理が[$ O(\sqrt n)]程度でおこなえる
- セグメント木は各操作が[$ O(logn)]なので、一般的にセグメント木を使うほうが効率的
- ただし、平方分割のほうが実装がシンプルであったり、セグメント木で実現できない機能も実現可能な場合がある
- 要素の値が動的に変化する数列に対して、範囲内の要素の和を高速に求める
- SA-IS法
- playground版より大きいデータを扱えるようにした。ただし遅い。
- [atcoder/suffix_array.go at main · showa-93/atcoder https://github.com/showa-93/atcoder/blob/main/template/structure/suffix_array.go]
- [Go言語による高速な接尾辞配列の実装(SA-ISの実装) - Qiita https://qiita.com/tobi-c/items/cf450a7b1d6b59f332d1#33-s-typeのソート]
- [接尾辞配列(Suffix Array) - Shogo Computing Laboratory https://shogo82148.github.io/homepage/memo/algorithm/suffix-array/]
- [SA-ISで高速にSuffix Arrayを構築する話【新歓ブログリレー 38日目】 | 東京工業大学デジタル創作同好会traP https://trap.jp/post/953/]
- playground版より大きいデータを扱えるようにした。ただし遅い。
- ベクトルライブラリ
- ![image](マンハッタン幾何(線分交差問題) https://go.dev/play/p/nKpe1kVfSIj)
- [Closest Pair]
- ![image](Diameter of a Convex Polygon)
- 凸多角形の直径(=最遠頂点対間距離)を求める問題
- キャリパー法
- [Convex Cut]
- 凸多角形を直線で切断する問題
- [調和級数]
- [調和級数などのはなし - Qiita https://qiita.com/ageprocpp/items/f6661deaa09dda124132]
書籍ででてきたなにか
- ラウンドロビンスケジューリング
- プロセスの割当手法
- [ラウンドロビン・スケジューリング - Wikipedia https://ja.wikipedia.org/wiki/ラウンドロビン%E3%83%BBスケジューリング#:~:text=ラウンドロビン%E3%83%BBスケジューリングは%E3%80%81オペレーティング,名前の由来である%E3%80%82]
- Allocation