初めてのビット演算

# はじめに
複数のデータを保存したい！でも保存先のカラムは１つしかない！
そんな時、役立ったビット演算について自分用にまとめる。

# シーン例
フォームのチェックボックスから複数の値を受け取りたい。
各選択肢用のカラムがある場合は、それぞれ格納すればよいがもし１つしかカラムがない場合はどうするのか...？

データをカンマ区切りで連結して、保存すればいいのかな？と思っていたがビット演算なるものがあるからそれを使うとよいとアドバイスを頂いた。


# ビット演算とは
> ビット演算とは、主にコンピュータ上で行われる演算の一つで、対象データをビット列（2進数の0と1の羅列）とみなして、ビットの移動やビット単位の論理演算などを行うもの。
引用元:[ビット演算とは](https://e-words.jp/w/%E3%83%93%E3%83%83%E3%83%88%E6%BC%94%E7%AE%97.html)

# 使ってみる
適当に書いているので、参考程度に
４つのチェックボックス（リンゴ、ミカン、バナナ、ブドウ）がありリンゴとバナナを選択したという設定

```php
$post_data = ['1', '4'];  //appleとbananaのデータを受け取った場合
$bit_sum = 0;  //受け取りデータ格納用変数

//整数を２進数で表記するために「0b」を先頭につけている（※コメントのため不要ではあるが区別するために）
//1.各選択肢に、それぞれ２進数の各桁が1となるよう値を振っていく。
$fruits = [
    1 => 'apple',    //0b0001　→　2^0 = 1
    2 => 'orange',   //0b0010　→　2^1 = 2
    4 => 'banana',   //0b0100　→　2^2 = 4
    8 => 'grape'     //0b1000　→　2^3 = 8
];

//2.データをまとめるときは、ビット和（論理和）で計算する
foreach ($post_data as $value) {
    $bit_sum = $bit_sum | $value;
}

$selects = [];  

//3.まとめたデータから元の選択肢に戻す場合は、ビット積（論理積）を使う
foreach (array_keys($fruits) as $key) {
    if ($key & $bit_sum) {
        $selects[] = $key;
    }
}

print_r($selects); //[1, 4] つまりappleとbananaが格納されているので無事選択肢データを取得できている

```

# 解説
## 1.それぞれ２進数の各桁が１となるように値を振っていく

```php
//1.各選択肢に、それぞれ２進数の各桁が1となるよう値を振っていく。
$fruits = [
    1 => 'apple',    //0b0001　→　2^0 = 1
    2 => 'orange',   //0b0010　→　2^1 = 2
    4 => 'banana',   //0b0100　→　2^2 = 4
    8 => 'grape'     //0b1000　→　2^3 = 8
];
```

各桁とデータが対応している。
「0b0001」は、(2^0 × 1) + (2^1 × 0) + (2^2 × 0) + (2^3 × 0) = 1
「0b0010」は、(2^0 × 0) + (2^1 × 1) + (2^2 × 0) + (2^3 × 0) = 2
「0b0100」は、(2^0 × 0) + (2^1 × 0) + (2^2 × 1) + (2^3 × 0) = 4
「0b1000」は、(2^0 × 0) + (2^1 × 0) + (2^2 × 0) + (2^3 × 1) = 8
となっている。


## 2.データを１つにまとめる
データをまとめるときは、`ビット和（論理和）`を行う。

ビット和とは、下記のような条件の元計算する。計算記号は`|`。
・どちらか１つでも`1`なら`1`
・どちらも`0`の場合`0`

```php
//ビット和計算例
$value1 = 0b0010  //2                         0010
$value2 = 0b1000  //8                       | 1000
//                                          -------
echo decbin($value1 | $value2); //0b1010      1010  
```

```php
$post_data = ['1', '4'];  //appleとbananaのデータを受け取った場合

//2.データをまとめるときは、ビット和（論理和）を行う
foreach ($post_data as $value) {
    $bit_sum = $bit_sum | $value;
}

/*今回の処理の場合
1ループ目　0b0000(0) | 0b0001(1) = 0b0001(1)
2ループ目　0b0001(1) | 0b0100(4) = 0b0101(5)

つまりappleとbananのデータをビット和による計算で「0b0101(5)」という一つのデータにした。
これで１つのカラムに複数データを保存できる！！！
*/
```

## 3.まとめたデータから元の選択肢を求める
まとめたデータから元の選択肢に戻す場合は、`ビット積（論理積）`を使う。

ビット積とは、下記のような条件の元計算する。計算記号は`&`。
・どちらも`1`の場合`1`
・どちらか１つでも`0`の場合`0`

```php
//ビット積計算例
$value1 = 0b0010  //2                         0010
$value2 = 0b1010  //10                      & 1010
//                                          -------
echo decbin($value1 & $value2); //0b0010      0010  
```


```php
$selects = [];  

//3.まとめたデータから元の選択肢に戻す場合は、ビット積（論理積）を使う
foreach (array_keys($fruits) as $key) {
    if ($key & $bit_sum) {
        $selects[] = $key;
    }
}

/*
array_keysを使っているため若干分かりにくくなっているが要はこのループは
「各選択肢に振られている番号」と「ビット和（１つにまとめたデータ）」をビット積で計算し、該当したもの（今回であればリンゴ:1とバナナ:4）を配列に格納している。

2.で計算した通り　$bit_sum = 0b0101(5)

1ループ目（リンゴ）　0b0001(1) & 0b0101(5) → 0b0001(1)
2ループ目（ミカン）　0b0010(2) & 0b0101(5) → 0b0000(0)
3ループ目（バナナ）　0b0100(4) & 0b0101(5) → 0b0100(4)
4ループ目（ブドウ）　0b1000(8) & 0b0101(5) → 0b0000(0)

このような計算結果となる。
0は論理値でfalseとなるため配列への格納処理は行われない。
1,4は論理値でtrueとなるため配列への格納処理が行われる。
よって、今回受け取ったリンゴとバナナのデータをビット和から再び求まる。
selects = ['1', '4']

*/
```

# 参考
[【PHP入門講座】 ビット演算](https://qiita.com/mpyw/items/ce626976ec4dc07dfec2)

# まとめ
今回は、ビット演算についてまとめた。
ビット演算を実際に使うのは初めてで感動した。

以上。