Monaca Educationで大学入学共通テストの試作問題に取り組む~釣り銭が少ない上手な払い方

先日(2022年11月)、大学入試センターから、共通テストの試作問題が公開されました。

令和7年度試験の問題作成の方向性,試作問題等|大学入試センター

この記事では、試作問題から、プログラミングの設問について取り上げます。問題の考え方を確認した後、PythonとJavaScriptによるプログラム例を紹介します。

※本記事の公開時、「サンプル問題」と表記していましたが、正しくは「試作問題」でした。お詫びして訂正いたします。

問題「上手な払い方」

11月に発表された試作問題では、第3問がプログラミングを扱う問題です。

問題の冒頭で、「お金を払う時に、釣り銭が少ない上手な払い方をする」というテーマが提示されます。続く対話の中で、「支払う硬貨の枚数と、受け取る釣り銭の枚数の合計が少ない払い方」を上手な払い方と定義し直し、以後はその払い方を実現するプログラムを考えていく流れになっています。

「指定された金額を、少ない枚数の硬貨で支払う」という問題はオーソドックスなもので、貪欲法(グリーディ法)というアルゴリズムで解決できます。「ぴったりの額を少ない硬貨の枚数で支払う」で終わらず、釣り銭が発生してもよい設定にして、釣り銭を払うときの硬貨の枚数を考えさせているのが、この問題の面白いところです。

問題の考え方
 ぴったりの金額を払うより、余分に払う方が、合計の枚数が少なくなることがあることを、問題文に示されている例をきちんと読み取って理解できるかどうか。これが、この問題を考える上での最初のポイントと思われます。

46円ぴったりの場合

支払い51円+釣り銭5円の場合

2つ目のポイントは、支払うときの硬貨の最小枚数を求める計算と、釣り銭の硬貨の最小枚数を求める計算は、同じ計算で、指定する金額が違うだけであることに気付けるかどうか、ではないでしょうか。

これら2つのポイントに気づくことができれば、あとはプログラムの穴を埋めていくことになります。問題文を丁寧に読み解くことが、まずは大切だと言えそうです。

Pythonによる解答例

問題の図1のプログラムを、Pythonで書き直したのが、次の例です。


import math
 
Kouka = [1,5,10,50,100]
kingaku = 46
maisu = 0
nokori = kingaku
for i in range(4,-1,-1):
    maisu = maisu + math.floor( nokori/Kouka[i] )
    nokori = nokori % Kouka[i]
print(maisu)

Monaca Educationにインポートする(要ログイン)

 Pythonでは、割り算をするのに算術演算子/を使います。/による割り算は、小数点以下の値まで計算するので、整数部分だけ取り出すために組み込みライブラリの関数math.floor()を使っています。
 また、「4から1ずつ減らしながら0まで」の繰り返し構造を、range(4,-1,-1)を使って実現しています。このrangeによって「第1引数(例では4)から始まり、第2引数(例では-1)より大きい間、第3引数(例では-1)ずつ足していく範囲」のデータが作られ、変数iに順次代入されます。

 図2のプログラムをPythonで書き直すと、次のようになります。試作問題では関数名を日本語で「枚数」として宣言していますが、ローマ字でMaisuとしています。


import math
 
def Maisu(kingaku):
    Kouka = [1,5,10,50,100]
    maisu = 0
    nokori = kingaku
    for i in range(4,-1,-1):
        maisu = maisu + math.floor( nokori/Kouka[i] )
        nokori = nokori % Kouka[i]
    return maisu
 
kakaku = 46
min_maisu = 100
for tsuri in range(0,100):
    shiharai = kakaku + tsuri
    maisu = Maisu(shiharai) + Maisu(tsuri)
    if maisu < min_maisu:
        min_maisu = maisu
print(min_maisu)

Monaca Educationにインポートする(要ログイン)

 プログラムの本体では、最小の枚数(min_maisu)を、暫定の値(100)に設定して始めています。繰り返しの制御構造を使い、「釣り銭の額が0円から99円の範囲で、1円ずつ増やしながら枚数を調べ、より小さい枚数の値が現れたら最小の枚数(min_maisu)を置き換える」という方法で、最も上手な払い方の枚数を見つけています。

JavaScriptによる解答例

 問題の図1のプログラムを、JavaScriptで書き直したのが、次の例です。


let Kouka = [1,5,10,50,100]
let kingaku = 46
let maisu = 0, nokori = kingaku
for(let i=4; i>=0; i-- ){
    maisu = maisu + Math.floor( nokori / Kouka[i] )
    nokori = nokori % Kouka[i]
}
document.write( maisu );

Monaca Educationにインポートする(要ログイン)

 JavaScriptでは、割り算をするのに算術演算子/を使います。/による割り算は、小数点以下の値まで計算するので、整数部分だけ取り出すために、組み込みライブラリの関数Math.floor()を使っています。

 図2のプログラムをJavaScriptで書き直すと、次のようになります。試作問題では関数名を日本語で「枚数」として宣言していますが、ローマ字でMaisuとしています。


function Maisu(kingaku){
    let Kouka = [1,5,10,50,100]
    let maisu = 0, nokori = kingaku
    for(let i=4; i>=0; i-- ){
        maisu = maisu + Math.floor( nokori / Kouka[i] )
        nokori = nokori % Kouka[i]
    }
    return maisu;
}
 
let kakaku = 55
let min_maisu = 100
for(let tsuri=0; tsuri <= 99; tsuri++){
    let shiharai = kakaku + tsuri;
    let maisu = Maisu(shiharai) + Maisu(tsuri);
    if ( maisu < min_maisu ){
        min_maisu = maisu;
    }
}
document.write(min_maisu);

Monaca Educationにインポートする(要ログイン)

 プログラムの本体では、最小の枚数(min_maisu)を、暫定の値(100)に設定して始めています。繰り返しの制御構造を使い、「釣り銭の額が0円から99円の範囲で、1円ずつ増やしながら枚数を調べ、より小さい枚数の値が現れたら最小の枚数(min_maisu)を置き換える」という方法で、最も上手な払い方の枚数を見つけています。

プログラムの構造と、共通テストに向けた対策について

 あらためて、完成したプログラムをみると、配列・繰り返し・条件分岐・関数(定義と呼び出し)の組み合わせになっていることが分かります。Pythonでも、JavaScriptでも、大きな差が生じる内容ではありません。

 「PythonやJavaScriptなど、教科書で使っているプログラム言語で、教科書に記載されている概念・機能を、1つ1つ使いこなせるようになること」が、共通テストのプログラム分野の対策として、結局は一番の近道なのではないでしょうか。

まとめ
 「プログラムでどんな問題を解決しようとしているか」、問題文の冒頭で示されています。続く会話のやりとりをきちんと読んで、どのような方式・手順で解決しようとしているか、理解することが第一歩です。逆に、いきなりDNCLで書かれたプログラムだけを見て、穴埋めをしようとするのは難しいように思います。
 JavaScriptやPythonを使って、同等のプログラムを書き、実行する学習をすることで、プログラミング問題の対策を進めることができると考えます。

 なお、今回の記事で使ったコインの画像は、あんこエデュケーションの素材集のものを使いました。

コイン

 あんこエデュケーションで配布している素材のライセンスは『クリエイティブ・コモンズのCC BY-NC 4.0』に設定しています。作品作りに是非ご活用ください。