極大値と極小値を検出する
今回は、極大値と極小値を検出するプログラミングを示す。
import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class PeakFind extends JFrame implements ActionListener { Random rand = new Random(); int no = 30; //データ数 int range = 3; //最大値の探索範囲 int thr = 5; //閾値 //解析例用のデータを入れる配列 int data [] = new int [no]; //極大値を取る番号を入れる配列の作成 int maxRNo [] = new int [no]; //極小値を取る番号を入れる配列の作成 int minRNo [] = new int [no]; //閾値以上の値を取る極大値の番号を入れる配列の作成 int subRNo [] = new int [no]; //解析用データの作成 public void makeData (int [] data) { System.out.println("解析用のデータ:"); for (int i = 0; i < data.length; i++) { data [i] = rand.nextInt(100) + 1; System.out.print(data [i] + " "); } System.out.println(); } //a ~ a + bの範囲での極大値を取る番号の取得 public int maxNo (int a, int b, int [] c) { int maxNum = a; int max = c[a]; for (int i = a; i < a + b && i < c.length; i++) { if (c[i] > max) { max = c[i]; maxNum = i; } } return maxNum; } //a ~ bの間で極小値を取る番号の取得 public int minNum (int a, int b, int [] c) { int minNum = a; int min = c[a]; for (int i = a; i < b; i++) { if (min > c[i]) { min = c[i]; minNum = i; } } return minNum; } //データの解析 public void AnalyData () { makeData(data); //極大値の探索 int count = 0; for (int i = 0; i < no - range; i++) { if (maxNo(i, range, data) == maxNo (i + 1, range, data)) { int check = 0; for (int k = 1; k < range; k++) { if (maxNo(i, range, data) == maxNo(i+k, range, data)) { check++; } } if (check == range - 1) { maxRNo[count] = maxNo(i, range, data); count++; } } } //極小値の探索 minRNo [0] = minNum (0, maxRNo[0], data); for (int i = 0; i < no; i++ ) { if (maxRNo[i+1] == 0) break; minRNo [i+1] = minNum(maxRNo[i], maxRNo[i+1], data); } //差分の計算 int subCount = 0; for (int i = 0; i < no; i++) { if (data [maxRNo[i]] - data [minRNo[i]] >= thr) { subRNo[subCount] = maxRNo[i]; subCount++; } } //極大値の出力 System.out.println("極大値:"); for (int i = 0; i < no; i++) { if (maxRNo [i] == 0) break; System.out.print(data[maxRNo[i]] + " "); } System.out.println(); //極小値の出力 System.out.println("極小値:"); for (int i = 0; i < no; i++) { if (i > 0 && minRNo [i] == 0) break; System.out.print(data[minRNo[i]] + " "); } System.out.println(); //Peakの出力 System.out.println("Peak : "); for (int i = 0; i < no; i++) { if (subRNo[i] == 0) break; System.out.print(data[subRNo[i]] + " "); } System.out.println(); } public void actionPerformed (ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("button")) { AnalyData(); } } PeakFind (String title) { setTitle(title); setSize(180, 80); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); JButton button = new JButton("Push"); button.addActionListener(this); button.setActionCommand("button"); JPanel pane = new JPanel(); pane.add(button); getContentPane().add(pane, BorderLayout.CENTER); } public static void main(String[] args) { PeakFind frame = new PeakFind("Max"); frame.setVisible(true); } }
極大値の定義は、
上図のように、前後“Range - 1”フレーム内で最大となる点を極大値としている。例えば、図に示した「Rang = 3」の場合は、前後2フレームで最大となる点が極大値であり、下記のコードのようにした。
//a ~ a + bの範囲での極大値を取る番号の取得 public int maxNo (int a, int b, int [] c) { int maxNum = a; int max = c[a]; for (int i = a; i < a + b && i < c.length; i++) { if (c[i] > max) { max = c[i]; maxNum = i; } } return maxNum; }
そして、極小値の定義は「極大値間の最小値」としている。ただし、最初の極小値は「最初のデータから最初の極大値の間の最小値」とし、下記のコードのようにした。
//a ~ bの間で極小値を取る番号の取得 public int minNum (int a, int b, int [] c) { int minNum = a; int min = c[a]; for (int i = a; i < b; i++) { if (min > c[i]) { min = c[i]; minNum = i; } } return minNum; }
このプログラミングでは、簡潔にするために解析用データはRandomで作成し、「Range = 3, 閾値 = 5」とした。
入力したデータ全体で、極大値を探索するために、
//極大値の探索 int count = 0; for (int i = 0; i < no - range; i++) { if (maxNo(i, range, data) == maxNo (i + 1, range, data)) { int check = 0; for (int k = 1; k < range; k++) { if (maxNo(i, range, data) == maxNo(i+k, range, data)) { check++; } } if (check == range - 1) { maxRNo[count] = maxNo(i, range, data); count++; } } }
とした。この中の“k”の部分で“Range - 1”の範囲で最大値となるかのチェックを行っている。
また、下記の部分で、「極大値 ― 極小値」が閾値を超えたものをPeakとした。
//差分の計算 int subCount = 0; for (int i = 0; i < no; i++) { if (data [maxRNo[i]] - data [minRNo[i]] >= thr) { subRNo[subCount] = maxRNo[i]; subCount++; } }
このプログラミングの実行した結果を下記に示す。
解析用のデータ: 30 38 43 9 5 22 63 64 50 12 8 92 95 98 64 46 2 87 57 65 51 27 15 12 90 90 20 31 16 96 極大値: 43 64 98 87 90 極小値: 30 5 8 2 12 Peak : 43 64 98 87 90
前回までのプログラミングと、今回の極大値検出ツールを組み合わせることで、エクセルファイルからデータを読み込み、Peakを検出し、解析結果をエクセルファイルに出力できるはず。
次回は、解析用データをグラフに描き、その中で極大値と極小値をプロットする方法を書く。(JFreeChartを用いて2種類のグラフを重ねて表記する)