talosのプログラミング教室

【基本・応用情報技術者】カルノー図の使い方

こんにちは。たろすです。

今回はカルノー図の使い方を説明します。

カルノー図の使い道

カルノー図は論理式を簡略化する際に使用します。

カルノーズの使い方

パターン1

例えば以下の論理式を簡略化したいとします。

 \overline{A} \cdot B \cdot \overline{C} \cdot D+ \overline{A} \cdot B \cdot C \cdot D

カルノー図を埋めていきましょう。

f:id:talosta:20210501151748p:plain

まず第1項の \overline{A} \cdot B \cdot \overline{C} \cdot Dを見ます。

AとCは否定になっています。

なので、A=0、B=1、C=0、D=1のところに1を埋めます。

f:id:talosta:20210501151819p:plain

続いて第2項の \overline{A} \cdot B \cdot C \cdot Dを見ます。

Aが否定になっています。

そのため、A=0、B=1、C=1、D=1のところに1を埋めます。

f:id:talosta:20210501151845p:plain

1が隣り合っていますね。

f:id:talosta:20210501151906p:plain

このような場合は二つに共通する部分を探しましょう。

するとAが0でBが1、Dが1ということ共通しているとわかると思います。

したがって、最初に与えられた論理式は次のように簡略化できます。

 \overline{A} \cdot B \cdot D

パターン2

次に以下の論理式を簡略化します。

 \overline{A} \cdot B \cdot \overline{C} \cdot \overline{D}+ \overline{A} \cdot B \cdot C \cdot \overline{D}

先程と同じようにカルノー図を埋めると次のようになります。

f:id:talosta:20210501151931p:plain

一見すると隣り合っていないように見えますが、実は一番左の列と一番右の列は隣り合っているとみなすことができます。

f:id:talosta:20210501151950p:plain

この二つに共通する部分はAが0でBが1、Dが0ということです。

なので最初に与えられた論理式を簡略化すると、

 \overline{A} \cdot B \cdot \overline{D}

となります。

なお、一番上の行と一番下の行も隣り合っているとみなします。

パターン3

次の論理式を簡略化します。

 \overline{A} \cdot B \cdot \overline{C} \cdot D+ \overline{A} \cdot B \cdot C \cdot D+ \overline{A} \cdot B \cdot C \cdot \overline{D}

カルノー図を書くと、

f:id:talosta:20210501152032p:plain

三つの1が隣り合っています。

しかし、以下のように三つを囲むことはできません。

f:id:talosta:20210501152054p:plain


このような場合は次のように囲みましょう。

f:id:talosta:20210501152112p:plain

赤い方の囲いの共通する部分はAが0でBが1、Dも1ということ、青い方の囲いの共通する部分はAが0でBが1、Cも1ということ。

なので、それらを論理和で繋げて

 \overline{A} \cdot B \cdot D+\overline{A} \cdot B \cdot C

となります。

このような囲むことができるのは2のべき乗×2のべき乗になる場合だけです。

パターン4

次の論理式を簡略化します。

 \overline{A} \cdot B \cdot \overline{C} \cdot D+ \overline{A} \cdot B \cdot C \cdot D+ A \cdot B \cdot \overline{C} \cdot D+A \cdot B \cdot C \cdot D

カルノー図にするとこのようになります。

f:id:talosta:20210501152131p:plain

先程説明した通り、2のべき乗×2のべき乗なら囲えるので、2×2はもちろん囲えます。

f:id:talosta:20210501152152p:plain

これらに共通する部分はBが1でDも1であるということ。

そのため、最初に与えられた論理式を簡略化すると、

 B \cdot D

となります。

おわりに

今回はカルノー図の使い方について説明しました。

場合によっては真理値表より簡単に論理式の変形ができるので、覚えておくとよいでしょう。

Java Silver合格への道 ~拡張for文の中でArryaList.removeしても例外がスローされない謎に迫る~

こんにちは。たろすです。

Java Silverの勉強をしている中で最も納得いかなかった問題を紹介します。

黒本にはあまり詳しく解説が書かれていなかったのでぜひ見ていただければと思います。

問題

以下のプログラムを実行したときの結果を答えよ。(黒本の問題を改題)

import java.util.ArrayList;

public class For1 {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ

		ArrayList<String> array = new ArrayList<>();

		array.add("A");
		array.add("B");
		array.add("C");
		array.add("D");

		for (String s : array) {
			if (s.equals("C")) {
				array.remove(s);
			} else {
				System.out.print(s);
			}
		}
	}

}

