Merge Sort の概要 および LinuxKernel における 応用
てめー誰だよ?
Twitter ID: @TakesxiSximada
職業:
- 警備員(自宅)
- 時々仕事するかも
アイコン:
- 土下座している訳ではありません
いきなりだけど...
マージソートって
なんとなーく知ってるけど
ちゃんと理解してるとはいえないよな...
おーい、マージソート書いといて!!
おーい、マージソート書いといて!!

とりあえずググらなきゃ!!
これでは理解しているとは言えない
じゃあこのタイミングで
理解してしまおう
Merge Sort
~ 空と海と大地と呪われしsort() ~
とりあえずググる
概要
マージソートは、ソートのアルゴリズムで、既に整列してある複
数個の列を1個の列にマージする際に、小さいものから先に新
しい列に並べれば、新しい列も整列されている、というボトム
アップの分割統治法による。
出典 マージソート: wikipe...
え?
つまりどういうこと?
つまりこういう事
ふむふむ
ポイント
1. 分解/結合する
2. 2つのリストを並べ替える
ポイント①
分解/結合する
● 要素数が1になるまで分解する
● 分解は要素数/2で分解する
○ 奇数の時は(要素数-1)/2

● 並べ替えつつ結合していく
● 結合する順番は分解とは逆の順序で行う
つまりこういう事
ポイント②
2つのリストを並べ替える
1. 新しいソート済みリストを用意
2. 2つのリストの先頭同士を比較して小さい方を取
り出しソート済みリストに入れる
3. どちらかのリストが空になるまで2を繰り返す
4. 余っている方のリストをソート済...
つまりこういう事
サンプルコード (Python)
#! /usr/bin/env python
#-*- coding: utf-8 -*import sys
import argparse
def merge(left, right):
sorted_lis...
大事なところを抜粋
def merge(left, right):
sorted_list = []
while left and right:
smaller = left if left[0] < right[0] else right
s...
やってみた
$ chmod 744 merge_sort.py
$ echo 4 1 3 5 6 6 1 9 8 2 | ./merge_sort.py --int
ドン!
$ chmod 744 merge_sort.py
$ echo 4 1 3 5 6 6 1 9 8 2 | ./merge_sort.py --int
1123456689
それなりに出来た
特徴
● 平均計算量 O(n log n)
● 最悪計算量 O(n log n)
● 配列にランダムアクセスする必要なし
良いところ
● そこそこ速い
● 速い時と遅い時で計算量があまり違わない
○ 平均計算量: O(n log n)
○ 最悪計算量: O(n log n)

● シーケンシャルアクセスしかできないデータ構造
にも使える
(ex. 連結リストとか)
悪いところ
● ソート済みリストの領域確保が必要
配列長の 1/2 のサイズ
● ランダムアクセスが出来る構造であればクイッ
クソートとかの方が速い
Linux Kernelでは...

(linux-2.6.34.14)

● ROOT/lib/list_sort.c
● ROOT/include/linux/list_sort.h
で定義されている
ROOT/include/linux/list_sort.h
#ifndef _LINUX_LIST_SORT_H
#define _LINUX_LIST_SORT_H
#include <linux/types.h>

コイツか!!

str...
void list_sort(...)の引数は?
● void *priv
private data, opaque to list_sort(), passed to
@cmp
● struct list_head *head
ソートする連結...
void list_sort(...)の処理
struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
-- last slot is a sentinel ...
入りきらねえし
よくわからねえ
そうだ!! テストコードを見よう
ROOT/lib/list_sort.c
static int __init list_sort_test(void)
static int __init list_sort_test(void)
int i, r = 1, count;
struct list_head *head = kmalloc(sizeof(*head), GFP_KERNEL);
s...
まずはテストデータを見てみる
int i, r = 1, count;
struct list_head *head = kmalloc(sizeof(*head), GFP_KERNEL);
struct list_head *cur;
pr...
テストデータの構造
struct debug_el
struct list_head
struct list_head *head

