C++で数値範囲のビット単位AND(Bitwise AND)を効率的に計算する方法
0 ≤ m ≤ n ≤ 2147483647 を満たす範囲 [m, n] が与えられたとき、この範囲に含まれるすべての整数のビット単位AND(論理積)を求めることを考えます。たとえば、範囲が [5, 7] の場合、5 AND 6 AND 7 = 4 となるため、答えは 4 になります。
アルゴリズムの考え方
範囲内のすべての数値を順番にANDしていくのは非効率です。そこで、m と n の2進表現に注目します。連続する整数同士をANDすると、下位のビットは必ずどこかで 0 になるため、結果として残るのは m と n の共通の上位ビット(共通プレフィックス)だけです。
この性質を利用すると、次の手順で解くことができます。
- カウンタ i を 0 で初期化します。
- m と n が等しくなるまで、両方を1ビット右シフトし、そのたびに i を1ずつ増やします。
- ループ終了後、m を i ビット左シフトして返します。
C++での実装例
以下に実際の実装を示します。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int rangeBitwiseAnd(int m, int n) {
int i = 0;
while(m != n){
m >>= 1;
n >>= 1;
i++;
}
return m << i;
}
};
main(){
Solution ob;
cout << (ob.rangeBitwiseAnd(5,7));
}入力
5 7
出力
4
動作の解説
例として [5, 7] の場合を見てみましょう。
- 5 = 101₂、6 = 110₂、7 = 111₂
- 1回目の右シフト後:m = 10₂、n = 11₂(まだ不一致)
- 2回目の右シフト後:m = 1₂、n = 1₂(一致)
ここで i = 2 となっているので、共通プレフィックス「1」を2ビット左に戻すと 100₂ = 4 となり、これが正解です。
このアルゴリズムの計算量は O(log n) であり、範囲内の全数値を走査する O(n) の素朴な実装と比べて大幅に高速です。特に n が大きい場合(最大約21億)でも、最大31回程度のシフト操作で答えが得られます。
-
C++のビットごとのAND演算子とは?仕組みと使い方を解説
C++におけるビットごとのAND演算子(&)は、2つのオペランドの各ビットを対応する位置ごとに比較する演算子です。両方のビットが1である場合のみ、結果の該当ビットが1に設定されます。それ以外の場合は0になります。この演算子を使用する際、両方のオペランドは整数型(integral型)である必要があります。浮動小数点型には使用できません。ビットごとのANDの真理値表各ビットの組み合わせに対する結果は以下の通りです。0 & 0 → 00 & 1 → 01 & 0 → 01 & 1 → 1サンプルコード次の例では、16進数で表された2つのunsigned sho
-
Pythonで指定範囲内のすべての数値のビットごとのAND(論理積)を求めるプログラム
2つの整数 start と end が与えられたとき、閉区間 [start, end](両端を含む)に含まれるすべての数値のビットごとのAND(論理積)を求めることを考えます。 例として、start = 8、end = 12 の場合を見てみましょう。8 は2進数で「1000」、12 は「1100」と表されます。したがって、 1000 AND 1001 AND 1010 AND 1011 AND 1100 を計算すると「1000」、すなわち答えは 8 になります。 解法の手順 この問題は、次の手順で解くことができます。 n := end − start + 1(範囲に含まれる数値の個数)としま