Subscribed unsubscribe Subscribe Subscribe

Memorandums?

This blog is written about technical-discovery and daily-event.

Conway's game of life Software!!

ライフゲームとは

まだ一日も経っていないですが、ソフトウェアを更新しました(9/21)

ライフゲームソフトウェアを製作しました!!!
ライフゲーム(Conway's game of life)とは...

ライフゲームとは2次元のセルオートマトンである。
n✕mのマスがあり、
白色状態を死、黒色状態を生とするならば、
生コマの周りに生コマが2〜3コマあるならば、そのコマは生き続ける。
死コマの周りに生コマが3コマあるならば、そのコマは復活する。
その他の条件のとき、そのコマは、死亡する。
つまり、程度な人数が周りにいれば、生まれ、
多すぎてもダメ、少なすぎても寂しくて死んでしまうというものである。

このようなルールに基づき、シミュレーションを行うのが、ライフゲームである。

このように、通常は、死→生(Born)が3、生→生(Survives)が2,3となっています。
これをB3/S23と表記します。
この標準ルールを含む様々なルールでシミュレーションを行います。
実装言語はRです。

Read more

Monty Hall Problemシミュレーション

What's モンティホール問題?

確率論(事後確率関連)でもっとも有名な問題のひとつがモンティホール問題です。
そんなモンティホール問題(Monty Hall Problem)とは、

これは、クイズ番組です。
3つの閉じられたドアがあります。
これらのドアのどれか1つを開けると、そこには高級車があり、それをもらえます。
しかし、間違ったドア(高級車の無いドア)を開けると、そこにはヤギしかおらず、挑戦は失敗となります。

挑戦者は、まず、1つドアを選びます。
すると、ドアのどれが正解かを知っている司会者が、
挑戦者が選んでいないドアかつ、正解でないドアを開け、こういいます。
「あなたが選ばなかったこのドアには、ヤギがいました。
今なら、まだ開けていないもう1つのドアに変更しても構いません。
どうしますか?」
このとき、挑戦者は、ドアを変えるべきでしょうか?
それとも、自分の決定を信じて、変えないほうがよいでしょうか?
(確率的にどちらが良いか)

という問題です。
この問題は、事後確率の問題であり、人間が直感で判断するのは、厳しい問題です。
IQが最も高いと言われるマリリン・ボス・サバント氏は、この問題を正解しました。
(この人が最初に答えを提唱したといわれている)

さて、確率はいくらでしょう?

Read more

Raspberry pi 導入

目的

RaspberryPiをディスプレイ・キーボード無しで導入し、使用します。
RaspberryPiをネット環境が無くても使えるように、PCと直接接続するだけで使用できるようにします。
RaspberryPiのデスクトップ画面(GUI)をPCから操作できるようにします。
RaspberryPiにて、PCをルーターとしてネットワーク接続ができるようにします。
※2015/07/30加筆訂正有

条件

  • Host-PC: Windows8.1
  • RaspberryPi: RaspberryPi2 ModelB
  • RaspberryPi-OS: 2015-05-05-raspbian-wheezy

必要なもの

  • ルーター(無線LAN・有線LAN)
  • 有線ストレートLANケーブル
  • Host PC
  • Raspberry pi 2
  • microSD(8GB以上が好ましい)
  • microSD-SD変換器(PCにmicroSDが入らない場合)
  • 5V1A供給ACアダプタ(端子はmicroUSB)

注意

RaspberryPi用として一切ディスプレイやキーボードを使用しません。
初期設定から全てホストPCから行います。
RaspberryPi2のRJ-45コネクタには、クロス-ストレート変換器が内臓されているようで、クロスLANケーブルは必要ありません。
※動作は保証しません

OSの書き込み

公式サイトからRaspbianOSのzipファイルをダウンロードします。
PCで解凍します。
次に、SDカードのフォーマットをします。
[Windows]+E でコンピュータを開き、SDカードを右クリック、フォーマットを選択します。
FAT32でフォーマットしてください。(データは全て削除されます)
Win32DiskImagerをダウンロードし、起動します。
ImageFileにはダウンロードしたRaspbianのイメージファイルを、ドライブにはSDカードを指定します。
Writeし、書き込みます。
書き込めたらRaspberryPiに差し込みます。

RaspberryPi起動

ネットワーク接続されたルーターにLANケーブルを差し、RaspberryPiのRJ-45コネクタと接続します。
microUSBを接続し、電源を入れます。
ホストPC側は無線LANが利用できるようにします。
[Windows]+R で検索画面を開き、[cmd]と入力し、コマンドプロンプトを起動します。
[ipconfig]と入力し、イーサネットの欄に出てきたIPアドレスを調べます。
例えば、192.168.0.3だった場合、192.168.0.2~192.168.0.15くらいまでの間におそらくRaspberryPiのIPアドレスがあるはずです。
(192.168.0.1はルーター自体のIPアドレスの可能性が高い)
それを調べます。
NetEnumなどのソフトウェアを使用してもいいですが、手打ち作業でもできます。
[ping 192.168.0.2]
と打ちます。"接続されていない"といった旨のメッセージが数秒おきに出てこれば、それは違います。
[ping 192.168.0.3]...
と続けていき、IPアドレスを調べます。
IPアドレスがわかったら、そこにSSHでログインします。
TeraTermをインストールしてください。
TeraTermで、IPアドレスに192.168.0.[調べたIPアドレス]と入力します。
SSHのセキュリティ確認画面が出てこれば成功です。
ユーザー名には[pi],パスワードには[raspberry]を入力します。

RaspberryPi初期設定

SSHに[raspi-config]と入力し、設定画面に行きます。
設定内容については、Googleで[raspberry pi raspi-config]などと検索して調べてみてください。
(今回それほど重要ではない)

DHCPに設定 固定IPアドレス付与

SSHにて、
[sudo vi /etc/network/interfaces]
と入力し、viの画面が出てきたら、
"
auto lo
iface lo inet loopback

auto eth0
allow-hotplug wlan0
iface eth0 inet manual

以下続く...
"
などと書かれていると思うので、

  1. "iface eth0 inet manual"を[iface eth0 inet dhcp static]に書き換える
  2. その文の下に次の文を加える
    address 192.168.0.[他とかぶらない番号]
    netmask 255.255.255.0
    dns-nameservers 192.168.0.1(←ルーターIPアドレス) 8.8.8.8

次に、わざわざIPアドレスを入力せずとも[raspberrypi.local]などと入力するだけでSSHログインできるように
[sudo apt-get install avahi-daemon]
と入力します。PC側には同様の理由でBonjourをインストールしておいてください。
(iTunesをインストールすると一緒にインストールされる)

できたら、RaspberryPiをシャットダウンします
[sudo halt]

RaspberryPi-PC直接接続

PC側の設定をします。
ネットワークと共有センターを開き、アダプター設定の変更を開きます。

Wifiを右クリックプロパティに行き、共有のタブをクリックし、一番上のチェックボックスにチェックを入れます。

イーサネットを右クリックプロパティに行き、IPv4を選択し、プロパティを開きます。
"次のIPアドレスを使う"を選択し、IPアドレスにはかぶらない任意のIPアドレスを指定し、
サブネットマスクには、255.255.255.0を指定します。(たぶん自動で入力される)

できたらOKでウィンドウを閉じます。

アダプター設定の変更の画面で、Wifiイーサネットを両方選択し、右クリックでブリッジ接続にします。
(RaspberryPiでネットワークを使用できるようにするため)

先ほど、ルーターとつないでいたLANケーブルを外し、PCとRaspberryPi間をそのLANケーブルで接続します。
そして、電源を入れます。
cmdで、IPアドレス(raspberrypi.local) (さきほどRaspberryPiでinterfacesファイルに書いた任意のIPアドレス)と接続を確認します。
[ping raspberrypi(ホスト名).local]
接続が確認できたら、TeratermSSH接続します。
これで、PC側が無線LANでネットワーク接続できているのであれば、RaspberryPi側のネットワークも接続されています。
以上でほとんど完了しました。

リモートデスクトップ(VNC)

RaspberryPiのデスクトップ画面をPCから操作できるようにします。
RaspberryPi側では、
[sudo apt-get update]
[sudo apt-get upgrade]
[sudo apt-get tightvncserver]
とし、インストールします。
[tightvncserver]
と入力し、起動させ、適当なパスワードを何回か打ちます。
PC側では、VNC-Viewer for Windowsをインストールし、exeを起動します。
IPアドレスには、
[192.168.0.[RaspberryPiのIPアドレス]:5901]
と書き、パスワードは先ほど決めたものを入力します。
すると、画面を見ることができるようになります。



感想

本当に導入には苦戦しました。導入だけで40時間くらい費やしてしまいました。。
ネット上には、RaspberryPi2の情報が少なく、異なる部分があったり、
当方、ネットワークの知識が薄く、なかなか理解できなかったりした部分があったためです。

今回特に苦戦した場面は、RaspberryPiの電源を切れず、やむなくUSBをぶち抜き、SDカードのメモリを破壊してしまうことです。
正しい手順でやればそのようなことはないのですが、
失敗すると、PCからアクセスできなくなり、シャットダウンシグナルが送れないためにぶち抜かなくてはいけない場面が発生することがあります。
2回初期化しましたが、その後は反省し、シャットダウンボタンをつくりました。
RaspberryPiにスクリプトを組み、起動時自動スクリプトにし、
タクトスイッチのみの回路をGPIOと接続し、PCなどなにもなくてもシャットダウンできるようにしました。
様々なサイトではGPIOの真ん中あたりのピンを使っており、他に回路を作るときに邪魔になりそうですが、
今回は、GPIO21とGNDに回路を作ったため、邪魔にもなりそうになく、満足しています。
これで失敗を恐れることがなくなり(笑)、直接接続に向けて奮闘したところ、なんとかできた、といった感じです。


RaspberryPi2では、デフォルトの/etc/network/interfacesはeth0がmanualと設定されているため、それをDHCPにする作業が必要(?)のようです。
それができれば、PCとRaspiの接続はもちろん、Windows側でネットワーク共有をするだけでRaspberryPiのネットワーク接続もできてしまいます。

RaspberryPiがもし壊れたりしても責任はとれませんが、不明な点があればコメントにてどうぞ。(誤植・アドバイスもお願いします)

InputStreamReaderでの文字列の受信

InputStreamReaderは、char文字(単一文字)単位でしかデータを受け取れません。
そのため、改行までを文字列として取得するのは、それなりに大変です。
今回は、文字列に変換する方法を示します。

それならBufferedReaderのreadLineメソッドで余裕じゃん!
といいたいところかもしれませんが、その理由については後ほど(笑)

文字列に変換するポイントは、StringBuilderを使うことです。
StringBuilder#appendによって、一文字づつ追加ができます。

コードを示します。

StringBuilder sb = new StringBuilder();
while ((data_tmp = in.read()) != '\n' && data_tmp != -1) {
	sb.append((char)data_tmp);
}
data_str = new String(sb);
sb.setLength(0);

まずは、InputStreamReader#readによってchar文字を読み取ります。
それが改行コードや、終了コードでないならば、
StringBuilderに追加します。
全て追加が終わり、ループを抜けた後、String型に戻して完成です。
おそらくこの処理は、常時受信処理でしょうから、次の受信のためにStringBuilderを空にするのを
忘れないようにします。

ちなみに先ほど'改行コード'とイイましたが、
Windowsの場合は、改行コードは"\r\n"です。
この区別については、前々回のエントリーのmeriyasu-blog.hatenablog.com
に書いています。
参考になれば幸いです。

なんやかんやで簡単にInputStreamReaderの文字列化ができてしまいました。

先延ばしにした、なぜBufferedReaderを使わないのか、ですが、

BufferedReader br = new BufferedReader(new ObjectInputStream());

ということができず、InputStreamReaderならば、

InputStreamReader isr = new InputStreamReader(new ObjectInputStream());

ということができるからです!!

前回のエントリー、meriyasu-blog.hatenablog.com
でも述べたように、今回、画像転送も必要としました。

しかし、もちろんBufferedReader, InputStreamReaderなどでは画像送信はできず、
ObjectInputStreamを使用する必要があります。

サーバークライアント通信において
一つのクライアントとサーバーを繋ぐソケットは基本的に一つです。
そのソケットのInputStreamも一つです。

そのため、ObjectInputStream用にsocketのInputStream,
BufferedReader用にsocketのInputStreamとするのはよくないです。
(closeするときなどに問題が起きるため)

よって、socketのInputStreamを一つにするため、

ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
InputStreamReader in = new InputStreamReader(ois);

とできるInputStreamReaderを採用しました。
(そうしないと自分が実装できないだけで、できるのかもしれません())

こういった理由があり、こうなりました。

ImageIO#readのBugSolver(代替方法)

画像送信プログラムを作製中、、

Bug ID: JDK-4821108 IIOException thrown when reading PNG images

このバグ報告が出ていることに暫く気づかず、ずっとImageIOクラスで戦っていました。
しかしながら、Bugにより、ImageIO#readを使用しての画像送信は無理です。

そこで様々な解決策を考えました。
真っ先に思いつくのが、画像をByte配列に変換し、送信し、デコードして復元する。
ByteArrayOutputStreamなどを使用すれば、変換することはできます。
しかし、複雑で送信に時間がかかったため、非採用としました。

もうひとつの方法として、ObjectOutputStreamを使用し、オブジェクトとして送信することです。
Imageオブジェクトを送信しても、読み込み側でImageIOを使用しなくてはいけないため意味がありません。
そこで、BufferedImageオブジェクトをObjectOutputStream#writeObjectに乗せて送信することを思いつきました。
しかし、まだ問題はあります。
それは、ObjectOutputStreamは直列化(serialize)されたオブジェクトしか扱えないということです。
BufferedImageはSerializeされていないため、そのまま送ることは不可能です。
そこで、自作クラスでラップし、そのクラスをSerializeしてみます。

Serializeする独自クラスは以下のようになります。

import java.awt.image.BufferedImage;
import java.io.Serializable;

public class BufferedImageSerializer implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private int width, height;
	private int[] pixels;
	
	/**
	 * 受け取ったBufferedImageクラスのインスタンスの情報を収集します。
	 * @param image 送信したいBufferedImageクラスインスタンス
	 */
	public BufferedImageSerializer(BufferedImage image) {
		width = image.getWidth();
		height = image.getHeight();
		pixels = new int[width * height];
		image.getRGB(0, 0, width, height, pixels, 0, width);
	}
	
	/**
	 * BufferedImageクラスのインスタンスを受け取ります。
	 * 画像受信元にて使用します。
	 * @return 送信されたBufferedImageインスタンス
	 */
	public BufferedImage getImage() {
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		image.setRGB(0, 0, width, height, pixels, 0, width);
		return image;
	}
	
}