struct list_head *prev
struct list_head *next

struct d...
うーん
激しく見づらい
テストデータの構造、見やすくした
※個数は適当

struct list_head *head

heap
debug_el

debug_el

debug_el

debug_el

list_head

list_head

list_h...
あ、連結リストってことね...
次はソートの実行を見てみる
int i, r = 1, count;
struct list_head *head = kmalloc(sizeof(*head), GFP_KERNEL);
struct list_head *cur;
pri...
ソート実行
list_sort(NULL, head, cmp);

第一引数はNULLで
いいのかよ
void list_sort(...)の処理① 初期化
struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
-- last slot is a sent...
void list_sort(...)の処理① 初期化
struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
-- last slot is a sent...
void list_sort(...)の処理②マージ
struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
-- last slot is a senti...
void list_sort(...)の処理②マージ
memset(part, 0, sizeof(part));
head->prev->next = NULL;
list = head->next;
while (list) {
struc...
分離
head->prev->next = NULL;
list = head->next;

①最後の要素の
nextにNULLを入
れる
③すべて舐めたら終了

while (list) {
struct list_head *cur = ...
void list_sort(...)の処理②のキモ
?????
for (lev = 0; part[lev]; lev++) {
cur = merge(priv, cmp, part[lev], cur);
part[lev] = NUL...
merge()を読もう
static struct list_head *merge(...) の引数
● void *priv
● int (*cmp)(void *priv, struct list_head *a, struct
list_head *b)
● ...
static struct list_head *merge(...)の処理
struct list_head head, *tail = &head;

a, bのどちらかが空になるまで

while (a && b) {
/* if equ...
static struct list_head *merge(...)は結局..
part[lev]

cur

h0

h3

h4

h1

h2

merge(priv, cmp, part[lev], cur);

cur

h0

h...
ふむふむ
void list_sort(...)の処理②が
終わる頃には
partはこんな感じ
struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
-- last slot is a sentinel */
int lev; ...
partはこんな感じ
part[0]
part[1]
part[2]
part[3]
part[4]
part[5]

= NULL
= NULL
=
=
=
= NULL
h1

h7

h0

h3

h2

h4

h5
h6

h8
この後は?
void list_sort(...)の処理③ さらにマージ
for (lev = 0; lev < max_lev; lev++)
if (part[lev])
list = merge(priv, cmp, part[lev], list)...
void list_sort(...)の処理③ さらにマージ
for (lev = 0; lev < max_lev; lev++)
if (part[lev])
list = merge(priv, cmp, part[lev], list)...
void list_sort(...)の処理③ さらにマージ
for (lev = 0; lev < max_lev; lev++)
if (part[lev])
list = merge(priv, cmp, part[lev], list)...
merge_and_restore_back_links()の
説明
Combine final list merge with restoration of
standard doubly linked list structure.
Thi...
最後の最後によくわからない
なんとなくだけど
2重になっているリンクの修復とか
するのかな...
まとめ
● 再帰使うと結構楽に書ける
● Pythonで書いたコードとCで書かれたLinux
Kernelのコードとではだいぶ違う
● てかそれなりにむずい
いいやつですが
傷つきやすいやつなので
まじ、そーっと使ってください
ばいばい
Upcoming SlideShare
Loading in …5
×

MergeSortの概要とLinuxでの実装

675 views

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
675
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

MergeSortの概要とLinuxでの実装

  1. 1. Merge Sort の概要 および LinuxKernel における 応用
  2. 2. てめー誰だよ? Twitter ID: @TakesxiSximada 職業: - 警備員(自宅) - 時々仕事するかも アイコン: - 土下座している訳ではありません
  3. 3. いきなりだけど...
  4. 4. マージソートって なんとなーく知ってるけど ちゃんと理解してるとはいえないよな...
  5. 5. おーい、マージソート書いといて!!
  6. 6. おーい、マージソート書いといて!! とりあえずググらなきゃ!!
  7. 7. これでは理解しているとは言えない
  8. 8. じゃあこのタイミングで 理解してしまおう
  9. 9. Merge Sort ~ 空と海と大地と呪われしsort() ~
  10. 10. とりあえずググる
  11. 11. 概要 マージソートは、ソートのアルゴリズムで、既に整列してある複 数個の列を1個の列にマージする際に、小さいものから先に新 しい列に並べれば、新しい列も整列されている、というボトム アップの分割統治法による。 出典 マージソート: wikipedia http://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%BC% E3%82%B8%E3%82%BD%E3%83%BC%E3%83%88
  12. 12. え?
  13. 13. つまりどういうこと?
  14. 14. つまりこういう事
  15. 15. ふむふむ
  16. 16. ポイント 1. 分解/結合する 2. 2つのリストを並べ替える
  17. 17. ポイント① 分解/結合する ● 要素数が1になるまで分解する ● 分解は要素数/2で分解する ○ 奇数の時は(要素数-1)/2 ● 並べ替えつつ結合していく ● 結合する順番は分解とは逆の順序で行う
  18. 18. つまりこういう事
  19. 19. ポイント② 2つのリストを並べ替える 1. 新しいソート済みリストを用意 2. 2つのリストの先頭同士を比較して小さい方を取 り出しソート済みリストに入れる 3. どちらかのリストが空になるまで2を繰り返す 4. 余っている方のリストをソート済みリストの後ろ にくっつける
  20. 20. つまりこういう事
  21. 21. サンプルコード (Python) #! /usr/bin/env python #-*- coding: utf-8 -*import sys import argparse def merge(left, right): sorted_list = [] while left and right: smaller = left if left[0] < right[0] else right sorted_list.append(smaller.pop(0)) sorted_list.extend(left if left else right) return sorted_list def merge_sort(elms): length = len(elms) if length <= 1: return elms else: harf = length / 2 sorted_left = merge_sort(elms[:harf]) sorted_right = merge_sort(elms[harf:]) return merge(sorted_left, sorted_right) def main(): parser = argparse.ArgumentParser() parser.add_argument('-s', dest='sep', default=' ') parser.add_argument('--int', dest='int_', default=None, action='store_true') opts = parser.parse_args() type_ = lambda s: s if opts.int_: type_ = int line = sys.stdin.readline() elms = map(type_, map(lambda elm: elm.strip(), line.split(opts.sep))) sorted_list = map(str, merge_sort(elms)) print opts.sep.join(sorted_list) if __name__ == '__main__': main()
  22. 22. 大事なところを抜粋 def merge(left, right): sorted_list = [] while left and right: smaller = left if left[0] < right[0] else right sorted_list.append(smaller.pop(0)) sorted_list.extend(left if left else right) return sorted_list def merge_sort(elms): length = len(elms) if length <= 1: return elms else: harf = length / 2 sorted_left = merge_sort(elms[:harf]) sorted_right = merge_sort(elms[harf:]) return merge(sorted_left, sorted_right)
  23. 23. やってみた $ chmod 744 merge_sort.py $ echo 4 1 3 5 6 6 1 9 8 2 | ./merge_sort.py --int
  24. 24. ドン! $ chmod 744 merge_sort.py $ echo 4 1 3 5 6 6 1 9 8 2 | ./merge_sort.py --int 1123456689
  25. 25. それなりに出来た
  26. 26. 特徴 ● 平均計算量 O(n log n) ● 最悪計算量 O(n log n) ● 配列にランダムアクセスする必要なし
  27. 27. 良いところ ● そこそこ速い ● 速い時と遅い時で計算量があまり違わない ○ 平均計算量: O(n log n) ○ 最悪計算量: O(n log n) ● シーケンシャルアクセスしかできないデータ構造 にも使える (ex. 連結リストとか)
  28. 28. 悪いところ ● ソート済みリストの領域確保が必要 配列長の 1/2 のサイズ ● ランダムアクセスが出来る構造であればクイッ クソートとかの方が速い
  29. 29. Linux Kernelでは... (linux-2.6.34.14) ● ROOT/lib/list_sort.c ● ROOT/include/linux/list_sort.h で定義されている
  30. 30. ROOT/include/linux/list_sort.h #ifndef _LINUX_LIST_SORT_H #define _LINUX_LIST_SORT_H #include <linux/types.h> コイツか!! struct list_head; void list_sort(void *priv, struct list_head *head, int (*cmp)(void *priv, struct list_head *a, struct list_head *b)); #endif
  31. 31. void list_sort(...)の引数は? ● void *priv private data, opaque to list_sort(), passed to @cmp ● struct list_head *head ソートする連結リストの先頭のポインタ ● int (*cmp)(void *priv, struct list_head *a, struct list_head *b) 要素比較用の関数
  32. 32. void list_sort(...)の処理 struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists -- last slot is a sentinel */ int lev; /* index into part[] */ int max_lev = 0; struct list_head *list; if (list_empty(head)) return; memset(part, 0, sizeof(part)); head->prev->next = NULL; list = head->next; while (list) { struct list_head *cur = list; list = list->next; cur->next = NULL; for (lev = 0; part[lev]; lev++) { cur = merge(priv, cmp, part[lev], cur); part[lev] = NULL; } if (lev > max_lev) { if (unlikely(lev >= ARRAY_SIZE(part)-1)) { printk_once(KERN_DEBUG "list passed to" " list_sort() too long for" " efficiencyn"); lev--; } max_lev = lev; } part[lev] = cur; } for (lev = 0; lev < max_lev; lev++) if (part[lev]) list = merge(priv, cmp, part[lev], list); merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
  33. 33. 入りきらねえし よくわからねえ
  34. 34. そうだ!! テストコードを見よう ROOT/lib/list_sort.c static int __init list_sort_test(void)
  35. 35. static int __init list_sort_test(void) int i, r = 1, count; struct list_head *head = kmalloc(sizeof(*head), GFP_KERNEL); struct list_head *cur; printk(KERN_WARNING "testing list_sort()n"); cur = head; for (i = 0; i < LIST_SORT_TEST_LENGTH; i++) { struct debug_el *el = kmalloc(sizeof(*el), GFP_KERNEL); BUG_ON(!el); /* force some equivalencies */ el->value = (r = (r * 725861) % 6599) % (LIST_SORT_TEST_LENGTH/3); el->serial = i; el->l_h.prev = cur; cur->next = &el->l_h; cur = cur->next; } head->prev = cur; list_sort(NULL, head, cmp);
  36. 36. まずはテストデータを見てみる int i, r = 1, count; struct list_head *head = kmalloc(sizeof(*head), GFP_KERNEL); struct list_head *cur; printk(KERN_WARNING "testing list_sort()n"); cur = head; for (i = 0; i < LIST_SORT_TEST_LENGTH; i++) { struct debug_el *el = kmalloc(sizeof(*el), GFP_KERNEL); BUG_ON(!el); /* force some equivalencies */ el->value = (r = (r * 725861) % 6599) % (LIST_SORT_TEST_LENGTH/3); el->serial = i; el->l_h.prev = cur; cur->next = &el->l_h; cur = cur->next; } head->prev = cur; list_sort(NULL, head, cmp); テストデータ作成
  37. 37. テストデータの構造 struct debug_el struct list_head struct list_head *head struct list_head *prev struct list_head *next struct debug_el struct list_head struct debug_el struct list_head struct list_head struct list_head *prev struct list_head *next struct debug_el struct list_head struct list_head *prev struct list_head *prev struct list_head *prev struct list_head *next struct list_head *next struct list_head *next
  38. 38. うーん 激しく見づらい
  39. 39. テストデータの構造、見やすくした ※個数は適当 struct list_head *head heap debug_el debug_el debug_el debug_el list_head list_head list_head list_head list_head *prev *prev *prev *prev *prev *next *next *next *next *next
  40. 40. あ、連結リストってことね...
  41. 41. 次はソートの実行を見てみる int i, r = 1, count; struct list_head *head = kmalloc(sizeof(*head), GFP_KERNEL); struct list_head *cur; printk(KERN_WARNING "testing list_sort()n"); cur = head; for (i = 0; i < LIST_SORT_TEST_LENGTH; i++) { struct debug_el *el = kmalloc(sizeof(*el), GFP_KERNEL); BUG_ON(!el); /* force some equivalencies */ el->value = (r = (r * 725861) % 6599) % (LIST_SORT_TEST_LENGTH/3); el->serial = i; el->l_h.prev = cur; cur->next = &el->l_h; cur = cur->next; } head->prev = cur; list_sort(NULL, head, cmp); ソートの実行
  42. 42. ソート実行 list_sort(NULL, head, cmp); 第一引数はNULLで いいのかよ
  43. 43. void list_sort(...)の処理① 初期化 struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists -- last slot is a sentinel */ int lev; /* index into part[] */ int max_lev = 0; struct list_head *list; if (list_empty(head)) return; memset(part, 0, sizeof(part)); head->prev->next = NULL; list = head->next; while (list) { struct list_head *cur = list; list = list->next; cur->next = NULL; for (lev = 0; part[lev]; lev++) { cur = merge(priv, cmp, part[lev], cur); part[lev] = NULL; } if (lev > max_lev) { if (unlikely(lev >= ARRAY_SIZE(part)-1)) { printk_once(KERN_DEBUG "list passed to" " list_sort() too long for" " efficiencyn"); lev--; } max_lev = lev; } part[lev] = cur; } for (lev = 0; lev < max_lev; lev++) if (part[lev]) list = merge(priv, cmp, part[lev], list); 初期化
  44. 44. void list_sort(...)の処理① 初期化 struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists -- last slot is a sentinel */ int lev; /* index into part[] */ int max_lev = 0; struct list_head *list; if (list_empty(head)) return; memset(part, 0, sizeof(part));
  45. 45. void list_sort(...)の処理②マージ struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists -- last slot is a sentinel */ int lev; /* index into part[] */ int max_lev = 0; struct list_head *list; if (list_empty(head)) return; memset(part, 0, sizeof(part)); head->prev->next = NULL; list = head->next; while (list) { struct list_head *cur = list; list = list->next; cur->next = NULL; for (lev = 0; part[lev]; lev++) { cur = merge(priv, cmp, part[lev], cur); part[lev] = NULL; } if (lev > max_lev) { if (unlikely(lev >= ARRAY_SIZE(part)-1)) { printk_once(KERN_DEBUG "list passed to" " list_sort() too long for" " efficiencyn"); lev--; } max_lev = lev; } part[lev] = cur; } for (lev = 0; lev < max_lev; lev++) if (part[lev]) list = merge(priv, cmp, part[lev], list); ソート実行 入りきらねえ
  46. 46. void list_sort(...)の処理②マージ memset(part, 0, sizeof(part)); head->prev->next = NULL; list = head->next; while (list) { struct list_head *cur = list; list = list->next; cur->next = NULL; ソート実行 for (lev = 0; part[lev]; lev++) { cur = merge(priv, cmp, part[lev], cur); part[lev] = NULL; } if (lev > max_lev) { if (unlikely(lev >= ARRAY_SIZE(part)-1)) { printk_once(KERN_DEBUG "list passed to" " list_sort() too long for" " efficiencyn"); lev--; } max_lev = lev; } part[lev] = cur; } for (lev = 0; lev < max_lev; lev++) if (part[lev]) list = merge(priv, cmp, part[lev], list); merge_and_restore_back_links(priv, cmp, head, part[max_lev], list); ちょっと ずらした
  47. 47. 分離 head->prev->next = NULL; list = head->next; ①最後の要素の nextにNULLを入 れる ③すべて舐めたら終了 while (list) { struct list_head *cur = list; ② ①でNULLを入れてる list = list->next; ためすべて舐めたら cur->next = NULL; NULLが入る
  48. 48. void list_sort(...)の処理②のキモ ????? for (lev = 0; part[lev]; lev++) { cur = merge(priv, cmp, part[lev], cur); part[lev] = NULL; } 完了した所はNULLを入れる ~省略~ 最後の要素をpartに入 part[lev] = cur; れる
  49. 49. merge()を読もう
  50. 50. static struct list_head *merge(...) の引数 ● void *priv ● int (*cmp)(void *priv, struct list_head *a, struct list_head *b) ● struct list_head *a ● struct list_head *b
  51. 51. static struct list_head *merge(...)の処理 struct list_head head, *tail = &head; a, bのどちらかが空になるまで while (a && b) { /* if equal, take 'a' -- important for sort stability */ if ((*cmp)(priv, a, b) <= 0) { tail->next = a; a = a->next; } else { tail->next = b; b = b->next; } tail = tail->next; } tail->next = a?:b; return head.next; a <= bの時は headの後ろにaをくっつける a > bの時は headの後ろにbをくっつける 残った方を最後にくっつける
  52. 52. static struct list_head *merge(...)は結局.. part[lev] cur h0 h3 h4 h1 h2 merge(priv, cmp, part[lev], cur); cur h0 h1 h2 h3 h4 整列+結合
  53. 53. ふむふむ
  54. 54. void list_sort(...)の処理②が 終わる頃には
  55. 55. partはこんな感じ struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists -- last slot is a sentinel */ int lev; /* index into part[] */ int max_lev = 0; struct list_head *list; if (list_empty(head)) return; memset(part, 0, sizeof(part)); head->prev->next = NULL; list = head->next; part
  56. 56. partはこんな感じ part[0] part[1] part[2] part[3] part[4] part[5] = NULL = NULL = = = = NULL h1 h7 h0 h3 h2 h4 h5 h6 h8
  57. 57. この後は?
  58. 58. void list_sort(...)の処理③ さらにマージ for (lev = 0; lev < max_lev; lev++) if (part[lev]) list = merge(priv, cmp, part[lev], list); merge_and_restore_back_links(priv, cmp, head, part [max_lev], list);
  59. 59. void list_sort(...)の処理③ さらにマージ for (lev = 0; lev < max_lev; lev++) if (part[lev]) list = merge(priv, cmp, part[lev], list); いくつかに別れた ソート済みリストを1 本にマージ merge_and_restore_back_links(priv, cmp, head, part [max_lev], list);
  60. 60. void list_sort(...)の処理③ さらにマージ for (lev = 0; lev < max_lev; lev++) if (part[lev]) list = merge(priv, cmp, part[lev], list); merge_and_restore_back_links(priv, cmp, head, part [max_lev], list); ん? これなんだ?
  61. 61. merge_and_restore_back_links()の 説明 Combine final list merge with restoration of standard doubly linked list structure. This approach duplicates code from merge(), but runs faster than the tidier alternatives of either a separate final prev-link restoration pass, or maintaining the prev links throughout.
  62. 62. 最後の最後によくわからない
  63. 63. なんとなくだけど 2重になっているリンクの修復とか するのかな...
  64. 64. まとめ ● 再帰使うと結構楽に書ける ● Pythonで書いたコードとCで書かれたLinux Kernelのコードとではだいぶ違う ● てかそれなりにむずい
  65. 65. いいやつですが 傷つきやすいやつなので
  66. 66. まじ、そーっと使ってください
  67. 67. ばいばい

×