明日使えないすごいビット演算

10,506 views
10,224 views

Published on

KMCの例会講座で用いたスライドを一部編集したものです。
ビット演算を組み合わせたトリッキーな方法で様々な操作を高速に行う方法を紹介します。

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

No Downloads
Views
Total views
10,506
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
54
Comments
0
Likes
38
Embeds 0
No embeds

No notes for slide

明日使えないすごいビット演算

  1. 1. 明日使えないすごいビット演算 KMC1回生 prime(Twitter id:@_primenumber)
  2. 2. ビット演算とは ● ● ● コンピューター内で数値や文字列などのデータは2進数で記 録されている ビット演算とは、2進数を0/1の列として操作するような演算 のこと ビット反転 (C言語では ~x) – 各ビットの0/1を反転させる x 0 1 1 0 1 0 1 1 ~x 1 0 0 1 0 1 0 0
  3. 3. ビット演算とは ● ビット論理和 (C言語では x|y) – 各桁を比較して、少なくとも一方が1なら1 x y 0 0 1 0 1 0 1 1 x|y 1 1 1 0 1 0 1 1 1 1 0 0 1 0 0 1
  4. 4. ビット演算とは ● ビット論理積 (C言語では x&y) – 各桁を比較して、両方とも1なら1 x y 0 0 1 0 1 0 1 1 x&y 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 1
  5. 5. ビット演算とは ● ビット排他的論理和 (C言語では x^y) – 各桁を比較して、片方のみが1なら1 x y 0 0 1 0 1 0 1 1 x^y 1 1 1 0 0 0 1 0 1 1 0 0 1 0 0 1
  6. 6. ビット演算とは ● 左ビットシフト (C言語では x<<n) – x 各桁を左に指定した桁数ずらす 0 0 1 0 1 0 1 1 x<<3 0 1 0 1 1 0 0 0
  7. 7. ビット演算とは ● 右ビットシフト (C言語では x>>n) – x 各桁を右に指定した桁数ずらす 0 0 1 0 1 0 1 1 x>>3 0 0 0 1 0 1 0 1 上位桁に何を詰めるかによっていくつか種類がある ● 0を詰める ● 元の最上位桁と同じ物を詰める
  8. 8. ビット演算とは ● ビット演算は回路が単純になるため、とても高速なことが多い – – ● とはいえ最近のCPUだと加減乗算も同じぐらい速い 組み合わせて使うことも多い うまく使うとものすごい高速化できる – ● ● 単純な実装に比べて数十倍速くなることも 今回はビット演算を用いていろいろな操作を高速にする例を 挙げます 数値は2の補数表現で格納されているものとします
  9. 9. 明日使えないすごいビット演算 ● 「1になっている一番下の桁」を取得する – 2の何乗で割り切れるか,みたいなことが分かったりする data 0 1 0 1 1 0 0 0
  10. 10. 明日使えないすごいビット演算 ● 「1になっている一番下の桁」を取得する – 2の何乗で割り切れるか,みたいなことが分かったりする data 0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0
  11. 11. 明日使えないすごいビット演算 ● 「1になっている一番下の桁」を取得する data & (-data) data 0 1 0 1 1 0 0 0 -data 1 0 1 0 1 0 0 0 実は,-dataは~data+1に等しい (足して0になるようにするため)
  12. 12. 明日使えないすごいビット演算 ● 「1になっている一番下の桁」を取得する data & (-data) data 0 1 0 1 1 0 0 0 -data 1 0 1 0 1 0 0 0 data & (-data) 0 0 0 0 1 0 0 0 実は,-dataは~data+1に等しい (足して0になるため)
  13. 13. 明日使えないすごいビット演算 ● 「1になっている一番下の桁」を0にする data &= data-1 data 0 1 0 1 1 0 0 0 data-1 0 1 0 1 0 1 1 1 data & data-1 0 1 0 1 0 0 0 0
  14. 14. 明日使えないすごいビット演算 ● 「1になっている一番上の桁」を求める – – ● 数値のだいたいの大きさを求める log2 ( n ) の整数部分を求めるのに使える これは一発では行かないが、うまい方法がある
  15. 15. 明日使えないすごいビット演算 ● 「1になっている一番上の桁」を求める – – ● 数値のだいたいの大きさを求める log 2 ( n ) の整数部分を求めるのに使える これは一発では行かないが、うまい方法がある – 二分探索!
  16. 16. 明日使えないすごいビット演算 ● 「1になっている一番上の桁」を求める 0 1 0 1 1 0 0 1 0xF0 1 1 1 1 0 0 0 0 ビット論理積 0 1 0 1 0 0 0 0
  17. 17. 明日使えないすごいビット演算 ● 「1になっている一番上の桁」を求める 0 1 0 1 1 0 0 1 0xF0 1 1 1 1 0 0 0 0 ビット論理積 0 1 0 1 0 0 0 0 != 0
  18. 18. 明日使えないすごいビット演算 ● 「1になっている一番上の桁」を求める 0 1 0 1 1 0 0 1 0xF0 1 1 1 1 0 0 0 0 ビット論理積 0 1 0 1 0 0 0 0 != 0 1になっている一番上の桁は上位4桁のどれか!
  19. 19. 明日使えないすごいビット演算 ● 「1になっている一番上の桁」を求める 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0 0 0xCC 1 1 0 0 1 1 0 0 ビット論理積 0 1 0 0 0 0 0 0
  20. 20. 明日使えないすごいビット演算 ● 「1になっている一番上の桁」を求める 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0 0 0xCC 1 1 0 0 1 1 0 0 ビット論理積 0 1 0 0 0 0 0 0 != 0 1になっている一番上の桁は上位2桁のどれか!
  21. 21. 明日使えないすごいビット演算 ● 「1になっている一番上の桁」を求める – data data data data data サンプルコード(32ビット) = = = = = data data data data data & & & & & 0xFFFF0000 0xFF00FF00 0xF0F0F0F0 0xCCCCCCCC 0xAAAAAAAA ? ? ? ? ? data data data data data & & & & & 0xFFFF0000 0xFF00FF00 0xF0F0F0F0 0xCCCCCCCC 0xAAAAAAAA : : : : : data; data; data; data; data;
  22. 22. 明日使えないすごいビット演算 ● ビット列を逆転する – 高速フーリエ変換などで用いる data 0 1 0 1 1 0 0 1 dataの逆転 1 0 0 1 1 0 1 0
  23. 23. 明日使えないすごいビット演算 ● ビット列を逆転する – これも一気にやるのは無理 data 0 0 1 data&0x55 data&0xAA 1 0 1 1 1 0 0 0 0 1 1 1 0
  24. 24. 明日使えないすごいビット演算 ● ビット列を逆転する data 0 1 0 1 1 0 0 1 (data&0x55)<<1 1 (data&0xAA)>>1 1 0 0 0 1 1 0
  25. 25. 明日使えないすごいビット演算 ● ビット列を逆転する data 0 1 0 1 1 0 0 1 (data&0x55)<<1 1 (data&0xAA)>>1 1 0 0 0 1 1 0 ビット論理和 1 0 1 0 0 1 1 0
  26. 26. 明日使えないすごいビット演算 ● ビット列を逆転する 変更前のdata 0 1 0 1 1 0 0 1 変更後のdata 1 0 1 0 0 1 1 0
  27. 27. 明日使えないすごいビット演算 ● ビット列を逆転する 0 1 0 1 1 0 0 1 変更後のdata 1 0 1 0 0 1 1 0 data&0x33 data&0xCC 1 0 1 0 1 0 0 1
  28. 28. 明日使えないすごいビット演算 ● ビット列を逆転する 0 1 0 1 1 0 0 1 変更後のdata 1 0 1 0 0 1 1 0 (data&0x33)<<2 1 0 (data&0xCC)>>2 1 0 1 0 0 1 1 0 1 0 1 0 0 1
  29. 29. 明日使えないすごいビット演算 ● ビット列を逆転する 0 1 0 1 1 0 0 1 1 0 1 0 0 1 1 0 1 0 1 0 1 0 0 1
  30. 30. 明日使えないすごいビット演算 ● ビット列を逆転する 0 1 0 1 1 0 0 1 1 0 1 0 0 1 1 0 1 0 1 0 1 0 0 1 1 0 0 1 1 0 1 0
  31. 31. 明日使えないすごいビット演算 ● ビット列を逆転する 0 1 0 1 1 0 0 1 1 0 0 1 1 0 1 0
  32. 32. 明日使えないすごいビット演算 ● ビット列を逆転する ● dataは32ビット符号なし型とする data = | data = | data = | data = | data = ((data & ((data & ((data & ((data & ((data & ((data & ((data & ((data & (data << 0x55555555) 0xAA555555) 0x33333333) 0xCCCCCCCC) 0x0F0F0F0F) 0xF0F0F0F0) 0x00FF00FF) 0xFF00FF00) 16) | (data << >> << >> << >> << >> >> 1) 1); 2) 2); 4) 4); 8) 8); 16);
  33. 33. 明日使えないすごいビット演算 ● 1になっているビットの数を数える ● ビットレベルでハミング距離を取りたい時などに使う ● 素直な実装(int型を32bitと仮定) int count = 0; for (int i = 0;i < 32;i++) { count += (data >> i) & 1; }
  34. 34. 明日使えないすごいビット演算 ● 1になっているビットの数を数える ● ちょっと速い実装 int count = 0; for(; data; data &= data – 1) { ++count; } data &= data-1で1になっている一番小さい桁が0になる
  35. 35. 明日使えないすごいビット演算 ● ● 1になっているビットの数を数える けっこう速い実装 10進数 2進数 1の個数 0 00000000 0 1 00000001 1 2 00000010 1 3 00000011 2 4 00000100 1 ... ... ... 255 11111111 8 あらかじめ0~255までの数について1の個数を数えて配列に 入れておく
  36. 36. 明日使えないすごいビット演算 ● 1になっているビットの数を数える ● けっこう速い実装 int count = 0; count += table[data & 0xFF]; count += table[(data >> 8) & 0xFF]; count += table[(data >> 16) & 0xFF]; count += table[(data >> 24) & 0xFF]; table[256] : 1の個数が入った配列
  37. 37. 明日使えないすごいビット演算 ● 1になっているビットの数を数える ● 配列を使った実装はけっこう速い – 素直な方法の20倍くらい
  38. 38. 明日使えないすごいビット演算 ● 1になっているビットの数を数える ● 配列を使った実装はけっこう速い – ● 素直な方法の20倍くらい しかし、さらに倍くらい速い実装が存在する
  39. 39. 明日使えないすごいビット演算 1 0 1 1 0 1 0 0 各桁の0/1を「その桁の1の個数」と読み替えることができる
  40. 40. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 各桁の0/1を「その桁の1の個数」と読み替えることができる
  41. 41. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 0x55 0 1 0 1 0 1 0 1 ビット論理積 0個 1個 1個 0個
  42. 42. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 0xAA 1 0 1 0 1 0 1 ビット論理積 1個 1個 0個 0個 0
  43. 43. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 &0xAA 1個 &0x55 1個 0個 0個 1個 0個 1個 0個
  44. 44. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 1個 1個 0個 0個 0個 1個 1個 0個
  45. 45. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 1個 1個 0個 0個 0個 1個 1個 0個 足し算 0 1個 1 0個 0 1個 0 0個 2桁ごとに「その2桁にあった1の数」が格納された!!!
  46. 46. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 0 1個 &0xCC 0 1個 0個 1 &0x33 1 0 0個 1個 0 0個 0 0 1個 0個
  47. 47. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 0 1個 1 0個 1 0 0 1個 0 0個 0個 0 0個 1個 0 1個
  48. 48. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 0 1個 1 0個 1 0 0 1個 0 0個 0個 0 0個 1個 0 1個 0 1個 足し算 0 0 1 1個 0 0
  49. 49. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 0 0 1 1個 &0xF0 0 0 1 1個 0 0 1個 0 &0x0F 0 0 0 1個
  50. 50. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 0 0 1 1個 0 0 0 1個 0 0 0 1個 0 0 1 1個
  51. 51. 明日使えないすごいビット演算 1個 0個 1個 1個 0個 1個 0個 0個 0 0 1 1個 0 0 0 1個 0 0 0 1個 0 0 1 1個 0 0個 足し算 0 0 0 0 0 1
  52. 52. 明日使えないすごいビット演算 ● 1になっているビットの数を数える ● かなり速い実装(dataはunsigned int型) data = + data = + data = + data = + data = + (data & 0x55555555) ((data & 0xAAAAAAAA) (data & 0x33333333) ((data & 0xCCCCCCCC) (data & 0x0F0F0F0F) ((data & 0xF0F0F0F0) (data & 0x00FF00FF) ((data & 0xFF00FF00) (data & 0x0000FFFF) ((data & 0xFFFF0000) >> 1); >> 2); >> 4); >> 8); >> 16);
  53. 53. 明日使えないすごいビット演算 ● こうして、苦労の末我々は爆速で1になっているビットの数を数 えるアルゴリズムを手に入れた!!!
  54. 54. 明日使えないすごいビット演算 ● ● こうして、苦労の末我々は爆速で1になっているビットの数を数 えるアルゴリズムを手に入れた!!! しかし・・・
  55. 55. 明日使えないすごいビット演算 ● ● ● こうして、苦労の末我々は爆速で1になっているビットの数を数 えるアルゴリズムを手に入れた!!! しかし・・・ IntelのSIMD拡張命令セット、SSE4.2から、ズバリ「1になって いるビットの数を数える」CPU命令が追加された!(popcnt)
  56. 56. 明日使えないすごいビット演算 ● ● ● こうして、苦労の末我々は爆速で1になっているビットの数を数 えるアルゴリズムを手に入れた!!! しかし・・・ IntelのSIMD拡張命令セット、SSE4.2から、ズバリ「1になって いるビットの数を数える」CPU命令が追加された!(popcnt) – 実際ビット演算を使ったアルゴリズムより2倍ほど速い
  57. 57. 明日使えないすごいビット演算 ● ● ● こうして、苦労の末我々は爆速で1になっているビットの数を数 えるアルゴリズムを手に入れた!!! しかし・・・ IntelのSIMD拡張命令セット、SSE4.2から、ズバリ「1になって いるビットの数を数える」CPU命令が追加された!(popcnt) – ● 実際ビット演算を使ったアルゴリズムより2倍ほど速い 我々の努力は無駄だった!!!!!
  58. 58. まとめと注意 ● ● ビット演算はうまく使うととても高速 ぱっと見何してるか判りづらいのでバグを埋め込 みやすい – ものすごい高速化をする必要のないときは使わ ないほうが吉
  59. 59. まとめと注意 ● ● ビット演算はうまく使うととても高速 ぱっと見何してるか判りづらいのでバグを埋め込 みやすい ものすごい高速化をする必要のないときは使わ ないほうが吉 CPU命令速い!!!!! – ● – 本当に高速化したいときはまずこっちを考えるべき
  60. 60. おわり

×