基本情報 科目B ビット反転の例題 ― シフトと論理積で「並びを逆にする」
8ビットの並びを逆にするプログラムの空欄を、オリジナルの例題で解説します。シフト(<<・>>)と論理積(∧)・論理和(∨)の役割を、1周ずつ動きを追えるツールで確かめられます。
基本情報技術者試験の科目Bでは、ビット演算のプログラムがよく出ます。記号が多くて身構えてしまいますが、やっていることは「1つずつ取り出して、1つずつ積む」だけ、ということがよくあります。この記事では、8ビットの並びを逆にするプログラムを、オリジナルの例題で解いてみます。
例題
関数 reverse は、8ビット型の引数 x のビットの並びを左右逆にした値を返す。たとえば reverse(00110101) の戻り値は 10101100 である。空欄に入る正しいものを選べ。
演算子 ∧ はビットごとの論理積、∨ はビットごとの論理和、<< は論理左シフト、>> は論理右シフトを表す。
○8ビット型: reverse(8ビット型: x)
8ビット型: b ← x
8ビット型: r ← 00000000
整数型: i
for (i を 1 から 8 まで 1 ずつ増やす)
[ 空欄 ]
endfor
return r| 空欄に入れる2行 | |
|---|---|
| ア | r ← (r << 1) ∨ (b ∧ 00000001)b ← b >> 1 |
| イ | r ← (r >> 1) ∨ (b ∧ 00000001)b ← b << 1 |
| ウ | r ← (r << 1) ∨ (b ∧ 10000000)b ← b >> 1 |
| エ | r ← r ∨ (b ∧ 00000001)b ← b >> 1 |
答えを見る
正解はアです。理由は下で順番に説明します。
考え方:トランプの山を1枚ずつ移す
机にトランプの山が2つあるところを想像してください。左の山 b のいちばん上(右端)から1枚取って、右の山 r に積む。これを8回くり返すと、最初に上にあったカードがいちばん下に行き、並びがちょうど逆になります。
プログラムでは、この「1枚取る」「積む」「次のカードを上に出す」を、3つの演算で書いています。
| やりたいこと | 書き方 | 意味 |
|---|---|---|
| b の右端を1つ取り出す | b ∧ 00000001 | 右端以外を0で消す(マスク)。結果は 0 か 1 |
| r に場所を空けて積む | (r << 1) ∨ … | r を左へ1つずらし、空いた右端に取り出した1ビットを入れる |
| b の次のビットを右端へ出す | b ← b >> 1 | 使い終わった右端を捨てる |
b の右端、積む先は r の右端。r を左へ押し出しながら積むので、先に入れたビットほど左(上の桁)へ移っていきます。1周ずつ動かしてみる
8ビットを入れて「1周すすめる」を押すと、ループ1回ごとの b と r が表に増えていきます。色のついたビットが、その周で移ったビットです。
| i | 取り出した bit (b ∧ 00000001) | r(積んだあと) | b(>> 1 のあと) |
|---|
ほかの選択肢がちがう理由
- イ:シフトの向きが逆です。
bを左へずらすと右端が0で埋まるので、2周目からは0しか取り出せません。rも右へずらすと、積んだビットが右から落ちて消えていきます。 - ウ:取り出す場所がちがいます。
b ∧ 10000000は左端を見ていて、結果は 0 か 10000000 になります。これをrに入れると、毎回いちばん左の桁を上書きするだけで、並びは逆になりません。 - エ:
rをずらしていません。毎回同じ右端の1か所に重ねるだけなので、結果は 00000000 か 00000001 にしかなりません。
どの選択肢も、例の 00110101 で1〜2周だけ試すと見分けられます。上のツールで、入力を変えて確かめてみてください。
同じことを Python で
擬似言語の ∧ は Python の &、∨ は | です。Python の整数は8ビットで止まらないので、& 0xFF で下8ビットだけ残しています。
def reverse(x):
b = x
r = 0
for _ in range(8):
r = ((r << 1) | (b & 0b00000001)) & 0xFF # r を左へ1つずらし、b の右端を入れる
b = b >> 1 # b の右端を捨てて、次のビットを右端へ
return r
print(format(reverse(0b00110101), '08b')) # 10101100ビット演算の問題で使えるコツ
- ∧(論理積)は「取り出す・消す」:1のところだけ残る。
∧ 00000001で右端だけ見る、のようにマスクとして使う。 - ∨(論理和)は「入れる・立てる」:空いている桁にビットを入れるときに使う。
- シフトは「動かす」:左シフト1回は2倍、右シフト1回は2で割る(余りは捨てる)のと同じ。
- 具体的な値で2周だけ書く:全部追わなくても、まちがいの選択肢はたいてい1〜2周で崩れます。
記号に慣れると、ビット演算は「小さな部品の組み合わせ」に見えてきます。最初はゆっくりで大丈夫です。
この記事の用語
言葉を選ぶと「☆ 単語を保存」が出ます。ここで保存した言葉は、単語帳の「用語」に入ります。
- 論理積(AND):両方1のときだけ1
- 論理和(OR):どちらかが1なら1
- 論理シフト:ビットを左右にずらし、空いた桁を0で埋める
- マスク:論理積で、必要な桁だけを取り出すための値
- トレース:変数の値を1行ずつ追って、プログラムの動きを確かめること