C++で階乗に末尾のゼロが少なくともY個含まれる最小の数Xを求める方法
この記事では、「X!(Xの階乗)の末尾に少なくともY個のゼロが含まれるような最小の数Xを求める」という問題を取り上げます。例えば、Y = 2の場合、答えはX = 10です。10! = 3,628,800であり、末尾にちょうど2個のゼロが含まれているためです。
解法の考え方:二分探索
この問題は二分探索(バイナリサーチ)を使うことで効率的に解けます。鍵となるのは次の事実です。
N!の末尾のゼロの個数 = N!に含まれる約数5の個数
末尾のゼロは10 = 2 × 5の積によって生じます。階乗の中では2の個数が常に5の個数より多いため、5が何回現れるかを数えればよいことになります。N!に含まれる5の個数は、ルジャンドルの公式に基づき次のように計算できます。
count = ⌊N/5⌋ + ⌊N/25⌋ + ⌊N/125⌋ + …
また、答えとなるXは必ず[0, 5×Y]の範囲内に存在するため、この範囲で二分探索を行います。
C++による実装例
#include<iostream>
using namespace std;
int factorCount(int n, int X) {
if (X < n)
return 0;
return (X / n + factorCount(n, X / n));
}
int findX(int Y) {
int left = 0, right = 5 * Y;
int N = 0;
while (left <= right) {
int mid = (right + left) / 2;
if (factorCount(5, mid) < Y) {
left = mid + 1;
} else {
N = mid;
right = mid - 1;
}
}
return N;
}
int main() {
int Y = 4;
cout << "Smallest value of X: " << findX(Y);
}コードの解説
factorCount関数
factorCount(n, X)は、X!に含まれるnの倍数の個数を再帰的に数える関数です。まずX/nで直接の倍数をカウントし、その商に対して再帰的に呼び出すことで、25や125といった5の累乗の寄与まで正確に数えています。
findX関数
findX(Y)が二分探索の本体です。探索範囲の左端を0、右端を5×Yに設定し、中央値midにおける末尾ゼロの個数がY未満であれば左端をmid + 1へ移動し、Y以上であれば答えの候補Nをmidに更新して右端をmid − 1へ移動させます。これにより、条件を満たす最小のXが求まります。
実行結果
Smallest value of X: 20
Y = 4の場合、出力は20になります。実際、20! = 2,432,902,008,176,640,000で末尾にちょうど4個のゼロが含まれ、19!ではゼロが3個しかないため、20が条件を満たす最小の数であることがわかります。
計算量
factorCountの計算量はO(log X)であり、二分探索と組み合わせた全体の計算量はO((log Y)2)程度に収まります。大きなYに対しても非常に高速に動作する効率的な手法です。
-
C++で解く!Nの階乗のB進表現における末尾ゼロの個数の求め方
はじめにこの記事では、与えられた数Nの階乗(N!)を基数Bで表したとき、末尾にいくつのゼロが連続するかを求める問題について詳しく解説します。問題の例入力 : N = 7、基数 = 2 出力 : 4 説明 : fact(7) = 5040(10進数)であり、2進数では「1001110110000」となるため、末尾にゼロが4個並びます。 入力 : N = 11、基数 = 5 出力 : 2 説明 : fact(11) = 39916800(10進数)であり、5進数では「40204314200」となるため、末尾にゼロが2個並びます。基数変換のおさらいまず、10進数から他の基数へ数値を変換する手順を確
-
C++で解く!Nの階乗の16進数表現における末尾のゼロの個数の求め方
この記事では、与えられた整数Nの階乗(N!)を16進数で表したとき、末尾に何個のゼロが連続するかを求める問題について詳しく解説します。 入力 : N = 7 出力 : 1 説明 : fact(7) = 5040(10進数)で、16進数では13B0となり、末尾のゼロは1個です。 入力 : N = 11 出力 : 2 説明 : fact(11) = 39916800(10進数)で、16進数では2611500となり、末尾のゼロは2個です。 10進数から16進数への変換のおさらい まず、任意の10進数を別の基数へ変換する手順をおさらいしましょう。ここでは、(5040)10 を16進数に変換する例を