EMM研究会でポスター発表しました

JAISTで開催されたマルチメディア情報ハイディング・エンリッチメント(EMM)にて、ポスター発表をしました。これまでの研究成果を実際にアプリケーションとして実装し、デモンストレーションを交えながら説明させて頂きました。多くのご意見を頂いたので今後の検討ポイントとしたいと思います。

論文名: センシティブデータの漏洩検知による適応的な公開範囲設定システムのプロトタイプ実装

ADCS

ISSI2013で発表しました

国立情報学研究所で開催された国際ワークショップ:社会のイノベーションを誘発する情報システムで口頭発表をさせて頂きました。
SNS版のプライバシー侵害情報分類表をベースとしたプライバシー侵害検知アプリケーションを開発し、その初お披露目となり緊張しましたが、たくさんのフィードバックをいただき、今後の展開に役立てられそうです。

発表名:Setting of Access Control by Detecting Privacy Leaks in SNS

国際会議 SITIS2013で発表しました

京都で開催された国際会議 SIGNAL IMAGE TECHNOLOGY & INTERNET BASED SYSTEMS 2013で口頭発表をしました。
英語による口頭発表は初めての経験でしたが、レアジョブを約4年間続けてきた甲斐があり、問題なくこなせたと思います。
今後は、論文を正しい英語で速く書けるように、ライティングレベルの向上させていきます。

論文名:Settings of Access Control by Detecting Privacy Leaks in SNS

また、帰りがけに清水寺の夜間開放に行ってきました。
20131205_184617

コンピュータセキュリティシンポジウム2013にて、優秀論文賞・コンセプト論文賞を頂きました

コンピュータセキュリティシンポジウム2013(CSS2013)にて、優秀論文賞とコンセプト論文賞の2賞を頂きました。

論文名:SNS上のプライバシーセンシティブ情報の漏洩検知に基づく公開範囲の設定方式

今後もSNSにおけるプライバシー侵害にフォーカスし、研究を深めていきたいと思います。

卒業と入学

先週末で大学院修士課程を修了しました。科目聴講生時代を含めると2年半通ったことになります。
会社勤めをしながらの大学院であった為、途中半年間の出張もあったりと非常に苦しい時もありましたが、無事乗り切ることができました。何よりもこのような状況を支えてくれた妻に感謝します。

2年間の修士課程から何を得たのかを考えてみると、技術的なことは当然ながら、研究の進め方や考え方・プレゼンテーションといったソフトスキルまで多くのことを得たと実感しています。
自分のスキルを縦軸と横軸で考えると、2年前は縦横どちらも経験相応に伸びていたと思います。但し、複数に伸びている縦軸同士が上手くリンクしていなかったり、横軸の伸びが甘いことから曖昧な知識のもとで何となく進めていた部分がありました。このような状況を打破する為に、もう一度学び直そうと考えました。
結果的に、当初の目的はクリアでき、業務を進めていく中でも自分の様々なスキルが向上していることが日々実感できています。
また、研究として「SNSにおけるプライバシー侵害」をテーマとして、現代社会のプライバシー侵害への考え方、機械による侵害要因分析と検知手法を研究しました。入学前は自分がプライバシー侵害の研究をするとは考えても見なかったですが、今後更なる技術革新が進んでいくなかで本研究テーマは非常に良いテーマであったと思います。当然、1年間の研究からは大きな成果が出せておらず、改善点も多くありますので博士後期課程で同テーマの研究を継続することにしました。
博士課程ではこれまで以上に困難があると推測しますが、3年後に学位が取れるよう研究に邁進していきます。
最後に2年前と同様に博士課程へ進むことに対して、快諾してくれた妻に感謝します。

PSP:Personal Software Process – Lesson7

Lesson3, Lesson5, Lesson6で開発したプログラムをもとに、Lesson7用の仕様を追加していきます。
これにより、自身の計測値さえあれば、開発に対する多少のブレを含めた上での見積時間と見積総行数を見積もることが出来ます。
※ なお、Lesson5, 6に関しては、クラスとして利用出来るよう変更してください。

Lesson7: 「線形回帰パラメータと予測区間を計算する」
● プログラム仕様スクリプト
ASGKIT PROG7を参照のこと

● 実行結果
線形回帰パラメータと予測区間を計算する

rxy: 0.954497
r2: 0.911064
tail area: 0.000018
β0: -22.552533
β1: 1.727932
yk: 644.429384
range: 230.001974
UPI: 874.431357
LPI: 414.42741

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson7: 
 * 線形回帰パラメータと予測区間を計算する
 * 
 */

public class Program7 {

	private int size;
	private double[] data1;
	private double[] data2;
	private double sumData1;
	private double sumData2;
	private double avgData1;
	private double avgData2;
	private double powData1;
	private double powData2;
	private double sumMulti;
	private double beta0;
	private double beta1;
	private double r;
	private double r2;
	private double y;
	private double tailArea;
	private double p;
	private double x;
	private double deviation;
	private double ran;
	private double range;
	private double upi;
	private double lpi;

