🐥

【基本情報技術者】B試験(新形式)サンプル問題16が難し過ぎたので、自分で解説を作った。

2023/03/20に公開

はじめに

こんにちは、基本情報技術者試験を受験するyouです。
試験対策として、公式が公開しているB試験のサンプル問題を解いています。

個人的な感想としては、全体的に旧形式よりも明らかに難易度が下がっているように感じます。
しかし、最後のアルゴリズム問題だけは異常に難しかったため、自分で解説を作ることにしました。

また、サンプル問題は下記リンクから確認出来ます。
https://www.jitec.ipa.go.jp/1_00topic/topic_20221226.html

B試験 サンプル問題16



めちゃくちゃ難しくないですか?笑
実際の試験でも難しい問題が一問出ることが予想されますね...。
もし難しい問題が出てきたら、最後に解くのが良さそうです。

解説

何をしたいプログラムなのかを理解する

全てのアルゴリズム問題で言えることですが、まずこのプログラムが何をしたいのかということを理解する必要があります。

問題文を読んでいくと、3段落目の下記部分が該当しそうです。

3 バイトの長さのビットパターンを 1110xxxx 10xxxxxx 10xxxxxx とする。ビット
パターンの下線の付いた“x”の箇所に,符号位置を 2 進数で表した値を右詰めで格
納し,余った“x”の箇所に, 0を格納する。この 3 バイトの値が UTF-8 の符号であ
る。

うん、よくわからないです...。
これがUnicodeの符号位置をUTF-8の符号に変換するプログラムということだそうです。

でもこのままだとプログラムのイメージが湧きませんので、とりあえず更に読み進めていきましょう。
すると、4段落目に下記のような例を書いてくれています。

例えば,ひらがなの“あ”の符号位置である 3042(16) を 2 進数で表すと
11000001000010 である。これを,上に示したビットパターンの“x”の箇所に右詰め
で格納すると,1110xx11 10000001 10000010 となる。余った二つの“x”の箇所に 0
を格納すると,“あ”の UTF-8 の符号 11100011 10000001 10000010 が得られる。
関数 encode は,引数で渡された Unicode の符号位置を UTF-8 の符号に変換し,先
頭から順に 1 バイトずつ要素に格納した整数型の配列を返す。encode には,引数と
して,800(16) 以上 FFFF(16) 以下の整数値だけが渡されるものとする。

なるほど...!
このプログラムはある符号位置である16進数を2進数で表し、その2進数を3バイトの長さのビットパターン1110xxxx, 10xxxxxx, 10xxxxxxのx部分に右詰めで格納(余ったxには0)していくことで、UTF-8の符号を得るということがわかりましたね。

以上の内容から何をしたいプログラムなのかが理解出来たと思うので、実際に擬似言語を読み解いて行きましょう。

擬似言語の解読

問題文が上の方に行ってしまったので、擬似言語プログラムを下記に示します。

○整数型の配列: encode(整数型: codePoint)
/* utf8Bytesの初期値は,ビットパターンの“x”を全て0に置き換え,
 8桁ごとに区切って,それぞれを2進数とみなしたときの値 */
 整数型の配列: utf8Bytes ← {224, 128, 128}
整数型: cp ← codePoint
 整数型: i
 for (i を utf8Bytesの要素数 から 1 まで 1 ずつ減らす)
 utf8Bytes[i] ← utf8Bytes[i] + (cp ÷ [選択肢]の余り)
 cp ← cp ÷ [選択肢]の商
 endfor
 return utf8Bytes

この解読がこれまた難しい。お察し能力が必要になります笑

解読する上でめちゃくちゃ重要なのが、下記の一番最初にあるコメントアウトの文章です。

/* utf8Bytesの初期値は,ビットパターンの“x”を全て0に置き換え,
 8桁ごとに区切って,それぞれを2進数とみなしたときの値 */

どういうことかと言いますと、このコメントアウトの次行で宣言されている整数型の配列: utf8Bytesの初期値が下記のように全て0の8ビットのデータということになります。

utf8Bytes[i] = [00000000, 00000000, 00000000, ...];

