C++でa^b mod 1337を高速に計算する「スーパーパウ」問題の解法
問題概要
正整数 a と、桁ごとの配列形式で与えられる非常に大きな正整数 b があるとき、ab mod 1337 を計算する問題を考えます。たとえば、a = 2、b = [1,0] の場合、210 = 1024 となるため、出力は 1024 になります。
アルゴリズムの流れ
この問題は、繰り返し二乗法(バイナリ法)と再帰的な処理を組み合わせることで効率的に解くことができます。具体的な手順は以下のとおりです。
- powerMod() メソッドの定義: 底(base)と指数(power)を受け取ります。
- m := 1337、ret := 1 で初期化します。
- power が 0 でない間、次の処理を繰り返します。
- power が奇数の場合、ret := ret × base mod m と更新します。
- base := base2 mod m と更新します。
- power := power ÷ 2 とします。
- ret を返します。
- superPow() メソッドの定義: a と b を受け取ります。
- b のサイズが 0 の場合は 1 を返します。
- last := b の末尾の要素とします。
- b から末尾の要素を取り除きます。
- (powerMod(superPow(a, b), 10) × powerMod(a, last)) mod 1337 を返します。
仕組みのポイント
指数 b を配列として扱うのは、b が通常の整数型では表現できないほど大きくなる可能性があるためです。b の各桁を末尾から順に処理し、「(これまでの結果)10 × a(現在の桁)」という形で再帰的に計算することで、mod 1337 の値を正しく求められます。また、繰り返し二乗法を採用することで、べき乗の計算量は O(log n) に抑えられます。
C++での実装例
それでは、理解を深めるために実際の実装を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
typedef long long int lli;
class Solution {
public:
int powerMod(lli base, lli power){
lli mod = 1337;
lli ret = 1;
while(power){
if(power & 1) ret = (ret * base) % mod;
base = (base * base) % mod;
power >>= 1;
}
return ret;
}
int superPow(int a, vector<int>& b) {
if(b.size() == 0) return 1;
int last = b.back();
b.pop_back();
return (powerMod(superPow(a, b), 10) * powerMod(a, last)) % 1337;
}
};
main(){
Solution ob;
vector<int> v = {1,0};
cout << (ob.superPow(2, v));
}
入力
2 [1,0]
出力
1024
-
C++でべき乗(pow)関数を自作する方法
べき乗関数(power function)は、基数と指数という2つの数値を受け取り、基数を指数回だけ掛け合わせた結果(累乗)を求めるための関数です。例を見てみましょう。基数 = 2 指数 = 5 2^5 = 32 つまり、2の5乗は32になります。ここでは、標準ライブラリの pow() 関数に頼らず、C++でべき乗計算を自前で実装する方法を紹介します。サンプルプログラム#include <iostream> using namespace std; int main(){ int x, y, ans = 1; cout << 基数を入力してくださ
-
C++におけるカプセル化の基本と実装方法
カプセル化(Encapsulation)とは、データとそのデータを操作するメソッドを1つのコンポーネントにまとめ、外部からの干渉から保護するオブジェクト指向プログラミングの重要な概念です。カプセル化を実現することで、「データ隠蔽(Data Hiding)」という非常に重要な概念が生まれます。C++では、ユーザー定義型であるクラスを使用してカプセル化を実現します。クラスは、データメンバとそれらを操作するメンバ関数をひとまとめにしたものです。以下に、C++のクラスを使ってカプセル化を表現するサンプルプログラムを示します。実装例#include <iostream> using name