【C言語】最小公倍数の求め方(3つ以上も可)

最小公倍数の求め方の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、C言語での「最小公倍数の求め方」について解説していきます。

まずは2つの自然数に対する最小公倍数の「力まかせ」での求め方について解説し、続いて「最大公約数」を利用した最小公倍数の求め方について解説します。

その後、3つ以上の自然数に対する最小公倍数の求め方についても解説していきたいと思います!

上述でも少し触れているように、このページでは最小公倍数を求める対象となる数は自然数であることを前提として解説させていただきます(自然数は 0 を “含まない” ものとします)。

また、最小公倍数を効率よく求めるためには最大公約数を利用するのが手っ取り早いです。最大公約数の求め方については下記ページで解説していますので、最大公約数の求め方をご存知ない方は下記ページを事前に読んでいただくことをオススメします。

最大公約数の求め方の解説ページアイキャッチ 【C言語】最大公約数の求め方(ユークリッドの互除法・3つ以上など)

力まかせで最小公倍数を求める

まずは、2つの自然数に対する最小公倍数について解説し、その最小公倍数の求め方について解説していきます。ここでは、最大公約数は利用せず、力まかせに最大公約数を求めていきます。

最小公倍数とは

では、最初に最小公倍数について整理しておきましょう!

2つの自然数における最小公倍数とは、2つの自然数の公倍数における 0 以外の最小の正の数のことを言います。

また、公倍数とは2つの自然数における共通の倍数のことです。さらに、ある自然数に対する倍数とは、その自然数の整数倍の自然数のことを言います。つまり、ある自然数に対する倍数は、必ずその自然数そのもので割り切ることができます。

そして、C言語においては、割り切れるかどうかは剰余演算(%)を行うことで調べることができます。

例えば自然数 a に対し、m が倍数であるかどうかは m % a == 0 が成立するかどうかを調べることで判断できます。剰余演算では、被除数(m)が除数(a)で割り切れる場合、演算結果が必ず 0 になります。

また、自然数 a と自然数 b に対し、m が公倍数であるかどうかは m % a == 0m % b == 0 の両方が成立するかどうかを調べることで判断できます。

この2つの式の両方が成立するということは、ma でも b でも割り切れるということですので、ma と b の両方に共通する倍数、すなわち公倍数であると言えます。

そして、このページで扱う最小公倍数とは、その公倍数の中で最小の正の数のことを言います。

さらに、この最小公倍数が取りうる値は、2つの自然数の大きい方の数2つの自然数の積 の範囲となります。

2つの自然数の大きい方の数 の倍数の中で、2つの自然数の大きい方の数 よりも小さな数は 0 しかありません。なので、2つの自然数の大きい方の数 よりも小さな数で、2つの自然数の公倍数となり得る数は 0 のみです。

ただ、0 は最小公倍数ではないため(最小公倍数とは、2つの自然数の公倍数の “0 以外” の最小の正の数)、結局 2つの自然数の大きい方の数 よりも小さな数は最小公倍数にはなり得ません。

また、2つの自然数を掛け合わせた結果、つまり 2つの自然数の積 は必ず2つの自然数の公倍数になりますので、最小公倍数は必ず 2つの自然数の積 以下の値となります。

ですので、2つの自然数の最小公倍数が取りうる値は、2つの自然数の大きい方の数2つの自然数の積 の範囲となります。

この辺りを考慮しながら、例えば 46 の最小公倍数について考えてみましょう!

まず、46 の公倍数は、012243648・・・・と無限に挙げることができるのですが、2つの自然数の大きい方の数2つの自然数の積 の範囲の公倍数に絞って考えれば、1224 の2つのみとなります。

4と6の公倍数を示す図

どの数が 46 の公倍数であるかは、前述の通り 46 で実際に剰余演算を行ってみることで調べることが可能です。

そして、この2つの数の中から最小のものが最小公倍数となります。今回の場合は 12 ということになりますね!

4と6の最小公倍数を示す図

スポンサーリンク

最小公倍数を力まかせで求める

最小公倍数をプログラムとして解く場合も、結局は上記の例で最小公倍数を求めた時と同様の演算を行えば良いだけです。

要は、剰余演算を利用して2つの自然数の最小の公倍数を見つけ出してやれば良いだけです。

例えば、数を 2つの自然数の大きい方の数2つの自然数の積 まで 1 ずつ増加させながら、その数が2つの自然数の公倍数であるかどうかを調べていけば、最初に見つけた公倍数が最小公倍数ということになります(最小公倍数とは で解説したように、最小公倍数は必ず 2つの自然数の大きい方の数2つの自然数の積 の範囲内に存在する)。

最初に見つけた公倍数を最大公倍数とする様子

