(O+P)ut

習うより慣れろ、Practice makes perfect。この言葉をモットーに、Slerで働く若手インフラエンジニアが、学んだ知識を【 (O+P)ut = OutPut 】していく場です。

ソートの結果を可視化してみる

Javaで学ぶデータ構造とアルゴリズム」という杉山行浩さんの本を読んでいたら,ソートの結果を可視化して図にしていた.これは面白い!と思ったので紹介させてもらうと同時に,僕も実際にソートした結果をGifにしてみました.

まずソートの可視化方法.例えば大きさ100の配列に,未ソートの状態で実数が入っている時に,配列のインデックスをx座標,その配列の値をy座標にとるというものでした.

つまり,例えば配列の大きさを1000として0~1で一様乱数を発生させると以下のようなグラフになります.
f:id:mtiit:20151003185351p:plain

これを初期状態としてソートを行っていく際の途中経過を表示していくのが,今回言っている「ソートの可視化」です.

百聞は一見に如かず.
選択ソートで可視化をしてみます.選択ソートとは,
例えば 大きさ5の配列
2 7 5 9 1
があった時に,
一番小さな 1 を 一番左に持ってきて,元あった2と入れ替える.
1 7 5 9 2
ここで一番左は整列済みなので,次は未整列な4つの中で一番小さい 2 を 一番左(整列しているところは除いて)と入れ替える
1 2 5 9 7
これを繰り返すと残りは
1 2 5 7 9
とソートが行われる,というアルゴリズムですね..

このアルゴリズムを用いて,0~1の一様分布に従った乱数1000個の配列に対して50個整列が終わる毎に出力したものをGifにしたのが下の画像です.







f:id:mtiit:20151003195514g:plain
分かりやすい...左側から整列していく様子が一目瞭然で分かる!

ついでにもう一つ.バブルソートではどのように整列が進むのでしょうか.
バブルソートとは,同じように大きさ5の配列
2 7 5 9 1
があった時に,一番右側の「1」から自分の左隣と比較して右側の方が小さければ値を交換していく.
上の例で言えば,9 と 1 を比較すると1の方が小さいので
2 7 5 1 9 となる.次は5と1の比較で
2 7 1 5 9
2 1 7 5 9
1 2 7 5 9
となって一番左側は整列済み,となる.同じようにまた 9と5を比較して,
1 2 7 5 9
1 2 5 7 9
1 2 5 7 9 となって1,2までが整列済み,というのを最後まで繰り返すアルゴリズムですね.

ではこれを同じようにGifにしてみると,どんな風に整列が進むか想像つきますかね?
正解は下の画像のようになりました.







f:id:mtiit:20151003195457g:plain
なるほど,バブルソートは隣あった数の比較を進めながら整列が進むので選択ソートよりかは途中段階においても緩やかなソートが行われていることが見て取れます.

このように,ソートのアルゴリズムを視覚的に表示させるのは,かなりいいアイデアだとは思う*1
上で紹介した本の中にはマージソートヒープソートも載っていて,「こんな整列の仕方してるのか!」と思った.

あと,今回はRでグラフを出したりしてたんですけど

plot(hoge)
dev.copy(png,file=filename)
dev.off()

でファイルネームでplotをpngファイルで保存できるんですね*2.便利でした.

*1:もちろんこの作者が考えついたとは思っていないが,僕はこの本で初めて知ったので!

*2:たしかpdfとか他の拡張子でもいける

【Mac】mdfindでiPhoneのスクショを見つけ出す

以前,Spotlightを使ってみるで,Macではmdlsやmdfindを使ってファイルの詳細な情報を使って検索をかけたりできることを自分で確認したのですが,今回はせっかくなのでそれを使ったプログラムを作ってみた*1

作ったプログラムは,iPhoneで撮った写真をPCに保存している時にスクリーンショットのみを一気に消す処理を行うものです.なんかiPhoneアプリスクリーンショットのみを消せるアプリはあるみたいなんですけど,実際にパソコン側にデータを持ってきた後ではいちいち手作業で消していくことになると思うので.

