Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions 191.Numberof1Bits/bit_manipulation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Solution {
public:
int hammingWeight(int n) {
int num_bits = 0;
while (n != 0) {
num_bits += 1;
// nと(n-1)のビットごとのAND演算を行うことで、
// nの最も右側にある1のビットを0に反転させる
n &= (n - 1);
}
return num_bits;
}
};
57 changes: 57 additions & 0 deletions 191.Numberof1Bits/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
## ステップ1
思いついた方法は、与えられた数字を2で割りつど余りがでたらbitのカウントを増やす方法
他の解法は思いつかずacceptまで2分
時間計算量はO(log n)
空間計算量はO(1)

15分ほど考えたが他の方法は思いつかなかった

## ステップ2
想定されている解法は愚直にbitが立っている場合を見る方法かbit manipulationか
愚直にbitが立っている場合を見る方法をstep2とstep3を進める

これは1ビットずつ確認する方法

例えば5(0101)の場合で考えると
マスクは0001 0010 0100 1000と動く(実際は32bit分)
マスクを動かしながらビットが立っている部分を数える

n & maskが返却しているものはbitが1である場合の2^1
=>これを理解するのに時間がかかった

int型しか対応できなさそうなのが気になった
=>step2_2.cppに実装
関数の型定義のテンプレート化部分は、分からなかったのでChatGptに聞いた

## ステップ3
**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。**

## 他の解法
Copy link

@nodchip nodchip Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

分割統治法による解法もあります。

n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff);
n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);
return n;

「ハッカーのたのしみ: 本物のプログラマはいかにして問題を解くか」に載っています。ソフトウェアエンジニアの常識には含まれていませんが、知っている人はいると思います。



他にはbit manipulationがあるbit_manipulation.cppに実装
解説を読んだが理解できていない気がする。

nの最も右側にある1を0に更新し、これを全て0になるまで続ける
n = 12の場合
n : 00001100
n - 1: 00001011
andをとると8: 00001000となる

n : 00001000
n - 1: 00000111
andをとると0: 00000000となる
この場合2ループで、1の数は2

コードが見れなくなっていたのでやり取りだけチェック
この辺りチェックしたら良さそう
https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation
C++のもありそうなので探してみる
https://pkg.go.dev/math/bits#OnesCount
https://github.com/rihib/leetcode/pull/46

文字列に変換する方法などもある
https://github.com/Kitaken0107/GrindEasy/pull/24
Comment on lines +46 to +54

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

考え方は他の解法にも似ているのですが、全てのbitをチェックする代わりに、最も右にあるbitを消すことで処理するアルゴリズムもあるようです。
Brian Kernighan’s Algorithm

int hammingWeight(unsigned int n) {
    int count = 0;
    while (n) {
        n &= (n - 1); 
        count++;
    }
    return count;
}


## Discordなど

13 changes: 13 additions & 0 deletions 191.Numberof1Bits/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Solution {
public:
int hammingWeight(int n) {
int num_bits = 0;
while (n > 0) {
if (n % 2 == 1) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

num_bits += n % 2;

とも書けます。趣味の範囲だと思います。

num_bits +=1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+=1 のあいだにスペースを空けることをおすすめします。

}
n /= 2;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的には

n >>= 1;

と書きます。コンパイラーで最適化オプションを付けてコンパイルすると、どちらもシフト演算の命令が生成されると思います。ぜひ確かめてみてください。

整数の割り算は遅い、ビット演算は速い、という感覚は持っておいたほうが良いと思います。以下
https://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/ia32.pdf
からの抜粋です。

Ice Lake and Tiger Lake

Instruction Operands Latency Reciprocal through put
DIV IDIV r32 12 6
SHR SHL SAR r,i 1 0.5

https://zenn.dev/herumi/articles/latency-throughput

CPUにおけるレイテンシ(latency)とは命令が発行されてからその実行が完了するまでの時間(クロックサイクル)です。

Intelのマニュアルによるとスループットとは同じ命令を続けて発行するときに待つclkです。

}
return num_bits;
}
};
14 changes: 14 additions & 0 deletions 191.Numberof1Bits/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Solution {
public:
int hammingWeight(int n) {
int num_bits = 0;
int mask = 1;
for (int i = 0; i < 32; i++) {
if ((n & mask) != 0) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分なら mask 変数は使わず、

if ((n & (1 << i)) != 0) {

と書くと思います。趣味の範囲だと思います。

num_bits++;
}
mask <<= 1;
}
return num_bits;
}
};
23 changes: 23 additions & 0 deletions 191.Numberof1Bits/step2_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class Solution {
public:
int hammingWeight(int n) {
return GenerichammingWeight(n);
}

private:
template <typename T>
std::enable_if_t<std::is_integral_v<T>, int> GenerichammingWeight(T n) {
int num_bits = 0;
T mask = 1;
constexpr int num_digits = std::numeric_limits<T>::digits;

for (int i = 0; i < num_digits; ++i) {
if ((n & mask) != 0) {
num_bits++;
}
mask <<= 1;
}

return num_bits;
}
};
14 changes: 14 additions & 0 deletions 191.Numberof1Bits/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Solution {
public:
int hammingWeight(int n) {
int num_bits = 0;
int mask = 1;
for (int i = 0; i < 32; i++) {
if ((n & mask) != 0) {
num_bits++;
}
mask <<= 1;
}
return num_bits;
}
};