【競プロ】最大公約数と最小公倍数

ここでは、最大公約数と最小公倍数について見ていきます。これらも、整数の問題ではよく登場します。競プロ 記事の一覧はこちら

【広告】

公倍数と公約数

$3$ の倍数には、 $3,6,9,12,\cdots$ があり、 $4$ の倍数には、 $4,8,12,16,\cdots$ があります。 $12$ はどちらにも含まれています。このように、2つの整数 $a,b$ について、 $a$ の倍数になっていて $b$ の倍数にもなっているものを、 $a,b$ の公倍数(common multiple) といいます。 $3,4$ の公倍数は、 $12$ 以外にも、 $24$, $36$ など、無数にあります。

ある整数 $c$ が、2つの整数 $a,b$ の公倍数であるかどうかは、 $c$ を $a,b$ で割ったときの余りがどちらも $0$ であるかどうかを確認すればいいので、コードで書くと次のようになります。

#include <iostream>
using namespace std;

int main() {
  int a = 3, b = 5, c = 15;
  if (c % a == 0 && c % b == 0) {
    cout << "Yes";
  } else {
    cout << "No";
  }
  return 0;
}
【広告】
高校で学ぶ数学の基礎(数I・A・II・B・III・C)を146項目に細分類して1冊に集約。本書の流れにそって一項目ずつ習得することで「高校で学ぶ数学」をマスターすることができます。
著者: 高橋 一雄
出版社: 日本実業出版社
発売日: 2009/07/16
510ページ

約数に対しても同じようなものを考えられます。 $12$ と $16$ の約数を考えると、 $4$ や $1$ などが共通しています。このように、2つの整数 $a,b$ について、 $a$ の約数になっていて $b$ の約数にもなっているものを、 $a,b$ の公約数(common divisor) といいます。

次のコードは、正の整数 $a,b$ に対して、公約数を小さい方から順番に書いていくものです。

#include <iostream>
using namespace std;

int main() {
  int a = 12, b = 16;
  for (int i = 1; i <= a; i++) {
    if (a % i == 0 && b % i == 0) {
      cout << i << " ";
    }
  }
  return 0;
}

最大公約数

公約数のうち、最大のものを最大公約数(greatest common divisor) といいます。最大公約数は整数に関連する問題で登場することが多く(競プロでも数学でも)、アルファベットの頭文字をとって、GCD と略されることもあります。

次のコードは、正の整数 $a,b$ の最大公約数を、愚直に求めるコードです。順番に割り算をしてみて、割り切れるものを探し、その中で最大のものを計算しています。

#include <iostream>
#include <cmath>
using namespace std;

int main() {
  int a, b;
  cin >> a >> b;
  if (a > b) swap(a, b);

  int ans = 1;
  for (int i = 1; i <= sqrt(a); i++) {
    if (a % i == 0) {
      int j = a / i;
      if (b % i == 0) ans = max(ans, i);
      if (b % j == 0) ans = max(ans, j);
    }
  }
  
  cout << ans;
  return 0;
}

約数は自分自身より大きくなることはない(例えば、 $36$ の約数が $36$ より大きくなることはない)ので、min(a, b)までの数を調べればいいです。上のコードでは、 ab 以下となるように、はじめに swap をしています。

a の約数を見つけるには、 sqrt(a) 以下の整数を調べればいいです(参考:倍数と約数素因数分解)。 $a$ 個の丸を縦に $i$ 個並べるとき、 $j$ 列ピッタリに並べられたら、 $i,j$ は約数であることがわかるので、これらが $b$ の約数になっているかどうかを調べます。

競プロでは、最大公約数を探すときには、上の方法よりももっと効率のいい方法がよく使われますが、それは別の機会に見ることにします。

上のコードでほとんどできていますが、AOJ ALDS1_1_B Greatest Common Divisorに挑戦できるでしょう。上のコードは想定解ではないので、現時点ではヒントは気にしないでおきましょう。

互いに素

どんな整数でも $1$ は約数になるので、 $1$ は必ず公約数になります。そのため、最大公約数は少なくとも $1$ です。

一方、2つの正の整数 $a,b$ の最大公約数が $1$ のとき、 $a$ と $b$ は互いに素(coprime) といいます。例えば、 $2$ と $5$ は互いに素です。 $12$ と $77$ も互いに素です。「互いに素」は「素数」と同じ「素」という漢字を使っていますが、別に素数でなくても「互いに素」になることはあります。

互いに素かどうかを調べるには、最大公約数が $1$ であるかどうかを調べればよく、先ほどのコードが使いまわせるでしょう。

「互いに素」は数学的には重要な概念で、これから何度も登場します。

【広告】

最小公倍数

公倍数のうち、正の整数で最小のものを最小公倍数(least common multiple) といいます。頭文字をとって、LCM と略されます。公倍数は無数にありますが、最小公倍数は一つだけです。

LCM と GCD のどちらがどちらを表しているかが紛らわしいですが、L が least(最小)の略で、G が greatest(最大)の略だということを頼りに思い出しましょう。

最大公約数とは異なり、最小公倍数のほうはどこまで調べればいいか、少しわかりにくいです。しかし、これも限界値はあります。

$a,b$ の両方の倍数になっているもののうち、すぐに見つけられるものは $ab$ です。これより大きいものが最小公倍数になることはありません。そのため、これ以下の数で、 $a,b$ で割ったときに割り切れるかどうかを調べていけば、最小公倍数を見つけられます。もう少し効率的にやるなら、 $b$ から $ab$ までの $b$ の倍数の中で、 $a$ で割り切れるものを小さい方から順番に調べていきます。

愚直に調べていくなら、次のようなコードになります。

#include <iostream>
using namespace std;

int main() {
  int a, b;
  cin >> a >> b;
  if (a > b) swap(a, b);

  for (int i = 1; i <= a; i++) {
    if (b * i % a == 0) {
      cout << b * i;
      return 0;
    }
  }
  return 0;
}

最小公倍数の見つけ方も、効率のいい方法がありますが、これも別の機会に見ることにしましょう。

おわりに

ここでは、公約数と公倍数、最大公約数(GCD)と最小公倍数(LCM)について見てきました。また、互いに素についても見ました。これらは、整数に関する問題ではよく出てきます。これからも何度も登場します。