今回はシェルスクリプトで書きました.最初はjavaで書いてたんですけど,外部プログラムを実行する

Runtime.getRuntime().exec(hoge)

hogeにダブルクォーテーションを入れるとなんか思った通りに動かなかったんですよね.
今回,スクリーンショットか否かを判断するのに
kMDItemPixelHeightとkMDItemPixelWidthを使いました.スクリーンショットの画像は全てこれが一緒だったので,ここで絞ればいいかな〜という安易な発想です.もっと明示的にこいつはスクリーンショットだ!ってのがあればそっちを使いたいので見つかればそっちに切り替えるかも.

作ったプログラムの全文は以下です.

#!/bin/sh
ARRAY=(`mdfind -onlyin ./ "kMDItemPixelHeight = '$1' && kMDItemPixelWidth = '$2$"`)

mkdir SS_`date +%Y-%m-%d`
i=0
for var in ${ARRAY[@]}
do
fext="${var##*/}"
#echo $var
mv $var ./SS_`date +%Y-%m-%d`/$fext
i=$((i+1))
done

echo $i pictures moved

簡単に動作を説明しますと,引数($1,$2)にPixelHeightとPixelWidthを入れます.これはスクリーンショット画像をmdlsで見ると分かります.もちろんFinderで画像の情報を見ても「大きさ 640x1136 」といった感じで出てきます.この情報を元にmdfindで検索をかけます.このプログラムではパスが ./ となっていることからも分かる通り,画像ファイルがあるところにこのコードを置く必要があるので注意です.
次にいきなりスクリーンショットを全部消すと間違えて消しちゃった場合にまずいのでスクリーンショットと判断した画像を移すためのディレクトリを作成しています.そしてそこにどんどん移していって最後に何枚移動させたかを表示して,おしまいです('ω')/~ スクリーンショットを消すっていいましたけど,結局はそこのフォルダに確認しにいって,もし大事な写真が混じっていたらそれはコピーしてもとのところに戻せるようにしています(笑)もし消していいならFinderからディレクトリ毎消すなり rm -r で消すなりすればOKです.

実際に自分の環境でやってみました.

hoge$ ./SS_delete.sh 1136 640
64 pictures moved

となりSS_2015-06-09というフォルダができていて,64枚の画像が移動していました.
結果なんですが,一応移動した画像は全てスクリーンショットで,高さと横を逆にした ./SS_delete.sh 640 1136 でも何枚かヒットしました.この検索方法は,スクリーンショット以外の画像が入ってきそうでまだまだ改善の余地はあるとは思いますが,とりあえずはうまいこといったので良かったです.

*1:もしかしたら車輪の再発明かも...まぁ勉強になったからいいけど

【Java】パソコン一台で通信プログラムを動かしてみる

最近,通信系のプログラムを理解しないといけない必要に迫られてるんで色々と本を読んで勉強しているのですが,一通りまとまってきたので一番とっつきやすかったプログラムを紹介します.同じような境遇の方の助けになれば!ネットワークの通信では「クライアントサーバモデル」がよく出てきます.例えばネットサーフィンなんかも,クライアントである僕たちがサーバー側にリクエストを送っているっていう構図ですよね.

今回はサーバー側とクライアント側でプログラムを二つ作りますが,通信を行う上で肝心なことは,クライアント側はサーバー側のポートをしっかり指定して接続しようとする必要があるし,サーバー側はクライアント側から接続されてもいいようにポートを開放しておく必要があるってことですかね.

クライアント側ではデータを読み取るために

Socket hoge = null;

を準備して,

hoge = new Socket("IPアドレス",(整数)ポート番号);

という風にソケットを作成します.あとはファイルからの読み込みと同じように,InputStreamでデータを受け取って...といった感じです.
第一引数でIPアドレスを,第二引数でポート番号を指定するクライアント側のプログラムのコードは以下にあるのでコピペしてコンパイルすればそのまま動くと思います.

