最大公約数と最小公倍数

2つの数の最大公約数と最小公倍数を作るプログラムをつくります。

最大公約数

まず、ある数の約数をすべてリストアップすることを考えます。CindyScriptのリスト処理をする関数を使うと次のように簡単にできます。

yaku=select(1..n,mod(n,#)==0);

1..n で、1からnまでの整数のリストを作成できます。select関数は、そのリストから、mod(n,#)==0 を満たすものを選び出してリストを作ります。mod(n.#) は、nを# ( #はリストの要素になります) で割った余りなので、mod(n,#)==0 ということは「割り切れる」ということです。

たとえば、 n=12 のとき

1..n で、[1,2,3,4,5,6,7,8,9.10,11,12] というリストができます。その要素の始めから mod(12,#) つまり12をその要素で割った余りを調べて割り切れるものを選び出します。結果として 1,2,3,4,6,12 が選び出されます。

つぎに、これを関数にします。

yakusuu(n):=select(1..n,mod(n,#)==0);

これで、yakusuu(12) とすれば12の約数がリストアップされます。

2つの数に共通な約数が「公約数」です。そのうち最大のものが「最大公約数」です。では、2つの数の約数のリストを作り、最大公約数を求めてみましょう。2つの数の約数のリストは、それぞれ先ほどの処理で求められているとしましょう。CindyScriptには、2つのリストから共通な要素を抜き出すcommon(list1,list2)という関数があらかじめ用意されています。

yaku3=comon(yaku1,yaku2);

とすれば公約数のリストができます。

次に、最大公約数ですが、CindyScriptには、リストの中から最大値をとり出す関数があり、中身がどんな順序になっていても構いません。

では、やってみましょう。90と72の公約数のリストと、最大公約数を求めます。

それぞれの約数のリストと公約数をすべて表示する必要がなく、最大公約数だけ求めればよいのなら

yakusuu(n):=select(1..n,mod(n,#)==0);

gcd(a,b):=max(common(yakusuu(a),yakusuu(b)));

println(gcd(90,72));

の3行でできます。最大公約数の18だけが表示されます。

ユークリッドの互除法

2つの数の最大公約数を求める方法に、ユークリッドの互除法があります。

a,bの最大公約数を gcd(a,b) で表すことにします。

いま、a>b とします。すると gcd(a,b)=gcd(a-b,b) が成り立ちます。

なぜなら、aとbの最大公約数をGとすると、

a=mG ・・ (1) 

b=nG ・・ (2)  m,nは互いに素

と表せるので a-b=(m-n)G です。このとき、m-nとnは互いに素なので

(そうでないとすると、1でない公約数c があり m-n=ck , n=ch から m=ck+n=c(k+h) で mとnが互いに素でなくなります)

(2)から、bとa-bの最大公約数はGということになります。つまり gcd(a,b)=gcd(a-b,b) です。

これをもう一度繰り返すと gcd(a,b)=gcd(a-b,b)=gcd(a-b-b,b) となります。つまり  gcd(a,b)=gcd(a-2b,b) です。 

さて、aをbで割った商をq , 余りを r とするとき、a=bq+r となりますので a-bq=r です。

すると、 gcd(a,b)=gcd(a-b,b)=gcd(a-2b,b)=・・・=gcd(a-bq,b)=gcd(r,b) となり

結局 

gcd(a,b)=gcd(b,r) 

が成り立ちます。これがユークリッドの互除法の原理です。

言葉でいうと、「aとbの最大公約数は、bと、aをbで割ったときの余りrとの最大公約数に等しい」ということです。

すると、つぎに、「bとrの最大公約数は、rと、bをrで割ったときの余りr2との最大公約数に等しい」となり

「割った余り」が0でない限り、このことが繰り返されます。そして、最終的に「余りが0」になればそこで最大公約数が得られることになります。

具体的にやってみましょう。72と20でやります。

gcd(72,20) ・・・72を20で割ると余りは12

=gcd(20,12) ・・・20を12で割ると余りは8

=gcd(12,8)  ・・・12を8で割ると余りは4

    =gcd(8,4) ・・・8を4で割ると余りは0 残ったのは4と0

ここで最後に残った数4が72と20の最大公約数です。

2つの数が互いに素の場合は次のようになります。

gcd(35,8) ・・・ 35を8で割ると余りは3

=gcd(8,3) ・・・ 8を3で割ると余りは2

=gcd(3,2) ・・・ 3を2で割ると余りは1

=gcd(2,1) ・・・ 2を1で割ると余りは0 残ったのは1と0

最後に残ったのは1なので最大公約数は1,すなわち互いに素です。

では、これをCindyScriptで書いて見ましょう。「小さい方の数と、割った余りで考える」ということを何度も繰り返します。このような場合、「再帰的プログラム」という方法を使います。この「再帰的プログラム」は少し難しい概念ですので、何が何だか分からない、という人もいると思います。それはそれで、まあ、よいことにしましょう。

aとbの最大公約数を求める関数をgcd(a,b) としましょう。この関数の処理の中で、aをbで割った余りrを求めてgcd(b,r) を求めます。

すると、関数の処理の中で、自分自身の関数を使うことになります。これが「再帰的プログラム」です。

次のようになります。

---------------------------------------------------------------------------

gcd(a,b):=if(b==0, a,       // 最後に0になったらそのときのaを返す

   if(b>a,               // そうでなければ、もし b>a なら

      gcd(b,a),          // 引数を入れ替えて gcd()を実行

      gcd(b,mod(a,b))    // そうでなければ、bと余りでgcd()を実行 

   );

);

println(gcd(72,20));

---------------------------------------------------------------------------

次が実行結果です。

最小公倍数

aとbの最大公約数を用いて最小公倍数を求めます。aとbの最大公約数をGとすると

a=mG , b=nG (m,nは互いに素)

で、最小公倍数は mnG ですから、ab/G が最小公倍数となります。

リストを使って最大公約数Gを求め、 ab/G で最小公倍数を表示しましょう。

----------------------------------------------------------------------------------------------------------------------

a=90;

b=72;

yakusuu(n):=select(1..n,mod(n,#)==0);

gcd(a,b):=max(common(yakusuu(a),yakusuu(b)));

LCM=a*b/gcd(a,b);

print(LCM);

----------------------------------------------------------------------------------------------------------------------

 

戻る