A.「AB」と表示される
B.「ABCD」と表示される
C.「ABD」と表示される
D.例外がスローされる
E.コンパイルエラーが起こる

解答

答えはAです。

私は訳がわかりませんでした。

拡張for文の中でArrayListの要素を操作すると例外がスローされると思っていましたから。

黒本の解説を見ても、

「3番目の要素が削除されたときに4番目の要素が3番目に繰り上がり、次に削除する要素がなくなるため繰返し処理が終了します」

とだけ。

いやいや、そこじゃないんだよ。

なんで例外にならないの?

と非常にもやもやしました。

例外がスローされない理由

実は削除した要素が最後から2番目だから例外がスローされないんです。

ちなみに1番目、2番目、最後の要素を削除すると例外がスローされます。

このときスローされる例外は「ConcurrentModificationException」です。

公式ドキュメントを読んでみる

公式ドキュメントで「ConcurrentModificationException」について調べたところ

この例外は、オブジェクトの並行変更を検出したメソッドによって、そのような変更が許可されていない場合にスローされます。
たとえば、あるスレッドがCollectionで反復処理を行っている間に、別のスレッドがそのCollectionを変更することは一般に許可されません。通常、そのような環境では、反復処理の結果は保証されません。いくつかのイテレータ(Iterator)の実装(JREが提供するすべての一般的な目的のコレクションの実装の、イテレータの実装を含む)は、その動作が検出された場合にこの例外をスローすることを選択できます。この例外をスローするイテレータは、フェイルファストイテレータと呼ばれます。イテレータは、将来の予測できない時点において予測できない動作が発生する危険を回避するために、ただちにかつ手際よく例外をスローします。

とのことです。

どうやら拡張for文の中で要素を削除するとこの例外がスローされるのが正しい挙動のようです。

ArrayListのコードを読んでみる

前提として拡張for文は内部でイテレータを使っています。

ArrayListが継承しているAbstractListイテレータのコードを見てみましょう(一部抜粋)。

    private class Itr implements Iterator<E> {
        int cursor = 0;
        int lastRet = -1;
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size();
        }

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

拡張for文ではまずhasNextメソッドを実行し、戻り値がtrueならnextメソッドでarray[i]の要素を取り出します。

f:id:talosta:20210424170215p:plain

nextメソッドの頭でcheckForComodificationメソッドを実行しています。

modCountとexpectedModCountが異なるとConcurrentModificationExceptionがスローされます。


次にremoveメソッドのコードを見てみます。

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

今回の問題の場合、引数oはnullでないのでelseブロックに入ります。

そしてArryaListのサイズ分インクリメントし、oと等しい要素があればfastRemoveメソッドを実行します。

つまり今回の場合はarray[i]がCのときにfastRemoveが実行されます。

f:id:talosta:20210424170228p:plain


ではfastRemoveメソッドを見ていきます。

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

modCountはインクリメントされて、numMovedには4-2-1で1が入ります。

numMovedが0より大きいので、System.arraycopyが実行され、array[3]に入っていたDがarray[2]にコピーされます。

そしてarray[3]にnullを入れて戻ります。

このときiの位置はまだ動いていません。

f:id:talosta:20210424171022p:plain


このあと拡張for文の処理でhasNextメソッドを実行します。

すると

cursor != size()

がtrueになるのでnextメソッドを実行せずに終了します(cursor=i+1=3、size()の戻り値も3)。

逆に削除する要素が最後から2番目の要素でない場合、対象の要素を削除した後でこれがfalseになるのでnextメソッドが呼ばれcheckForComodificationが実行されます。

例えば一番最後の要素を削除した場合はこのようになります(cursor=i+1=4、size()の戻り値は3)。

f:id:talosta:20210424170250p:plain

modCountはfastRemoveメソッドの中でインクリメントされてexpectedModCountとは異なる値になっているので、ConcurrentModificationExceptionがスローされます。

おわりに

仕様のミスだと思うのですが、そこを突いてくるJava Silver。

いやらしいですね。

【基本・応用情報技術者】真理値表の使い方

こんにちは。たろすです。

今回は真理値表の使い方を説明します。

基礎

真理値表は論理式の変形が難しい場合などに役立ちます。

まず基礎的な説明からしていきます。

扱う変数が2つの場合、それぞれの変数がとり得る値の組み合わせは
 (A,B)=\{(0,0),(0,1),(1,0),(1,1)\}
の4通りです。

 A Bの列にはこの組み合わせを書きます。