逆に、数を 2つの自然数の積2つの自然数の大きい方の数 まで 1 ずつ減少させながら、その数が2つの自然数の公倍数であるかどうかを調べていけば、最後に見つけた公倍数が最小公倍数ということになります

最後に見つけた公倍数を最大公倍数とする様子

上記の両方とも、結局は総当たりで力まかせに最小公倍数を見つけ出しているだけなので処理効率は悪いです。なので、特に 2つの自然数の積 が大きな値である場合、最小公倍数を求めるのに時間がかかる可能性があります。

最小公倍数を力まかせで求める関数

ここまで解説してきた内容に基づいて作成した「最小公倍数を力まかせに求める関数」は、下記ソースコードにおける lcm 関数となります。

lcm 関数は、引数 a と引数 b で与えられる2つの自然数の最小公倍数を返却値として求める関数になります。

lcm 関数内部では、i2つの自然数の大きい方の数2つの自然数の積 まで 1 ずつ増加させ、最初に見つけた ab の公倍数となる i を最小公倍数として返却するようにしています。

力まかせで最小公倍数を求める
#include <stdio.h>

unsigned int lcm(unsigned int a, unsigned int b) {
    unsigned int big;

    if (a == 0 || b == 0) {
        printf("a or b is 0!!!\n");
        return 0;
    }

    if (a > b) {
        big = a;
    } else {
        big = b;
    }

    unsigned int ans = 0;
    for (unsigned int i = big; i <= a * b; i++) {
        if (i % a == 0 && i % b == 0) {
            ans = i;
            break;
        }
    }

    return ans;
}

int main(void) {
    
    unsigned int x = 776;
    unsigned int y = 1261;

    printf("lcm(%u, %u) = %u\n", x, y, lcm(x, y));

    return 0;

}

最小公倍数は 0 にはなり得ないため、引数に 0 が指定された場合は最小公倍数を求めることができません(0 の倍数は 0 のみなので公倍数も 0 のみになってしまう)。この場合は、とりあえず printf でエラー表示し、0 を返却するようにしています。

この点に関しては以降で紹介する関数も同様になります。

最大公約数を利用して最小公倍数を求める

先程紹介した手順でも最小公倍数を求めることは可能ですが、要は最小公倍数となりうる全ての候補の数に対して最小公倍数であるかどうかを調べていく方法ですので、処理効率が悪いです。

ただ、最小公倍数は最大公約数を利用して求めることができ、最大公約数はユークリッド互除法で効率的に求めることができます。そのため、最大公約数を利用することで、力まかせに求めるよりも効率よく最小公倍数を求めることができます。

次は、その最大公約数を利用して2つの自然数の最小公倍数を求めていきたいと思います!

スポンサーリンク

最大公約数を利用した最小公倍数の求め方

2つの自然数の最小公倍数は、その2つの自然数の最大公約数を利用して下記の式により求めることができます。

最小公倍数 = 1つ目の自然数 * 2つ目の自然数 / 最大公約数

つまり、2つの自然数の数を掛け合わせ、それを最大公約数で割ってやれば良いだけです。簡単ですね!

ただ、上記の計算を行うためには2つの自然数の最大公約数を求めてやる必要があります。この最大公約数の求め方については下記のページで紹介していますので、最大公約数の求め方をご存知ない方は下記ページを参照していただければと思います。

最大公約数の求め方の解説ページアイキャッチ 【C言語】最大公約数の求め方(ユークリッドの互除法・3つ以上など)

上記の式の計算を行う上での注意点を1つ挙げておくと、1つ目の自然数 * 2つ目の自然数 を計算する際に「桁あふれ(オーバーフロー)」が発生しないように注意してください。

C言語では、計算結果も変数同様に特定の型のデータとして扱われます。例えば、2つの自然数を unsigned int 型の変数として扱う場合、掛け算結果も unsigned int 型として扱われます。そのため、 1つ目の自然数 * 2つ目の自然数 の結果が unsigned int 型で扱える値の最大値を超えた場合、桁あふれが発生し、計算結果がおかしくなってしまいます。

桁あふれの発生を考慮した場合、上記の式そのままではなく、次のように式変形を行なって最小公倍数を求める方が無難です。

最小公倍数 = 1つ目の自然数 / 最大公約数 * 2つ目の自然数

先に 1つ目の自然数 / 最大公約数 を計算することで、最大公約数 2 以上の場合は計算結果が小さくなります。そして、小さくなった結果に対して 2つ目の自然数 を掛けることになるので、そのまま 1つ目の自然数 * 2つ目の自然数 を計算するよりも桁あふれが発生しにくくなります。

