C++で同じ数字の組み合わせから「次に大きい数」を求める方法
この記事では、与えられた数値 N に対して、「同じ数字の組み合わせを使って表せる数のうち、N より大きい最小の数」を求める方法を解説します。これはいわゆる「次の順列(next permutation)」を求める問題として知られています。
問題を理解するための例
入力
N = "92534"
出力
92543
「92534」と同じ数字 {9, 2, 5, 3, 4} を使って作れる数の中で、92534 より大きい最小の数は 92543 です。
解法のアプローチ
次に大きい数を求めるためのシンプルな手順は以下の通りです。
数値を最下位桁から最上位桁へ向かって走査し、「現在の桁が左隣の桁より大きい」箇所が見つかった時点で停止します。この位置より右側は降順に並んでいます。
続いて、右側の部分から「基準となる桁(number[i-1])より大きく、かつ候補の中で最小の数字」を探し、基準の桁と交換します。
最後に、交換した位置より右側の部分配列を昇順にソートすれば、それが答えになります。
なお、走査しても条件を満たす箇所が見つからない場合(数字がすべて降順に並んでいる場合)は、それより大きい数が存在しないため「次の数は作れない」と判断します。
解法の動作を示すプログラム
例
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
void findNextGreater(char number[], int n) {
int i, j;
// 最下位桁から走査し、number[i] > number[i-1] となる位置を探す
for (i = n-1; i > 0; i--)
if (number[i] > number[i-1])
break;
// 見つからなければ次の数は作れない
if (i == 0) {
cout<<"Next number is not possible";
return;
}
int x = number[i-1], smallest = i;
// x より大きく、かつ最小の数字を探す
for (j = i+1; j < n; j++)
if (number[j] > x && number[j] < number[smallest])
smallest = j;
// 見つけた数字と交換
char temp = number[smallest];
number[smallest] = number[i-1];
number[i-1] = temp;
// 残りの部分を昇順ソート
sort(number + i, number + n);
cout<<number;
return;
}
int main(){
char number[] = "92534";
int n = strlen(number);
cout<<"The next number with same set of digits is ";
findNextGreater(number, n);
return 0;
}
出力
The next number with same set of digits is 92543
計算量について
このアルゴリズムの計算量は O(n log n) です(末尾のソート部分が支配的)。桁の走査や交換そのものは O(n) で行えるため、非常に効率的な手法といえます。
-
C++で指定された数より大きい次の完全平方数を求める方法
整数 n が与えられたとき、n より大きい最小の完全平方数(ある整数の 2 乗で表される数)を求める問題を考えてみましょう。例えば、n = 1000 の場合、次の完全平方数は 32² = 1024 となります。 解法の考え方 この問題は、以下のシンプルな手順で解くことができます。 与えられた数 n の平方根を求める その値の小数点以下を切り捨てる(floor 処理) 切り捨てた値に 1 を加え、その 2 乗を計算して返す n の平方根の整数部分を r とすると、r² ≤ n が成り立つため、n より大きい次の完全平方数は (r + 1)² となります。 C++での実装例 #include
-
【C++】Dで割り切れるN桁の数を見つけるアルゴリズム
2つの整数 N と D が与えられたとき、D で割り切れる N 桁の数を見つける問題を考えます。例えば、N = 3、D = 5 の場合、答えは 500 になります。一見難しそうに思えるこの問題ですが、実はとてもシンプルな発想で解決できます。解法のアイデア基本となる考え方は、「D を先頭に置き、その後ろに 0 を付け足して N 桁にする」というものです。D の桁数を m とすると、D の末尾に (N − m) 個の 0 を連結した数は、全体でちょうど N 桁となり、必ず D で割り切れます。これは、作成される数が D × 10(N−m) に相当し、10 のべき乗を掛けても D で割り切れるという