C++で総ハミング距離を求める:全ペアのビット差を効率的に計算する方法
数値のリストが与えられたとき、リスト内のすべてのペアに対するハミング距離の合計(総ハミング距離)を求めることを考えます。ハミング距離とは、2つの整数を比較した際に、対応するビットが異なる位置の個数のことです。
たとえば、入力が [4, 14, 17, 2] の場合、出力は 17 となります。
解法のアプローチ
すべてのペアを総当たりで比較すると計算量が膨大になるため、ここではビット位置ごとに着目する効率的な手法を紹介します。
あるビット位置 j に注目したとき、それまでに処理済みの数値の中で「現在の数値と逆のビット値を持つもの」の個数を順次加算していけば、最終的に全ペアのハミング距離の合計が求まります。結果が巨大な数になる可能性を考慮し、剰余 109 + 7 を用いて計算を行います。
アルゴリズムの手順
m := 109 + 7 とする(剰余演算用の定数)
add(a, b):((a mod m) + (b mod m)) を返す関数を定義する
mul(a, b):((a mod m) × (b mod m)) を返す関数を定義する
cntBits(a):配列 a を引数にとる関数を定義する
32 × 2 のサイズを持つ2次元配列 bits を用意する
ans := 0、n := 配列 a のサイズとする
i := 0 から n - 1 まで繰り返す:
x := a[i]
j := 0 から 31 まで繰り返す:
b := (x ÷ 2j) AND 1(j 番目のビットの値)
ans := add(ans, mul(1, bits[j][!b]))(逆のビットを持つ過去の数の個数を加算)
bits[j][b] := add(bits[j][b], 1)(現在のビット値の出現回数を更新)
ans を返す
main 関数からは cntBits(nums) の結果を返す
C++での実装例
理解を深めるために、以下の実装を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
typedef long long int lli;
const int m = 1e9 + 7;
class Solution {
public:
lli add(lli a, lli b){
return ((a % m) + (b % m));
}
lli mul(lli a, lli b){
return ((a % m) * (b % m));
}
int cntBits(vector<int>& a){
vector<vector<lli> > bits(32, vector<lli>(2));
lli ans = 0;
int n = a.size();
for (int i = 0; i < n; i++) {
lli x = a[i];
for (lli j = 0; j < 32; j++) {
lli b = (x >> j) & 1;
ans = add(ans, mul((lli)1, bits[j][!b]));
bits[j][b] = add(bits[j][b], (lli)1);
}
}
return ans;
}
int totalHammingDistance(vector<int>& nums){
return cntBits(nums);
}
};
main(){
Solution ob;
vector<int> v = {4,14,17,2};
cout << (ob.totalHammingDistance(v));
}
入力
{4,14,17,2}
出力
17
計算量について
このアルゴリズムは、各数値について最大32ビット分の走査を行うだけなので、時間計算量は O(n × 32)、空間計算量は O(1) で抑えられます。全ペアを直接比較する O(n²) の総当たり方式と比べ、特に n が大きい場合に大幅な高速化を実現できます。
-
C++で最初のN個の数字をKの距離になるように並べ替える方法
問題概要 整数 N と K が与えられたとき、まず 1 から N までの順列を作成し、その後、すべての要素が元の位置からちょうど K だけ離れるように並べ替えることを考えます。 入出力のシナリオ例 入力 − int n = 20, int k = 2 出力 − 最初のN個の数字をK距離に並べ替えた結果: 3 4 1 2 7 8 5 6 11 12 9 10 15 16 13 14 19 20 17 18 説明 − 整数 N = 20、K = 2 が与えられています。まず順列 1, 2, 3, ..., 20 を作成し、続いて各要素が元の位置からちょうど「k」の距離に配置されるように並べ替え
-
【C++】n個の点のうちm個が同一直線上にあるときに作れる三角形の数を求める方法
問題の概要2次元平面上の点の総数を表す2つの変数 n と m が与えられます。このうち m 個の点は同一直線上(コリニア)に並んでいます。ここでの課題は、これら n 個の点から作ることができる三角形の数を求めることです。同一直線上の点(共線点)とは、同じ一本の直線上に乗っている点のことです。例えば下図では、点 A と点 B が同一の直線上に位置しています。考え方の基本まず、n=4(A, B, C, D)、m=2(A, B)という具体例で確認してみましょう。三角形の数は次の手順で計算できます。・4 点から任意の 3 点を選ぶ組み合わせ = 4C3・ただし、同一直線上の点だけでは三角形が成立しない