import java.io.*;
import java.net.*;

public class Readnet {

 public static void main(String[] args) {
  byte[] buff = new byte[1024];
  Socket readsocket = null;
  InputStream instr = null;
  boolean cont = true;
  
  try {
    readsocket = new Socket(args[0],Integer.parseInt(args[1]));
    instr = readsocket.getInputStream();
  }
  catch(Exception e) {
   System.err.println("Network Error");
   System.exit(1);
  }
		
  while(cont) {
   try {
    int n = instr.read(buff);
    System.out.write(buff,0,n);
    }
   catch(Exception e) {
    cont = false;
   }
  }
   
  try {
   instr.close();
  }
  catch(Exception e) {
   System.err.println("Network Error");
   System.exit(1);
  }
 }
}

次はサーバー側ですね.実はこっちも似たように

ServerSocket hoge2;

を作って

hoge2 = new ServerSocket(ポート番号,最大接続数)

とソケットを作成します.
クライアント側の違いとしては,作成したサーバーソケットに対する接続を待ち受けるオブジェクトを作り,acceptメソッドを使用することで接続を常に受け付けるようにすることですね.

Socket sock = hoge2.accept();

こんな感じ.

サーバー側のプログラムの全体は以下です.これもそのままコピペで動くはず.単なる文字列を返してもいいんですけど,せっかくなのでDateを使って日時を返します.

import java.io.*;
import java.net.*;
import java.util.Date;

public class Netclock {
 public static void main(String args[]) {
  ServerSocket servsock = null;
  Socket sock;
  OutputStream out;
  String outstr;
  Date d;
 
  try {
   servsock = new ServerSocket(6000,100);
   while(true) {
    sock = servsock.accept();
    d = new Date();
    outstr = "\n" + d.toString() +"\n";
    out = sock.getOutputStream();
    
    for(int i=0;i<outstr.length();++i) {
     out.write((int)outstr.charAt(i));
    }
    out.write('\n');
    sock.close();
   }
  }
  catch(IOException e) {
   System.exit(1);
  }
 }
}

では実際にサーバー側のプログラムを実行します.おそらく何も表示されず待機されるはず.その後にもう一つターミナルを開いて,クライアント側のプログラムを実行します.今回は同じパソコンなので,第一引数は localhost,第二引数はサーバー側のソケットを6000にしているので6000とします.
実行結果はこんな感じです.

java Readnet localhost 6000

Sun Apr 19 17:47:26 JST 2015

すごいシンプルなんですけど,これを踏まえた上でもっと理解を深めたいです.*1

*1:Javaによるネットワークプログラミング[鶴沢偉伸],TCP/IP Javaネットワークプログラミング[小高知宏] は読破した本の中でも良本でした

【Mac】Spotlightを使ってみる

Spotlightという便利な機能を知ったのでちょっと使ってみた感じをまとめてみます.
Wikipediaによると,Spotlight(スポットライト)とは、Mac OS X v10.4 Tigerから搭載されたSQLiteをベースとしたデスクトップ検索機能、およびその基盤技術のことである。とか書いてますけど僕のイメージでいうとコマンドでよく使うlsとかfindでは取得できない情報を検索できるってイメージですね.
ls,find の代わりに使うコマンドは mdlsmdfindです.
さっそくmdlsコマンドを使ってみましょう.
例えばあるjpgの画像ファイルをmdlsで見てみます

mdls hoge.jpg
kMDItemAcquisitionMake         = "Canon"
kMDItemAcquisitionModel        = "MG6100 series"
kMDItemBitsPerSample           = 32
kMDItemColorSpace              = "RGB"
kMDItemContentCreationDate     = 2015-01-03 03:06:08 +0000
kMDItemContentModificationDate = 2015-01-03 03:06:08 +0000
kMDItemContentType             = "public.jpeg"
kMDItemContentTypeTree         = (
    "public.jpeg",
    "public.image",
    "public.data",
    "public.item",
    "public.content"
)
kMDItemCreator                 = "MP Navigator EX 4.0"
kMDItemDateAdded               = 2015-01-05 13:43:27 +0000
kMDItemDisplayName             = "hoge.jpg"

