ファジイ推論

  以下,複数のファイル構成になっています.ファイル間の区切りを「---・・・」で示します.

------------------------規則ファイル--------------
推論方法 0 規則の数 3 変数の数 1 積分の分割数 100
規則1
 変数1 1 中心と幅 1.0 1.0
 右辺(中心と幅) 1.0 15.0
規則2
 変数1 1 中心と幅 2.0 1.0
 右辺(中心と幅) 11.0 15.0
規則3
 変数1 1 中心と幅 3.0 1.0
 右辺(中心と幅) 21.0 15.0

------------------------推論ファイル--------------
変数の数 1 データ数 21
変数1 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0
       2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0

---------------------プログラム------------------
/****************************/
/* ファジイ推論             */
/*      coded by Y.Suganuma */
/****************************/
import java.io.*;
import java.util.StringTokenizer;

public class Test {
	/****************/
	/* main program */
	/****************/
	public static void main(String args[]) throws IOException, FileNotFoundException
	{
		double x[], xx[][], y;
		int i1, i2, n_d, n_val;
		StringTokenizer str;
					// エラー
		if (args.length != 3) {
			System.out.print("***error   入力データファイル名を指定して下さい\n");
			System.exit(1);
		}

		else {
					// オブジェクトの生成
			Fuzzy fz = new Fuzzy (args[0]);
					// 推論データの読み込み
			BufferedReader in = new BufferedReader(new FileReader(args[1]));
			str = new StringTokenizer(in.readLine(), " ");
			str.nextToken();
			n_val = Integer.parseInt(str.nextToken());
			str.nextToken();
			n_d = Integer.parseInt(str.nextToken());
			x  = new double [n_val];
			xx = new double [n_val][n_d];
			for (i1 = 0; i1 < n_val; i1++) {
				str = new StringTokenizer(in.readLine(), " ");
				str.nextToken();
				for (i2 = 0; i2 < n_d; i2++) {
					if (!str.hasMoreTokens())
						str = new StringTokenizer(in.readLine(), " ");
					xx[i1][i2] = Double.parseDouble(str.nextToken());
				}
			}
			in.close();
					// 推論とその結果の出力
			PrintStream out = new PrintStream(new FileOutputStream(args[2]));
			for (i1 = 0; i1 < n_d; i1++) {
				for (i2 = 0; i2 < n_val; i2++) {
					x[i2] = xx[i2][i1];
					out.print(x[i2] + " ");
				}
				y = fz.Inf(x);   // 推論
				out.println(y);
			}
		}
	}
}

class Fuzzy {
	private double left[][][];   // [i][j][0] : 中心
                                 //       [1] : 幅
	private double right[][];   // [i][0] : 中心
                                //    [1] : 幅
	private double omega[];   // ωの値
	private int bun;   // 積分の分割数
	private int method;   // 推論方法
                          //    =0 : and,or
                          //    =1 : *,or
                          //    =2 : *,+
	private int n_rule;   // 規則の数
	private int n_val;   // 変数の数
	private int rule[][];   // [i][j] =0 : i番目の規則はj番目の変数を使用しない
                            //        =1 : i番目の規則はj番目の変数を使用する

	/************************************/
	/* コンストラクタ                   */
	/*      name : 入力データファイル名 */
	/************************************/
	Fuzzy (String name) throws IOException, FileNotFoundException
	{
		int i1, i2;
		StringTokenizer str;

		BufferedReader in = new BufferedReader(new FileReader(name));
	/*
	     基本データの入力
	*/
		str = new StringTokenizer(in.readLine(), " ");
		str.nextToken();
		method = Integer.parseInt(str.nextToken());
		str.nextToken();
		n_rule = Integer.parseInt(str.nextToken());
		str.nextToken();
		n_val = Integer.parseInt(str.nextToken());
		str.nextToken();
		bun = Integer.parseInt(str.nextToken());
	/*
	     領域の確保
	*/
		left  = new double [n_rule][n_val][2];
		right = new double [n_rule][2];
		omega = new double [n_rule];
		rule  = new int [n_rule][n_val];
	/*
	     規則データの入力
	*/
		for (i1 = 0; i1 < n_rule; i1++) {

			in.readLine();
						// 左辺
			for (i2 = 0; i2 < n_val; i2++) {
				str = new StringTokenizer(in.readLine(), " ");
				str.nextToken();
				rule[i1][i2] = Integer.parseInt(str.nextToken());
				if (rule[i1][i2] > 0) {
					str.nextToken();
					left[i1][i2][0] = Double.parseDouble(str.nextToken());
					left[i1][i2][1] = Double.parseDouble(str.nextToken());
				}
			}
						// 右辺
			str = new StringTokenizer(in.readLine(), " ");
			str.nextToken();
			right[i1][0] = Double.parseDouble(str.nextToken());
			right[i1][1] = Double.parseDouble(str.nextToken());
		}

		in.close();
	}

