この記事を読むことで、PythonとGASでループを使って階乗の計算とその計算結果の末尾に「0」がいくつ付くのかを求めます。 階乗の計算はforループを使い、末尾の「0」はPythonでは文字列に変換して求めてからwhileループで末尾の0の数をリストを反転して求め、GASでは10と2×5がいくつ含まれているのかwhileループを使って求めます。
このコーナーでは、学習コンテンツpaizaラーニング のレベルアップ問題集 をPythonとGASの両方で解いて全コードの解説をしています。 PythonとGASの両方のコードを用いて、全コード及び部分的にも可能な限り詳細に記載いたしました。 GASはスプレッドシートを使っています。 GASはGoogle Apps Scriptと言って、JavaScriptの文法をベースにしているので、JavaScriptの学習中の方にもお役立て出来るかも知れません。
サイトマップはこちらから
paizaレベルアップ問題集でPythonとGASを解いて見たに戻る メインメニューに戻る Python自作サンプル GASサンプル 基本情報技術者試験
paizaでの解答はPythonで行いましたが、この記事ではPythonのコードと共に、同じ問題を現在学習中のGASだったらどう解くのか、スプレッドシートでバインドして作ってみました。
階乗の末尾に 0 はいくつ付く? (paizaランク C 相当)
問題: 整数 N が与えられます。 N の階乗 N! の末尾に 0 がいくつ付くか求め、出力してください。
この記事では、下記の入力例1の場合を例にして、整数N=100の階乗を求め、その計算結果100!の末尾に「0」がいくつ付くかを求めて行きます。
入力例1 100
出力例1 24
ではまず、Pythonで解いてみます。
今回は、paiza.io を使って解きます。paiza.ioの使い方はこちら から。
■ Pythonでの解き方 ■
下準備として、paiza.ioにこの様に入力します。 (入力例1をそのままioにコピーしただけ。)
手順として、
1:Nを標準入力で取り込む 2:階乗の計算結果factを1で初期化する 3:ループを使ってNの階乗N!を変数factに求める 階乗の計算で乗算する数をmultに格納する 4:factが膨大な数になり、通常の10で割っていって0の数を求める方法が適用出来ない為、文字列に変換する 5:末尾の’0’を求めるため、文字列を逆順にする 6:’0’の数を数える変数zeroを0で初期化する 7:whileループで文字列のfactのi番目が’0’というの条件を満たす間、ループを回して’0’の数を数える変数zeroをインクリメントする 8:zeroを表示
で、行います。
まずは、プログラムの各変数の動きを追いやすいように、トレースのio出力結果と、トレースのコードを添えます。
100!は、100×99×98×97×・・・×4×3×2×1で求まります。
97まで掛けたログです。
途中、飛ばします。
1まで掛けたログです。
とても膨大な数になりましたので、画像は一部のみを切り取りました。
この計算結果factは膨大すぎてそのままでは10で割って0の数を数えるということが出来ない為、文字列に変換しました。 Strを使って、文字列に出来ます。
fact=str(fact)
問題文によると、文字列の末尾から’0’を数えるので、上の画像の文字列を反転しました。文字列のリストとして反転してから数えるので、このようなコードになりました。
fact=list(reversed(fact))
その後、文字列の先頭から’0’を数えていくので、whileループでリストfact[i]=’0’の時に、変数zeroをインクリメントするコードを書きました。
Fact[0]からfact[4]までのトレースです。
途中、省略します。
求めた0の個数です。
ここまでのトレースのコードです。
N= int ( input ( ) )
fact= 1
print ( '<<<forループに入ります。>>>' )
for i in range ( N) :
print ( '計算前のfact:' + str ( fact) )
mult= N- i
print ( 'fact×「' + str ( mult) + '」を計算する' )
fact*= mult
print ( '計算後のfact:【' + str ( fact) + '】' )
print ( '------------------' )
print ( '<<<forループを抜けました。>>>' )
fact= str ( fact)
print ( '文字列に変換したfact' )
print ( fact)
fact= list ( reversed ( fact) )
print ( '文字列を反転したfact' )
print ( fact)
zero= 0
print ( '<<<whileループに入ります。>>>' )
i= 0
print ( 'zeroの初期値' + str ( zero) )
while ( fact[ i] == '0' ) :
print ( 'fact[' + str ( i) + ']=「0」なのでzeroをインクリメントする' )
zero+= 1
print ( 'zero:' + str ( zero) )
i+= 1
print ( '--------------' )
print ( '<<<whileループを抜けました。>>>' )
print ( zero)
このままでは、出力結果である出力例1に対して冗長なコードが含まれているので、解答以外のprint文をコメントアウトします。
N= int ( input ( ) )
fact= 1
for i in range ( N) :
mult= N- i
fact*= mult
fact= str ( fact)
fact= list ( reversed ( fact) )
zero= 0
i= 0
while ( fact[ i] == '0' ) :
zero+= 1
i+= 1
print ( zero)
スッキリするように、コメントアウトした部分を省いたコードです。
N= int ( input ( ) )
fact= 1
for i in range ( N) :
mult= N- i
fact*= mult
fact= str ( fact)
fact= list ( reversed ( fact) )
zero= 0
i= 0
while ( fact[ i] == '0' ) :
zero+= 1
i+= 1
print ( zero)
ioの出力結果です。
■ GASでの解き方 ■
では、同じ問題をGASで解いてみます。 まず、スプレッドシートにこの様に配置しました。
スプレッドシートの緑のセルに階乗を求める整数の100を入力します。 灰色のセルには100!の計算結果を出力します。 黄色いセルには、100の階乗100!の末尾に0が付く個数を出力します。 0の個数は変数zeroに求めます。
また、factを二次元配列としてfact2に追加して、zeroを二次元配列zero2に追加して、それぞれスプレッドシートに出力します。
※スプレッドシートに表示する場合は、二次元配列としてからの配列に追加をして作成します※
手順はこのようになります。
1:スプレッドシートからアクティブシートをアクセスする 2:階乗を求める整数N(この例の場合は100)を取得する 3:階乗の計算結果factを1で初期化する 4:100×99×98×97・・・4×3×2×1の掛けていく数を格納するmultを宣言する 5:10で割れる回数tenを0で初期化する 6:5で割れる回数fiveを0で初期化する 7:2で割れる回数twoを0で初期化する 8:末尾に付く0の個数zeroを0で初期化する 9:multが10,5,2それぞれで何回割れるのか数えて変数countに格納するので0で初期化する 10:multを代入する変数tempを宣言する11:forループで100!を計算する 12:階乗の計算に用いたmultをtempに代入してwhileループに入る 13:10,5,2のいずれかで割れる間にtempを割っていってcountとten,five,twoをインクリメントさせる 14:10,5,2それぞれの割れた回数をログ出力 15:2,5で割れた回数が少ない方と10で割れた回数を合計してzeroに格納する 16:0が付く個数をログ表示 17:factをスプレッドシートに表示する二次元配列fact2を宣言する 18:fact2にfactを二次元配列になるように追加する 19:zeroをスプレッドシートに表示する二次元配列zero2を宣言する 20:zero2にzeroを二次元配列になるように追加する 21:スプレッドシートに出力前のfact2とzero2をログ出力して確認する 22:スプレッドシートの灰色の所に100!と、黄色い所に0の個数をそれぞれ二次元配列として出力する
手順1: スプレッドシートからアクティブシートをアクセスする
const ss=SpreadsheetApp.getActiveSheet();
ここで定数ssにSpreadsheetAppから階層を辿ってアクティブシートにアクセスしています。
手順2:階乗を求める整数N(この例の場合は100)を取得する
const N=ss.getRange(1,2).getValue();
手順3:階乗の計算結果factを1で初期化する
let fact=1;
手順4:100×99×98×97・・・4×3×2×1の掛けていく数を格納するmultを宣言する
let mult;
手順5:10で割れる回数tenを0で初期化する
let ten=0;
手順6:5で割れる回数fiveを0で初期化する
let five=0;
手順7:2で割れる回数twoを0で初期化する
let two=0;
手順8:末尾に付く0の個数zeroを0で初期化する
let zero=0;
手順9:multが10,5,2それぞれで何回割れるのか数えて変数countに格納するので0で初期化する
let count;
手順10:multを代入する変数tempを宣言する
let temp;
手順11から13はループです。 まず、forループで100!を求めます。 100!はとてつもなく大きい数になるので、whileループを使って、末尾の0の数を「掛ける数mult」から求めます。
multの値が変わらないように、multをtempに代入して、10または5または2で割れる間割り算をして行き、それぞれの割れる回数ten,five,twoをインクリメントさせます。
手順11:forループで100!を計算する 手順12:階乗の計算に用いたmultをtempに代入してwhileループに入る 手順13:10,5,2のいずれかで割れる間にtempを割っていってcountとten,five,twoをインクリメントさせる
下の図は、fact=1にまず初めに100を掛ける所です。 forループで階乗の計算をしながら、multを使って10で何回割れるのか求める為に、変数tempを利用しています。
この、for,while部分のコードです。
console. log ( '<<<forループに入る>>>' ) ;
for ( let i = 0 ; i < N ; i++ ) {
console. log ( `計算前のfact: ${ fact} ` ) ;
count = 0 ;
mult = N - i;
console. log ( `mult=(N-i):【 ${ mult} 】` ) ;
console. log ( `fact: ${ fact} *「 ${ mult} 」を計算` ) ;
fact *= mult;
console. log ( `計算後のfact:【 ${ fact} 】` ) ;
temp = mult;
while ( temp % 10 == 0 || temp % 5 == 0 || temp % 2 == 0 ) {
console. log ( '<<<whileループに入りました。>>>' ) ;
if ( temp % 10 == 0 ) {
temp /= 10
count++ ;
ten++ ;
console. log ( `mult: ${ mult} の時、10で ${ count} 回割れました。` ) ;
} else if ( temp % 5 == 0 ) {
temp /= 5
count++ ;
five++ ;
console. log ( `mult: ${ mult} の時、5で ${ count} 回割れました。` ) ;
} else if ( temp % 2 == 0 ) {
temp /= 2
count++ ;
two++ ;
console. log ( `mult: ${ mult} の時、2で ${ count} 回割れました。` ) ;
}
console. log ( '<<<whileループを抜けました。>>>' ) ;
}
console. log ( `現在の計算結果fact: ${ fact} ` ) ;
console. log ( '----------------------------' ) ;
}
console. log ( '<<<forループを抜けました。>>>' ) ;
手順14:10,5,2それぞれの割れた回数をログ出力
console. log ( `multが10で割れた回数: ${ ten} 回` ) ;
console. log ( `multが5で割れた回数: ${ five} 回` ) ;
console. log ( `multが2で割れた回数: ${ two} 回` ) ;
手順15:2,5で割れた回数が少ない方(two または five)と10で割れた回数(ten)を合計してzeroに格納する。階乗の末尾に何個0が付くかは、「10で割れた回数」と「2×5で割れた回数」の合計になります。 なので、2×5のペアを作るため、twoとfiveの少ない方をtenに足した回数がzeroになります。
if ( two < five) {
zero = ten + two
console. log ( `5で割れた回数よりも2で割れた回数の方が少ないので、\n0が付く個数はzero=ten: ${ ten} +two: ${ two} = ${ zero} です。` ) ;
} else {
zero = ten + five;
console. log ( `2で割れた回数よりも5で割れた回数の方が少ないので、\n0が付く個数はzero=ten: ${ ten} +five: ${ five} = ${ zero} です。` ) ;
}
手順16:0が付く個数をログ表示
console.log(`0は${zero}個付きます。`);
手順17:factをスプレッドシートに表示する二次元配列fact2を宣言する
let fact2=[];
手順18:fact2にfactを二次元配列になるように追加する
fact2.push([fact]);
手順19:zeroをスプレッドシートに表示する二次元配列zero2を宣言する
let zero2=[];
手順20:zero2にzeroを二次元配列になるように追加する
zero2.push([zero]);
手順21:スプレッドシートに出力前のfact2とzero2をログ出力して確認する
console.log(fact2); console.log(zero2);
手順22:スプレッドシートの灰色の所に100!と、黄色い所に0の個数をそれぞれ二次元配列として出力する
ss.getRange(2,2).setValue(fact); ss.getRange(3,2).setValue(zero2);
実行後のスプレッドシートです。
GASでの全コードはこちらになります。
function loop2no15 ( ) {
const ss = SpreadsheetApp. getActiveSheet ( ) ;
const N = ss. getRange ( 1 , 2 ) . getValue ( ) ;
let fact = 1 ;
let mult;
let ten = 0 ;
let five = 0 ;
let two = 0 ;
let zero = 0 ;
let count;
let temp;
console. log ( '<<<forループに入る>>>' ) ;
for ( let i = 0 ; i < N ; i++ ) {
console. log ( `計算前のfact: ${ fact} ` ) ;
count = 0 ;
mult = N - i;
console. log ( `mult=(N-i):【 ${ mult} 】` ) ;
console. log ( `fact: ${ fact} *「 ${ mult} 」を計算` ) ;
fact *= mult;
console. log ( `計算後のfact:【 ${ fact} 】` ) ;
temp = mult;
while ( temp % 10 == 0 || temp % 5 == 0 || temp % 2 == 0 ) {
console. log ( '<<<whileループに入りました。>>>' ) ;
if ( temp % 10 == 0 ) {
temp /= 10
count++ ;
ten++ ;
console. log ( `mult: ${ mult} の時、10で ${ count} 回割れました。` ) ;
} else if ( temp % 5 == 0 ) {
temp /= 5
count++ ;
five++ ;
console. log ( `mult: ${ mult} の時、5で ${ count} 回割れました。` ) ;
} else if ( temp % 2 == 0 ) {
temp /= 2
count++ ;
two++ ;
console. log ( `mult: ${ mult} の時、2で ${ count} 回割れました。` ) ;
}
console. log ( '<<<whileループを抜けました。>>>' ) ;
}
console. log ( `現在の計算結果fact: ${ fact} ` ) ;
console. log ( '----------------------------' ) ;
}
console. log ( '<<<forループを抜けました。>>>' ) ;
console. log ( `multが10で割れた回数: ${ ten} 回` ) ;
console. log ( `multが5で割れた回数: ${ five} 回` ) ;
console. log ( `multが2で割れた回数: ${ two} 回` ) ;
if ( two < five) {
zero = ten + two
console. log ( `5で割れた回数よりも2で割れた回数の方が少ないので、\n0が付く個数はzero=ten: ${ ten} +two: ${ two} = ${ zero} です。` ) ;
} else {
zero = ten + five;
console. log ( `2で割れた回数よりも5で割れた回数の方が少ないので、\n0が付く個数はzero=ten: ${ ten} +five: ${ five} = ${ zero} です。` ) ;
}
console. log ( `0は ${ zero} 個付きます。` ) ;
let fact2 = [ ] ;
fact2. push ( [ fact] ) ;
let zero2 = [ ] ;
zero2. push ( [ zero] ) ;
console. log ( fact2) ;
console. log ( zero2) ;
ss. getRange ( 2 , 2 ) . setValue ( fact) ;
ss. getRange ( 3 , 2 ) . setValue ( zero2) ;
}
宜しかったらコピペしてアレンジして見て下さい。 お疲れ様でした、ブレイクタイムフォトはこちらになります。
代々木公園で撮影した紫陽花
■ 参考文献の紹介■
じっくり丁寧にPythonを学びたい方向け。 まずはpaizaラーニング などの学習コンテンツで学んで、基礎をマスターしたら、この本でじっくりと初級から中級レベルを目指せます。
初めてGASを学ぶ方向け。 スプレッドシートの基本的な使い方からGASのベースとなるJavaScriptの基礎文法、GASでの初歩的なプログラミングを学べます。
GASに少し慣れて来たら、基礎固めとリファレンスとしてこの本でじっくり学べます。
サイトマップはこちらから
paizaレベルアップ問題集でPythonとGASを解いて見たに戻る メインメニューに戻る Python自作サンプル GASサンプル 基本情報技術者試験
←前の問題へ 次の問題へ→