学習内容について

 どうも、アロマキャンドルを焚く間がなくて寝不足がちなしがないITエンジニアです。
アロマキャンドル焚くと香りと火の揺らぎでリラックスしてよく眠れるのだが、
零時頃まで作業をしてしまうため焚く間がない。
それでも、焚けば多少寝付きは良くなるのだが寝る時間が減るジレンマ。

今日こそは早く寝たい。

アロマキャンドル
アロマキャンドルを付けた時の様子

 導入はここまでにして問題を見ていこう。
なお、前の問題が見たい方は下記のリンクに移動してほしい。

[問題3]最小公倍数

 与えられた2個以上の正の整数について、
その最小公倍数を計算して出力するプログラムを書きなさい。

ヒント

ヒント

最小公倍数とは

最小公倍数(さいしょうこうばいすう、英: least common multiple)とは、
0ではない複数の整数の公倍数のうち最小の自然数をさす。
たびたび、L.C.M.やlcm等の省略形で記述される。

最小公倍数 – Wikipedia

 最小公倍数の求め方は人が求めるのであれば総当たり、足りないものを考える、筆算、素因数分解、
など4通りほどあるらしいが、どれもプログラミングするのは難しい概念だ。

少なくとも筆者は解けなかった。

 正の整数2個の場合は以下の関係が成り立つパターンを求めればいいだろうが、
3個以上の正の整数の場合は別の解を求めるプログラムが必要だろう。

正整数a, bに対して、aとbの最大公約数 gcd(a, b)と最小公倍数lcm(a, b)との間には
gcd (a, b) ・ lcm (a, b) = ab
という関係がある。

最小公倍数 – Wikipedia

解答

解答
// [問題2]最大公約数を求める
unsigned int gcd(const unsigned int a, const unsigned int b)
{
	return (b == 0) ? a : gcd(b, a % b);
}

// 最小公倍数を求める
unsigned int lcm(unsigned int const a, unsigned int const b)
{
	unsigned int h = gcd(a, b);
	return h ? (a * (b / h)) : 0;
}

3つ以上の整数の最小公倍数を計算するには、ヘッダ<numeric>で定義されているstd::accumulateアルゴリズムが使えるそうだ。

// 3つ以上の整数の最小公倍数を計算するには、ヘッダ<numeric>で定義されているstd::accumulateアルゴリズムが使えます。
template<class InputIt>
int lcmr(InputIt first, InputIt last)
{
	return std::accumulate(first, last, 1, lcm);
}

解説

解説

2つ以上の0ではない整数の最小公倍数(lcm:least common multiple)は
英語でlowest common multiple、またはsmallest common multipleとも呼びますが、
それらすべての数で割り切れる最小の正の整数です。
最小公倍数を計算する1つの方法は、最大公約数の問題に帰着させることです。
次の公式が成り立ちます。

lcm(a, b) = abs(a*b) / gcd(a, b)

// [問題2]最大公約数を求める
unsigned int gcd(const unsigned int a, const unsigned int b)
{
	return (b == 0) ? a : gcd(b, a % b);
}

// 最小公倍数を求める
unsigned int lcm(unsigned int const a, unsigned int const b)
{
	unsigned int h = gcd(a, b);
	return h ? (a * (b / h)) : 0;
}

3つ以上の整数の最小公倍数を計算するには、
ヘッダ<numeric>で定義されているstd::accumulateアルゴリズムが使えるそうだ。

// 3つ以上の整数の最小公倍数を計算するには、ヘッダ<numeric>で定義されているstd::accumulateアルゴリズムが使えます。
template<class InputIt>
int lcmr(InputIt first, InputIt last)
{
	return std::accumulate(first, last, 1, lcm);
}

また、C++17には、ヘッダ<numeric>の中に、2つの数の最小公倍数を計算するconstexpr関数lcm()があるらしい。

https://cpprefjp.github.io/reference/numeric/lcm.html

補足説明

なお、実際に実行した結果が下図の通りである。
おそらく3つ以上の整数の最小公倍数を求める場合、
先に2つの整数の最小公倍数を計算し、その値と3つ目の整数の最小公倍数を求めるのだろう。
4つ目以降も同様の考え方で演算できる。

C++の実行例

下図は、整数42、72、180の最小公倍数を求めた結果である。
最小公倍数の値が 2520 になっていることが確認できる。
最小公倍数を計算するサイトで検証してみたが、同じ値になるため計算が合っていることがわかる。

C++の実行結果
最小公倍数の計算結果
最大公約数と最小公倍数 – 高精度計算サイト (casio.jp)

最後に

 というわけで3問目の問題を学習してみたがいかがだっただろうか。
普段人間がさも当たり前のように求めている最小公倍数ですら、
いざプログラムとして実装しようとすると高度な知識が要求される。

 それだけ人間の思考が複雑ということの証左だろう。
いつか人間を超えると散々言われてきたAIがなかなか人間を超えないのもうなづける。
勿論、特化させたAIがその専門分野で人間に勝つのは簡単なのだろうが、
人間本来の強みは思考の柔軟性であって、それこそがAIとの差別化ポイントなのだと思う。

 少なくとも現在のAIは人間ほど汎用性があるものは作れていない。
絵を描くために最適化されたAIでは、複雑な文章を理解することは出来ないのである。

 まあ、無駄話はここまでにしておこうと思う。
次回はちゃんと解きたいものだ。。。

 なお、次の問題については下記リンクに移動してほしい。
第4問目は与えられた正の整数より小さい最大の素数である。

By ta-boss

2 thoughts on “[C++]Modern C++チャレンジの問題を解く~3問目~”

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です