	/*******************************************/
	/* 交点の計算                              */
	/*      x : x                              */
	/*      c : 中心                           */
	/*      h : 幅                             */
	/*      r : <0 : 左辺のメンバーシップ関数  */
	/*          >=0 : 右辺(ω)                 */
	/*      return : 交点                      */
	/*******************************************/
	double Cross(double x, double c, double h, double r)
	{
		double x1, x2, y, y1, y2;

		if (x < c) {
			x1 = c - h;
			x2 = c;
			y1 = 0.0;
			y2 = (r < 0.0 || (r >= 0.0 && method == 0)) ? 1.0 : r;
		}
		else {
			x1 = c;
			x2 = c + h;
			y1 = (r < 0.0 || (r >= 0.0 && method == 0)) ? 1.0 : r;
			y2 = 0.0;
		}

		y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);

		if (y < 0.0)
			y = 0.0;
		else {
			if (r >= 0.0 && method == 0 && y > r)
				y = r;
		}

		return y;
	}

	/**********************************************/
	/* 右辺の計算                                 */
	/*      x : 値                                */
	/*      om[i] : <0 : i番目の規則を使用しない */
	/*              >=0 : ωの値                  */
	/*      return : xにおける右辺の値           */
	/**********************************************/
	double Height(double x, double [] om)
	{
		double y = 0.0, y1;
		int i1, sw;

		sw = 0;

		for (i1 = 0; i1 < n_rule; i1++) {

			if (om[i1] >= 0.0) {

				y1 = Cross(x, right[i1][0], right[i1][1], om[i1]);

				if (sw == 0) {
					y  = y1;
					sw = 1;
				}

				else {
					if (method < 2) {
						if (y1 > y)
							y = y1;
					}
					else
						y += y1;
				}
			}
		}

		return y;
	}

	/**************************/
	/* 推論の実行             */
	/*      x[i] : 各変数の値 */
	/*      return : 推論値   */
	/**************************/
	double Inf(double [] x)
	{

		double x1, y;
		int i1, i2;
	/*
	     ωの計算
	*/
		for (i1 = 0; i1 < n_rule; i1++) {

			omega[i1] = -1.0;

			for (i2 = 0; i2 < n_val; i2++) {

				if (rule[i1][i2] > 0) {

					if (x[i2] > left[i1][i2][0]-left[i1][i2][1] &&
                        x[i2] < left[i1][i2][0]+left[i1][i2][1])
						x1 = Cross(x[i2], left[i1][i2][0], left[i1][i2][1], -1.0);
					else
						x1 = 0.0;

					if (omega[i1] < 0.0)
						omega[i1] = x1;
					else {
						if (method == 0) {
							if (x1 < omega[i1])
								omega[i1] = x1;
						}
						else
							omega[i1] *= x1;
					}
				}
			}
		}
	/*
	     右辺の計算
	*/
		y = Result(omega);

		return y;
	}

	/**********************************************/
	/* 右辺による推論                             */
	/*      om[i] : <0 : i番目の規則を使用しない */
	/*              >=0 : ωの値                  */
	/*      return : 推論値                       */
	/**********************************************/
	double Result(double [] om)
	{
		double h, max = 0.0, min = 0.0, x1, x2, y = 0.0, y1, y2, z1, z2;
		int i1, sw;
	/*
	     積分範囲と積分幅の計算
	*/
		sw = 0;

		for (i1 = 0; i1 < n_rule; i1++) {
			if (om[i1] >= 0.0) {
				x1 = right[i1][0] - right[i1][1];
				x2 = right[i1][0] + right[i1][1];
				if (sw == 0) {
					min = x1;
					max = x2;
					sw  = 1;
				}
				else {
					if (x1 < min)
						min = x1;
					if (x2 > max)
						max = x2;
				}
			}
		}

		h = (max - min) / bun;

		if (h < 1.0e-15) {
			System.out.print("***error  invalid data (h = 0) ***\n");
			System.exit(1);
		}
	/*
	     積分(重心の計算,台形則)
	*/
		z1 = Height(min, om);
		z2 = Height(max, om);
		y1 = 0.5 * (min * z1 + max * z2);
		y2 = 0.5 * (z1 + z2);
		x1 = min;

		for (i1 = 0; i1 < bun-1; i1++) {
			x1 += h;
			z1  = Height(x1, om);
			y1 += x1 * z1;
			y2 += z1;
		}

		y1 *= h;
		y2 *= h;

		if (Math.abs(y2) > 1.0e-10)
			y = y1 / y2;
		else {
			System.out.print("***error  invalid data (y2 = 0) ***\n");
			System.exit(1);
		}

		return y;
	}
}
		

  コンパイルした後,