という感じででできます.実は長いから後半を打ち切ったくらいで本当はもっと下にツラツラと出てきます.情報を見てると,商品名とかも出てきて面白い.

PDFを見てみるとまた↑みたいに色々情報が出てきて便利ですね.例えば

kMDItemNumberOfPages           = 6

といった項があってこれはページ数を返してくれるみたい.

pdfファイルがいくつかあるディレクトリの下で

mdfind -onlyin ./ "kMDItemNumberOfPages > '数字'" 

と入れると,自分がいるディレクトリ下で数字ページ以上のpdfファイルを返してくれたりします.こういうことができるっていうのを頭に入れておくと,いざファイルを探す時に便利かもしれないので備忘録更新でした.

【Java】JSONで標高をゲットしてみる

大学から北野天満宮にチャリ移動をした時に,行きはきつくて帰りは楽だった気がしたので標高を取得してどんなもんか確認してみたのでそれらに関してまとめます.
標高API - 地理院地図に公開されているAPIを使ってみます.形式はJSONでした.
JavaJSONを使うために,
JacksonDownload - FasterXML Wikiからjarファイルをゲットしました.
この3つのjarファイル

  • jackson-core
  • jackson-databind
  • jackson-annotations

をゲットすればいいです(coreとdatabindだけで今回のプログラムは動きます).
f:id:mtiit:20150305142522p:plainのように,Mavenとかがなくても3つ全てダウンロードできる.

このjarファイルにパスを通して,標高をゲットしてみます.

コードはこんな感じ.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;


public class UseJson {

 public static void main(String[] args) {
  ObjectMapper mapper = new ObjectMapper();
  URL apiUrl=null;
  double start_lon =135.779286;
  double start_lat = 35.028779;
  //  double stop_lon = 135.735928;
  //  double stop_lat = 35.028131;
  String json2 = null;
  String json = null;	

  try {
   String url_base = "http://cyberjapandata2.gsi.go.jp/general/dem/scripts/getelevation.php?";
   String url_lon = "lon="+	Double.toString(start_lon);
   String url_lat = "lat="+Double.toString(start_lat);
   String url = url_base+ url_lon+"&"+url_lat+"&outtype=JSON";
   apiUrl = new URL(url);
   BufferedReader reader = new BufferedReader(new InputStreamReader(apiUrl.openStream(),"UTF-8"));
   String line = null;

   while((line = reader.readLine()) != null){
    json+=line;
   }
   reader.close();
   json2 = json.substring(4,json.length());
   JsonNode rootNode = mapper.readTree(json2);

   for(JsonNode node:rootNode){
    System.out.println(node);
   }
  }
  catch (IOException e) {
   e.printStackTrace();
  }
 }
}

出力結果は

55.8
"5m(レーザ)"

とか出ます.
JSON形式の標高値elevationが上でいう55.8,ですね.経緯度は手動で入れてます...*1
プログラムの

json2 = json.substring(4,json.length());

jsonにnull{"elevation":55.8,"hsrc":"5m\uff08\u30ec\u30fc\u30b6\uff09"}が入っていてnull部分を泥臭く消しています..*2

スタートとゴールの間を100で刻んで取得した標高はこんな感じ
f:id:mtiit:20150305143628p:plain
やっぱりちょっと標高高いんですね...疲れたのも気のせいじゃなかったみたいです.お疲れさまでした...

*1:経緯度はGoogleMapで右クリックをすれば「この場所について」でその地点の経緯度が見れます.プログラムで取得する方法も検討してみよう...

*2:ここは最初から消せないんですかね

【Java】log4J入門 ~System.out.println()とはサヨナラ!~

