LoginSignup
4
1

More than 3 years have passed since last update.

初心者向け(小学生の算数で解くプログラミング)

Last updated at Posted at 2019-08-23

はじめに

先日、twitterでみかけた「任意の自然数の各桁を、一桁になるまで掛け算する回数の最大回数とその数を示せ。」という課題をお子様がpythonで解いたというツイートに触発されて、僕も子供にプログラミングを教えたいと思って作ったものです。
小学生の算数の知識と少しのプログラミングの知識でできるようにしています。
いまだに、夏休みの自由研究が終わっていないお子様にいかがでしょうか。
なお、GAS(JavaScript)を使用しています。

必要な知識

四則演算(+-*/)、小数点以下切り下げ(Math.Floor())
変数(小学生が対象のためローマ字で書いています)
whileによる繰り返し
forによる繰り返し
ifによる条件分岐
(最後に少しだけ配列を使います)

1.二桁(10〜99)の計算

冒頭でも書きましたが、「任意の自然数の各桁を、一桁になるまで掛け算する回数の最大回数とその数を示せ。」という問いに対し、二桁の場合の掛け算する回数の最大回数とその数はいくつでしょうか。
スクリーンショット 2019-08-21 22.28.38.png

これは、時間があれば自分で計算して解いてほしいと思います。いろいろな規則性が見えてくるかもしれません。

回答

スクリーンショット 2019-08-21 22.38.53.png
答えは77(7*7⇛4*9⇛3*6⇛1*8⇛8)で4回です。

2.複数桁の計算(各桁の数字の抽出方法)

三桁からはどうやら時間がかかりそうなのでプログラムを利用します。
各桁の数字を抽出するにはどのようにすればよいでしょうか。
四則演算と小数点以下切り下げを利用します。
例として「679」を分解する方法を考えます。

やり方としては単純に10で割って小数点以下を切り下げてもとの数字から引くだけです。
スクリーンショット 2019-08-21 23.38.25.png

いろいろな数字が使いまわせそうです。これをプログラムにしてみます。

function threeDigits() {
  var kazu = 679
  var warizan

  warizan = Math.floor(kazu/10)       //679÷10=67.9⇛⇛⇛67(小数点以下切り下げ)
  var ichi = kazu - warizan * 10      //679 − 67×10=9(一の位の数)
  Logger.log(ichi)

  kazu = warizan
  warizan = Math.floor(kazu/10)       //67÷10=6.7⇛⇛⇛6(小数点以下切り下げ)
  var zyuu = kazu - warizan * 10      //67−6×10=7(十の位の数)
  Logger.log(zyuu)                    

  var hyaku = warizan                 //6(百の位の数)
  Logger.log(hyaku)                    

  Logger.log(ichi * zyuu * hyaku)     //一の位の数×十の位の数×百の位の数=378
}

しかし、これは三桁の数の計算しかできませんので、使い物になりません。いろいろな桁数に対応できるように「whileによる繰り返し」を使ってみます。

function multiDigits() {
  var kazu = 679                       //最初の数
  var kotae = 1                        //各桁をかけ合わせた数、初期値は1

  do{
    var warizan = Math.floor(kazu/10)  //10で割って小数点以下を切り下げ
    var kakuketa = kazu - warizan * 10 //各桁の数字
    kotae = kotae * kakuketa           //各桁をかけ合わせた数
    kazu = warizan                     //一桁少なくして再計算
  }while(kazu>0)                       //最終的に数が0になるまで繰り返し

  Logger.log(kotae)
}

ずいぶんすっきりしました。これで何桁でも計算できます。

3.結果が一桁になるまで掛け算する

問題は一桁になるまで掛け算する回数の最大回数とその数を示すことでしたが、これでは1回しか計算できないので、1桁になるまで繰り返すように計算します。これも同様に「whileによる繰り返し」を利用します。

function calcMultiDigits() {
  var kazu = 679                         //最初の数
  var kaisuu = 0                         //計算回数
  do{
    var kotae = 1                        //各桁をかけ合わせた数
    do{
      var warizan = Math.floor(kazu/10)  //10で割って小数点以下を切り下げ
      var kakuketa = kazu - warizan * 10 //各桁の数字
      kotae = kotae * kakuketa           //各桁をかけ合わせた数
      kazu = warizan                     //一桁少なくして再計算
    }while(kazu>0)                       //最終的に数が0になるまで繰り返し
      Logger.log(kotae)
      kaisuu = kaisuu + 1                //1回計算するごとに回数の変数を増やしていく
      kazu = kotae                       //求めた答えをもとに再計算
  }while(kotae>=10)                      //一桁になるまで繰り返す
  Logger.log(kaisuu + '回計算')
}

/*結果
378.0
168.0
48.0
32.0
6.0
5回計算
*/

これで理論上は何桁でも計算できるようになりました。

4.繰り返し計算をする

これでひたすら繰り返し計算する土台ができました。あとは「forによる繰り返し」でひたすらコンピュータで計算するだけです。ためしに100までやってみます。

