JavaScript でなるべく速くビットカウントする

1 が立っているビット数を数えることを「ビットカウント」や "population count" 等と呼びます。

このときのビット数は 2 進数の「ハミング重み (hamming weight)」と一致します。

既に様々な高速なアルゴリズムが見つかっていますが、C 言語や Rust 等で高速だからと言って JavaScript でも高速とは限らないため、実際に実行時間を測ってみました。

JavaScript のビット演算では値を 32 ビットの整数として扱うため、ビットカウントも 32 ビットの整数について考えます。

参考「[バイナリービット演算子 - 式と演算子 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators#%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%83%BC%E3%83%93%E3%83%83%E3%83%88%E6%BC%94%E7%AE%97%E5%AD%90)」

# 1. 様々なアルゴリズム

おおよそ速い順に紹介します。

## 1.1. 分割統治法

ビットカウントの有名なアルゴリズムです (固有の名前は付いていない？) 。

環境によりますが、かなり速いです (具体的な実行時間の比較は後述します) 。

```js
const popCountA = x => {
	const a = x - (x >>> 1 & 0x55555555);
	const b = (a & 0x33333333) + (a >>> 2 & 0x33333333);
	const c = b + (b >>> 4) & 0x0f0f0f0f;
	const y = c * 0x01010101 >>> 24;
	return y;
};

const popCountB = x => {
	const a = x - (x >>> 1 & 0x55555555);
	const b = (a & 0x33333333) + (a >>> 2 & 0x33333333);
	const c = b + (b >>> 4) & 0x0f0f0f0f;
	const y = c % 0xff;
	return y;
};

const popCountC = x => {
	const a = x - (x >>> 1 & 0x55555555);
	const b = (a & 0x33333333) + (a >>> 2 & 0x33333333);
	const c = b + (b >>> 4) & 0x0f0f0f0f;
	const d = c + (c >>> 8);
	const e = d + (d >>> 16);
	const y = e & 0xff;
	return y;
};

const popCountD = x => {
	const a = (x & 0x55555555) + (x >>>  1 & 0x55555555);
	const b = (a & 0x33333333) + (a >>>  2 & 0x33333333);
	const c = (b & 0x0f0f0f0f) + (b >>>  4 & 0x0f0f0f0f);
	const d = (c & 0x00ff00ff) + (c >>>  8 & 0x00ff00ff);
	const y = (d & 0x0000ffff) + (d >>> 16 & 0x0000ffff);
	return y;
};

const popCountE = x => {
	const a = x >>> 1 & 0o3_3333333333;
	const b = x - a - (a >>> 1 & 0o3_3333333333);
	// メモ: JavaScript でのビット演算は基本的に符号ありで計算されるため、符号なしの数に変換する
	const c = (b + (b >>> 3) & 0o3_0707070707) >>> 0;
	const y = c % 0o77;
	return y;
};

const popCountF = x => {
	const a = x >>> 1 & 0o3_3333333333;
	const b = x - a - (a >>> 1 & 0o3_3333333333);
	// メモ: JavaScript でのビット演算は基本的に符号ありで計算されるため、符号なしの数に変換する
	const c = (b + (b >>> 3) & 0o3_0707070707) >>> 0;
	const y = Number(BigInt(c) * 0o1_0101010101n >> 30n & 0o77n);
	return y;
};
```

1 ビットずつのカウント、2 ビットずつのカウント、4 ビットずつのカウント、8 ビットずつのカウント…というように分割統治法で計算します。

初めて見ると謎の定数が並んでいるように見えますが、定数を 2 進数にすると分かりやすいと思います。

```js
0x55555555 === 0b01010101010101010101010101010101;
0x33333333 === 0b00110011001100110011001100110011;
0x0f0f0f0f === 0b00001111000011110000111100001111;
0x00ff00ff === 0b00000000111111110000000011111111;
0x0000ffff === 0b00000000000000001111111111111111;
```

`popCountC` 関数内の式を変形してできるのが `popCountB` および `popCountA` 関数になります。

※計算方法に関するより詳しい説明は後述します。

## 1.2. 計算済みのルックアップテーブルを作成する方法

あらかじめ値ごとにビットカウントして保存しておき、実際にビットカウントしたいときにルックアップテーブルを参照するだけにする方法です。

実際には 32 ビットの値全てに関してルックアップテーブルを作成するとメモリを消費しすぎるため、16 ビットや 8 ビットの値に関してルックアップテーブルを作成し、32 ビットの値を分割して計算します。

JavaScript では 32 ビットの値を配列のように単純に分割することができないため、ビット演算や `DataView` を用いて値を分割します。

### 1.2.1. 16 ビットに分割する場合

ほぼルックアップテーブルを参照するだけなので高速に動作しそうですが、`TypedArray` へのアクセスが遅い環境では前述の方法程は速くなりませんでした。

```js
const POP_COUNTS_16 = new Uint8Array(0x10000);
for (let x = 0; x < POP_COUNTS_16.length; x++) {
	POP_COUNTS_16[x] = popCountA(x); // ここの計算は他のアルゴリズムでも良い
}

// ビット演算で値を分割する場合
const popCountG = x => POP_COUNTS_16[x & 0xffff] + POP_COUNTS_16[x >>> 16 & 0xffff];

// DataView で値を分割する場合
const aH = new Uint16Array(2);
const dataViewH = new DataView(aH.buffer);
const popCountH = x => {
	dataViewH.setUint32(0, x);
	return POP_COUNTS_16[aH[0]] + POP_COUNTS_16[aH[1]];
};
```

### 1.2.2. 8 ビットに分割する場合

速度的には 16 ビットに分割する方が 8 ビットに分割するより速いのは当たり前ですが、メモリ使用量は 8 ビットに分割する方が少なくできます。

```js
const POP_COUNTS_8 = new Uint8Array(0x100);
for (let x = 0; x < POP_COUNTS_8.length; x++) {
	POP_COUNTS_8[x] = popCountA(x); // ここの計算は他のアルゴリズムでも良い
}

// ビット演算で値を分割する場合
const popCountI = x => (
	POP_COUNTS_8[x        & 0xff] +
	POP_COUNTS_8[x >>>  8 & 0xff] +
	POP_COUNTS_8[x >>> 16 & 0xff] +
	POP_COUNTS_8[x >>> 24 & 0xff]
);

// DataView で値を分割する場合
const aJ = new Uint8Array(4);
const dataViewJ = new DataView(aJ.buffer);
const popCountJ = x => {
	dataViewJ.setUint32(0, x);
	return POP_COUNTS_8[aJ[0]] + POP_COUNTS_8[aJ[1]] + POP_COUNTS_8[aJ[2]] + POP_COUNTS_8[aJ[3]];
};
```

## 1.3. WebAssembly の `i32.popcnt` 命令を使用する方法

WebAssembly にビットカウントするための命令 `i32.popcnt` があるため高速に動作するかと思いましたが、値のやり取りに時間がかかるのか、前述の方法程は速くなりませんでした。

```popcnt.wat
(module
  (func (export "popcnt") (param i32) (result i32)
    local.get 0
    i32.popcnt
  )
)
```

```txt:popcnt.wasm を Base64 エンコードしたテキスト
AGFzbQEAAAABBgFgAX8BfwMCAQAHCgEGcG9wY250AAAKBwEFACAAaQsACgRuYW1lAgMBAAA=
```

```js
const code = Uint8Array.from(
	atob('AGFzbQEAAAABBgFgAX8BfwMCAQAHCgEGcG9wY250AAAKBwEFACAAaQsACgRuYW1lAgMBAAA='),
	binaryChar => binaryChar.charCodeAt(0),
);
const { instance } = await WebAssembly.instantiate(code.buffer);
const { popcnt: popCountK } = instance.exports;
```

参考「[Population count - WebAssembly | MDN](https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Numeric/Population_count)」
参考「[wat2wasm demo](https://webassembly.github.io/wabt/demo/wat2wasm/)」
参考「[[JavaScript] Unicode 文字列やバイナリデータを Base64 エンコードおよびデコードする - Qiita](https://qiita.com/kerupani129/items/1d6b936974ec65ae4833)」

## 1.4. 1 が立っているビットを 1 つずつ消していく方法

右 (下位) から 1 が立っているビットを 1 つずつ消していき、その回数を数えることでビットカウントする方法です。

1 が立っているビットが少ない場合は速いですが、32 ビットの値をまんべんなく受け取る可能性がある場合は期待値 (平均) 的にはかなり遅いです。

(ソースコードだけ見ると賢いやり方に見えますが、性能は良くないです。)

```js
const popCountL = x => {
	let y = 0;
	for (let a = x; a !== 0; a &= a - 1) {
		y++;
	}
	return y;
};
```

`a & a - 1` とすると 1 が立っている一番右のビットを 0 にできます。

```js:例
0b10000001 & 0b10000001 - 1 === 0b10000001 & 0b10000000 === 0b10000000;
0b10001000 & 0b10001000 - 1 === 0b10001000 & 0b10000111 === 0b10000000;
```

# 2. 実行時間を比較

値によって実行時間が大きく異なるアルゴリズムがあるため、単純に考えると `0x00000000` から `0xffffffff` まで計算したいですが、実行時間があまりにも長くなり過ぎる可能性があるため、等しい個数の乱数で測ります。

## 2.1. 実行時間

具体的なコードは後述します。

Google Chrome で確認した結果の平均は以下の表の通りです。

|関数名|実行時間|備考|
|-|-|-|
|popCountX|48.133|ビットカウント以外の処理|
|popCountA|**75.633**|分割統治、乗算|
|popCountB|89.400|分割統治、剰余|
|popCountC|80.167|分割統治、減算、論理積|
|popCountD|92.667|分割統治|
|popCountE|90.700|分割統治、3 ビットずつ、剰余|
|popCountF|780.767|分割統治、3 ビットずつ、乗算、BigInt 変換|
|popCountG|97.333|ルックアップテーブル、16 ビット、ビット演算|
|popCountH|148.700|ルックアップテーブル、16 ビット、`DataView`|
|popCountI|108.800|ルックアップテーブル、8 ビット、ビット演算|
|popCountJ|160.667|ルックアップテーブル、8 ビット、`DataView`|
|popCountK|115.233|WebAssembly の `i32.popcnt` 命令|
|popCountL|554.633|1 が立っているビットを 1 つずつ数える|

Deno (`--allow-hrtime` オプション使用) で確認した結果の平均は以下の表の通りです。

|関数名|実行時間|備考|
|-|-|-|
|popCountX|60.295|ビットカウント以外の処理|
|popCountA|81.789|分割統治、乗算|
|popCountB|94.585|分割統治、剰余|
|popCountC|89.185|分割統治、減算、論理積|
|popCountD|99.545|分割統治|
|popCountE|98.218|分割統治、3 ビットずつ、剰余|
|popCountF|791.697|分割統治、3 ビットずつ、乗算、BigInt 変換|
|popCountG|**74.178**|ルックアップテーブル、16 ビット、ビット演算|
|popCountH|106.869|ルックアップテーブル、16 ビット、`DataView`|
|popCountI|88.058|ルックアップテーブル、8 ビット、ビット演算|
|popCountJ|122.055|ルックアップテーブル、8 ビット、`DataView`|
|popCountK|100.639|WebAssembly の `i32.popcnt` 命令|
|popCountL|563.477|1 が立っているビットを 1 つずつ数える|

(Deno に別途ベンチマークの機能がありますが、ここでは Web ブラウザと同じ方法で比較しています。)

参考「[now(): number - Performance | Runtime APIs | Deno](https://deno.land/api@v1.35.0?s=Performance#method_now_10)」

Deno の方が Google Chrome より `TypedArray` のアクセスが速いらしく、分割統治法とルックアップテーブルを作成する方法の実行時間の大小が逆転しました。

## 2.2. コード

ここでは以下のコードで実行時間を測ることにします。

JavaScript エンジンの最適化の影響を小さくするため、入力を乱数にし、ビットカウントの値全てを出力に影響させるようにしました。

```js
// 実行時間を確認
const sleep = milliseconds => new Promise(resolve => { setTimeout(resolve, milliseconds); });

const bench = (name, fn) => {
	const beginTime = performance.now();
	fn();
	const endTime = performance.now();
	console.log(`${name}: ${endTime - beginTime}`);
};

for (let i = 0; i < 3; i++) {

	await sleep(1000);

	bench('popCountX', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= values[k];
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountA', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountA(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountB', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountB(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountC', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountC(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountD', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountD(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountE', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountE(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountF', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountF(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountG', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountG(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountH', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountH(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountI', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountI(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountJ', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountJ(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountK', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountK(values[k]);
			}
		}
		console.log(z);
	});

	await sleep(1000);

	bench('popCountL', () => {
		const values = new Uint32Array(0x4000);
		let z = 0;
		for (let j = 0; j < 0x800; j++) {
			crypto.getRandomValues(values);
			for (let k = 0; k < 0x4000; k++) {
				z ^= popCountL(values[k]);
			}
		}
		console.log(z);
	});

}
```

ちなみに以下のようにコードをまとめてしまうと最適化の影響か安定した結果が得られなくなるため、しない方が良さそうです。

```js:悪い例
const popCounts = popCount => () => {
	const values = new Uint32Array(0x4000);
	let z = 0;
	for (let j = 0; j < 0x800; j++) {
		crypto.getRandomValues(values);
		for (let k = 0; k < 0x4000; k++) {
			z ^= popCount(values[k]);
		}
	}
	console.log(z);
};

bench('popCountA', popCounts(popCountA));
bench('popCountB', popCounts(popCountB));
bench('popCountC', popCounts(popCountC));
bench('popCountD', popCounts(popCountD));
bench('popCountE', popCounts(popCountE));
bench('popCountF', popCounts(popCountF));
bench('popCountG', popCounts(popCountG));
bench('popCountH', popCounts(popCountH));
bench('popCountI', popCounts(popCountI));
bench('popCountJ', popCounts(popCountJ));
bench('popCountK', popCounts(popCountK));
bench('popCountL', popCounts(popCountL));
```

# 3. 分割統治法によるビットカウントの説明

## 3.1. 基本の計算方法

### 3.1.1. 2 ビットずつから計算を始める場合

1 ビットずつのカウント、2 ビットずつのカウント、4 ビットずつのカウント、8 ビットずつのカウント…というように分割統治法で計算します。

```text:8 ビットまでのビットカウントの例
1 1 1 1 1 1 1 1    1 1 1 1 1 1 1 1    11111111
  2   2   2   2     10  10  10  10    10101010
      4       4       0100    0100    01000100
              8           00001000    00001000

1 1 0 1 1 0 1 1    1 1 0 1 1 0 1 1    11011011
  2   1   1   2     10  01  01  10    10010110
      3       3       0011    0011    00110011
              6           00000110    00000110

0 1 0 0 1 0 0 1    0 1 0 0 1 0 0 1    01001001
  1   0   1   1     01  00  01  01    01000101
      1       2       0001    0010    00010010
              3           00000011    00000011

0 0 0 0 0 0 0 0    0 0 0 0 0 0 0 0    00000000
  0   0   0   0     00  00  00  00    00000000
      0       0       0000    0000    00000000
              0           00000000    00000000
```

例えば、8 ビットまでのビットカウントは以下のコードによって計算できます。

```js:各定数を 2 進数で表す場合
const a = (x & 0b01010101) + (x >>> 1 & 0b01010101);
const b = (a & 0b00110011) + (a >>> 2 & 0b00110011);
const y = (b & 0b00001111) + (b >>> 4 & 0b00001111);
```

```js:各定数を 16 進数で表す場合
const a = (x & 0x55) + (x >>> 1 & 0x55);
const b = (a & 0x33) + (a >>> 2 & 0x33);
const y = (b & 0x0f) + (b >>> 4 & 0x0f);
```

同様に 32 ビットまでビットカウントすると、以下のコードになります。

```js
const popCountD = x => {
	const a = (x & 0x55555555) + (x >>>  1 & 0x55555555);
	const b = (a & 0x33333333) + (a >>>  2 & 0x33333333);
	const c = (b & 0x0f0f0f0f) + (b >>>  4 & 0x0f0f0f0f);
	const d = (c & 0x00ff00ff) + (c >>>  8 & 0x00ff00ff);
	const y = (d & 0x0000ffff) + (d >>> 16 & 0x0000ffff);
	return y;
};
```

各定数を 2 進数にすると以下のようになります。

```js
0x55555555 === 0b01010101010101010101010101010101;
0x33333333 === 0b00110011001100110011001100110011;
0x0f0f0f0f === 0b00001111000011110000111100001111;
0x00ff00ff === 0b00000000111111110000000011111111;
0x0000ffff === 0b00000000000000001111111111111111;
```

### 3.1.2. 3 ビットずつから計算を始める場合

1 ビットずつのカウント、3 ビットずつのカウント、6 ビットずつのカウント…というように分割統治法で計算します。

```text:8 ビットまでのビットカウントの例
1 1 1 1 1 1 1 1    1 1 1 1 1 1 1 1    11111111
  2     3     3     10   011   011    10011011
  2           6     10      000110    10000110
              8           00001000    00001000

1 1 0 1 1 0 1 1    1 1 0 1 1 0 1 1    11011011
  2     2     2     10   010   010    10010010
  2           4     10      000100    10000100
              6           00000110    00000110

0 1 0 0 1 0 0 1    0 1 0 0 1 0 0 1    01001001
  1     1     1     01   001   001    01001001
  1           2     01      000010    01000010
              3           00000011    00000011

0 0 0 0 0 0 0 0    0 0 0 0 0 0 0 0    00000000
  0     0     0     00   000   000    00000000
  0           0     00      000000    00000000
              0           00000000    00000000
```

例えば、8 ビットまでのビットカウントは以下のコードによって計算できます。

```js:各定数を 2 進数で表す場合
const a = (x & 0b01001001) + (x >>> 1 & 0b01001001) + (x >>> 2 & 0b01001001);
const b = (a & 0b11000111) + (a >>> 3 & 0b11000111);
const y = (b & 0b00111111) + (b >>> 6 & 0b00111111);
```

```js:各定数を 8 進数で表す場合
const a = (x & 0o1_11) + (x >>> 1 & 0o1_11) + (x >>> 2 & 0o1_11);
const b = (a & 0o3_07) + (a >>> 3 & 0o3_07);
const y = (b & 0o0_77) + (b >>> 6 & 0o0_77);
```

※上記のコード中で下線 `_` は見やすさのための数値の区切りとして書いているだけで、文法上必須なものではありません。
※ 9 ビットずつのビットカウントを計算することもできますが、後に式変形して演算の回数を減らせるようにするため 6 ビットずつのビットカウントを計算しています。

同様に 32 ビットまでビットカウントすると、以下のコードになります。

```js
const a = (x & 0o1_1111111111) + (x >>> 1 & 0o1_1111111111) + (x >>> 2 & 0o1_1111111111);
// メモ: JavaScript でのビット演算は基本的に符号ありで計算されるため、符号なしの数に変換する
const b = (a & 0o3_0707070707) + (a >>> 3 & 0o3_0707070707) >>> 0;
const y = (
	(b        & 0o77) +
	(b >>>  6 & 0o77) +
	(b >>> 12 & 0o77) +
	(b >>> 18 & 0o77) +
	(b >>> 24 & 0o77) +
	(b >>> 30 & 0o77)
);
```

※ 12 ビットずつのビットカウントを計算することもできますが、後に式変形して演算の回数を減らせるようにするため 6 ビットずつのままビットカウントを計算しています。

各定数を 2 進数にすると以下のようになります。

```js
0o1_1111111111 === 0b01_001001001001001001001001001001;
0o3_0707070707 === 0b11_000111000111000111000111000111;

0o77 === 0b111111;
```

## 3.2. 演算の回数を減らす

### 3.2.1. 2 ビットずつのビットカウントで減算を用いる

基本の計算方法では、2 ビットまでのビットカウントは以下のコードによって計算します。

```js
const y = (x & 0b01) + (x >>> 1 & 0b01);
```

ここで、以下のような式変形をすることができます。

```js
(x >>> 1 & 0b01) + (x >>> 1 & 0b01) === ((x >>> 1 & 0b01) << 1);
(x >>> 1 & 0b01) === ((x >>> 1 & 0b01) << 1) - (x >>> 1 & 0b01);

(
	(x & 0b01) + ((x >>> 1 & 0b01) << 1)
) === (
	(x & 0b01) + (x & 0b10)
) === (
	x
);

(
    (x & 0b01) + (x >>> 1 & 0b01)
) === (
	(x & 0b01) + ((x >>> 1 & 0b01) << 1) - (x >>> 1 & 0b01)
) === (
    x - (x >>> 1 & 0b01)
);
```

よって、2 ビットまでのビットカウントは以下のコードでも計算できます。

```js
const y = x - (x >>> 1 & 0b01);
```

同様に考えると、32 ビットまでのビットカウント時の 2 ビットずつのビットカウントは以下のコードによって計算できます。

```js
const a = x - (x >>> 1 & 0x55555555);
```

### 3.2.2. 8 ビットずつのビットカウントの論理積の計算をまとめる

基本の計算方法では、8 ビットまでのビットカウントは以下のコードによって計算します。

```js
const y = (b & 0b00001111) + (b >>> 4 & 0b00001111);
```

ここで、`y` の最大値が `8 === 0b00001000` となり 4 ビットで表現できるため、論理積 `& 0b00001111` によって 4 ビット切り出す計算をまとめることができます。

```js
const y = b + (b >>> 4) & 0b00001111;
```

同様に考えると、32 ビットまでのビットカウント時の 8 ビットずつのビットカウントは以下のコードによって計算できます。

```js
const c = b + (b >>> 4) & 0x0f0f0f0f;
```

ちなみに上記の式変形は「8 ビットまでのビットカウントを 4 ビットで表現できる」ために成り立ちます。

「2 ビットまでのビットカウントは 2 ビット」、「4 ビットまでのビットカウントは 3 ビット」が表現するために必要なため、同様の式変形はできません。

### 3.2.3. 16 ビットずつから 32 ビットまでのビットカウントの論理積の計算をまとめる

基本の計算方法では、16 ビットずつから 32 ビットまでのビットカウントは以下のコードによって計算します。

```js
const d = (c & 0x00ff00ff) + (c >>>  8 & 0x00ff00ff);
const y = (d & 0x0000ffff) + (d >>> 16 & 0x0000ffff);
```

ここで、`y` の最大値が `32 === 0b00100000` より「16 ビットまでのビットカウント」も「32 ビットまでのビットカウント」も 8 ビット以下で表現できるため、論理積によって 8 ビット切り出す計算をまとめることができます。

```js
const d = c + (c >>> 8);
const e = d + (d >>> 16);
const y = e & 0xff;
```

また、以下のコードでも同様に計算できます。

```Js
const y = c + (c >>> 8) + (c >>> 16) + (c >>> 24) & 0xff;
```

### 3.2.4. 16 ビットずつから 32 ビットまでのビットカウントで剰余を用いる

以下のように式変形すると、16 ビットずつから 32 ビットまでのビットカウントを剰余によって計算できることが分かります。

```js
c0 = c        & 0xff;
c1 = c >>>  8 & 0xff;
c2 = c >>> 16 & 0xff;
c3 = c >>> 24 & 0xff;

c === c0 + (c1 << 8) + (c2 << 16) + (c3 << 24);

0x00000001 % 0xff === 1;
0x00000100 % 0xff === 1;
0x00010000 % 0xff === 1;
0x01000000 % 0xff === 1;

(
	c % 0xff
) === (
	(c0 + (c1 << 8) + (c2 << 16) + (c3 << 24)) % 0xff
) === (
	(c0 * 0x00000001 + c1 * 0x00000100 + c2 * 0x00010000 + c3 * 0x01000000) % 0xff
) === (
	(c0 + c1 + c2 + c3) % 0xff
) === (
	c0 + c1 + c2 + c3
) === (
	(c & 0xff) + (c >>> 8 & 0xff) + (c >>> 16 & 0xff) + (c >>> 24 & 0xff)
) === (
	c + (c >>> 8) + (c >>> 16) + (c >>> 24) & 0xff
);
```

よって、以下のように計算をまとめることもできます。

```Js
const y = c % 0xff;
```

### 3.2.5. 16 ビットずつから 32 ビットまでのビットカウントで乗算を用いる

論理積をまとめた式から、以下のような式変形をすることができます。

```js
(
    c + (c >>> 8) + (c >>> 16) + (c >>> 24) & 0xff
) === (
    (c << 24) + (c << 16) + (c << 8) + c >>> 24 & 0xff
) === (
    c * 0x01010101 >>> 24 & 0xff
);
```

ここでは `c` の最大値が `0x08080808` のため、`c * 0x01010101` の最大値は `0x08080808 * 0x01010101 === 0x00081018_20181008` となり 52 ビットの数になります。

JavaScript での通常の (`Number` 型の) 数は 53 ビットまでの正の整数を正しく扱えるため、問題なく計算できます。

参考「[Number.MAX_SAFE_INTEGER - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)」

符号なし右シフト `>>>` は値を 32 ビットとして扱うため、32 ビットの値を右に 24 ビットシフトすることで 8 ビットの値しか残らないため、論理積 `& 0xff` を取り除くことができます。

```js
(
    c * 0x01010101 >>> 24 & 0xff
) === (
    c * 0x01010101 >>> 24
);
```

よって、以下のように計算をまとめることもできます。

```Js
const y = c * 0x01010101 >>> 24;
```

### 3.2.6. 3 ビットずつのビットカウントで減算を用いる

基本の計算方法では、3 ビットまでのビットカウントは以下のコードによって計算します。

```js
const y = (x & 0b001) + (x >>> 1 & 0b001) + (x >>> 2 & 0b001);
```

ここで、以下のような式変形をすることができます。

```js
(x >>> 1 & 0b001) + (x >>> 1 & 0b001) === ((x >>> 1 & 0b001) << 1);
(x >>> 1 & 0b001) === ((x >>> 1 & 0b001) << 1) - (x >>> 1 & 0b001);

(x >>> 2 & 0b001) + (x >>> 2 & 0b001) + ((x >>> 2 & 0b001) << 1) === ((x >>> 2 & 0b001) << 2);
(x >>> 2 & 0b001) === ((x >>> 2 & 0b001) << 2) - ((x >>> 2 & 0b001) << 1) - (x >>> 2 & 0b001);

(
	(x & 0b001) + ((x >>> 1 & 0b001) << 1) + ((x >>> 2 & 0b001) << 2)
) === (
	(x & 0b001) + (x & 0b010) + (x & 0b100)
) === (
	x
);

(
	(x & 0b001) +
	(x >>> 1 & 0b001) +
	(x >>> 2 & 0b001)
) === (
	(x & 0b001) +
	((x >>> 1 & 0b001) << 1) - (x >>> 1 & 0b001) +
	((x >>> 2 & 0b001) << 2) - ((x >>> 2 & 0b001) << 1) - (x >>> 2 & 0b001)
) === (
	x - (x >>> 1 & 0b001) - ((x >>> 2 & 0b001) << 1) - (x >>> 2 & 0b001)
) === (
	x - (x >>> 1 & 0b001) - (x >>> 1 & 0b010) - (x >>> 2 & 0b001)
) === (
	x - (x >>> 1 & 0b011) - (x >>> 2 & 0b001)
) === (
	x - (x >>> 1 & 0b011) - ((x >>> 1 & 0b011) >>> 1 & 0b011)
);
```

よって、3 ビットまでのビットカウントは以下のコードでも計算できます。

```js
const y = x - (x >>> 1 & 0b011) - ((x >>> 1 & 0b011) >>> 1 & 0b011);
```

ここで、一部の計算結果を変数に代入することで計算量をさらに減らせます。

```js
const a = x >>> 1 & 0b011;
const y = x - a - (a >>> 1 & 0b011);
```

同様に考えると、32 ビットまでのビットカウント時の 3 ビットずつのビットカウントは以下のコードによって計算できます。

```js
const a = x >>> 1 & 0o3_3333333333;
const b = x - a - (a >>> 1 & 0o3_3333333333);
```

### 3.2.7. 6 ビットずつのビットカウントの論理積の計算をまとめる

基本の計算方法では、6 ビットまでのビットカウントは以下のコードによって計算します。

```js
const y = (b & 0b000111) + (b >>> 3 & 0b000111);
```

ここで、`y` の最大値が `6 === 0b000110` となり 3 ビットで表現できるため、論理積 `& 0b000111` によって 3 ビット切り出す計算をまとめることができます。

```js
const y = b + (b >>> 3) & 0b000111;
```

同様に考えると、32 ビットまでのビットカウント時の 6 ビットずつのビットカウントは以下のコードによって計算できます。

```js
// メモ: JavaScript でのビット演算は基本的に符号ありで計算されるため、符号なしの数に変換する
const c = (b + (b >>> 3) & 0o3_0707070707) >>> 0;
```

ちなみに上記の式変形は「6 ビットまでのビットカウントを 3 ビットで表現できる」ために成り立ちます。

「3 ビットまでのビットカウントは 2 ビット」が表現するために必要なため、同様の式変形はできません。

### 3.2.8. 6 ビットずつから 32 ビットまでのビットカウントの論理積の計算をまとめる

基本の計算方法では、6 ビットずつから 32 ビットまでのビットカウントは以下のコードによって計算します。

```js
const y = (
	(c        & 0o77) +
	(c >>>  6 & 0o77) +
	(c >>> 12 & 0o77) +
	(c >>> 18 & 0o77) +
	(c >>> 24 & 0o77) +
	(c >>> 30 & 0o77)
);
```

ここで、`y` の最大値が `32 === 0b100000` より「32 ビットまでのビットカウント」を 6 ビット以下で表現できるため、論理積によって 6 ビット切り出す計算をまとめることができます。

```Js
const y = c + (c >>> 6) + (c >>> 12) + (c >>> 18) + (c >>> 24) + (c >>> 30) & 0o77;
```

### 3.2.9. 6 ビットずつから 32 ビットまでのビットカウントで剰余を用いる

以下のように式変形すると、6 ビットずつから 32 ビットまでのビットカウントを剰余によって計算できることが分かります。

```js
c0 = c        & 0o77;
c1 = c >>>  6 & 0o77;
c2 = c >>> 12 & 0o77;
c3 = c >>> 18 & 0o77;
c4 = c >>> 24 & 0o77;
c5 = c >>> 30 & 0o77;

c === c0 + (c1 << 6) + (c2 << 12) + (c3 << 18) + (c4 << 24) + (c5 << 30);

0o0_0000000001 % 0o77 === 1;
0o0_0000000100 % 0o77 === 1;
0o0_0000010000 % 0o77 === 1;
0o0_0001000000 % 0o77 === 1;
0o0_0100000000 % 0o77 === 1;
0o1_0000000000 % 0o77 === 1;

(
	c % 0o77
) === (
	(
		 c0        +
		(c1 <<  6) +
		(c2 << 12) +
		(c3 << 18) +
		(c4 << 24) +
		(c5 << 30)
	) % 0o77
) === (
	(
		c0 * 0o0_0000000001 +
		c1 * 0o0_0000000100 +
		c2 * 0o0_0000010000 +
		c3 * 0o0_0001000000 +
		c4 * 0o0_0100000000 +
		c5 * 0o1_0000000000
	) % 0o77
) === (
	(c0 + c1 + c2 + c3 + c4 + c5) % 0o77
) === (
	c0 + c1 + c2 + c3 + c4 + c5
) === (
	(c        & 0o77) +
	(c >>>  6 & 0o77) +
	(c >>> 12 & 0o77) +
	(c >>> 18 & 0o77) +
	(c >>> 24 & 0o77) +
	(c >>> 30 & 0o77)
) === (
	c + (c >>> 6) + (c >>> 12) + (c >>> 18) + (c >>> 24) + (c >>> 30) & 0o77
);
```

よって、以下のように計算をまとめることもできます。

```Js
const y = c % 0o77;
```

### 3.2.10. 6 ビットずつから 32 ビットまでのビットカウントで乗算を用いる

論理積をまとめた式から、理論上は以下のような式変形をすることができます。

```js:理論上の式変形
(
    c + (c >>> 6) + (c >>> 12) + (c >>> 18) + (c >>> 24) + (c >>> 30) & 0o77
) === (
    (c << 30) + (c << 24) + (c << 18) + (c << 12) + (c << 6) + c >>> 30 & 0o77
) === (
    c * 0o1_0101010101 >>> 30 & 0o77
);
```

ここでは `c` の最大値が `0o2_0606060606` のため、`c * 0o1_0101010101` の最大値は理論上は `0o2_0606060606 * 0o1_0101010101 === 0o2_1016243240_3630221406` となり 62 ビットの数になります。

JavaScript での通常の (`Number` 型の) 数で正しく扱える正の整数は 53 ビットまでのため、計算で問題が発生します。

実際に JavaScript で計算すると `0o2_0606060606 * 0o1_0101010101 === 0o2_1016243240_3630222000` になります。

参考「[Number.MAX_SAFE_INTEGER - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)」

`Math.imul()` 関数を使用することで 32 ビットまでの乗算を行うことができますが、ここでは 36 ビットまでの値を計算に用いるため、使えません。

参考「[Math.imul() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/imul)」

よって、正しく計算するためには `BigInt` を使う必要があります。

参考「[BigInt - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/BigInt)」

ただし `BigInt` は常に符号ありとして扱われるため、符号なし右シフト `>>>` を使用することはできません。

参考「[演算子 - BigInt - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/BigInt#%E6%BC%94%E7%AE%97%E5%AD%90)」

よって、以下のように計算をまとめることもできます。

```Js
const y = Number(BigInt(c) * 0o1_0101010101n >> 30n & 0o77n);
```

ちなみに最長 62 ビットの値を右に 30 ビットシフトすることで 32 ビットの値が残る可能性があるため、論理積 `& 0o77n` を取り除くことはできません。

## 3.3. 演算の回数を減らした計算方法

実際に演算の回数を減らす式変形をすると、以下のようなコードで 32 ビットまでのビットカウントを行えます。

```js
const popCountA = x => {
	const a = x - (x >>> 1 & 0x55555555);
	const b = (a & 0x33333333) + (a >>> 2 & 0x33333333);
	const c = b + (b >>> 4) & 0x0f0f0f0f;
	const y = c * 0x01010101 >>> 24;
	return y;
};

const popCountB = x => {
	const a = x - (x >>> 1 & 0x55555555);
	const b = (a & 0x33333333) + (a >>> 2 & 0x33333333);
	const c = b + (b >>> 4) & 0x0f0f0f0f;
	const y = c % 0xff;
	return y;
};

const popCountC = x => {
	const a = x - (x >>> 1 & 0x55555555);
	const b = (a & 0x33333333) + (a >>> 2 & 0x33333333);
	const c = b + (b >>> 4) & 0x0f0f0f0f;
	const d = c + (c >>> 8);
	const e = d + (d >>> 16);
	const y = e & 0xff;
	return y;
};
```

```js
const popCountE = x => {
	const a = x >>> 1 & 0o3_3333333333;
	const b = x - a - (a >>> 1 & 0o3_3333333333);
	// メモ: JavaScript でのビット演算は基本的に符号ありで計算されるため、符号なしの数に変換する
	const c = (b + (b >>> 3) & 0o3_0707070707) >>> 0;
	const y = c % 0o77;
	return y;
};

const popCountF = x => {
	const a = x >>> 1 & 0o3_3333333333;
	const b = x - a - (a >>> 1 & 0o3_3333333333);
	// メモ: JavaScript でのビット演算は基本的に符号ありで計算されるため、符号なしの数に変換する
	const c = (b + (b >>> 3) & 0o3_0707070707) >>> 0;
	const y = Number(BigInt(c) * 0o1_0101010101n >> 30n & 0o77n);
	return y;
};
```