私はこの前提を理解していなかったため、間違ってしまいました笑
焦りは禁物です。こういうときこそ冷静にやらないといけませんね...。

これを踏まえて上で、読み解いていきます。

整数型の配列: utf8Bytes ← {224, 128, 128}

ここで、「あれ?2進数じゃなくない?」となるのですが、問題文の最初に下記のような記載があったと思います。

本問で数値の後ろに“(16)”と記載した場合は,その数値が 16 進数であることを表す。

つまり、utf8Bytes ← {224, 128, 128}の224、128、128は10進数で、utf8Bytesには2進数に変換してから格納するということになります。

よって、utf8Bytesの中身は下記のようになります。

utf8Bytes[1] = 11100000;
utf8Bytes[2] = 10000000;
utf8Bytes[3] = 10000000;

問題文にあった3バイトの長さのビットパターンと一致してそうですね。
問題無さそうなので、更に読み解いて行きます。

整数型: cp ← codePoint

codePointとは、このプログラムである関数encodeの引数になりますね。
つまり、Unicodeの符号位置になるので、今回は例でも使われていたひらがなの“あ”の符号位置である 3042(16)を引数として取ってあげようと思います。

整数型: cp ← 3042(16)

一回ここまでの流れを整理しましょう。現在は、下記のようなプログラムになっています。

○整数型の配列: encode(整数型: 3042(16))
/* utf8Bytesの初期値は,ビットパターンの“x”を全て0に置き換え,
 8桁ごとに区切って,それぞれを2進数とみなしたときの値 */
 整数型の配列: utf8Bytes ← {11100000, 10000000, 10000000}
整数型: cp ← 3042(16)
 整数型: i
 for (i を utf8Bytesの要素数 から 1 まで 1 ずつ減らす)
 utf8Bytes[i] ← utf8Bytes[i] + (cp ÷ [選択肢]の余り)
 cp ← cp ÷ [選択肢]の商
 endfor
 return utf8Bytes

それでは、for文の中身に入って行きます。
i を utf8Bytesの要素数 から 1 まで 1 ずつ減らすと書かれているので、iは3から始まって、1になるまで繰り返すということになりますね。

それでは中身の処理に入って行きたいのですが、ここで1つ注意点があります。
現在、cpの値が16進数になっていると思いますが、問題文に「ある符号位置である16進数を2進数で表し」と記載があるので、2進数に変換する必要があります。

3042(16)を2進数に直すと、問題文の例から11000001000010になります。

以上を踏まえて、i=3の場合を確認して行きましょう。

 for (i を utf8Bytesの要素数 から 1 まで 1 ずつ減らす)
 utf8Bytes[3] ← 10000000 + (11000001000010 ÷ [選択肢]の余り)
 cp ← cp ÷ [選択肢]の商
 endfor
 return utf8Bytes

遂に選択肢を選ぶところまで来ましたね。

ここでもう一度、何をしたいプログラムなのかを再確認しましょう。
このプログラムはある符号位置である16進数を2進数で表し、その2進数を3バイトの長さのビットパターン1110xxxx, 10xxxxxx, 10xxxxxxのx部分に右詰めで格納(余ったxには0)していくことで、UTF-8の符号を得るということでしたね。

つまり、cp下位6ビット、6ビット、4ビットずつ欲しいということになります。

よって、cp2の6乗で割れば良いので、答えは選択肢ということになります。

「何故2の6乗ってわかったの?」と思う方もいらっしゃるかと思いますが、10進数の話で考えてみると非常に理解し易くなります。
例えば、1250という10進数10の3乗で割ると余りは下位3桁の250になります。
2進数でも同じことで、2の欲しい下位の桁数分の乗で割れば良い訳です。
※ 右にシフトするとも言う(1250/10の3乗 = 1.250の小数点以下から3回右にシフトしていると考える)

おわりに

如何でしたでしょうか!本当にこの問題だけ異様に難易度が高かったです...笑
本番で難しい問題が出たら、一回飛ばして余った時間で冷静に解くのが得策そうですね!
もう試験間近ですが、最後まで気を抜かず仕上げて行きたいと思います!

それでは良いエンジニアライフを。

Discussion