ただ、桁があふれが発生しにくくはなるものの、完全に桁あふれを防ぐことができるというわけではないです。桁あふれをもっと発生しにくくするためには、自然数を扱う変数や引数の型に、もっと大きな数を扱える型を選ぶ必要があります。

最大公約数を利用して最小公倍数を求める関数

ここまで解説してきた内容に基づいて作成した「最大公約数を利用して最小公倍数を求める関数」は、下記の lcm になります。

最大公約数を利用して最小公倍数を求める
#include <stdio.h>

unsigned int gcd_r(unsigned int a, unsigned int b) {
    if (a == 0) {
        return b;
    }
    if (b == 0) {
        return a;
    }

    return gcd_r(b, a % b);
}

unsigned int gcd(unsigned int x, unsigned int y) {
    unsigned int big, small;

    if (x > y) {
        big = x;
        small = y;
    } else {
        big = y;
        small = x;
    }

    return gcd_r(big, small);
}

unsigned int lcm(unsigned int a, unsigned int b) {
    unsigned int big;

    if (a == 0 || b == 0) {
        printf("a or b is 0!!!\n");
        return 0;
    }

    return a / gcd(a, b) * b;
}

int main(void) {
    
    unsigned int x = 776;
    unsigned int y = 1261;

    printf("lcm(%u, %u) = %u\n", x, y, lcm(x, y));

    return 0;
}

lcm 関数では 最大公約数を利用した最小公倍数の求め方 で紹介した式をそのまま実行しているだけですが、最大公約数を求めるのに gcd 関数を利用しています。さらに、その gcd 関数からは gcd_r 関数を利用して最小公約数を求めています。

これらの gcd 関数と gcd_r は下記ページの 再帰呼び出しでユークリッドの互除法を行う関数 をそのまま利用しています。

最大公約数の求め方の解説ページアイキャッチ 【C言語】最大公約数の求め方(ユークリッドの互除法・3つ以上など)

3つ以上の自然数の最小公倍数を求める

ここまで2つの自然数に対する最小公倍数の求め方について解説してきましたが、次は3つ以上の自然数に対する最小公倍数の求め方について解説していきたいと思います。

下記ページで3つ以上の自然数の最大公約数の求め方について解説していますが、最小公倍数の場合もほぼ同様で、2つの自然数の最小公倍数を段階的に繰り返し求めていくことで、3つ以上の最小公倍数を求めることが可能です。

最大公約数の求め方の解説ページアイキャッチ 【C言語】最大公約数の求め方(ユークリッドの互除法・3つ以上など)

スポンサーリンク

3つ以上の自然数の最小公倍数の求め方

まず、n 個の自然数の最小公倍数を求める関数を lcm_n としたいと思います。この時、n = 3 の場合、つまり 3 個の自然数の最小公倍数を求める lcm_3 には下記が成立します。

lcm_3(a, b, c) = lcm_2(lcm_2(a, b), c)

つまり、3つの自然数に対する最小公倍数は、そのうちの2つの自然数に対する最小公倍数を求め、さらに、その 結果と残った1つの自然数に対する最小公倍数を求めることで得ることができます。

3つの自然数の最小公倍数を求める様子

ということは、2つの自然数の最小公倍数を求める関数さえ用意できれば、その関数を実行していくことで3つの自然数の最小公倍数も求めることができるということになります。

2つの自然数の最小公倍数を求める関数に関しては、下記で紹介した lcm 関数(lcm 関数から実行されている関数も必要)がそのまま使えます。

4つ以上の自然数の最小公倍数に関しても同様で、2つの自然数の最小公倍数を求めていくことで求めることができます。

lcm_n(a, b, c, d,...) = lcm_2(... lcm_2(lcm_2(lcm_2(a, b), c), d) ...)

上記の式を見ると難しそうに思えますが、要は n 個の自然数の先頭の2つの自然数の最小公倍数を求め、その結果と次の自然数の最小公倍数を求め、その結果と次の自然数の最小公倍数を求め…、といった処理を n - 1 回分繰り返せば良いだけです。最後に求めた最小公倍数が、n 個の自然数の最小公倍数となります。

n個の自然数の最小公倍数を求める様子

3つ以上の自然数の最小公倍数を求める関数

ここまで解説してきた内容に基づいて作成した「3つ以上の自然数の最小公倍数を求める関数」は下記の lcm_n となります。

lcm_n 関数は n 個の自然数の最小公倍数を求める関数であり(n は引数で与えられる)、n 個の自然数は配列 nums に格納されているものとしています。

また、lcm 関数は 最大公約数を利用して最小公倍数を求める関数 で紹介したものと全く同じ関数となります。さらに、lcm 関数内では最大公約数を求めるために、最大公約数を利用して最小公倍数を求める関数 で紹介した gcd 関数および gcd_r 関数を利用しています。

