C++で指定された長さの連続する合成数の範囲を求める方法
正整数 n が与えられたとき、「範囲内のすべての数が合成数であり、かつ範囲の長さがちょうど n となる」ような正整数の範囲を求める問題を考えます。条件を満たす範囲が複数存在する場合は、そのうちのどれか1つを出力すれば構いません。なお、合成数(composite number)とは「1 とその数自身以外に、少なくとも1つの約数を持つ数」のことです。
アルゴリズムの考え方
範囲の長さが n である以上、先頭の数を a とすると、範囲内の残りの数は a + 1, a + 2, …, a + n − 1 となり、これらがすべて合成数でなければなりません。
ここで役立つのが階乗(factorial)の性質です。正整数 p に対して、p! は 2, 3, 4, …, p − 1 のすべてを約数として持ちます。したがって、2 ≤ i ≤ p を満たす任意の i について、p! + i は必ず i を約数に含むため、必ず合成数になります。
つまり、p! + 2, p! + 3, …, p! + p − 1 はすべて合成数であり、範囲 [p! + 2, p! + p − 1] を出力すればよいことになります。この範囲に含まれる数は p − 2 個なので、長さ n の範囲を得るには p = n + 2 と置きます。こうして得られる範囲は [(n+2)! + 2, (n+2)! + n + 1] となり、ちょうど n 個の連続する合成数から構成されます。
C++での実装例
#include<iostream>
using namespace std;
int fact (int n) {
if (n == 0)
return 1;
return n * fact(n-1);
}
void showRange(int n) {
int a = fact(n + 2) + 2;
int b = a + n - 1;
cout << "[" << a << ", " << b << "]";
}
int main() {
int n = 3;
showRange(n);
}コードの解説
fact 関数は再帰呼び出しによって階乗を計算します。showRange 関数では、範囲の先頭を a = (n + 2)! + 2 とし、末尾を b = a + n − 1 として求め、その範囲を標準出力に表示します。
出力結果
[122, 124]
例として n = 3 の場合、(n + 2)! = 5! = 120 となるため、範囲は [122, 124] になります。122 = 2 × 61、123 = 3 × 41、124 = 4 × 31 と、いずれも合成数であることが確認できます。
注意点
階乗は極めて大きな値に増加するため、n が大きくなると int 型ではすぐにオーバーフローを起こします。実用上は long long 型や多倍長整数ライブラリの利用を検討してください。
-
C++で指定された制約条件下で重複要素を検索する方法
問題の概要6種類の異なる数値からなるリストがあり、そのうち1つの数値だけが5回繰り返し出現しています。つまり、配列には合計10個の要素が存在することになります。この重複している数値を、たった2回の比較だけで見つけ出すのが本記事の課題です。例えば、リストが [1, 2, 3, 4, 4, 4, 4, 4, 5, 6] の場合、重複している数値は 4 なので、出力は「4」となります。アルゴリズムの考え方ここでは、配列が昇順にソートされているものとします。合計10個の要素の中に同じ数値が5個連続して並んでいる場合、そのブロックは必ずインデックス3から5の範囲のいずれかに位置します。これは、5個の連続
-
【C++】指定された範囲内で x が y を割り切るペア(x, y)を O(1) で見つける方法
今回は興味深いアルゴリズムの問題を取り上げます。範囲 l ≤ x, y ≤ r を満たすペア(x, y)を見つけるというもので、このペアには「x が y を割り切る」という性質が必要です。条件を満たすペアが複数存在する場合は、そのうちの 1 つを出力すればよいことになっています。解法のアイデアこの問題は、実は O(1) の計算量で解くことができます。鍵となるのは、下限値 l とその 2 倍の値 2l です。その理由を考えてみましょう。y/x の最小値は 2 です。もし範囲内により大きな値(y/x ≥ 3 となる組み合わせ)が存在するなら、必ず y/x = 2 となる組み合わせも同じ範囲内に存在