C++
 Computer >> コンピューター >  >> プログラミング >> C++

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 が大きい場合に大幅な高速化を実現できます。

  1. 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」の距離に配置されるように並べ替え

  2. 【C++】n個の点のうちm個が同一直線上にあるときに作れる三角形の数を求める方法

    問題の概要2次元平面上の点の総数を表す2つの変数 n と m が与えられます。このうち m 個の点は同一直線上(コリニア)に並んでいます。ここでの課題は、これら n 個の点から作ることができる三角形の数を求めることです。同一直線上の点(共線点)とは、同じ一本の直線上に乗っている点のことです。例えば下図では、点 A と点 B が同一の直線上に位置しています。考え方の基本まず、n=4(A, B, C, D)、m=2(A, B)という具体例で確認してみましょう。三角形の数は次の手順で計算できます。・4 点から任意の 3 点を選ぶ組み合わせ = 4C3・ただし、同一直線上の点だけでは三角形が成立しない