そして A \cdot Bの列を埋めていきます。

 A \cdot Bが1となるのは A Bがともに1のときなので、 (A,B)=\{(0,0),(0,1),(1,0)\}のときは0、 (A,B)=\{(1,1)\}のときは1 が入ります。

(なぜ A \cdot Bが1となるのは A Bがともに1のときなのかというと、そういう決まりだからです。)

f:id:talosta:20210414150118p:plain

同様に A Bの列を作り、 A + Bの列を埋めていきます。

 A + Bが1となるのは A Bのどちらかあるいは両方が1のときなので、 (A,B)=\{(0,0)\}のときは0、 (A,B)=\{(0,1),(1,0),(1,1)\}のときはは1が入ります。

f:id:talosta:20210414151403p:plain

排他的論理和

同様に A Bの列を作り、 A \oplus Bの列を埋めていきます。

 A \oplus Bが1となるのは A Bのどちらか一方のみが1のときなので、 (A,B)=\{(0,0),(1,1)\} のときは0、 (A,B)=\{
(0,1),(1,0)\}のときは1が入ります。

f:id:talosta:20210414165206p:plain

含意

同様に A Bの列を作り、 A \to Bの列を埋めていきます。

 A \to Bが0となるのは Aが1で Bが0のときなので、 (A,B)=\{(1,0)\} のときは0、 (A,B)=\{
(0,0),(0,1),(1,1)\}のときは1が入ります。

f:id:talosta:20210414153620p:plain

否定

 Aの列を作り、 \overline{A}の列を埋めていきます。

これは値を反転させるだけです。

f:id:talosta:20210414154024p:plain

応用

実際の使い方を説明します。

基本情報技術者試験応用情報技術者試験では論理式1つが提示され、その論理式と等価の論理式を4つから選ぶといった問題がよく出ます。

変形するのが難しいときは真理値表を使いましょう。

例えば、「 \overline{A \cdot B}と等しい論理式を選べ」という問題が出たときに、選択肢の中に \overline{A} + \overline{B}があったとしましょう。

それぞれの真理値表を書いてあげます。

f:id:talosta:20210414165734p:plain

 \overline{A \cdot B} \overline{A} + \overline{B}の真理値表が一致しているのでこれらは等価ということがわかります。

おわりに

今回は真理値表の使い方について説明しました。

論理式の変形が難しいときは重宝するので覚えておきましょう。

Java Silver合格への道 ~受験編~

こんにちは。たろすです。

Java Silverを受験してきたので、過程と結果を報告します。

勉強前のJavaの知識

大学生の頃に授業で習ったことがありました。

その後社会人になり研修も受けました。

あとはこのブログでも少しJavaについて書いてます。

勉強期間と方法

4か月の間、週に2日、1日2~3時間程度勉強しました。

この期間に黒本を3周しました。

最後の方にはほとんど間違えなくなっていたので、経験者なら3周すれば十分かと思います。

ちなみに黒本というのはこれのことです。

私は安かったので初版を買いましたが、最新のものを買うことをおすすめします。

誤植が結構多かったので。。。

とはいえ、得点に直結するようなクリティカルな誤植はそれほどなかったので、安さ重視なら初版でもOKです。

他の参考書は必要?

Java Silverの参考書として紫本や白本と呼ばれるものがありますが、経験者なら必要ないです。

逆に言うと、Java自体がほぼ初めてという方は買っておいてもよいかと思います。

紫本と白本は以下の本を指してます。

受験

秋葉原の試験会場で受けました。

春なのに冷房ガンガンに効いている上に、スタッフの人がやたらとうろうろしていて非常にやりにくかったです。

もう二度とここでは受験しません。

結果

結果はその場で見ることができます。

正解率はなんと94%でした。

合格ラインが65%なので余裕の合格です。

黒本で見た問題もいくつかあったので、やはり黒本を完璧にしておいたのは正解でした。

おわりに

Java Silver合格までの道のりを書いてみました。

これから受ける方の参考になったら幸いです。

【基本・応用情報技術者】論理式の公式

こんにちは。たろすです。

今回は論理演算の公式について説明します。

論理式

論理式とは以下のようなもの指します。

 (A+B) \cdot C+A \cdot B+\overline{C}

・は「かつ」、+は「または」、上線は「否定」を表します。

「かつ」に \land、「または」に \lor、「否定」に \lnotを使うこともあります。

また、排他的論理和(AかBのどちらか一方のみが真のときは真になる)には \oplusを使うので覚えておきましょう。

論理式の公式

論理式の公式には以下のようなものがあります。


