0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ビット計算の練習【Atcoder】2進数で表したときのiビット目に注目する問題

Last updated at Posted at 2024-08-22

記事の内容

この記事で紹介したいのは、

n までの整数の中で、2進数で表したときに j ビット目が1である数の個数を効率的に数える

という考え方を紹介します。

なぜこの考え方に注目したのか

AtCoderのABCにて出題されたこの問題を解くために必要な考え方だからです。

色々と勉強になる考え方なので、記録のために記事化しておきたいです。
整数問題やビットの考え方のいい練習になるはずです。

問題の具体化

目的は、n までの整数の中で、2進数で表したときに j ビット目が1である数の個数を数えることです。
n = 13 の場合の整数(0から13まで)を2進数で表すと次のようになります。
j=2、つまり、2ビット目に注目します。

0:  0000
1:  0001
2:  0010
3:  0011
4:  0100
5:  0101
6:  0110
7:  0111
8:  1000
9:  1001
10: 1010
11: 1011
12: 1100
13: 1101

このとき、2ビット目(j = 2)が1であるのは 4, 5, 6, 7, 12, 13 で、合計6つあります。
よって、求める数は6です。

答えを求めるコード

答えを求めるコードはこちらです。(くどいくらいコメントを入れています。)

# 関数 f(j, n): jビット目のカウントを計算する
def f(j, n):
    p2 = (1 << j)  # 2^j を計算
    k = n // (2 * p2)  # n を 2 * 2^j で割った商を計算
    res = k * p2  # 商に 2^j を掛けた結果を res に保存
    l = n % (2 * p2)  # n を 2 * 2^j で割った余りを計算
    if l >= p2:  # もし余りが 2^j 以上であれば
        res += (l - p2 + 1)  # res に (余り - 2^j + 1) を足す
    return res

なぜこのコードで計算できるのか

私は、最初にこのコードを見たときに、なぜこの方法で答えが得られるのかわかりませんでした。そのため、このコードで答えが求められる理由を具体的に解説します。

関数 f(j, n) のアルゴリズムが動作する理由は、2進数におけるビットの規則性を利用して、j ビット目が立っている(つまり 1 である)数を効率的に数えるためです。これを理解するために、ビットのパターンがどのように繰り返されるかを考えます。

2進数でのビットパターンの周期性

2進数では、各ビットは特定の周期で 0 と 1 を繰り返します。たとえば、ビットの位置が固定されている場合、そのビットが 1 になる回数には周期的なパターンがあります。

例えば、j = 2 の場合(2ビット目)、そのビットが 1 になるのは次のような周期で発生します。

  • ビットのパターン: 00 01 10 11 (4つの数ごとに周期)
    4つの数(00, 01, 10, 11)のうち、2ビット目 が 1 であるのは、10 と 11 のときです。

f(j, n) の具体的な流れ

  1. k = n // (2 * p2)
    この部分は、n を 2 * p2 で割り、その整数部分 k を得ることで、j ビット目が完全に周期を満たす回数を求めています。
    例えば、p2 = 4 の場合、この計算は「j ビット目が 1 である完全な周期がいくつ含まれるか」を計算しています。周期ごとに p2 個のビットが 1 になるため、k * p2 がその範囲での 1 の個数となります。

  2. l = n % (2 * p2)
    次に、n が完全な周期に収まらない余り(残りの部分)を計算します。この余り部分が、次の部分での計算に使われます。

  3. if l >= p2
    ここで、余り l が p2 以上であれば、その残りの範囲でも j ビット目が 1 になる範囲があるため、その数だけ追加します。具体的には、余り l が p2 以上であれば、l - p2 + 1 の個数が 1 であると計算されます。

まとめ: なぜこの方法で求められるのか

  • ビットの周期性の利用: 2進数におけるビットの値が 0 と 1 を繰り返す周期性を利用しています。j ビット目が 1 になる周期を 2^j で表し、そのビットが 1 になる数の範囲を効率的に計算しています。
  • 完全な周期のカウント: n を 2 * p2 で割ることで、j ビット目が 1 である完全な周期が何回含まれているかを数え、その中で 1 が出現する回数を計算しています。
  • 部分的な周期の処理: 余りの部分を別途処理することで、n が完全な周期に含まれない場合でも、正確に 1 になるビットの個数を計算しています。

関連記事

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?