3つ以上の自然数の最小公倍数を求める
#include <stdio.h>

unsigned int gcd_r(unsigned int a, unsigned int b) {
    if (a == 0) {
        return b;
    }
    if (b == 0) {
        return a;
    }

    return gcd_r(b, a % b);
}

unsigned int gcd(unsigned int x, unsigned int y) {
    unsigned int big, small;

    if (x > y) {
        big = x;
        small = y;
    } else {
        big = y;
        small = x;
    }

    return gcd_r(big, small);
}

unsigned int lcm(unsigned int a, unsigned int b) {
    unsigned int big;

    if (a == 0 || b == 0) {
        printf("a or b is 0!!!\n");
        return 0;
    }

    return a / gcd(a, b) * b;
}

unsigned int lcm_n(unsigned int nums[], unsigned int size) {

    if (size == 0) {
        return 0;
    }

    unsigned int ans = nums[0];
    for (int i = 1; i < size; i++) {
        ans = lcm(ans, nums[i]);
    }

    return ans;
}

int main(void) {

    unsigned int nums[] = {
        776, 1843, 291, 970, 2037
    };

    printf("%u\n", lcm_n(nums, sizeof(nums) / sizeof(nums[0])));

    return 0;
}

lcm_n 関数では、2つの自然数の最小公倍数を lcm 関数で求めることを繰り返すことで、n 個の自然数の最小公倍数 ans を求めています。

この lcm_n 関数に関しても、下記ページの 3つ以上の自然数の最大公約数を求める関数 で紹介している gcd_n 関数と作りはほとんど同じです。なので、下記ページを読んでくださった方であれば、lcm_n 関数の動作についても理解していただけるのではないかと思います。

最大公約数の求め方の解説ページアイキャッチ 【C言語】最大公約数の求め方(ユークリッドの互除法・3つ以上など)

一応このページでも解説しておくと、まず for ループに入る直前に ans = nums[0] を実行し、さらに for ループの最初の繰り返しにおいて ans = lcm(ans, nums[1]) を行うことで、ansnums[1] の最小公倍数を求めています。

ここで、ans には元々 nums[0] の値が格納されているので、結局ここで求められるのは nums[0]nums[1] の2つの自然数の最小公倍数ということになります。そしてその最小公倍数で ans を更新しています。

さらに、次の繰り返しにおいて ans = lcm(ans, nums[2]) を行うことで、ansnums[2] の最小公倍数を求めています。

ここで、ans には元々 nums[0]nums[1] の2つの自然数の最小公倍数が格納されているので、結局ここで求められるのは nums[0]nums[1]nums[2] の3つの自然数の最小公倍数ということになります。そしてその最小公倍数で ans を更新しています。

こんな感じで、i に対する for ループの中で ans = lcm(ans, nums[i]) が実行される際には、i + 1 個の自然数の最小公倍数が求められることになります。

そして、for ループの最後には in - 1 の状態で  ans = lcm(ans, nums[i]) が実行されるわけですから、最終的には n 個の自然数の最小公倍数が求められることになります。

そしてその最小公倍数が格納される変数 ans を最後に return するようにすることで、n 個の自然数の最小公倍数を返却できるようにしています。

また、main 関数では求めたい個数の自然数をあらかじめ配列 nums に格納しておき、引数に配列 numsnums の要素数を指定して lcm_n 関数を実行して最小公倍数を求めています。

要素数を求める際に使用している sizeof をご存知ない方は、是非下記ページを読んでみてください。sizeof は配列の要素数を求めるだけでなく、型のサイズ等を調べる際にも有用な演算子になります。

C言語のsizeof演算子の解説ページアイキャッチ 【C言語】sizeof演算子とは?sizeof演算子を利用するメリットは?

まとめ

このページでは、C言語での「最小公倍数の求め方」について解説しました!

力まかせで最小公倍数を求める で解説したように、最小公倍数がどんな数であるかを理解しておけば、力まかせに総当たりで最小公倍数を求めることが可能です。

また、最大公約数を利用して最小公倍数を求める で解説したように、力まかせではなく最大公約数を利用して最小公倍数を求めることも可能です。ユークリッドの互除法等を用いて最大公約数を効率的に求めることで、最小公倍数も効率的に求めることができます。

さらに、3つ以上の自然数の最小公倍数を求める でも解説したように、2つの自然数の最小公倍数を求める関数さえ用意できれば、その関数を利用して3つ以上の自然数の最小公倍数も簡単に求めることができます。

特に最大公約数を利用する場合、変数等を式に当てはめれば良いだけなので簡単に最小公倍数を求めることができます(最大公約数を求める関数が必要ではありますが)。是非この求め方はしっかり覚えておきましょう!

同じカテゴリのページ一覧を表示