CONTENTS コンテンツ
2020.03.05
【初心者向け】バイナリ腕時計との付き合い方
初めまして!こんにちは!ごっちです。
どんな記事書こうか悩んでいていくつか候補を考えていました。トピックは料理、数学、生き物などから選ぼうと思っていましたが、数ある候補の中から選んだ今回のテーマは…
「バイナリ腕時計を素早く読むコツ」です!
長くなりますが、最後まで読んでくださるとうれしいです。また、この記事ではあらゆる用語が出てきます。その部分が理解できないとしても内容は伝わると思いますが、馴染みのない読者を想定して簡単に最低限の説明は添えています。
この記事に出てくる主な用語 → 「バイナリ」「2の補数」「正規表現^,$,[],{}」「はがねのつるぎ」「not only A but also B構文」「相加相乗平均の不等式」「ラストエリクサー症候群」など。後半は特に記事には関係ありませんが。
まずはバイナリ腕時計について簡単に紹介します。もし「付けているよ!」って人がいたら教えてください。
このような腕時計です。バイナリ(2進数)については、読者の方のほとんどはご存知かと思います。
というより普段からバイナリにバリバリ触れている方も多くいらっしゃるかと思います。
2012年以降のカリキュラムでは高校でも数学Aの整数の単元でn進法の項目があるので、この概念は現役高校生にも馴染み深いのではないでしょうか。(n進法自体はボリューム的にちょこっとですが、京都大学とかは結構出題していますよね!整数単元の追加以前もよく出ていました。等比数列の単元でもお馴染みですね。あと、中学受験とかでもよくあります。)
そのため読み方や細かい説明については省略しますね。よくわからない場合は「位取り記数法」などで調べると色々なサイトが説明しています。学習用参考書などもおすすめです。
<※知らない人向けに超簡単に説明※>
私たちはモノを数えるときに0 ~ 9までの10個の数字を使い、9の次は位を増やして1を置いて、9を0に戻して、10と表現しますよね。2進数では使える数が0,1のみであり、
0,1,10,11,100,101,110,111,1000,1001,1010,1011,1100,1101,1110,1111,10000,…と続いていきます。
それぞれは、0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,…と対応しています。
0,1は電流のOFF,ONに対応しており、コンピュータが2進数により演算を行っていることは特に有名です。ノイズに強い、ブール演算を扱えるなどといったメリットがあります。
この画像の上の4桁は「時」、下の6桁は「分」を表します。ボタンを押すとLEDが光ります。光った部分は1を表し、光のないところは0を表します。
上の1011(2)なら、23 × 1 + 22 × 0 + 21 × 1 + 20 × 1 = 8 + 0 + 2 + 1 = 11
(※1011(2)は、1011を2進数として扱うことを意味しています。そのまま書くと10進数とも読めてしまいますからね。ちなみに1011は2進数だと、1111110011(2)となりますね。0b1011や、(1011)2など様々な書き方がありますが、本記事では1011(2)の方式を採用します。視覚的に見やすいことに加え、htmlタグのコピペが楽だからです。本来は10進数表記の数字にも(10)と付けたいところですが、省略した場合は10進数だと思ってください。)
となり、上は11時を表します。1増えると1100(2)となり、12時を表します。また表記上の上限は1111(2)= 15 となるため、23まで表すことはできません。1100(2)から1増えると0001(2)となり、また1時からと戻ります。すなわち、この時計は12時制の表記( 1~ 12)になります。0~23の24時制の表記をするためには10111(2)( = 23)まで使いたいので、5桁必要になってしまいますからね。
(↑時の表記について、この腕時計の仕様を勘違いしていたため修正させていただきました。申し訳ございません。)
AM・PMの区別もないため、10時か22時かわからなければ空の明るさなどで判断しろということですね。
下はどうでしょうか?
001111(2) = 23 × 1 + 22 × 1 + 21 × 1 + 20 × 1 = 8 + 4 + 2 + 1 = 15
ですが、 こんな計算いちいちしていられないですよね?
この場合は、0001(2)が1111(2)の2の補数であることを利用しましょう。
※ここで、2の補数とは桁を繰り上げるのに必要な最小の値を意味します。1111(2)はあと0001(2)を足したら、10000(2)となって繰り上がりますよね。もう少し細かい話をすると、2進数は10進数のように±を使って正負の数を表すのではなく、最上位が1ならその数を負の数として扱うこともあります。1010(2)ならば、10と読むこともできれば、-2と読むこともできます。最上位を符号(0なら+, 1なら-として扱う)のか、そのまま正の数の最上位の値として扱うかは状況次第です。人間が見たときには区別できません。しかし、どちらの場合でも表せるパターンの総数は変わりません。もし最上位が符号を表す場合、例えば101011(2)は-21を表します。この数字は010101(2)( = 21 )の2の補数となっており、011000(2) – 010101(2)( = 24 – 21 )という計算を行うときに、011000(2) + 101011(2) = 1000011(2) = 3 といった計算が可能となります。(最後が3になる理由はオーバーフローで調べると良いと思います。)このように「2の補数」を用いることで足し算のみで引き算を行うことが可能となります。今回扱う2進数は符号なしで正のもののみ(6ケタでは-32 ~ 31または0 ~ 63まで扱うことが出来ますが、今回扱える数値の範囲は後者としています。)ですが、わざと桁を溢れさせて計算しやすくするといった操作を、2の補数を用いることによって実現しています。読者の多くの方は符号ありの方が馴染み深いと思い、符号なしでは「2の補数」という表現をあまり使わないような気がした(混乱させてしまう)ので念のためと思い、追記させていただきました。この記事で登場する2進数については符号なしとして、正の数のみを扱っているとお考え下さい。
001111(2) + 000001(2) = 010000(2)ですから、
001111(2) + 1 = 16
001111(2) = 16 – 1 = 15
となり、一瞬で計算できます。これはさながら1次方程式を解く流れのようですね。青で書かれたものが既知数というわけです。
同様にして、011111(2) についても
011111(2) + 1 = 32
011111(2) = 32 – 1 = 31
とできますね。
これらは極端な例ですが、このようにバイナリは時に2の補数を考えて引き算をしたほうが計算がスムーズに出来ることが多いのです。もちろん、これは2の補数が小さい数字の場合に限って有効な方法です。
2の補数が1のときは使わない手はないと思っています。1の3回以上の繰り返しで終わっているものは絶好のチャンスです。私は2の補数が1~3の範囲に収まるならこの引き算方式を採用することが多いです。
例えば、011101(2)ならば、11101(2)の2の補数となる00011(2)( = 3 )を足して100000(2)( = 32 )として、32 – 3 = 29と計算できるわけです。(個人的にこの計算は繰り下がりがあるのでイマイチだと思っています。後述しています。)
ここまでの知識だけでも、バイナリを読むのは格段に速くなります。
桁が多くても同じことは言えますね。例えば、1111110011(2)なら、10000000000(2)( = 1024 )から、00000001101(2)( = 13 )を引けば求めやすいですよね。
ちなみに私は普段いちいち10進数に直したりしません。英語を聞いたときにいちいち日本語に直さないのと一緒です。友達とかに時間を聞かれたときに困るので、スラスラ直せるように訓練しました。
さて、本題はここからです!
まず、下3桁、もっと言うと「時」を表す0001(2)~1100(2)については暗記必須です。というより、読んでいるうちに勝手に覚えられます。大事なので、以降は赤文字で書いておきますね。0000(2)は言わずもがな0なので割愛します。
0100(2) = 4
0111(2) = 7
1010(2) = 10
0101(2) = 5
1000(2) = 8
1011(2) = 11
0110(2) = 6
1001(2) = 9
1100(2) = 12
といった具合です。時間の例でみましたが、分でも5,6桁目に00と追加されるだけなので、同じです。
さらにこのバイナリ表示をスムーズに読み解く(=10進数に変換する)ため、私は
「 HALFWAY POINT-BASED READING 」
なるものを導入しました。(めちゃくちゃカッコいい・・・)
HALFWAY POINTは、文字通りですが、中間ポイントを意味します。~ -based … は、~に基づいたという意味です。すなわち、「中間ポイントに基づく読法」ということです。読みやすい数字やキリのいい数字を中間ポイントに定めて、その中間ポイントから数字を足し引きすることで素早くバイナリを読み取るということをやっていきます。
ここでは以下の数字を中間ポイントに定めました。Navyの色を付けます。特にぱっと見でわかりやすいもの、重要なものは赤とします。
- 001100(2) (= 12)…12,13,14に使います。
- 010000(2) (= 16)…14,15,16,17,18,19に使います。
- 010100(2) (= 20)…20,21,22に使います。
- 011000(2) (= 24)…22,23,24,25,26,27に使います。ドラクエで例えると「はがねのつるぎ」の立ち位置です。
- 011100(2) (= 28)…28,29に使います。ショボい気がしますが、28,29は1が多くて間違えやすいのでこれも中間ポイントに定めておきます。優先度は低いです。
- 100000(2) (= 32)…30,31,32,33,34,35に使います。
- 100100(2) (= 36)…36,37に使います。一見無意味に見えますが、読み間違いを減らすためです。
- 101000(2) (= 40)…これ自身は40を意味しますが、個人的には一番重要です。英語で言うと「not only A but also B構文」くらい重要な中間ポイントです。38,39,40,41,42,43,45,46に使います。
- 110000(2) (= 48)…終盤で大活躍な中間ポイント。46,47,48,49,50,51,52,53に使います。数学で言うと「相加相乗平均の不等式」くらい重要です。
- 111000(2) (= 56)…FFで例えると「ラストエリクサー」。54,55,56,57,58,59などに。減るものじゃないので「ラストエリクサー症候群」のあなたにもおすすめ。余談ですが私はエリクサーすらもったいなくて使えません。
ざっとこんな感じですね。どの数字に使うかは自由なので、
例えば、101110(2)を110000(2) – 000010(2)と処理してもいいし、101000(2) + 000110(2)と処理してもいいと思います。私は後者が好みです。
例えば、100111(2)は、100000(2) + 000111(2)(32 + 7)とするよりは101000(2) – 000001(2)(40 – 1)としたほうが、スマートな印象を受けます。また、48は繰り上げりがちょっと嫌なので、個人的には^1101[01]{2}$は52 + … と新たな中間ポイントを設けてもいい気がしています。これによって、110011(2)も110100(2) – 000001(2)というオシャレな読み方が出来るようになります。
^1101[01]{2}$という表記について。^は文頭(直後の文字から始まる文)、$は文末(直前の文字で終わる文)を意味します。[01]は、[]内の任意の1文字と見ます。この場合は0または1ということです。{2}は2回の繰り返しになるので、この正規表現は以下の文字列にmatchします。
110100, 110101, 110110, 110111
バイナリの並びを文字列ととらえ、脳内でパターンマッチングをする感覚で10進数に直すコマンドに変換するので、今回は正規表現を取り入れて表記してみました。^1101[01]{2}$は52 + … とあるように「1101から始まり、0または1の2回の繰り返しで終わる文字列を感知したら、52 + ( 下2桁 )という計算を実行せよ」と脳内で処理をさせるイメージです。
では、ここからは実際にいくつかのバイナリをこの方式に則り読んでみることにします!
- 110111(2) → 111000(2) – 000001(2) = 56 – 1 = 55
- 101101(2) → 101000(2) + 000101(2) = 40 + 5 = 45 ( または 110000(2) – 000011(2) = 48 – 3 )
- 100101(2) → 100100(2) + 000001(2) = 36 + 1 = 37 ( または 101000(2) – 000011(2) = 40 – 3 )
100100(2)はここで役に立ちます。これ、100000(2) + 000101(2) でよさそうに見えますよね?0.5秒以内に計算しろって言われると、案外テンパって32 + 5 = 39とかやってしまったりするんですよ。
- 001110(2) → 010000(2) – 000010(2) = 16 – 2 = 14
または 001100(2) + 000010(2) = 12 + 2, ちょっと異端ですが 001010(2) + 000100(2) = 10 + 4
ただこれらは数字が連なって判断を誤りやすいため、個人的にはオススメできないです。
- 011101(2) → 011100(2) + 000001(2) = 28 + 1 = 29
※ちなみに28の中間ポイントがない場合 011000(2) + 000101(2) = 24 + 5 = 29
出来なくはないですし繰り上がりもないのですが、1が連なっているせいで一瞬判断に迷います。わずかなこのラグが相手をイライラさせてしまう可能性を生んでしまうというわけです。100000(2) – 000011(2) = 32 – 3 という手もありますが(前述したものです)、繰り下がりはちょっと嫌ですよね…?011110(2) = 30から000001(2) = 1を引くという手は結構ありだと思っています。
- 100111(2) → 101000(2) – 000001(2) = 40 – 1 = 39
- 011010(2) → 011000(2) + 000010(2) = 24 + 2 = 26
- 010101(2) → 010100(2) + 000001(2) = 20 + 1 = 21
もし中間ポイント16を使った場合は 010000(2) + 000101(2) = 16 + 5 = 21となりますが、繰り上がりがあまり嬉しくないです。あと、私はなぜか19と間違えてしまうことが多かったので、010100(2)を中間ポイントにすることにしました。19は010011(2)なので、見間違えをすることはないはずなんですよ。010101(2)はぱっと見、どこで区切っていいのか分かりにくいので、それも混乱の元と思っています。010111(2) ( = 23)は011000(2) ( = 24 )から引き算すればいいとしても、21,22を計算するのに中間ポイント010000(2)を使うのは危険だと考えています。
- 010111(2) → 011000(2) – 000001(2) = 24 – 1 = 23
- 111011(2) → 111000(2) + 000011(2) = 56 + 3 = 59
ちなみに1増えると、111100(2)となりますが、これは60を意味するので、分での表記は000000(2)と戻ります。
それを利用して、000000(2) – 000001(2)= 0 – 1 = -1 → ( 1分前と解釈する ) → 59としてもいいかなと思っています。ちょっと難しく言うと「-1と59は60を法として合同」ということです。慣れてくると見た瞬間に59ってわかりますけどね。
ここまで見ていただいてわかるように、基本的には中間ポイントを基準として考えており、そこからの差分を考えて計算をしています。その場合は足し引きする数は原則3以内に収めていることにお気づきでしょうか?
中間ポイントを定数として押さえておくと、非常に速い演算が可能となります。桁がもう少しで繰り上がる状況のときに2の補数を足すことであえて溢れさせ引き算を行うといった発想は、誰もがたどり着くのではないのかなと思いますが、この中間ポイント読法はさらに強力な効果を発揮します。
足し算の場合は7まで足していることもありますが、これは繰り上がりがないときに限った話です。
100111(2) = 100000(2) + 000111(2) = 32 + 7 = 39
はまだ許容範囲だと思いますが(たまたま繰り上がりがないので)、
110111(2) = 110000(2) + 000111(2) = 48 + 7 = 55
はイマイチだと思います。そのくらい大した計算ではないでしょって思うかもしれませんが、後者の場合は56-1のほうが安定すると思います。少しでも計算が楽になる、速くなる方法を模索するべきです。ちなみに前者も40-1のほうが良いと思っています。
10進数計算をする上で、繰り上がりや繰り下がりは計算ミスの温床になります。(ただし、下1桁が0なら足し算・引き算もたやすいですけどね。)それを防ぐためにこのような中間ポイントに基づいた読み方を取り入れたというわけです。
この読み方の正当性については是非が分かれそうにも思えます。中間ポイントを覚えることを負担と感じる方もいらっしゃるでしょう。これは英語でいうところの熟語や構文のようなものだと思っていただきたいです。熟語や構文が英語→日本語翻訳を助けるのと同じように、中間ポイントは2進数→10進数変換を助けるのです。
また、冒頭にも言った通り、これは飽くまでも6桁表示の2進数を10進数に変換するという極めて特殊なケースにおいて役に立つよという話です。
私たちは10進数に慣れすぎています。最初は100とか1000とか大きい数の感覚を覚えるのって苦労したはずです。試しに小学2年生の算数の教材を見てください。1015, 1150, 1510, 1051の並べ替えとか、数直線に矢印を書き込むとかいう問題がたくさんあるはずです。でもいまじゃ数直線なんか使わなくても、1150と1051がどの程度離れているかって当たり前のように分かりますよね。私たちは小さいころに積み重ねた練習の結果、このような10進数の数字感覚が身についたのだと思うんです。各数字がどのくらいの大きさなんだろうという感覚は、何も生まれた時から備わっているものではないということです。もし小さいころに10進数ではなく2進数で学習を続けていたとしたら、110011(2)と101011(2)がどのくらい離れているかなんてすぐにわかりそうなものじゃないですか。この記事を読んでくださっている皆さんには、是非初めて数字を勉強したころの気持ちを思い出していただきたいなと思っています。
あ、ちなみに電車に乗り遅れそう、待ち合わせに遅れそう、ってときはスマホを開いて正確な時計を見てますね。
オチも付いたところで、去年作った不等式の問題を載せておきますね。
見かけほど難しくはないので、暇つぶしにチャレンジしてみてください。以上です。
ヒントは、以下の2つの数式です。
- a2 + b2 + c2 ≧ ab + bc + ca
- a4 + b4 + c4 ≧ abc(a + b + c)