Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
速いクイックソート: Pattern-defeating quicksort
Search
WATANABE Yuki
August 24, 2022
Programming
0
230
速いクイックソート: Pattern-defeating quicksort
社内勉強会から。Pattern-defeating quicksort アルゴリズムの大まかな説明。
WATANABE Yuki
August 24, 2022
Tweet
Share
More Decks by WATANABE Yuki
See All by WATANABE Yuki
C++ はなぜあんなにも複雑なのか
magicant
0
71
鉄道シミュレーターで自動運転を実装した話
magicant
0
220
ドキュメント、書けてますか?
magicant
0
110
Other Decks in Programming
See All in Programming
A Journey of Contribution and Collaboration in Open Source
ivargrimstad
0
520
Macとオーディオ再生 2024/11/02
yusukeito
0
340
OpenTelemetryでRailsのパフォーマンス分析を始めてみよう(KoR2024)
ymtdzzz
5
2k
Compose 1.7のTextFieldはPOBox Plusで日本語変換できない
tomoya0x00
0
170
Java ジェネリクス入門 2024
nagise
0
700
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
290
ふかぼれ!CSSセレクターモジュール / Fukabore! CSS Selectors Module
petamoriken
0
140
CPython 인터프리터 구조 파헤치기 - PyCon Korea 24
kennethanceyer
0
250
Dev ContainersとGitHub Codespacesの素敵な関係
ymd65536
1
140
AI時代におけるSRE、 あるいはエンジニアの生存戦略
pyama86
4
1k
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
110
cXML という電子商取引の トランザクションを支える プロトコルと向きあっている話
phigasui
3
2.3k
Featured
See All Featured
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
Building Your Own Lightsaber
phodgson
102
6.1k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
1.8k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.3k
Why You Should Never Use an ORM
jnunemaker
PRO
54
9k
The Language of Interfaces
destraynor
154
24k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
231
17k
Typedesign – Prime Four
hannesfritz
40
2.4k
Designing for Performance
lara
604
68k
Building Better People: How to give real-time feedback that sticks.
wjessup
364
19k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.4k
Product Roadmaps are Hard
iamctodd
PRO
49
11k
Transcript
速いクイックソート 渡邊裕貴 / 2022-08-24
Pattern-defeating quicksort • Orson Peters という人が 2015 年ごろに発明した不安定ソートアルゴリズム • クイックソートを速くするいろいろな工夫の集大成みたいな感じ
• オリジナルのコードは C++ で書かれている • Rust の標準ライブラリーで採用された • 最近 Go の標準ライブラリーでも採用された
1分でわかる普通のクイックソート 5 4 1 6 7 3 2 9 8
配列の中から一つ軸を選ぶ (pivot) 2 4 1 3 5 7 6 9 8 軸未満→軸→軸以上 の順になるように入れ替える 以下、前後それぞれ繰り返し 2 4 1 3 5 7 6 9 8 1 2 4 3 5 6 7 9 8
クイックソートの計算量 全ての分割が二等分になる場合が最も効率的で、O(n log n) 4 3 7 6 1 5
2 2 3 1 4 6 5 7 1 2 3 4 5 6 7 n log n
クイックソートは遅い時もある (1) 常に配列の先頭を軸にする場合、配列が元からソート済みだと分割が偏る 1 2 3 4 5 6 7
n 1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 2 3 4 5 6 7 n O(n2)
軸の選び方の工夫 • 配列の先頭 • 配列の真ん中 • 三つ選んで中央値を取る ◦ 3 回の比較でできる
• John Tukey’s ninther ◦ 「三つ選んで中央値を取る」を 3 回やって中央値を取る ◦ 12 回の比較でできる 簡単 分割が偏りにくい
クイックソートは遅い時もある (2) 同じ値がたくさんある場合、分割が偏る 1 1 1 1 1 1 1
n 1 1 1 1 1 1 1 1 1 1 1 1 1 1 n 1 1 1 1 1 1 1 どれを選んでも軸は 1 O(n2)
軸に等しい値の振り分けを工夫する 1 1 2 1 1 1 1 1 1
2 1 1 1 1 新しい軸が前回の軸と等しいなら 同じ値がたくさんある公算が高い 軸に等しい値→軸より大きい値 の順に入れ替える 軸に等しい値どうしはもうソートしなくていい O(n2) が O(n) になった 1 1 1 1 1 1 2
クイックソートは遅い時もある (3) それでも分割が偏る可能性はゼロではない • とても運が悪かった場合 • 分割が偏るように仕組まれている場合
軸の候補をシャッフルする 分割が偏っていた場合 (分割比が 1:7 未満の場合) は 次の軸の選び方 (John Tukey’s ninther
で使う値) を変える • オリジナル版: ◦ 配列の端っこや真ん中だけじゃなくて四等分した位置から選ぶ • Rust/Go 版: ◦ 四等分した位置だけじゃなくてランダムに選ぶ ▪ 簡易的な乱数を使う
最後の手段: ヒープソート 偏った分割が log n 回発生したら、諦めてヒープソートに移行する ヒープソートとは: • 二分木と等価な構造を配列上に再現するソート手法 •
クイックソートより 2 倍くらい遅い • 最悪計算量 O(n log n) が保証されている
部分挿入ソートによる高速化 • 以下の条件が全て満たされたとき、配列全体がソート済みの可能性が高そう ◦ 前回の分割比が 1:7 より悪くなかった ◦ 前回の分割時、軸の前後で入れ替わった要素がなかった ◦
John Tukey’s ninther で軸を選ぶとき候補となる九つの値がソート済みだった • この場合は挿入ソートを試す ◦ ただし一定ループ回数で終わらなければ諦める (O(n))
配列の分割 (軸前後の値の入れ替え) の遅さ 典型的コード while (left < right && array[left]
< pivot) left++; while (left < right && array[right] >= pivot) right--; if (left < right) { tmp = array[left]; array[left] = array[right]; array[right] = tmp; } 分岐予測の外れやすい分岐 1 回の入れ替えで 3 回の代入
BlockQuicksort による高速化 1. 配列の先頭 128 個と末尾 128 個を一気に軸と比較して 入れ替えるべき値の位置を一時配列にまとめる ◦
ループ回数を固定したことで分岐予測が当たるようになる ◦ ループアンローリングも可能に 2. 一次配列に入った位置の値を一気に入れ替える ◦ 2n 個の値を 2n+1 回の代入で入れ替える tmp
まとめ Pattern-defeating quicksort にはソートを速くするいろんな工夫が詰まっている • 中央値に近い値を軸にする: John Tukey’s ninther •
軸に等しい値を効率よく集める • 軸の候補をシャッフルする • 再帰回数が O(log n) を超えそうならヒープソート • 既にソート済みっぽいなら挿入ソート • 値の入れ替えを速くする: BlockQuicksort