java Test 規則ファイル名 推論ファイル名 出力ファイル名

と入力してやれば実行できます.出力ファイル名は,結果を出力するファイルの名前です.また,規則ファイル名は,規則等を記述したファイルの名前であり,たとえば以下のような形式で作成します.
推論方法 0 規則の数 3 変数の数 1 積分の分割数 100 規則1  変数1 1 中心と幅 1.0 1.0  右辺(中心と幅) 1.0 15.0 規則2  変数1 1 中心と幅 2.0 1.0  右辺(中心と幅) 11.0 15.0 規則3  変数1 1 中心と幅 3.0 1.0  右辺(中心と幅) 21.0 15.0
  日本語で記述した部分(「推論方法」,「規則の数」等)は,次に続くデータの説明ですのでどのように修正しても構いませんが,削除したり,または,複数の文(間に半角のスペースを入れる)にするようなことはしないでください.上のデータは,次に示す 3 つの規則を表現したものであり,また,各データの意味はそれ以降に示す通りです.

   if xがおよそ1 then yはおよそ1
   if xがおよそ2 then yはおよそ11
   if xがおよそ3 then yはおよそ21

推論方法

=0 : AND, OR  AND 演算と OR 演算を利用
=1 : *, OR  AND 演算の代わりに乗算を利用
=2 : *, +  上に加え,OR 演算の代わりに加算を利用

規則の数

  規則の数を入力します

変数の数

  変数の数を入力します

積分の分割数

  推論値を求めるため,メンバーシップ関数の数値積分を行います.その際の分割数を入力します.

規則1

  次の「規則2」までのデータを規則の数だけ繰り返して入力します.

変数1 1 中心と幅 1.0 1.0

  この例では,変数の数が 1 であるため,1 行だけですが,一般的には,この行のデータを変数の数だけ入力します.「変数1」の次の数字は,この規則で対応する変数を使用するか否かを指定するためのものです.0 の場合は使用しない(「中心と幅」以降のデータは入力しないでください),また,1 の場合は使用することを意味しています.使用する場合は,メンバーシップ関数の形を決めるため,「中心と幅」以降のデータが必要となります.このプログラムでは,三角型のメンバーシップ関数だけを扱っています.「中心と幅」に続く 2 つのデータは,三角形の中心の位置と,底辺の半分の長さを意味しています.

右辺(中心と幅) 1.0 15.0

  規則の右辺のメンバーシップ関数の形を指定します.右辺におけるメンバーシップ関数の形も三角形です.

  推論ファイルは,推論値を得たい x の値を指定するためのファイルであり,以下のような形式で作成します.
変数の数 1 データ数 21 変数1 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0
  各データの意味は以下の通りです.

変数の数

  変数の数を入力します.規則ファイルで指定した値と一致する必要があります.

データ数

  各変数に与えるデータ数を入力します.

変数1 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0

  推論結果を得たいデータを入力します.1 つの変数に対するデータが複数行になっても構いません.変数の数だけこのデータが必要になります.

  以上のデータを準備して実行すると,推論データに対応した推論結果がファイルに出力されます.