Javaでコードを書く時には,いつもeclipseを使ってまして,コンソールで結果を確認する時にはSystem.out.printなどを使ってたんですが,Log4Jを使ってみたくなったので,今日からはこっちを使っていくことにします.
Log4Jのメリットを箇条書きでまとめてみると

  • ログ生成の制御が簡単
  • 動作のパフォーマンスも良い
  • ちょっとかっこいい*1

って感じみたいですね.
ただデメリットじゃないですけど,最初は設定やらなんやらを理解するのが大変でちょっとハードルは高い気がする.だから,まずはめちゃくちゃ簡単な使い方をしてLog4Jに慣れていくのが大事ではないのか!?とか思ったので超入門を書いてみます.自分のこれからのLog4J人生にとっても良さそう.*2

まずはLog4Jにはログを出力するか否かのレベルがあります.ざっと並べると,

    • FATAL
    • ERROR
    • WARN
    • INFO
    • DEBUG
    • TRACE

とあって上から順に出力する優先度(?)が高いらしい.だから,動作テストとかをする時はいっぱいログを出力するけど,有る程度までバグは減ってくると画面がごちゃごちゃするし大事なとこだけ出すか〜とかが簡単にできる.

Eclipseでの実行の仕方について.

まずテキトーにLog4Jを試したいファイルをプロジェクトの中に作って,

import org.apache.log4j.*;

とインポートを試みると,おそらくエラーが出るはず.
log4jをインストールしてないからですね.僕は
The Jakarta Site - Jakarta Downloads
のarchivesの中にlog4jというフォルダがあるのでjakarta-log4j-1.2.8.tar.gzをインストールしました.バージョンはその都度で違ってるかもしれないですけど,とりあえずこの手のファイルをインストール,解凍すると.そのフォルダの中の下図の位置にlog4jっぽいjarファイルがあるはず.
f:id:mtiit:20150208115713p:plain
Eclipseに戻る.
EclipseLog4Jを試したいファイルが存在するプロジェクトを左クリックでプロパティに行きます.ここで Java Build Path を選択してライブラリーを選ぶと,Add External JAR...とかいうボタンがあります.要は,外部のjarファイルにパスを通してくれるっていうことをしてくれます.そこでさっきのjarファイルを選択してOKしてみてください.おそらく

import org.apache.log4j.*;

のエラーは消えているはず.
ここまで来れば実際に使える('ω')/~

import org.apache.log4j.*;

public class Test {

	public static void main(String args[]){

		Logger logger = Logger.getLogger("Sample");
		BasicConfigurator.configure();
		logger.setLevel(Level.DEBUG);

		logger.debug("debug (^o^)/");
		logger.info("info (;w;)/");

	}
}

細かいことは置いといて,コンソールへの出力は

0 [main] DEBUG Sample  - debug (^o^)/
1 [main] INFO Sample  - info (;w;)/

になります.ポイントは,logger.setLevelのLevel.DEBUGをLevel.INFOにすると,(^o^)/は出てこないことです.System.out.println()からの乗り換え第一歩としては,今までこれで書いてたとこを全部INFOで出力する.次の段階では,出力するほどでもないけど細かいところを見たい時には出力して欲しいところをDEBUGにして書いておく.そっから先は,例えば出力先をコンソールじゃなくてファイルにしてみたり,などどんどん便利になっていくはず.僕もこれからもっと勉強していって,記事としてアウトプットすることで頭の整理になればな〜とか思ってます:-)

*1:個人的な見解

*2:昨日まではSystem.out.println()を使いまくってました

本ブログの趣旨

電気系から情報系の大学院に来た就職予備軍のmくんです.

なので情報系の一般常識があまりないのでいろいろとインプットを頑張ることはいいんですが,せっかくなのでアウトプットもしていこう〜とのことでぼちぼち自己満足ですが自分が詰まったところや自分の頭の整理のためにちょこちょこツラツラ書いていこうと思いますのでよろしくお願いします.