	public static void main(String[] args) {

		Program7 pg7 = new Program7();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			// 分析対象となるデータ行を指定する
			int data1 = Integer.parseInt(args[1]);
			int data2 = Integer.parseInt(args[2]);

			// 分析対象のLOC
			long num = Long.parseLong(args[3]);

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[data1 - 1]));
				linkData2.add(Double.parseDouble(strrec[data2 - 1]));
			}
			pg7.size = linkData1.size();

			pg7.data1 = new double[pg7.size];
			pg7.data2 = new double[pg7.size];

			// 合計値を計算する
			pg7.sumData1 = pg7.calcSum(linkData1);
			pg7.sumData2 = pg7.calcSum(linkData2);

			// 合計値から平均値を計算する
			pg7.avgData1 = pg7.calcAvg(pg7.sumData1);
			pg7.avgData2 = pg7.calcAvg(pg7.sumData2);

			// X, Yの二乗の合計値、X*Yの合計値を計算する
			pg7.powData1 = pg7.calcSumPow(linkData1);
			pg7.powData2 = pg7.calcSumPow(linkData2);
			pg7.sumMulti = pg7.calcSumMulti(linkData1, linkData2);

			// β1 を計算する
			pg7.beta1 = pg7.calcBeta1();

			// β0 を計算する
			pg7.beta0 = pg7.calcBeta0();

			// R, R2 を計算する
			pg7.r = pg7.calcR();
			pg7.r2 = pg7.calcR2();

			// tail areaを計算する
			pg7.tailArea = pg7.calcTail();

			// yを計算する
			pg7.y = pg7.calcY(num);

			// xを計算する
			pg7.x = pg7.calcX();

			// 標準偏差を計算する
			pg7.deviation = pg7.calcDeviation();

			// Rangeを前計算する
			pg7.ran = pg7.calcRan(num);

			// Rangeを計算する
			pg7.range = pg7.calcRange();

			// UPIを計算する
			pg7.upi = pg7.calcUPI();

			// LPIを計算する
			pg7.lpi = pg7.calcLPI();

			System.out.println("rxy: " + pg7.changeScale(pg7.r, 6));
			System.out.println("r2: " + pg7.changeScale(pg7.r2, 6));
			System.out.println("tail area: "
					+ new DecimalFormat("0.#######").format(pg7.changeScale(
							pg7.tailArea, 8)));
			System.out.println("β0: " + pg7.changeScale(pg7.beta0, 6));
			System.out.println("β1: " + pg7.changeScale(pg7.beta1, 6));
			System.out.println("yk: " + pg7.changeScale(pg7.y, 6));
			System.out.println("range: " + pg7.changeScale(pg7.range, 6));
			System.out.println("UPI: " + pg7.changeScale(pg7.upi, 6));
			System.out.println("LPI: " + pg7.changeScale(pg7.lpi, 6));

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 合計値を計算する
	 * 
	 */
	private Double calcSum(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Double.parseDouble(i.next().toString());
		}

		return sum;

	}

	/**
	 * 平均値を計算する
	 * 
	 */
	private Double calcAvg(Double sum) {

		return sum / size;

	}

	/**
	 * X, Yの2乗合計値を計算する
	 * 
	 */
	private Double calcSumPow(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Math.pow(Double.parseDouble(i.next().toString()), 2);
		}

		return sum;

	}

	/**
	 * X*Yの合計値を計算する
	 * 
	 */
	private Double calcSumMulti(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double sum = 0;
		int i = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			data1[i] = linkData1.pop();
			data2[i] = linkData2.pop();

			sum = sum + (data1[i] * data2[i]);
			i++;
		}

		return sum;

	}

	/**
	 * β1 を計算する
	 * 
	 */
	private Double calcBeta1() {

		double num1 = (sumMulti - (size * avgData1 * avgData2));
		double num2 = (powData1 - (size * avgData1 * avgData1));

		return num1 / num2;

	}

	/**
	 * β0 を計算する
	 * 
	 */
	private Double calcBeta0() {

		return avgData2 - (beta1 * avgData1);

	}

	/**
	 * R を計算する
	 * 
	 */
	private Double calcR() {

		double num1 = (size * sumMulti) - (sumData1 * sumData2);

		double num21 = (size * powData1) - (sumData1 * sumData1);
		double num22 = (size * powData2) - (sumData2 * sumData2);

		return num1 / (Math.sqrt(num21 * num22));

	}

	/**
	 * R2 を計算する
	 * 
	 */
	private Double calcR2() {

		return r * r;

	}

	/**
	 * Tail Area を計算する
	 * 
	 */
	private Double calcTail() {

		double num1 = r * Math.sqrt(size - 2);
		double num2 = Math.sqrt(1 - r * r);

		Tdist tDist = new Tdist();
		tDist.start(num1 / num2, size - 2);
		p = tDist.getP();

		return (double) (1 - 2 * p);

	}

	/**
	 * Y を計算する
	 * 
	 */
	private Double calcY(long num) {

		return beta0 + beta1 * num;

	}

	/**
	 * PからXを計算する
	 * 
	 */
	private Double calcX() {

		RevTdist RevTdist = new RevTdist();
		RevTdist.start(0.0, size - 2, 0.35);
		double x = RevTdist.getX();

		return x;

	}

	/**
	 * 標準偏差を計算する
	 * 
	 */
	private Double calcDeviation() {

		double sum = 0;
		for (int i = 0; i < data1.length; i++) {
			sum += Math.pow((data2[i] - beta0 - beta1 * data1[i]), 2);
		}

		double dist = sum / (size - 2);
		double dev = Math.sqrt(dist);

		return dev;

	}

	/**
	 * Rangeを前計算する
	 * 
	 */
	private Double calcRan(long num) {

		double num1 = Math.pow(num - avgData1, 2);

		double num2 = 0;
		for (int i = 0; i < data1.length; i++) {
			num2 += Math.pow(data1[i] - avgData1, 2);
		}

		double sq = 1 + (double) 1 / size + (double) (num1 / num2);

		return Math.sqrt(sq);

	}

	/**
	 * Rangeを計算する
	 * 
	 */
	private Double calcRange() {

		return x * deviation * ran;

	}

	/**
	 * UPIを計算する
	 * 
	 */
	private Double calcUPI() {

		return y + range;

	}

	/**
	 * LPIを計算する
	 * 
	 */
	private Double calcLPI() {

		return y - range;

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson6

Lesson5で開発したプログラムをもとに、Lesson6用の要件を追加します。

Lesson6: 「t分布関数を0.0からxの範囲で積分した結果がpとなるようなxを求める」
● プログラム仕様スクリプト
ASGKIT PROG6を参照のこと

● 実行結果
正しい積分結果値よりxをを絞り込みながら調整しつつ、シンプソン法の最適幅、積分結果、t分布関数値を得る

OK! [W=2]
Result P: 0.0
NG! [W=2] 0.062143
NG! [W=4] 0.000017
OK! [W=8]
Result P: 0.178335
NG! [W=2] 0.116564
NG! [W=4] 0.000061
OK! [W=8]
Result P: 0.31305
NG! [W=2] 0.145412
NG! [W=4] 0.00164
OK! [W=8]
Result P: 0.396
NG! [W=2] 0.139119
NG! [W=4] 0.008551
NG! [W=8] 0.000075
OK! [W=16]
Result P: 0.441942
NG! [W=2] 0.10295
NG! [W=4] 0.021035
NG! [W=8] 0.000398
OK! [W=16]
Result P: 0.466616
NG! [W=2] 0.048413
NG! [W=4] 0.035425
NG! [W=8] 0.001493
OK! [W=16]
Result P: 0.480029
NG! [W=2] 0.014094
NG! [W=4] 0.047057
NG! [W=8] 0.003926
NG! [W=16] 0.00001
OK! [W=32]
Result P: 0.487552
NG! [W=2] 0.077696
NG! [W=4] 0.052655
NG! [W=8] 0.007991
NG! [W=16] 0.00004
OK! [W=32]
Result P: 0.491935
NG! [W=2] 0.13875
NG! [W=4] 0.050897
NG! [W=8] 0.013594
NG! [W=16] 0.000132
OK! [W=32]
Result P: 0.494589
NG! [W=2] 0.195789
NG! [W=4] 0.041945
NG! [W=8] 0.020339
NG! [W=16] 0.000344
OK! [W=32]
Result P: 0.496255
NG! [W=2] 0.13875
NG! [W=4] 0.050897
NG! [W=8] 0.013594
NG! [W=16] 0.000132
OK! [W=32]
Result P: 0.494589
NG! [W=2] 0.167808
NG! [W=4] 0.04727
NG! [W=8] 0.016859
NG! [W=16] 0.000218
OK! [W=32]
Result P: 0.495514
NG! [W=2] 0.153413
NG! [W=4] 0.049303
NG! [W=8] 0.015197
NG! [W=16] 0.000171
OK! [W=32]
Result P: 0.495078
NG! [W=2] 0.146114
NG! [W=4] 0.050156
NG! [W=8] 0.014388
NG! [W=16] 0.000151
OK! [W=32]
Result P: 0.49484
NG! [W=2] 0.149772
NG! [W=4] 0.049743
NG! [W=8] 0.01479
NG! [W=16] 0.000161
OK! [W=32]
Result P: 0.494961
NG! [W=2] 0.151592
NG! [W=4] 0.049525
NG! [W=8] 0.014993
NG! [W=16] 0.000165
OK! [W=32]
Result P: 0.49502
NG! [W=2] 0.150681
NG! [W=4] 0.049634
NG! [W=8] 0.014892
NG! [W=16] 0.000162
OK! [W=32]
Result P: 0.49499
NG! [W=2] 0.151136
NG! [W=4] 0.049578
NG! [W=8] 0.014943
NG! [W=16] 0.000164
OK! [W=32]
Result P: 0.495005
NG! [W=2] 0.150912
NG! [W=4] 0.049609
NG! [W=8] 0.014916
NG! [W=16] 0.000164
OK! [W=32]
Result P: 0.494998
NG! [W=2] 0.151025
NG! [W=4] 0.049595
NG! [W=8] 0.014929
NG! [W=16] 0.000163
OK! [W=32]
Result P: 0.495001
NG! [W=2] 0.150967
NG! [W=4] 0.049602
NG! [W=8] 0.014923
NG! [W=16] 0.000163
OK! [W=32]
Result P: 0.495
Result X: 4.60400390625

● ソースコード

import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * PSP Lesson6:
 * t分布関数を0.0からxの範囲で積分した結果がpとなるようなxを求める
 */

public class Program6 {

    private double x;
    private int dof;
    private double p;
    private double d;
    private int W;
    private int initW;
    private double E;
    private int checkF1;
    private int checkF2;
    private long adjustCount;

    public Program6() {
        initW = 1;
        W = initW;
        E = 0.00001;
        d = 0.5;
        checkF1 = 0;
        checkF2 = 0;
        adjustCount = 0;
    }

    public static void main(String[] args) {

        Program6 pg6 = new Program6();

        Double doubleArray[] = new Double[2];

        // 実行時引数からx(trial value), dof, pを取得する
        pg6.x = Double.parseDouble(args[0]);
        pg6.dof = Integer.parseInt(args[1]);
        pg6.p = Double.parseDouble(args[2]);
        boolean loopF = true;

        while (loopF) {

            for (int i = 0; i < 2; i++) {

                doubleArray[i] = (double) 0;

                // wを計算する
                double w = pg6.x / pg6.W;

                for (int j = 0; j <= pg6.W; j++) {

                    // p2を計算する
                    double p2 = 1 + (Math.pow((double) w * j, 2) / pg6.dof);
                    p2 = pg6.changeScale(p2, 6);

                    // p3を計算する
                    double p3 = Math.pow(p2, -(double) (pg6.dof + 1) / 2);
                    p3 = pg6.changeScale(p3, 6);

                    // p4を計算する
                    double p4 = pg6.calcP4(p3, pg6.dof);
                    p4 = pg6.changeScale(p4, 6);

                    // p5を計算する
                    double p5 = p3 * p4;
                    p5 = pg6.changeScale(p5, 6);

                    // p6を計算する
                    double p6 = pg6.calcP6(w, p5, j);

                    doubleArray[i] += p6;

                }

                doubleArray[i] = pg6.changeScale(doubleArray[i], 6);

                if (i == 0) {
                    pg6.W = pg6.W * 2;
                } else {
                    if ((doubleArray[i - 1] - doubleArray[i]) < pg6.E
                            && (doubleArray[i] - doubleArray[i - 1]) < pg6.E) {
                        System.out.println("OK! [W=" + pg6.W + "]");
                        System.out.println("Result P: " + doubleArray[i]);

                        if (doubleArray[i] != pg6.p) {
                            pg6.adjustD(doubleArray[i]);
                            pg6.x += pg6.d;
                            pg6.W = pg6.initW;
                            i = -1;
                        } else {
                            System.out.println("Result X: " + pg6.x);
                            loopF = false;
                        }
                    } else {
                        if (doubleArray[i - 1] > doubleArray[i]) {
                            System.out.println("NG! [W="
                                    + pg6.W
                                    + "] "
                                    + new DecimalFormat("0.######")
                                            .format(doubleArray[i - 1]
                                                    - doubleArray[i]));
                        } else {
                            System.out.println("NG! [W="
                                    + pg6.W
                                    + "] "
                                    + new DecimalFormat("0.######")
                                            .format(doubleArray[i]
                                                    - doubleArray[i - 1]));
                        }
                        // 結果保持
                        doubleArray[i - 1] = doubleArray[i];

                        // シンプソン法適用時の幅拡張
                        pg6.W = pg6.W * 2;
                        i = 0;
                    }
                }

            }
        }
    }

    /**
     * dを調整する
     * 
     */
    private void adjustD(Double result) {

        adjustCount += 1;
        double w = (double) (result - p);

        if (d < 0) {
            d = -d;
        }
        if (adjustCount == 1) {
            if (w > 0) {
                checkF1 = -1;
                checkF2 = -1;
            } else {
                checkF1 = 1;
                checkF2 = 1;
            }
        }

        // 正しい積分結果値(P)を通り越えるまでは、0.5ポイント単位で増減させる
        // 通過後は、より詳細に増減させて積分結果値(P)に近づくように調整する
        if (checkF1 == checkF2) {
            if (w > 0) {
                d = -0.5;
                checkF2 = -1;
            } else {
                d = 0.5;
                checkF2 = 1;
            }
        } else {
            if (w > 0) {
                d /= 2;
                d = -d;
            } else {
                d /= 2;
            }
        }

    }

    /**
     * p4を計算する
     * 
     */
    private Double calcP4(Double p3, int dof) {

        double base = fact(((double) (this.dof + 1) / 2) - 1);
        double frac = fact((double) this.dof / 2 - 1.0);
        return base / (Math.pow(this.dof * Math.PI, 0.5) * frac);

    }

    /**
     * p6を計算する
     * 
     */
    private Double calcP6(Double w, Double p5, int i) {

        int multi = 0;

        if (i == 0 || i == this.W) {
            multi = 1;
        // 偶数の場合はmulti = 2
        } else if (i % 2 == 0) {
            multi = 2;
        // 奇数の場合はmulti = 4
        } else {
            multi = 4;
        }

        return (double) (w / 3) * multi * p5;

    }

    /**
     * 階乗計算
     * 
     */
    private double fact(double i) {

        if (i == 1) {
            return 1;
        } else {
            if (i == 0) {
                return i;
            } else if (i < 1) {
                return i * Math.sqrt(Math.PI);
            } else {
                // 再帰呼び出しする
                return i * fact(i - 1);
            }
        }

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

PSP:Personal Software Process – Lesson5

Lesson5: 「シンプソン法を使用して、t分布関数を数値的に分析する」
● プログラム仕様スクリプト
ASGKIT PROG5を参照のこと

● 実行結果
シンプソン法の幅をより詳細に設定していき、差分が0.00001以下であれば適切な幅とみなし終了する。

NG! [W=2] 0.094385
NG! [W=4] 0.02666
NG! [W=8] 0.000162
OK! [W=16]
Result: 0.494999

● ソースコード

import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * PSP Lesson5:
 * シンプソン法を使用して、t分布関数を数値的に分析する
 */

public class Program5 {

    private double x;
    private int dof;
    private int W;
    private double E;

    public static void main(String[] args) {

        Program5 pg5 = new Program5();

        Double doubleArray[] = new Double[2];

        // 実行時引数からx, dof, Wを取得する
        pg5.x = Double.parseDouble(args[0]);
        pg5.dof = Integer.parseInt(args[1]);
        pg5.W = Integer.parseInt(args[2]);
        pg5.E = 0.00001;

        for (int i = 0; i < 2; i++) {

            doubleArray[i] = (double) 0;

            // wを計算する
            double w = pg5.x / pg5.W;

            for (int j = 0; j <= pg5.W; j++) {

                // p2を計算する
                double p2 = 1 + (Math.pow((double) w * j, 2) / pg5.dof);
                p2 = pg5.changeScale(p2, 6);

                // p3を計算する
                double p3 = Math.pow(p2, -(double) (pg5.dof + 1) / 2);
                p3 = pg5.changeScale(p3, 6);

                // p4を計算する
                double p4 = pg5.calcP4(p3, pg5.dof);
                p4 = pg5.changeScale(p4, 6);

                // p5を計算する
                double p5 = p3 * p4;
                p5 = pg5.changeScale(p5, 6);

                // p6を計算する
                double p6 = pg5.calcP6(w, p5, j);

                doubleArray[i] += p6;

            }

            doubleArray[i] = pg5.changeScale(doubleArray[i], 6);

            if (i == 0) {
                pg5.W = pg5.W * 2;
            } else {
                if ((doubleArray[i-1] - doubleArray[i]) < pg5.E
                        && (doubleArray[i] - doubleArray[i-1]) < pg5.E) {
                    System.out.println("OK! [W=" + pg5.W + "]");
                    System.out.println("Result: " + doubleArray[i]);
                } else {
                    if (doubleArray[i-1] > doubleArray[i]) {
                        System.out
                                .println("NG! [W="
                                        + pg5.W
                                        + "] "
                                        + new DecimalFormat("0.######")
                                                .format(doubleArray[i-1]
                                                        - doubleArray[i]));
                    } else {
                        System.out
                                .println("NG! [W="
                                        + pg5.W
                                        + "] "
                                        + new DecimalFormat("0.######")
                                                .format(doubleArray[i]
                                                        - doubleArray[i-1]));
                    }
                    // 結果保持
                    doubleArray[i - 1] = doubleArray[i];

                    // シンプソン法適用時の幅拡張
                    pg5.W = pg5.W * 2;
                    i = 0;
                }
            }
        }
    }

    /**
     * p4を計算する
     * 
     */
    private Double calcP4(Double p3, int dof) {

        double base = fact(((double) (this.dof + 1) / 2) - 1);
        double frac = fact((double) this.dof / 2 - 1.0);
        return base / (Math.pow(this.dof * Math.PI, 0.5) * frac);

    }

    /**
     * p6を計算する
     * 
     */
    private Double calcP6(Double w, Double p5, int i) {

        int multi = 0;

        if (i == 0 || i == this.W) {
            multi = 1;
            // 偶数の場合はmulti = 2
        } else if (i % 2 == 0) {
            multi = 2;
            // 奇数の場合はmulti = 4
        } else {
            multi = 4;
        }

        return (double) (w / 3) * multi * p5;

    }

    /**
     * 階乗計算
     * 
     */
    private double fact(double i) {

        if (i == 1) {
            return 1;
        } else {
            if (i == 0) {
                return i;
            } else if (i < 1) {
                return i * Math.sqrt(Math.PI);
            } else {
                // 再帰呼び出しする
                return i * fact(i - 1);
            }
        }

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

PSP:Personal Software Process – Lesson4

Lesson4: 「ファイルから数値データのリストを読み込み、相対規模表を作成する」
● プログラム仕様スクリプト
ASGKIT PROG4を参照のこと

● 実行結果
シンプソン法の幅をより詳細に設定していき、差分が0.00001以下であれば適切な幅とみなし終了する。

Very Small: 6.3375
Small: 8.4393
Medium: 11.2381
Large: 14.965
Very Large 19.928

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson4: 
 * ファイルから数値データのリストを読み込み、相対規模表を作成する
 */

public class Program4 {

	private int size;
	private LinkedList<Double> linkArray;
	private LinkedList<Double> logArray;

	private double vs;
	private double s;
	private double m;
	private double l;
	private double vl;

	public static void main(String[] args) {

		Program4 pg4 = new Program4();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[1]));
				linkData2.add(Double.parseDouble(strrec[2]));
			}
			pg4.size = linkData1.size();

			// アイテム当たりの規模を計算する
			pg4.linkArray = new LinkedList<Double>();
			pg4.calcVol(linkData1, linkData2);

			// アイテム毎のln(x)を計算する
			pg4.logArray = new LinkedList<Double>();
			Double lnSum = pg4.calcLn();

			// ln(x)の平均値を計算する
			Double lnAvg = pg4.calcLnAvg(lnSum);

			// 平均からの対数値の分散を計算する
			double lnPowSum = pg4.calcSumPow(lnAvg);

			// 標準偏差を計算する
			double lnDeviation = pg4.calcDeviation(lnPowSum);

			// 規模範囲を計算する
			pg4.calcScale(lnAvg, lnDeviation);

			System.out.println("Very Small: " + pg4.vs);
			System.out.println("Small: " + pg4.s);
			System.out.println("Medium: " + pg4.m);
			System.out.println("Large: " + pg4.l);
			System.out.println("Very Large " + pg4.vl);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * アイテム当たりの規模を計算する
	 * 
	 */
	private void calcVol(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double i = 0;
		double j = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			i = linkData1.pop();
			j = linkData2.pop();

			this.linkArray.add(this.changeScale(i / j, 4));

		}

	}

	/**
	 * アイテム毎のln(x)を計算する
	 * 
	 */
	private Double calcLn() {

		double j = 0;
		double logSum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {

			j = this.changeScale(
					Math.log(Double.parseDouble(i.next().toString())), 6);
			this.logArray.add(j);

			// 合計値も計算する
			logSum += j;
		}

		return this.changeScale(logSum, 5);

	}

	/**
	 * ln(x)の平均値を計算する
	 * 
	 */
	private Double calcLnAvg(Double sum) {

		return this.changeScale(sum / size, 6);

	}

	/**
	 * 平均からの対数値の分散を計算する
	 * 
	 */
	private Double calcSumPow(double calclnAvg) {

		double sum = 0;
		for (Iterator i = logArray.iterator(); i.hasNext();) {
			sum = sum
					+ Math.pow(Double.parseDouble(i.next().toString())
							- calclnAvg, 2);
		}

		return this.changeScale(sum, 6);

	}

	/**
	 * lnの標準偏差を計算する
	 * 
	 */
	private Double calcDeviation(Double sum) {

		return this.changeScale(Math.sqrt(sum / (size - 1)), 6);

	}

	/**
	 * 規模範囲を計算する
	 * 
	 */
	private void calcScale(double lnAvg, double deviation) {

		vs = this.changeScale(Math.exp(lnAvg - (2 * deviation)), 4);
		s = this.changeScale(Math.exp(lnAvg - deviation), 4);
		m = this.changeScale(Math.exp(lnAvg), 4);
		l = this.changeScale(Math.exp(lnAvg + deviation), 4);
		vl = this.changeScale(Math.exp(lnAvg + (2 * deviation)), 4);

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson3

Lesson1, Lesson3に続いて、進めていきます。
各回のスクリプトはSoftware Engineering Instituteよりダウンロード可能です。

Lesson3: 「ファイルから数値データのリストを読み込み、相関係数と線形回帰パラメータを計算し、時間等の見積を行う」
● プログラム仕様スクリプト
ASGKIT PROG3を参照のこと

● 実行結果

β0: -22.54
β1: 1.7279
r: 0.9545
r2: 0.9111
y: 644.4294

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson3:
 * ファイルから数値データのリストを読み込み、相関係数と線形回帰パラメータを計算し、時間等の見積を行う
 */

public class Program3 {

	private int size;
	private double sumData1;
	private double sumData2;
	private double avgData1;
	private double avgData2;
	private double powData1;
	private double powData2;
	private double sumMulti;
	private double beta0;
	private double beta1;
	private double r;
	private double r2;
	private double y;

	public static void main(String[] args) {

		Program3 pg3 = new Program3();

		try {
			File f = new File(args[0]);
			FileInputStream fi = new FileInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(fi,
					"UTF-8"));

			// 分析対象となるデータ行を指定する
			int data1 = Integer.parseInt(args[1]);
			int data2 = Integer.parseInt(args[2]);

			// 分析対象のLOC
			long num = Long.parseLong(args[3]);

			LinkedList<Double> linkData1 = new LinkedList<Double>();
			LinkedList<Double> linkData2 = new LinkedList<Double>();
			String line;
			while ((line = br.readLine()) != null) {
				String[] strrec = line.split("t");
				linkData1.add(Double.parseDouble(strrec[data1 - 1]));
				linkData2.add(Double.parseDouble(strrec[data2 - 1]));
			}
			pg3.size = linkData1.size();

			// 合計値を計算する
			pg3.sumData1 = pg3.calcSum(linkData1);
			pg3.sumData2 = pg3.calcSum(linkData2);

			// 合計値から平均値を計算する
			pg3.avgData1 = pg3.calcAvg(pg3.sumData1);
			pg3.avgData2 = pg3.calcAvg(pg3.sumData2);

			// X, Yの二乗の合計値、X*Yの合計値を計算する
			pg3.powData1 = pg3.calcSumPow(linkData1);
			pg3.powData2 = pg3.calcSumPow(linkData2);
			pg3.sumMulti = pg3.calcSumMulti(linkData1, linkData2);

			// β1 を計算する
			pg3.beta1 = pg3.calcBeta1();

			// β0 を計算する
			pg3.beta0 = pg3.calcBeta0();

			// R, R2 を計算する
			pg3.r = pg3.calcR();
			pg3.r2 = pg3.calcR2();

			// 最終的な結果を得る
			pg3.y = pg3.calcY(num);

			System.out.println("β0: " + pg3.beta0);
			System.out.println("β1: " + pg3.beta1);
			System.out.println("r: " + pg3.r);
			System.out.println("r2: " + pg3.r2);
			System.out.println("y: " + pg3.y);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 合計値を計算する
	 * 
	 */
	private Double calcSum(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Double.parseDouble(i.next().toString());
		}

		return sum;

	}

	/**
	 * 平均値を計算する
	 * 
	 */
	private Double calcAvg(Double sum) {

		return this.changeScale(sum / size, 2);

	}

	/**
	 * X, Yの2乗合計値を計算する
	 * 
	 */
	private Double calcSumPow(LinkedList<Double> linkArray) {

		double sum = 0;
		for (Iterator i = linkArray.iterator(); i.hasNext();) {
			sum = sum + Math.pow(Double.parseDouble(i.next().toString()), 2);
		}

		return sum;

	}

	/**
	 * X*Yの合計値を計算する
	 * 
	 */
	private Double calcSumMulti(LinkedList<Double> linkData1,
			LinkedList<Double> linkData2) {

		double sum = 0;
		double i = 0;
		double j = 0;
		while (linkData1.size() != 0 || linkData2.size() != 0) {
			i = linkData1.pop();
			j = linkData2.pop();

			sum = sum + (i * j);
		}

		return sum;

	}

	/**
	 * β1 を計算する
	 * 
	 */
	private Double calcBeta1() {

		double num1 = (sumMulti - (size * avgData1 * avgData2));
		double num2 = (powData1 - (size * avgData1 * avgData1));

		return this.changeScale(num1 / num2, 4);

	}

	/**
	 * β0 を計算する
	 * 
	 */
	private Double calcBeta0() {

		return this.changeScale(avgData2 - (beta1 * avgData1), 2);

	}

	/**
	 * R を計算する
	 * 
	 */
	private Double calcR() {

		double num1 = (size * sumMulti) - (sumData1 * sumData2);

		double num21 = (size * powData1) - (sumData1 * sumData1);
		double num22 = (size * powData2) - (sumData2 * sumData2);

		return this.changeScale(num1 / (Math.sqrt(num21 * num22)), 4);

	}

	/**
	 * R2 を計算する
	 * 
	 */
	private Double calcR2() {

		return this.changeScale(r * r, 4);

	}

	/**
	 * Y を計算する
	 * 
	 */
	private Double calcY(long num) {

		return this.changeScale(beta0 + beta1 * num, 4);

	}

	/**
	 * 四捨五入を行う
	 * 
	 */
	private double changeScale(double val, int i) {

		BigDecimal bd = new BigDecimal(val);
		return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

	}

}

PSP:Personal Software Process – Lesson2

前回に続いて、Lesson2を実施しました。
分析結果は特に載せていませんが、簡単なプログラムでもバグがあったり、結構時間を食ったりしました。
なお、PSPではコーディングに凝り過ぎる必要は無いと明記していますので、大まかな感じでOKです。
但し、コーディングフェーズを終了する際は要件が確実に含まれているか、コードエラーがないかなどのチェックを設けてる必要があります。

Lesson2: 「ソースファイルの行数、クラ数数、メソッド数をカウントする」
● プログラム仕様スクリプト
ASGKIT PROG2を参照のこと

● 実行結果

Part Name: Program3
Number of Items: 11
Part Size: 112
Total Size: 122

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.ArrayList;

/**
 * PSP Lesson2: 
 * ソフトウェア開発プロセス特論 演習1 Date 2011/10/13 ソースファイルの行数、クラス数、メソッド数をカウントする
 */
public class Program2 extends StreamTokenizer {

	private Boolean wordBool = false;
	private String beforeWord = null;
	private String className = null;
	private int lineSize = 0;
	private int totalSize = 0;
	private int methodCnt = 0;
	ArrayList<String> classArray = new ArrayList<String>();
	ArrayList<String> methodArray = new ArrayList<String>();

	public Program2(Reader r) {
		super(r);

		this.eolIsSignificant(true); // 改行コードを検出する
		this.slashStarComments(true); // /* */ 型のコメント処理を有効にする
		this.slashSlashComments(true); // //型コメントの処理を有効にする
		this.wordChars('_', '_'); // 英数字の他に識別子に使える文字の定義
		this.parseNumbers(); // 数値リテラルを解析する

		classArray.add("class");
		classArray.add("interface");
		methodArray.add("void");
	}

	public static void main(String[] args) {

		Reader reader = null;

		try {
			reader = new BufferedReader(new FileReader(new File(args[0])));
			Program2 pg2 = new Program2(reader);
			pg2.checkCnt();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	void checkCnt() throws IOException {
		while (this.nextToken() != TT_EOF) {
			switch (this.ttype) {
			case TT_EOL: // 改行コードの検出
				if (this.wordBool == true) {
					this.lineSize++;
					this.wordBool = false;
				}
				this.beforeWord = "r";
				break;
			case TT_WORD: // 語の検出(キーワードを含む)
				this.wordBool = true;

				if (this.beforeWord != null) {
					// method件数をカウントする (コンストラクタは対応出来ていない)
					if (this.methodArray.contains(this.sval)
							|| this.beforeWord.equals("return")) {
						this.methodCnt++;
					}

					// class名を取得する, 複数class対応
					if (this.classArray.contains(this.beforeWord)) {
						this.className = this.sval;
						if (this.totalSize > 0) {
							this.print();
						}
						this.initialize();
					}
				}
				this.beforeWord = this.sval;

				break;
			default: // その他のトークン {}()[]+-=*/<>,;:
				this.wordBool = true;
				this.beforeWord = "symbol";
				break;
			}
		}

		// 最終行に改行が無いケースの対応
		if (this.beforeWord.equals("symbol")) {
			this.lineSize++;
		}

		this.totalSize += this.lineSize;

		this.print();
		System.out.println("Total Size: " + this.totalSize);

	}

	private void print() {

		System.out.println("Part Name: " + this.className);
		System.out.println("Number of Items: " + this.methodCnt);
		System.out.println("Part Size: " + this.lineSize);

	}

	private void initialize() {

		this.totalSize += this.lineSize;
		this.lineSize = 0;
		this.methodCnt = 0;

	}
}

PSP:Personal Software Process – Lesson1

PSP: Personal Software Process とは、ソフトウェア開発効率の向上の為に、個人毎のソフトウェア開発プロセスを見直しする枠組みです。
PSPはグローバルに多くの大学やソフトウェア開発会社で適用しており、マイクロソフトやボーイングでも使用しているそうです。
個々がPSPによって、自分の開発時の癖やパフォーマンスを把握・改善を行い、開発効率向上を行います。
最終的には全チームメンバがPSPを実施し、TSP:Team Software Process へと発展させます。

今回はPSPガイドブック ソフトウェアエンジニア自己改善 (IT Architects’ Archive)に沿いながら、8本ほどのプログラムを開発し、開発プロセスの見直しをしました。
各プログラムは小さいものばかりで、コーディング自体は2時間程あればコーディング出来ると思います。
今回使用する言語はJAVAを選択しましたが、PHPやPascal、Cでも何でも大丈夫です。
但し、狙いはコーディングではなく、実装前の全体計画や設計、テスト、バグの傾向等を含めた事後分析と多岐に俯瞰する必要があります。
なお、各回のスクリプトはSoftware Engineering Instituteよりダウンロード可能です。

Lesson1: 「ファイルから数値データのリストを読み込み、データの平均値と標準偏差を計算する」
● プログラム仕様スクリプト
ASGKIT PROG1を参照のこと

● 実行結果

Average: 60.32
Standard Deviation: 62.26

● ソースコード

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * PSP Lesson1:
 * ファイルから数値データのリストを読み込み、データの平均値と標準偏差を計算する
 */

public class Program1 {

    private LinkedList<Double> linkArray;
    private double resultAvg;
    private double resultStgDeviation;

    public static void main(String[] args) {

        Program1 pg1 = new Program1();

        try {
            File f = new File(args[0]);
            FileInputStream fi = new FileInputStream(f);
            BufferedReader br = new BufferedReader(new InputStreamReader(fi,
                    "UTF-8"));
            pg1.linkArray = new LinkedList<Double>();
            String line;
            while ((line = br.readLine()) != null) {
                pg1.linkArray.add(Double.parseDouble(line));
            }

            pg1.calcAvg();
            pg1.calcStgDeviation();

            System.out.println("Average: " + pg1.resultAvg);
            System.out.println("Standard Deviation: " + pg1.resultStgDeviation);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 平均値を計算する
     * 
     */
    private void calcAvg() {

        double avg = 0;
        for (Iterator i = linkArray.iterator(); i.hasNext();) {
            avg = avg + Double.parseDouble(i.next().toString());
        }

        resultAvg = this.changeScale(avg / linkArray.size(), 2);

    }

    /**
     * 標準偏差を計算する
     * 
     */
    private void calcStgDeviation() {

        // 1. データ毎に平均値との差分を2乗し合計値を取る
        // 2. 合計値をデータ個数-1で除算し、平方根を計算する
        double wkStg = 0;
        for (Iterator i = linkArray.iterator(); i.hasNext();) {
            wkStg = wkStg
                    + Math.pow(Double.parseDouble(i.next().toString())
                            - resultAvg, 2);
        }

        resultStgDeviation = this.changeScale(
                Math.sqrt(wkStg / (linkArray.size() - 1)), 2);

    }

    /**
     * 四捨五入を行う
     * 
     */
    private double changeScale(double val, int i) {

        BigDecimal bd = new BigDecimal(val);
        return bd.setScale(i, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

}

Perl, Python, PHPのベンチマークを計測しました

Perl, Python, PHPのベンチマークを計測しました。

=====
1. 環境 及び シナリオ

  1. 実行環境
  2. 実行環境は以下である。
    Platform: Linux(CentOS 5.6)  @sakura VPS
    Memory: 512 MB
    CPU: 仮想2Core

  3. プログラミング言語
  4. 実行・考察を行うプログラミング言語はLAMP構成として通常扱われるスクリプト言語の以下を使用する。
    ・Perl
    ・Python2.4
    ・PHP5.2

  5. 実施シナリオ
  6. No. 演算 概要
    1 i = i + 1 加算
    2 i = i – 1 減算
    3 i = i * i 乗算
    4 i = i / 3 除算
    5 i = i % 3 剰余
    6 s = “Hello,World!” 文字列代入
    7 i = len(string) 文字列数
    8 s = random() ランダム(指定なし)
    9 s = random(1, 100) ランダム(指定あり)
    10 IF (s = “Hello,World!) 文字列判断
    11 IF (i = 5) 数値判断
    12 IF (i = TRUE) Bool値判断
    13 IF文ネスト 条件分岐ネスト
    14 Switch文 ループ(Switch)
    15 While文 ループ(While)
    16 For文 ループ(For)
    17 Foreach文 ループ(Foreach)
    18 print “Hello, World!” 文字列出力
    19 関数呼び出し 関数
    20 File Open, Close オープン, クローズ
    21 File read 1行毎の読み込み
    22 File Write 1行毎の書き込み

    実行時のループ回数は、10000000回ループさせる。但し、実行に時間の掛かり過ぎるシナリオ等に関しては回数を減らす事とする。例外シナリオは以下である。
    No.17: 100000回 PHPにおいて例外が発生する為。
    No.18: 10000回 標準出力に文字列出力する為、処理
    対象のprint文の計測が行いづらい為。

  7. 実施前考察
  8. 本レポートで実行対象となるプログラミング言語はそれぞれ実績のある言語であり、Webサービス開発においてよく使用されている。私はPerl, PHPの経験はあるがPythonは今回が初めての使用となる。
    Perlは全体的に処理が速く、ファイル操作に長けているイメージがある為、全般的にPerlのパフォーマンスが良いと考えている。
    PHPは全体的に処理が遅いイメージがある。特にrandom()やファイル操作が遅いイメージがある。
    Pythonを使用するのは今回が初めてである為、あまりイメージが出来ないが、処理速度的にはPerlと同様の計測値となると予測する。

    よって、 ほぼ全シナリオでPerl < Python < PHP の順番で処理コストが高くなると考える。

  9. 実施方法
  10. 以下の要領で実施を行う。

    1. プログラミング言語毎に22個のプログラムを作成する。
    2. 実施シナリオ毎のアルゴリズムは同一とする。実行時間計測アルゴリズムに関しては後述する。
    3. 言語によっては命令が存在しないケースがある為、その場合は代替(同等)機能を使用する。No.16のPythonにおけるFor文等。
    4. Perlに関しては、ミリ秒単位を取得する組み込み関数がない為、Time::Hiresモジュールを使用する。
    5. シナリオ毎に10回実行し、実行結果として平均値を記録する。その際のコマンドは以下ように実行する。(以下はPythonでの例)
      # loop=1; while [ $loop -le 10 ]; do loop=`expr $loop + 1`; ./check1.py >> result/check1; done;
    6. eの実行結果から平均値を出力するスクリプトを実行し、平均値を得る。平均値出力スクリプトは以下である。
      ====
      #! /usr/bin/perl
      open(IN, $ARGV[0]);
      while (<IN>) {
      $total += $_;
      $count++;
      }
      close(IN);
      print $total / $count . “n”;
      ====
  11. 実行時間計測方法とサンプルコード
  12. 言語毎の実行時間計測は以下のように行う。

    以下では、各言語のシナリオ1のプログラムである。

    Perl
    ====
    #!/usr/bin/perl
    use Time::HiRes;

    my $starttime = Time::HiRes::time;
    for($i=0; $i<10000000; $i++) {
    }
    my $looptime = Time::HiRes::time – $starttime;
    my $starttime = Time::HiRes::time;

    my $j=0;
    for(my $i=0; $i<10000000; $i++) {
    $j = $j + 1;
    }
    print Time::HiRes::time – $starttime – $looptime . “n”;
    ====

    Python
    ====
    #!/usr/bin/python
    import time

    starttime = time.clock()
    for i in range(10000000):
    pass

    looptime = time.clock() – starttime
    starttime = time.clock()

    j = 0
    for i in range(10000000):
    j = j + 1

    print time.clock() – starttime – looptime
    ====

    PHP
    ====
    <?php
    $starttime = microtime(true);
    for($i = 0; $i<10000000; $i++) {
    }
    $looptime = microtime(true) – $starttime;
    $starttime = microtime(true);

    $j=0;
    for($i=0; $i<10000000; $i++) {
    $j = $j + 1;
    }
    echo microtime(true) – $starttime – $looptime . “n”;
    ?>
    ====

2. 実行

  1. 実行結果
  2. 以下が実行結果である。シナリオ毎に最も良いタイムに橙色にしている。また、特徴的な値となった結果は赤色とし、次項以降で考察を行う。なお、単位は全て”秒”である。

    演算 Perl Python PHP
    i = i + 1 0.8 1.04 0.44
    i = i – 1 0.81 1.13 0.45
    i = i * i 0.82 1.3 0.46
    i = i / 3 0.74 1.56 0.73
    i = i % 3 0.84 1.47 0.5
    s = “Hello,World!” 0.87 0.65 0.93
    i = len(string) 1.12 1.64 1.8
    s = random() 0.22 2.05 1.73
    s = random(1, 100) 0.95 27.65 2.86
    IF (s = “Hello,World!) 0.89 0.51 0.78
    IF (i = 5) 0.94 0.49 0.44
    IF (i = TRUE) 0.97 1.08 0.37
    IF文ネスト 0.60 2.05 1.67
    Switch文 251.03 2.81 2.01
    While文 1.13 1.64 0.77
    For文 0.67 1.18 0.91
    Foreach文 0.02 0.01 0.06
    print “Hello, World!” 1.2 0.47 0.96
    関数呼び出し 1.74 1.52 1.56
    File Open, Close 69.01 61.46 80.04
    File read 2 1.46 3.54
    File Write 2.02 4.94 23.18

3. 考察

実行前の考察では、全てのシナリオでPerlが優位だと考えていたが、結果は異なっていた。
この考察としてはPerlの時間計測用の関数において、ミリ秒単位で計測が出来なかった為、
Time::Hiresモジュールを使用した事が影響している可能性がある。
但し、Perlならではの優位性が示されているシナリオもある為、現状のまま考察を進める事とする。

  1. randint()
  2. Pythonのrandint()が非常に遅い結果となった。Perlと比較すると約27倍の差である。調査したところ、Perl, PHPのrand()は組み込み関数であるのに対し、Pythonのrandint()は標準モジュールでの提供であった。
    また、randint()よりもchoice()を使用した方が速いとの情報があった為、変更し実行計測したところ、
    27.65 → 12.36 と半分以下の実行結果となった。但し、Perl, PHPと比較するとまだ遅い結果であった。
    この事からC等で記述される組み込み関数はやはり速いということが分かった。

  3. switch文
  4. Perlのswitch文が異常な程、遅い結果となった。これはPerlには組み込みでswitch文がない為、標準モジュールのSwitchを使用した事が影響していると考えられる。最速であったPHPと比較するとその差は100倍である。
    3.1のrand()と同様に以下に組み込み機能が速いかを再認識させられた。

  5. File操作
  6. 実行前の考察よりPHPはファイル操作が不得意であると考えていたが、その通りの結果であった。特にfopen(), fclose(), fwrite()のパフォーマンスが悪かった。
    また、ファイル操作はPerlが非常に有利だと考えていたが、Pythonが健闘している。

  7. その他
  8. シナリオ毎の実行結果の色付けを確認すると、PHP列に橙色が多く付いている。PHPは全般的に処理が遅いと考えていたが、結果から見ると異なっていた。以前の経験ではPHPのrandom()は遅いと感じていたが、やはりPerlと比較すると倍以上の差が出ていた。(Pythonとの比較に関しては、3.1を参照の事)

  9. 最後に
  10. それぞれのプログラミング言語には高速化技術が存在する。例えば、Perl: mod_perl, Python: mod_wsgi, PHP: APC, eAcceralator等である。これらの技術を使用すれば、より良いパフォーマンスが出やすいが、その前段階として、言語毎の特徴を押さえた上でプログラミング言語の選択や処理ロジック, アルゴリズムを組んでいく必要がある。

=====

おわりです。

技術サイクルと情報アーキテクトの位置づけ

大学院で技術サイクルと情報アーキテクトの位置づけを事例と共に考察しました。
合っているかは分かりません。適当に流してください。。

1. 技術サイクルとは
技術または企業が進化・発展する為には技術サイクルが必要不可欠である。
具体的には以下の流れを繰り返すことで、成長していく。

  1. 技術の不連続性
  2. 1→2の変化には、エバンジェリストが必要

  3. 不安定期
  4. 2→3の変化には、市場創造者が必要

  5. ドミナントデザイン
  6. 3→4の変化には、マネジメントが必要

  7. 安定期
  8. 4→1の変化には、発明・発見者必要

本考察では、1: ”技術の不連続性”→2: ”不安定期”→3: ”ドミナントデザイン”までの2期で活躍をする、
エバンジェリストと市場創造者の動きに着目し、今後の技術者の在り方について考察する。

2. MySQL
考察時のモデル事例として、MySQLを取り上げる。
私が初めてMySQLを知ったのは2005年春でバージョンは4.0の頃であった。
それまでMS SQLServer, DB2といった商用データベース(以下、DB)を使用して、
アプリケーションを組んでいたが、この頃初めてオープンソースDBに触れた。
Webサービス構築を通して、すぐにMySQLの虜になってしまった。
その当時を振り返りながら、MySQLの進化・発展、取り巻く環境の変化を確認していく。

2.1. MySQLの収益源
MySQLはオープンソースDBである為、無償で使用可能である。(※厳密にはGPL Version2)
使用者側でMySQLを使いこなす事が出来れば、追加のコンサルティング費用等は不要である。
MySQL社では、各種サポート(問題発生時のサポートなど)やサービス(チューニングなど)を提供しており、
年間売上は$60million – 70million程である。(認知度に対して決して大きくない)

2.2. ある日本人MySQLコンサルタント
現在、日本語によるMySQLの本が多数出版されており、中には内部構造を詳細に解説した本や、
パフォーマンスチューニングのTIPSをふんだんに散りばめられた本もある。
2005年当時、英語の本は多数出ていたが、日本語によるMySQL本は3冊のみであった。
但し、内容的に古かったり薄かったりとコンテンツとしては充分とは言えない状況であった。
自分が担当していたWebサービスのパフォーマンス改善をしていたが、MySQL4.0→4.1→5.0→・・・と、
バージョンアップする毎にパフォーマンス改善や新機能が盛り込まれていった為、これらの情報収集には苦労していた。
そんな時「現場で使える MySQL」(著:松信 嘉範さん)という本が出版された。
今でこそ、この本よりも多くのTIPSが書かれた本は多くあるが、当時としてはこれ以上の本はなく、
何度も繰り返し読み、また実機で実践をした。
この頃は既にMySQLはWeb系サービスでかなり使用されており、
メジャーな存在であったが、まだまだ未完成なデータベースで
文字コードの問題やストレージエンジンのパフォーマンス問題などの問題があった。
この本はこういった問題を丁寧に解説+対応方針を示し、他エンジニアも含めて参考にしていた。

2.3. WebサービスとMySQL
今日の有名なWeb系サービスでMySQLを使用していないサービスはほぼ無いと思われる。
前述の2005,6年頃では、YahooやGoogleは大規模に使用をしていたし、
新鋭サービスとしてはYoutubeや日本ではmixiが大量のアクセス・データを
どのようなアーキテクチャで捌くかに注目が集まっていた。
これらのサービスの成功により、MySQLの認知度は一気に上がり、様々なサービスの中核となった。
LAMPがWebサービスのデファクトスタンダードとして広まったのもこの頃であった。
一方、エンタープライズ業界ではまだ浸透しきれておらず、現場で使用されるまでに至っていなかった。
現在はWeb・エンタープライズ共に、多くのMySQLが稼働しており、商用データベースを脅かす存在となっている。

2.4. SUNによる買収
2008年2月、SUNによりMySQLは買収された。
収益はサービス・サポートからで$60million – 70million程であったMySQLを何故買収したのか。
理由はMySQLを使用する多数のユーザの存在である。
これらユーザの大多数はSUN以外のサーバを使用しており、
買収をする事で自社のサーバへ誘導する事が狙いであったと考えられる。

2.5. SAPの画策
SAPとMySQLは2003年頃から提携して、次世代DBを開発してきた。
MySQLをベースとして、SAP用に機能を削ぎ落したDBである。
このDBではSAP DBと言われ、後にMAX DB, live cacheと言われるデータベースとなった。
SAPがMySQLと組んだ理由としては、Oracleを打ち負かす為だと考えられる。
OracleはSAPと競合するERPを保持している為、SAPとしてはOracle DBを使用する事は推奨しない。
また、TomorrowNowに関連した賠償問題もある。
現在、SAP ERPのバックエンドで稼働可能なDBとしてSQL Server, DB2, Oracle, MAXDBが選択肢としてあるのだが、
OracleはERPで競合となるソフトウェアを保持しており競合にあたる為、メリットが無い。
そこで、メリット/デメリットあるもののライセンス費用がかかる3社ではなく、
ライセンス費用をかけずにSAPに特化したDBを共同開発可能なMySQLを選択したと考えられる。
しかし、SAPは2007年9月頃にMySQL社との提携を解除し、次なる道を模索し始めた。
結果、2010年5月にSybase買収へと進んだと思われる。この買収の狙いはOracleへの対抗である事は明白である。
また、Sybaseはインメモリ技術を保持していた為、現状のアーキテクチャの流れに足していたとも言える。
その後、このインメモリを搭載したリアルタイム分析ツール HANAをリリースした。

2.6. Oracleによる買収
2009年4月、OracleはSUNを買収した。この買収は最近の流行りである垂直統合システム化への道である。
自社でハードウェア・ソフトウェア・データベース等を一貫して提供し、
自社製品によるエコシステムを作り上げて親和性を武器に顧客を囲い込む作戦である。
前段のSAPによるSybase買収もこの垂直統合システム化を狙ったエコシステム構築の1つの動きである。
またOracleにはSUN買収にもう1つ狙いがあった。それはMySQLの存在である。
Webアプリケーションを席巻するMySQLを手にすることで、エンタープライズ側のOracle DB、
Web側のMySQLを使い分けることが出来る。
Oracleには更にメリットがあった。ストレージエンジンのInnoDBである。
MySQLはDBの核となるストレージエンジンを入れ替える事が出来る。
デフォルトのストレージエンジンはMyISAMであったが、このエンジンはトランザクションをサポートしていない為、
トランザクションを伴う場合はInnoDBを使用する。
以前からMySQLの存在が邪魔であったOracleは、2005年10月にInnoDBを保有するInnobase社を買収した。
これにより、MySQLは自身のソフトウェアの中核ストレージエンジンを奪われる事となる。
但し、Oracle側で制限をしなかった為、今までと変わらずにInnoDBは使用可能であった。
但し、SUN買収後のバージョン5.5からはデフォルトストレージエンジンがMyISAM→InnoDBへと変更された。
Oracle社内では元々あったInnoDBチームと買収したMySQLチームを統合し、開発にスピード間が出てきたとの事である。

2.7. RDBMSへの刺客
2009年、RDBMSへの刺客としてkey-value型のデータベースが注目され始めた。
これはNoSQL(Not Only SQL)と言われる。
Key-value型の考え方は以前からあり、memcached等によりWebのアプリケーションサーバではよく使用されてきた。
情報が爆発的に増えていくなかで、Big Dataを効率的に処理する為にkey-value型データベース等のNoSQLに注目が集まっている。当初は、RDBMSが無くなる・・と言った意見もあったが、現状は適材適所でRDBMSとNoSQLを使い分けていくというのが流れである。
NoSQLの流れに対抗する為に、MySQLではバージョン5.6からmemcachedプロトコルを利用したNoSQLアクセスを提供する予定である。
NoSQLなDBが今後どのように発展していくかは分からないが、RDBMSと適材適所で使い分ける必要がある。

2.8. MySQLコアメンバの動き
冒頭で、日本人のMySQLコンサルタントの話をしたが、今度はMySQLのコアメンバの動きを確認する。
まず、MySQLの創設者であるMichael “Monty” Wideniusは、MySQLからforkしたMariaDBを開発し始めた。(Mariaは娘の名前)
このDBは、ストレージエンジン”Maria”をデフォルトエンジンとするDBで、MySQL6.0(リリースされず)で出荷される予定だった機能が盛り込まれている。データベース本体として、MySQLを打ち負かす可能性は低いが、ストレージエンジンの1つの選択肢として使用されていく可能性はある。
 次にMySQLの開発責任者であったBrian Akerは、MySQLからforkしたDrizzleを開発し始めた。
このDBはクラウド上での使用を想定しており、Webサービスのバックエンドで使用する時に不要と思われる機能を極限まで削ったDBである。
今後、Cassandra等のNoSQLのライバルとなってくる可能性がある。

3. 考察
以上が、ここ数年でMySQLに起こった事象である。
この個人や企業の様々なアクションを技術サイクルに当て込み考察する。

3.1. 技術の不連続性 (エバンジェリスト)
まず、日本人MySQLコンサルタントの松信嘉範さんである。
彼はMySQLがまだ日本に浸透したとは言えなかった時代にDBマガジン(少し前に廃刊)や
書籍でMySQLの良さを布教していった。
彼がいなければ、日本におけるMySQLの成功はなかったかもしれない。

3.2. 不安定期 (市場創造者)
市場創造者はいくつかありそうである。以下にまとめた。

  1. SUN
  2.  MySQLを使用しているたくさんのユーザを狙った買収を行い、
     MySQLユーザを自社のサーバへ誘導しようとした。

  3. SAP
  4.  Oracleに勝つ為に、自前のDBを開発しようとしていた。
     また、開発を取りやめ、Sybaseを買収し、最近の流行りの垂直統合化で自社のエコシステムを構築しようとしている。クラウドに関しては、流れに乗り遅れているように感じる。

  5. Oracle
  6.  SUNの買収、MySQLへ対抗する為にInnoBaseの買収と、無駄がないように感じる。
     周囲の競合ベンダを威嚇しながら、買収を繰り返し、垂直統合システムを完成させていっている。だが、これはベンダーロックインとなる為、懸念される可能性もある。(中立的な立場であるPublicSectorがMicrosoftを嫌がり、OpenOfficeへ入れ替えていっているように)

  7. MySQL
  8.  MySQLはWebで圧倒的な強さを見せた。
     これは、Web業界を対象として製品をプッシュしていったからではないだろうか。
     また、Webがオープンソースを利用していくという流れにも乗っていた。
     だが、その他のオープンソースDB御三家(MySQL, PostgreSQL, Firebird)と何が違ったのだろうか。
     特に機能としては、優っているとの見解もあるPostgreSQLは何が足りなかったのか興味がある。

3.3. ドミナントデザイン (マネジメント)
ここでの登場人物はいなかったので、割愛する。

3.4. 安定期 (発明・発見者)
2人のMySQLの開発者は、新たな道を歩み初めている。
MontyはMariaDBを始め、BrianAkerはDrizzleを進めている。
先日、どちらのDBも新バージョンをリリースした為、次のTermである技術の不連続性へ進んでいく。
彼らはMySQL時代にエバンジェリストを経験し、既に流れを作った経験を持っている。
これはまさに技術のサイクルでもある。

3.5. 1技術者として自分が出来る事、取るべき行動
ここ数年は今後を左右する技術の変遷期にあたると考えている。
まず、技術者が避けなければならない事として、自分のスキルが陳腐化することである。
バズワードのように、たくさんの技術が出てきて、1部はメジャーとなり、一部はすぐ消える。
また、既存の技術であっても消えていく事も充分ありえる。
よって、業界動向、技術サイクルを見極め、今どこのTermにいるのか、
エバンジェリストや市場創造者はどこに進もう・進ませようとしているかを冷静に判断しなければならない。
その上で自分がアーリーアダプターとなり、他者をリードしていけるように心がける。(そうなりたい)
以前はエンタープライズ業界がIT業界を牽引していた。
例えば、IBM、Microsoftといったベンダーが新しい技術を産み、市場を創造していった。
しかし、今はWeb業界がIT業界を牽引している。
例えば、GoogleがAndroidを開発し、携帯メーカーはそのOSを利用して製品を作る
(もはや、auのCMはAndroidの機能紹介となっており、au独自の機能はないように感じる)。
Twitterがブームとなったことにより、Salesforce.のchatterやSAPのマイクロブログツールへと連鎖した。
このことから、まずはWebに認められるか否かが、ポイントとなると考えられる。
そのWebを支えているのは技術者である。よって、Webに認められたければ、技術者へ訴え続ける必要がある。
エグゼクティブを対象としたカンファレンスはかりを開くのではなく、
技術者を対象とした技術カンファレンスを定期的に開き、技術者を味方につけることである。
技術者は次の技術サイクルを創出していく可能性を常に秘めている。

以上。

QRコード with ZXing

ZXing(”Zebra Crossing”)を使用したQRコード生成ツールを作成しました。
ZXingはGoogleがオープンソースで公開しているQRコード生成ライブラリです。
Androidでも使用されていますが、今回はローカル起動までとなります。
以下では、Antを使用してビルドしていますので、Antのセットアップをしておいてください。

1. ZXing セットアップ
まず、Google CodeからZXingをダウンロードします。
任意のディレクトリへ展開します。私は “C:Eclipse_programzxing-1.6” としました。
次に展開済みのフォルダ内にある READ MEファイルを開き、ビルド用のコマンドを確認します。

Please refer to the project page for more information:
http://code.google.com/p/zxing/
in particular:
http://code.google.com/p/zxing/wiki/GettingStarted

To get started, you can try building and running the command-line client;
you will need to have Apache's Ant tool installed to run this:

ant -f core/build.xml
ant -f javase/build.xml
java -cp javase/javase.jar:core/core.jar com.google.zxing.client.j2se.CommandLineRunner [URL]

コマンドプロンプトを開き、展開をしたディレクトリへ移動し、12,13行目のAntコマンドを実行します。

C:Eclipse_programzxing-1.6>ant -f core/build.xml
Buildfile: C:Eclipse_programzxing-1.6corebuild.xml

clean:
   [delete] Deleting directory C:Eclipse_programzxing-1.6corebuild
   [delete] Deleting: C:Eclipse_programzxing-1.6corecore.jar

build:

init:

compile:
    [mkdir] Created dir: C:Eclipse_programzxing-1.6corebuild
    [javac] C:Eclipse_programzxing-1.6corebuild.xml:36: warning: 'includeant
runtime' was not set, defaulting to build.sysclasspath=last; set to false for re
peatable builds
    [javac] Compiling 171 source files to C:Eclipse_programzxing-1.6corebuild
      [jar] Building jar: C:Eclipse_programzxing-1.6corecore.jar

BUILD SUCCESSFUL
Total time: 10 seconds
C:Eclipse_programzxing-1.6>ant -f javase/build.xml
Buildfile: C:Eclipse_programzxing-1.6javasebuild.xml

init:

build:
    [javac] C:Eclipse_programzxing-1.6javasebuild.xml:40: warning: 'includea
ntruntime' was not set, defaulting to build.sysclasspath=last; set to false for
repeatable builds

BUILD SUCCESSFUL
Total time: 0 seconds

最後にEclipseで使用する為に “C:Eclipse_programzxing-1.6corecore.jar” をライブラリ追加すればOKです。

2. 最終的なソースコード
何らかの文字列を入力すると、都度QRコードを生成するようになっています。
また、保存ボタン押下で生成済みのQRコードを保存します。
ちょっと怪しい部分もありますが、、ひとまず動くと思います。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;

public class Url2QR extends JFrame implements ActionListener {

	private static Url2QR frame;
	private final JTextField textField;
	private final JButton dispButton;
	private final JButton saveButton;
	private final JLabel label;
	private final JPanel panelTop;
	private final JPanel panelBottom;
	private final ImageIcon icon;
	private final BufferedImage bufImg;
	private static int SIZE = 4;

	public Url2QR() throws WriterException {
		super("QRコード生成");

		panelTop = new JPanel();
		panelBottom = new JPanel();

		textField = new JTextField(20);
		dispButton = new JButton("表示");
		saveButton = new JButton("保存");

		bufImg = barcodeWrite();
		icon = new ImageIcon(bufImg);
		label = new JLabel(icon);

		Container container = getContentPane();
		container.setLayout(new BorderLayout());

		container.add(textField, BorderLayout.CENTER);
		container.add(panelTop, BorderLayout.EAST);
		container.add(panelBottom, BorderLayout.SOUTH);
		panelTop.setLayout(new BorderLayout());
		panelTop.add(dispButton, BorderLayout.WEST);
		panelTop.add(saveButton, BorderLayout.EAST);
		panelBottom.setLayout(new BorderLayout());
		panelBottom.add(label, BorderLayout.WEST);

		dispButton.setActionCommand("display");
		dispButton.addActionListener(this);
		saveButton.setActionCommand("save");
		saveButton.addActionListener(this);
		saveButton.setEnabled(false);

		KeyListener keyListener = new KeyListener() {
			public void keyPressed(KeyEvent keyEvent) {
			}

			public void keyReleased(KeyEvent keyEvent) {
				repaintIcon();
			}

			public void keyTyped(KeyEvent keyEvent) {
			}
		};
		textField.addKeyListener(keyListener);
	}

	public void actionPerformed(ActionEvent e) {

		if (e.getActionCommand().equals("display")) {
			repaintIcon();
		} else if (e.getActionCommand().equals("save")) {
			saveQR();
		}

	}

	public void repaintIcon() {

		try {
			if (textField.getText().isEmpty()) {
				saveButton.setEnabled(false);
			} else {
				saveButton.setEnabled(true);
			}
			label.setIcon(new ImageIcon(barcodeWrite()));
		} catch (WriterException e) {
			e.printStackTrace();
		}
		label.setToolTipText(textField.getText());
		frame.pack();

	}

	public void saveQR() {

		JFileChooser filechooser = new JFileChooser();

		int selected = filechooser.showSaveDialog(this);
		if (selected == JFileChooser.APPROVE_OPTION) {
			File file = filechooser.getSelectedFile();
			try {
				ImageIO.write(barcodeWrite(), "png", file);
			} catch (IOException e) {
				System.out.println(e);
			} catch (WriterException e) {
				e.printStackTrace();
			}
		} else if (selected == JFileChooser.CANCEL_OPTION) {
		} else if (selected == JFileChooser.ERROR_OPTION) {
			label.setText("Error");
		}
	}

	public BufferedImage barcodeWrite() throws WriterException {

		Hashtable hints = new Hashtable();
		hints.put(EncodeHintType.CHARACTER_SET, "SHIFT_JIS");
		QRCode qrCode = new QRCode();
		Encoder.encode(textField.getText(), ErrorCorrectionLevel.L, hints,
				qrCode);
		ByteMatrix byteMatrix = qrCode.getMatrix();

		BufferedImage image = new BufferedImage(byteMatrix.getWidth() * SIZE,
				byteMatrix.getHeight() * SIZE, BufferedImage.TYPE_4BYTE_ABGR);

		if (!textField.getText().isEmpty()) {
			Graphics2D g2D = image.createGraphics();
			for (int y = 0; y < byteMatrix.getHeight(); y++) {
				for (int x = 0; x < byteMatrix.getWidth(); x++) {
					if (byteMatrix.get(x, y) == 1) {
						g2D.setColor(Color.black);
					} else {
						g2D.setColor(Color.white);
					}
					g2D.fillRect(x * SIZE, y * SIZE, SIZE, SIZE);
				}
			}
		}

		return image;

	}

	public static void startGUI() throws WriterException {

		frame = new Url2QR();
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
		frame.setVisible(true);

	}

	public static void main(String[] args) {

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				try {
					startGUI();
				} catch (WriterException e) {
					e.printStackTrace();
				}
			}
		});
	}

}

ソフトウェア技術者としての責務に関する考察

大学院のソフトウェア工学で、「ソフトウェア開発の専門家としてのソフトウェア技術者は、雇用主によって明示的に要求されていない場合であっても、保守と変更が容易なプログラムを作成する責務がある。」に対する考察を行ったので、今後の備忘のために残します。

考察があっているかは分かりませんが、心に留めながら仕事していきたいと思います。

 

1. 「ソフトウェア開発の専門家としてのソフトウェア技術者は、雇用主によって明示的に要求されていない場合であっても、保守と変更が容易なプログラムを作成する責務がある。」に対する考察をせよ。

結論から述べると、ソフトウェア技術者は成果物リリース後の運用フェーズを常に意識しながらソフトウェア開発を行う必要がある。また、常にそのソフトウェアを使用するユーザの立場に立ち、ソフトウェア開発を行う事も重要である。理由として、一度リリースしたシステムやソフトウェアは7~10年間程使用される為、この長いソフトウェアライフサイクルの中で、どのような事が起きる・行われるかを意識するかしないかにより、ソフトウェア本体や各種ドキュメント等の成果物の優劣が大きく変わってくるからである。この結論に至るまでの過程を次項以降で詳細に分析を行う。

2. 「ソフトウェア技術者の職業倫理」による分析

ソフトウェア技術者の職業倫理として常に心掛ける事として、「ユーザ志向であること」を挙げる。ユーザとは、ソフトウェアの発注元(顧客)やそのソフトウェアを実際に使用する使用者を指す。私達ソフトウェア技術者は誰の為に何の為にこのソフトウェアを開発しているかを考えると、最終的にユーザ志向に行き着くと考えている。また、再発注を受ける為には、顧客満足度を上げる必要もある。ユーザ志向を持つと、ユーザインタフェースが格段に良くなる。開発者としては工数の掛かる機能であっても、その機能を10年間使用するユーザにとっては、その一瞬の不都合な機能が積み重なる事により、大きな対応工数になる事が多い。例えば、1機能毎に開発担当者が別であったとする。それぞれの開発担当者が実現し切れない部分を安易に”ユーザによるマニュアル対応”と位置付けて処理した場合、何が起きるか。対象機能が100あれば、100のマニュアル対応が発生する事になる。マニュアル対応によるヒューマンエラーを無くし、極力システム化する事が私達の使命である事が念頭にあれば、安易な仕様決定とはならないはずである。次の職業倫理として、「保守性を意識すること」がある。ソフトウェアはリリース後に必ず機能追加やバグが発生する。その際に、保守性が高いソフトウェアは機能追加やバグ修正が容易である。保守性が低い場合、対応時の工数が大きく異なる。1機能毎の対応工数に差がある場合、ソフトウェアが使用される10年間ではどの程度差が開くかを考えると、保守性を意識することの重要性は理解出来る。また、対応工数差の比率は常に同じではない。保守性が低いながらも、機能追加を行い続けたソフトウェアは、後続の機能追加が非常に難しくなる。難しくなると、設計、実装、テスト等の各工程で工数を大きく取られる為、対応工数差の比率は更に広がる。よって、保守性を意識することは、対価を支払う顧客の為でもあり、運用保守を行う仲間の為にもなる。

3. 「ソフトウェアの品質」による分析

ソフトウェアの品質とは人により捉え方が異なるものである。例えば、ソフトウェア技術者にとっては、ソースコードの品質であり、そのレベル感も個々に異なる。ユーザにとっては、システム(ソフトウェア)の品質であり、同様に個々に使用感や、ビジネスプロセスの効率化に品質を感じるかもしれない。よって、ソフトウェア技術者として大事な事は、誰の為のソフトウェアであるかを意識することである。個人的にはソースコードの品質が大事なのは当然の事として、よりユーザの立場に立ち、分析・設計・実装等を行って行く事が、ソフトウェアの価値向上に繋がり、ソフトウェア品質が向上する。ユーザの立場に立ったのと同様に運用保守担当者の立場に立った場合はどのように振舞うべきか。意識をしていなければ、成果物には反映されない事は明白である。

4. 「ソフトウェアプロセス」による分析

ウォーターフォールモデルのような規範的なプロセスモデルとアジャイル開発のようなアジャイルプロセスモデルを比較すると、どちらも一長一短があり、どちらが優れているということは言えない。よって、必ずどちらかのプロセスモデルで実施するのではなく、状況により選択していく柔軟性が大事である。例えば、運用保守フェーズであれば、アジャイルプロセスモデルが適している。顧客もチームメンバとして迎え、設計→プロト実装→本実装→テスト→リリースのイテレーションを行う。これにより保守運用フェーズのスピード感を出し易い。一方、規範的なプロセスモデルでは、開発フェーズの前にプロトタイプフェーズを行うのが良い。プロトタイプを行う事で余分な工数が掛かるが、顧客やユーザとの仕様のズレが解消され、顧客満足度を上げやすい。また、プロトタイプを実装するにあたり、調査等も行う為、設計・実装工程以降のインプットともなり易く、結果として工数短縮に繋がりやすい。設計・実装の前にある程度詳細に把握出来ていると、後工程の成果物精度が上がり、結果より良いソフトウェアライフサイクルに繋がるのではないだろうか。

5. 「ソフトウェアの分析、設計、実装、テスト」による分析

本項ではソフトウェアの分析、設計、実装、テストの各工程における分析を行う。

 
5.1. 分析

分析工程ではオブジェクト指向分析、構造化分析等の分析方法があるが、最終的な成果物は、顧客のようなソフトウェア技術者以外の人が確認し易い記法で書かなければならない。例えば、オブジェクト指向分析であれば、UMLを使用し、ユースケース図やユースケース記述、アクティビティ図等であり、構造化分析であれば、DFD等である。これらの成果物は設計工程以降のインプット情報となり、運用保守にも読まれる成果物となる。また、要求仕様化をするにあたり、As-Isモデル、To-Beモデルを定義すると良い。これらは顧客側や開発側の漏れを無くし、ビジネスプロセスの再構築に役立つ。同様に運用保守時の理解促進に役立つ。

 
5.2. 設計

設計書は何の為にあるか。私は運用保守フェーズ以降の為にあると考える。運用保守フェーズでは、新しいベンダーや担当者が入ってくる事が多いが、分析工程の成果物と一緒に参照されるのは設計書類となる。よって、設計書は実装の為に書くのではなく、保守対応者へのメッセージとして書く必要がある。但し、注意点として、設計書が必ず正しいわけではない。結局真実はソースコードそのものである。また、設計と言っても、基本設計、概要設計、詳細設計と複数あるが、実装をする為の詳細設計は必要ないと考えている。必要があれば、後追いで作成が可能である。実装中に詳細設計に記述している内容とズレが出てくる事は普通であるし、運用保守において詳細設計書を確認する事は少ないはずだからである。前述しているが、詳細設計書を参照するのであれば、ソースコードを確認したほうが良い。詳細設計が無い事により、設計書の作成や修正といった無駄な工数削減が可能である。注意点として、その前工程にあたる概要設計の記述レベルとして詳細に記述する事は極力避けるべきである。特に大規模開発で開発工程を開発パートナーへ依頼する場合は、ある程度の仕様に揺れ(不明点や考慮が必要だと感じてもらう)を持たせ、開発工程で検討を行ってもらう事が大事である。理由として、請け負った時点である程度詳細に決まっていると、更なる詳細部分(例えば、頻度の低いエラーケース等)を考慮しなくなる恐れがあるからである。よって、各設計書の記述レベルを事前に定義し、周知徹底する事が結果的に品質のよいソフトウェアが完成する。

 
5.3. 実装

シンプルなソースコードは、可読性・保守性・開発効率、そして美がある。言い換えると美しいソースコードは結局シンプルである。シンプルなコードを実装する事は、ソフトウェアライフサイクルが上手く循環する為の根幹となる。シンプルなコード→可読性が良い→保守性が良い→開発効率が上がる→機能毎の工数が減る→他機能に工数を掛ける→→→……(循環する)また、最低限の処理効率化は必要あるが、過度な効率化やテクニカルな記述は避けるべきである。個々の要件にもよるが、以前と比較し、CPUやメモリ等のハードウェア性能も上がっている為、過度な効率化を進めるよりも、多少の緩みを持たせ可読性を上げるべきである。必要であれば、スケールアウト・スケールアップや他技術の併用によりパフォーマンス改善は可能である。

 
5.4. テスト

テストの無いソフトウェア開発は有り得ない。これは誰もが共通の認識であると思うが、テストにたいする意識レベルの差により出来は全く違うものになる。まず、ソフトウェアのテストは何の為にあるのかを考えると、テストケースやテストデータ作成に対して、気を抜けなくなるはずである。私にとってテストケース・データの作成タスクは楽しい作業ではないが、ケースやデータがソフトウェアの仕様を網羅しているかを常に意識している。テスト方法としては、ホワイトボックステストよりもブラックボックステストの方が大事である。ソフトウェアが持つ本来の仕様を満たしているかどうかは、ブラックボックスでないと分からないからである。また、後工程で発覚したテスト不足は前工程に逆戻りするだけでなく、コストに大きなインパクトを与える事が多い。テスト工程は時間が掛かるが、やはり大事な工程である。以上のような意識を持ながらテストを行う事で、運用保守フェーズにおけるバグ発覚は非常に少なくなる。

6. まとめ

以上の事から、保守と変更が容易なプログラムを作成する為には、ソフトウェア技術者がどの立ち位置で振舞うかによって、結果は全く異なる。ソフトウェア構築サービスを請け負う以上、当然これらの責務を負い、遂行しなければならないと考える。

以上

 

JUDE→astah*

大学院のレポートでオブジェクト指向分析設計をする必要があったので、家のマシンにUMLモデリングツールを
入れようと思い、チェンジビジョンのサイトにいったら、JUDEは提供終了となっていました。
後継製品として、astah*(アスター)という製品が出ていたようだ。
community版があったので、使用してみましたが、違和感なく普通に使えました。

astah*とJUDEの機能比較、データ互換性のまとめが以下にありました。
機能比較
データ互換性

今後はこちらを使用させてもらおうと思います。

ちなみに、チェンジビジョンは少し前に親会社が永和システムマネジメントさんに変わっていたのですね。
チェンジビジョンの親会社の異動について

入学式に行ってきました

今日は大学院の入学式に行ってきました。
昨年から科目履修生として通っていましたが、今日から1年生です。

私はオライリー本の目次を見るとワクワクする事が多いのですが、この大学院の科目群を初めて見た時、同様の感覚を感じました。
単純ですが、これが複数候補からこの大学院から選んだ理由です。きっと、自分が今欲しい知識・情報・技術と合致していたのでしょう。
何でもそうですが、今したいと思った時が最善の時です。読みたいと思った本は今読むのが最も効果があるのです。
私は行動をして上手くいかなかったら次に行くようにしています。

この2年間は以下をより意識しながら過ごしてみます。
 ■既に得ている知識を更に深く・体系的に学ぶ
 ■不足している知識・技術を幅広く補完する
 ■全ての情報を得る時間は無い為、取捨選択を大事にする

快く了承してくれた妻に感謝しつつ、安くはないお金と大切な時間を大事にしながら、
少しでも大きく成長して、2年後を迎えたいです。

なお、入学式はスーツで行った方が良いようです・・・(私は私服で行って、失敗しました。。)