利用方法はコードを示すまでもなく、
画像を送信する側は、コンストラクタにBufferedImageのインスタンスを指定する。
画像を受信する側は、getImageで取得する。
これでOKです。

サーバークライアントソフトウェアはこの問題を最後についにリリース版となりました!!
これからもバージョンアップを重ねていきます。

クロスプラットフォーム問題

マルチプラットフォームに対応しているJavaであっても、
OSの違いに気をつけなくてはいけない場面があります。

まず、OSを認識するプログラムです。

public String getOS() {
   if (System.getProperty("file.separator").equals("\\"))
      return "WINDOWS";
   else
      return "UNIX";
}

ファイルの区切り文字から認識することができます。

次に、InputStreamReader#readを用いて1文字づつ認識するための方法を紹介します。
1文字づつ認識するにあたっての問題点は、改行コードです。
Windowsでは'\r\n', Unixでは'\n'となっており、これを区別しなければなりません。

#nextLineProcedure

public int nextLineProcedure(String os) throws Exception {
   switch (os) {
   case "WINDOWS":
      if ((data_tmp = in.read()) == '\r')
         return (((data_tmp2 = in.read()) == '\n') ? -1 : -2);
      break;
   case "UNIX":
      if ((data_tmp = in.read()) == '\n')
         return -1;
   break;
   }
   return -3;
}

このメソッドを使い、
-1が返却された場合は、読み込まない。
-3が返却された場合は、data_tmpのみを読み込む。
-2が返却された場合は、data_tmpとdata_tmp2を読みこめばOKです。

TeX R言語同期

TeXによるR言語同期についてです。

R言語にて、

pictex("graph.tex");
plot(x, y)
dev.off()

TeXにて、

\usepackage{pictex}
\usepackage{graphics}


\begin{figure}[h]
\centerline{\input{graph.tex}}
\end{figure}

Remove all ads