Your SlideShare is downloading. ×
MergeSortの概要とLinuxでの実装
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

MergeSortの概要とLinuxでの実装

322
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
322
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Merge Sort の概要 および LinuxKernel における 応用
  • 2. てめー誰だよ? Twitter ID: @TakesxiSximada 職業: - 警備員(自宅) - 時々仕事するかも アイコン: - 土下座している訳ではありません
  • 3. いきなりだけど...
  • 4. マージソートって なんとなーく知ってるけど ちゃんと理解してるとはいえないよな...
  • 5. おーい、マージソート書いといて!!
  • 6. おーい、マージソート書いといて!! とりあえずググらなきゃ!!
  • 7. これでは理解しているとは言えない
  • 8. じゃあこのタイミングで 理解してしまおう
  • 9. Merge Sort ~ 空と海と大地と呪われしsort() ~
  • 10. とりあえずググる
  • 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. え?
  • 13. つまりどういうこと?
  • 14. つまりこういう事
  • 15. ふむふむ
  • 16. ポイント 1. 分解/結合する 2. 2つのリストを並べ替える
  • 17. ポイント① 分解/結合する ● 要素数が1になるまで分解する ● 分解は要素数/2で分解する ○ 奇数の時は(要素数-1)/2 ● 並べ替えつつ結合していく ● 結合する順番は分解とは逆の順序で行う
  • 18. つまりこういう事
  • 19. ポイント② 2つのリストを並べ替える 1. 新しいソート済みリストを用意 2. 2つのリストの先頭同士を比較して小さい方を取 り出しソート済みリストに入れる 3. どちらかのリストが空になるまで2を繰り返す 4. 余っている方のリストをソート済みリストの後ろ にくっつける
  • 20. つまりこういう事
  • 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. 大事なところを抜粋 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. やってみた $ chmod 744 merge_sort.py $ echo 4 1 3 5 6 6 1 9 8 2 | ./merge_sort.py --int
  • 24. ドン! $ chmod 744 merge_sort.py $ echo 4 1 3 5 6 6 1 9 8 2 | ./merge_sort.py --int 1123456689
  • 25. それなりに出来た
  • 26. 特徴 ● 平均計算量 O(n log n) ● 最悪計算量 O(n log n) ● 配列にランダムアクセスする必要なし
  • 27. 良いところ ● そこそこ速い ● 速い時と遅い時で計算量があまり違わない ○ 平均計算量: O(n log n) ○ 最悪計算量: O(n log n) ● シーケンシャルアクセスしかできないデータ構造 にも使える (ex. 連結リストとか)
  • 28. 悪いところ ● ソート済みリストの領域確保が必要 配列長の 1/2 のサイズ ● ランダムアクセスが出来る構造であればクイッ クソートとかの方が速い
  • 29. Linux Kernelでは... (linux-2.6.34.14) ● ROOT/lib/list_sort.c ● ROOT/include/linux/list_sort.h で定義されている
  • 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. 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. 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. 入りきらねえし よくわからねえ
  • 34. そうだ!! テストコードを見よう ROOT/lib/list_sort.c static int __init list_sort_test(void)
  • 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. まずはテストデータを見てみる 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. テストデータの構造 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. うーん 激しく見づらい
  • 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. あ、連結リストってことね...
  • 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. ソート実行 list_sort(NULL, head, cmp); 第一引数はNULLで いいのかよ
  • 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. 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. 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. 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. 分離 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. 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. merge()を読もう
  • 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. 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. 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. ふむふむ
  • 54. void list_sort(...)の処理②が 終わる頃には
  • 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. 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. この後は?
  • 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. 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. 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. 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. 最後の最後によくわからない
  • 63. なんとなくだけど 2重になっているリンクの修復とか するのかな...
  • 64. まとめ ● 再帰使うと結構楽に書ける ● Pythonで書いたコードとCで書かれたLinux Kernelのコードとではだいぶ違う ● てかそれなりにむずい
  • 65. いいやつですが 傷つきやすいやつなので
  • 66. まじ、そーっと使ってください
  • 67. ばいばい

×