function calcMultiDigitsWithItr() {

  for(kurikaeshi = 10;kurikaeshi<100;kurikaeshi++){      //10から99まで繰り返し
    var kazu = kurikaeshi                  
    var kaisuu = 0                         //計算回数
    do{
      var kotae = 1                        //各桁をかけ合わせた数
      do{
        var warizan = Math.floor(kazu/10)  //10で割って小数点以下を切り下げ
        var kakuketa = kazu - warizan * 10 //各桁の数字
        kotae = kotae * kakuketa           //各桁をかけ合わせた数
        kazu = warizan                     //一桁少なくして再計算
      }while(kazu>0)                       //最終的に数が0になるまで繰り返し
        kaisuu = kaisuu + 1                //1回計算するごとに回数の変数を増やしていく
        kazu = kotae                       //求めた答えをもとに再計算
    }while(kotae>=10)                      //一桁になるまで繰り返す
      Logger.log(kurikaeshi + '----->' + kaisuu + '回計算')
      }
}
/*結果
75----->3回計算
76----->2回計算
77----->4回計算
78----->3回計算
79----->3回計算
*/

いい感じです。では、最大の計算回数とその時の数字、あとはタイマーを追加します。

function calcMultiDigitsWithTimer() {
  var saidaiKaisuu = 0                               //最大の計算回数
  var saidaiSuu                                      //そのときの数
  var zikan = new Date()                             //時間計測用
  for(kurikaeshi = 10;kurikaeshi<3000000;kurikaeshi++){     //10から2999999まで繰り返し
    var kazu = kurikaeshi                  
    var kaisuu = 0                         //計算回数
    do{
      var kotae = 1                        //各桁をかけ合わせた数
      do{
        var warizan = Math.floor(kazu/10)  //10で割って小数点以下を切り下げ
        var kakuketa = kazu - warizan * 10 //各桁の数字
        kotae = kotae * kakuketa           //各桁をかけ合わせた数
        kazu = warizan                     //一桁少なくして再計算
      }while(kazu>0)                       //最終的に数が0になるまで繰り返し
        kaisuu = kaisuu + 1                //1回計算するごとに回数の変数を増やしていく
        kazu = kotae                       //求めた答えをもとに再計算
    }while(kotae>=10)                      //一桁になるまで繰り返す
      if(kaisuu > saidaiKaisuu){           //もし回数が最大回数より大きければ
        saidaiKaisuu = kaisuu              //最大回数を更新
        saidaiSuu = kurikaeshi             //その時の数を記録
      }
      }
  Logger.log(saidaiSuu + 'のときに' + saidaiKaisuu + '')
  Logger.log((new Date() - zikan)/1000 + '')
}
/*結果
2677889のときに8回
244.07秒
*/

ためしに2999999まで計算してみました。4分くらいかかりましたがいい結果が出ました。

5.もっと処理を速くしたい(配列の利用)

さらに計算の回数を少なくして処理時間を速くしてみます。
各桁を掛け算した結果はもとの自然数よりかならず少ない数になります。少ない数から始めて、結果をメモしていけば、過去の結果を利用することができます。
スクリーンショット_2019-08-21_22_38_53.jpg
例として77を考えます。77の計算は7*7=49です。これまでの計算ではさらに49を4*9=36、36を3*6…としましたが、最初から49の結果は3と判明しているため過去の結果をメモしていれば再計算の必要はありません。つまり、過去の結果をメモしていれば上記「3.結果が一桁になるまで掛け算する」をする必要はなく、過去の記録+1回が答えです。これをプログラムにしてみます。

function calcMultiDigitsWithDP() {
  var memo = new Array()                             //記録用配列
  for(i=0;i<10;i++){
  memo[i] = 0                                        //1桁目の答えは0でるために配列にいれておく
  }

  var saidaiKaisuu = 0                               //最大の計算回数
  var saidaiSuu                                      //そのときの数
  var zikan = new Date()                             //時間計測用
  for(kurikaeshi = 10;kurikaeshi<3000000;kurikaeshi++){    //10から2999999まで繰り返し
    var kazu = kurikaeshi                  

      var kotae = 1                        //各桁をかけ合わせた数
      do{
        var warizan = Math.floor(kazu/10)  //10で割って小数点以下を切り下げ
        var kakuketa = kazu - warizan * 10 //各桁の数字
        kotae = kotae * kakuketa           //各桁をかけ合わせた数
        kazu = warizan                     //一桁少なくして再計算
      }while(kazu>0)                       //最終的に数が0になるまで繰り返し
        var kaisuu = 1 + memo[kotae]       //計算回数 過去の記録+1回
        memo[kurikaeshi] = kaisuu          //記録を配列に入れる。
      if(kaisuu > saidaiKaisuu){           //もし回数が最大回数より大きければ
        saidaiKaisuu = kaisuu              //最大回数を更新
        saidaiSuu = kurikaeshi             //その時の数を記録
      }
      }
  Logger.log(saidaiSuu + 'のときに' + saidaiKaisuu + '')
  Logger.log((new Date() - zikan)/1000 + '')
}
/*結果
2677889のときに8回
227.922秒
*/

ちょっと速くなりました。
さらに速くする余地はまだありそうですが、とりあえずはここまでにします。

以上です。最後まで読んでいただきありがとうございました。

4
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1