恒等則  A \cdot 0=0
 A \cdot 1=A
 A+0=A
 A+1=1
同一則  A \cdot A=A
 A+A=A
交換則  A \cdot B=B \cdot A
 A+B=B+A
結合則  A \cdot (B \cdot C)=(A \cdot B) \cdot C
 A+(B+C)=(A+B)+C
分配則  A \cdot (B+C)=A \cdot B+A \cdot C
 A+(B \cdot C)=(A+B) \cdot (A+C)
吸収則  A \cdot (A+B)=A
 A+(A \cdot B)=A
 A \cdot (\overline{A}+B)=A・B
 A+(\overline{A} \cdot B)=A+B
矛盾則  A \cdot \overline{A}=0
排中則  A+\overline{A}=1
二重否定  \overline{\overline{A}}=A
ド・モルガンの法則  \overline{A \cdot B}=\overline{A}+\overline{B}
 \overline{A+B}=\overline{A} \cdot \overline{B}

特にド・モルガンの法則はよく問われるので必ず覚えておきましょう。

例題

問題

 (A+B) \cdot C+\overline{A \cdot C} \cdot A A+B \cdot Cに変形せよ。

答え


\begin{eqnarray}
(A+B) \cdot C+\overline{A \cdot C} \cdot A &=& A \cdot C+B \cdot C+\overline{A \cdot C} \cdot A \\

&=& A \cdot C+B \cdot C+(\overline{A}+\overline{C}) \cdot A \\

&=& A \cdot C+B \cdot C+A \cdot \overline{A}+A \cdot \overline{C} \\

&=& A \cdot C+B \cdot C+0+A \cdot \overline{C} \\

&=& A \cdot C+B \cdot C+A \cdot \overline{C} \\

&=& A \cdot (C+ \overline{C})+B \cdot C \\

&=& A \cdot 1+B \cdot C \\

&=& A+B \cdot C \\

\end{eqnarray}

おわりに

今回は論理式の公式について説明しました。

基本情報技術者試験応用情報技術者で必ず1問は出てくるのでできるようにしておきましょう。

【基本・応用情報技術者】浮動小数点数(2進数の浮動小数点表示)

こんにちは。たろすです。

今回は浮動小数点について説明します。

浮動小数点数

浮動小数点数は次のような形式で表します。

 X = (-1)^S ×M × 2^E

Mを仮数、2を基数、Eを指数と呼びます。

また、Sが0のときは1となるので符号は正、Sが1のときは-1となるので符号は負となります。

 (110100.1)_2浮動小数点数で次のように表せます。

 11010.01 × 2^1
 110100.1 × 2^0
 1101001.0 × 2^{-1}

IEEE方式

IEEE方式は次のような形式で浮動小数点数を表す方式です。

f:id:talosta:20210307215322p:plain

 (110100.1)_2IEEE方式で表すには次のようにします。

1. 整数部が1になるように形式を変える

 2^5 × (1.101001)_2

2. 指数部をE-127の形式に変える

 2^{(132-127)} × (1.101001)_2

3.符号を (-1)^Sの形式に変える

 (-1)^0 × 2^{(132-127)} × (1.101001)_2

4. それぞれをフォーマットに当てはめる

f:id:talosta:20210321183521p:plain

おわりに

今回は浮動小数点数について説明しました。

よく出る部分なのでしっかり覚えておきましょう。

【基本・応用情報技術者】2進数の負数表現

こんにちは。たろすです。

今回は2進数の負数表現について説明します。

2進数の負数表現には二種類あるのでそれぞれ説明していきます。

1の補数

n桁の2進数kの1の補数は「kをビット反転した値」です。

例えば86を8桁の2進数にすると (01010110)_2なので、-86の1の補数は (10101001)_2になります。

2の補数

n桁の2進数kの2の補数は「1の補数+1」です。

つまり86の2の補数は (10101001)_2に1を足して (10101010)_2です。

計算してみる

本当に計算できるのか確かめてみましょう。

計算するときは2の補数を使います。

例として88-86を計算してみます。

88-86は88+(-86)と置き換えることができるので、88と-86をそれぞれ8桁の2進数にして加算してみます。

f:id:talosta:20210307190252p:plain

8桁の計算をしているため9桁目以降は無視します。

そのため結果は (00000010)_2となり、10進数で表すと2になります。

正しく計算できることが確かめられました。

おわりに

今回は2進数の負数表現を説明しました。

基本情報技術者試験応用情報技術者試験ではよく問われるのでしっかり覚えておきましょう。