数学の力

京大生が数学の定理・公式の証明や入試問題の解説をするブログ.

プロジェクトオイラー5. ~ Smallest multiple ~


スポンサードリンク

No.5 Smallest multiple

問題.

Q. 2520 は, 1 から 10 のすべてで割り切れる最小の数です.

では, 1 から 20 のすべてで割り切れる最小の数はいくつか.

 

要は 1 から 20 までの数の最小公倍数を求める問題です.

 

考え方とプログラム例(c++)

1. 1から順に最小公倍数を求めていく

2つの引数 ( a, b) の最大公約数  G を返す関数  \mathrm{gcd}(a, b) を, ユークリッドの互除法を用いて再帰関数で定義します.

 

2つの数  a, b の最小公倍数  L=\mathrm{lcm}(a, b) は, 最大公約数を  G とすると,

\begin{align*}
L = \dfrac{a\times b}{G}
\end{align*}

で求まります.

ちなみに,この問題では特に関係ないのですが, a bが大きな数のときに, a\times bの計算でオーバーフローしてしまう可能性があるので,最小公倍数を求める場合は a Gで割り切れることを用いて

\begin{align*}
L = (a / G) \times b
\end{align*}

とすることが多いです.

 

後は,

\begin{align*}
\mathrm{lcm}(\mathrm{lcm}(\ldots \mathrm{lcm}(\mathrm{lcm}(1, 2), 3), \ldots, 20))
\end{align*}

を求めれば答えになります.

 

#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b){
    if(a % b == 0) return b;
    else	return gcd(b, a % b);
}
int main(){
    int ans = 1;
    for(int i = 1; i <= 20; i++)
        ans = i / gcd(ans, i) * ans;
    cout << ans << endl;
    return 0;
}

 

2. 素数がいくつ答えに含まれるかを考える

今回は20までの最小公倍数ですが,より大きなNに対してN以下の自然数の最小公倍数を求めようと思うと上の方法では時間がかかってしまいます.そこで,数学的な工夫をして答えを求めていきます.

 

20以下の素数は, 2, 3, 5, 7, 11, 13, 17, 19 の8個です.

(下のプログラムではエラトステネスの篩によって20以下の素数を求めています. )

 

すると, 1~20の最小公倍数  X は,

\begin{align*}
X = 2^{q_2}\times 3^{q_3}\times\cdots\times17^{q_{17}}\times19^{q_{19}}
\end{align*}

という形になります.

 

このとき, 20 以下に  2^{q_2} の倍数が含まれていて,  2^{q_2+1} の倍数は含まれていないことになるので,

\begin{align*}
q_2 = \left\lfloor\log_{2}{20}\right\rfloor
\end{align*}

です. 他の素数  p に対する  q_p も同様に求められます.

 

#include<bits/stdc++.h>
using namespace std;
int main(){
    int ans = 1;
    int N = 20;
    int num[N] = {0};
    num[0] = 1;
    int n;
    for(int i = 0; i < N; i++){
        if(num[i] == 0){
            ans *= (pow(i + 1, int(log(N)/log(i+1))));
            n = 2 * (i + 1);
            while(n <= N){
                num[n-1] = 1;
                n += (i + 1);
            }
        }
    }
    cout << ans << endl;
    return 0;
}