明解 Javaによるアルゴリズムとデータ構造 6-3_単純選択ソート

6-3 単純選択ソート straight selection sort

最小要素を先頭に、2番めに小さい要素を先頭から2番目に移動する、を繰り返すアルゴリズム

  1. 未ソート部から最小のキーを持つ a[min] を選択
  2. a[min] と、未ソート部の先頭要素を交換
  • 離れた要素を交換する可能性があるため、安定ではない
  • 比較回数 : \displaystyle \sum_{k=1}^{n-1} \(n - k)} = \frac{(n-1)n}{2}
    • 1パスにつき、最小要素1つに、その他の要素を n - (現パス数) 回ぶつけて最小判定。それを n - 1 パス実施
ソースコード(途中経過表示板)
import java.util.Scanner;

class SelectionSort {
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1];
		a[idx1] = a[idx2];
		a[idx2] = t;
	}

	static void selectionSort(int[] a, int n) {
		for (int i = 0; i < n - 1; i++) {
			System.out.println("パス: " + (i + 1));
			int min = i;
			for (int j = i + 1; j < n; j++) { //  j -> 交換検討対象のインデックス
				if (a[j] < a[min]) {
					// ソート前状態の表示
					for (int k = 0; k < n; k++) {
						if (k == min) {
							System.out.printf("%2d* ", a[k]); // "*" -> 現時点での最小要素
						}  else if (k == j) {
							System.out.printf("%2d+ ", a[k]); // "+" -> 比較の結果新たに最小となった要素
						}	else {
							System.out.printf("%2d  ", a[k]);
						}
					}
					System.out.println();
					min = j;
					
				} else {
					// ソート前状態の表示
					for (int k = 0; k < n; k++) {
						if (k == min) {
							System.out.printf("%2d* ", a[k]);
						}  else if (k == j) {
							System.out.printf("%2d- ", a[k]); // "-" -> 比較の結果最小にならなかった要素
						}	else {
							System.out.printf("%2d  ", a[k]);
						}
					}
					System.out.println();
				}
			}
			swap(a, i, min);
			
			// ソート後状態の表示
			for (int e : a) {
				System.out.printf("%2d  ", e);
			}
			System.out.println();
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("単純選択ソート");
		System.out.print("要素数: ");
		int nx = stdIn.nextInt();
		;
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		selectionSort(x, nx);

		System.out.println("昇順にソートしました");
		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "] = " + x[i]);
		}
	}
}
単純選択ソート
要素数: 4
x[0]:
4
x[1]:
3
x[2]:
2
x[3]:
1
パス: 1
 4*  3+  2   1  
 4   3*  2+  1  
 4   3   2*  1+ 
 1   3   2   4  
パス: 2
 1   3*  2+  4  
 1   3   2*  4- 
 1   2   3   4  
パス: 3
 1   2   3*  4- 
 1   2   3   4  
昇順にソートしました
x[0] = 1
x[1] = 2
x[2] = 3
x[3] = 4
明解 Javaによるアルゴリズムとデータ構造

明解 Javaによるアルゴリズムとデータ構造

明解 Javaによるアルゴリズムとデータ構造 6-2_バブルソート

6-2 バブルソート

概要

隣り合う2要素の大小関係を調べて必要に応じて交換を繰り返す、単純交換ソート straight exchange sort の一種。

  • 素数 n の配列に対して、n-1 回の比較・交換(この作業をパスという)を行うことで、最小要素を先頭に移動する
  • 更に n-2 回の比較・交換で、2倍目に小さい要素をソートできる
  • …(以下同様)
  • パスを k 回行えば、先頭側 k 個の要素がソート済みとなる


液体中の気泡がぶくぶくと上に上がってくるイメージ → バブルソート!

  • 隣り合う2つを交換するだけのため、安定アルゴリズムである点に留意

 

計算量

合計比較回数:

  • (n-1) + (n-2) + \cdots + 1 = \frac{n(n-1)}{2}

合計交換回数の平均:

  • (比較の半分が交換を伴うとして、) 合計比較回数 / 2 = \frac{n(n-1)}{4}

合計移動回数の平均:

  • (swap()内で代入が3回行われるので、) 合計交換回数の平均 * 3 = \frac{3n(n-1)}{4}

 

ソースコード(初版)

  • ケツからソート版
import java.util.Scanner;

class BubbleSort {
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1];
		a[idx1] = a[idx2];
		a[idx2] = t;
	}

	static void bubbleSort(int[] a, int n) {
		for (int i = 0; i < n - 1; i++) {
			for (int j = n - 1; j > i; j--) {
				if (a[j - 1] > a[i]) {
					swap(a, j - 1, j);
				}
			}
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("単純交換ソート(バブルソート)");
		System.out.print("要素数: ");
		int nx = stdIn.nextInt();
		;
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		bubbleSort(x, nx);

		System.out.println("昇順にソートしました");
		for (int i = 0; i < nx; i++) {
			System.out.println("x[ " + i + "] = " + x[i]);
		}
	}
}
  • 頭からソート版
import java.util.Scanner;

class BubbleSortFromTop {
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1];
		a[idx1] = a[idx2];
		a[idx2] = t;
	}

	static void bubbleSort(int[] a, int n) {
		for (int i = 0; i < n - 1; i++) {
			for (int j = 0; j < n - 1 - i; j++) {
				if (a[j] > a[j + 1]) {
					swap(a, j, j + 1);
				}
			}
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("単純交換ソート(バブルソート)");
		System.out.print("要素数: ");
		int nx = stdIn.nextInt();
		;
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		bubbleSort(x, nx);

		System.out.println("昇順にソートしました");
		for (int i = 0; i < nx; i++) {
			System.out.println("x[ " + i + "] = " + x[i]);
		}
	}
}
  • 頭からソートして、途中経過を標準出力する版
import java.util.Scanner;

class BubbleSortFromTop {
	static int comparisonCnt = 0;
	static int changeCnt = 0;
	static int moveCnt = 0;

	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1];
		a[idx1] = a[idx2];
		a[idx2] = t;
		moveCnt += 3;
	}

	static void bubbleSort(int[] a, int n) {
		for (int i = 0; i < n - 1; i++) {
			System.out.println("パス" + i + ": ");
			
			//ソート途中経過
			for (int j = 0; j < n - 1 - i; j++) {
				// 比較対象でない場合はただ標準出力するだけ
				for (int k = 0; k < j; k++) {
					System.out.printf("%2d  ", a[k]);
				}
				// 比較対象であり、かつ交換される場合
				if (a[j] > a[j + 1]) {
					System.out.printf("%2d +%2d  ", a[j], a[j + 1]);
					swap(a, j, j + 1);
					for  (int k = j + 2; k < n; k++) {
						System.out.printf("%2d  ", a[k]);
					}
					System.out.print("\n");
					changeCnt++;
				// 比較対象であり、かつ交換されない場合
				} else {
					System.out.printf("%2d -%2d  ", a[j], a[j + 1]);
					for (int k = j + 2; k < n; k++) {
						System.out.printf("%2d  ", a[k]);
					}
					System.out.print("\n");
				}
				comparisonCnt++;
			}

			//交換後の一覧表示
			StringBuilder sb = new StringBuilder();
			for (int k = 1; k <= n; k++) {
				sb.append("----");
			}
			System.out.println(sb.toString());
			for (int e : a) {
				System.out.printf("%2d  ", e);
			}
			System.out.print("\n\n");
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("単純交換ソート(バブルソート)");
		System.out.print("要素数: ");
		int nx = stdIn.nextInt();
		;
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		bubbleSort(x, nx);

		System.out.println("昇順にソートしました");
		for (int i = 0; i < nx; i++) {
			System.out.println("x[ " + i + "] = " + x[i]);
		}
		
		System.out.println();
		System.out.println("比較は " + comparisonCnt + " 回でした。");
		System.out.println("移動は " + moveCnt + " 回でした。");
		System.out.println("交換は " + changeCnt + " 回でした。");
	}
}
    • 出力結果はこんな感じ
単純交換ソート(バブルソート)
要素数: 5
x[0]:
1
x[1]:
2
x[2]:
3
x[3]:
6
x[4]:
5
パス0: 
 1 - 2   3   6   5  
 1   2 - 3   6   5  
 1   2   3 - 6   5  
 1   2   3   6 + 5  
--------------------
 1   2   3   5   6  

パス1: 
 1 - 2   3   5   6  
 1   2 - 3   5   6  
 1   2   3 - 5   6  
--------------------
 1   2   3   5   6  

パス2: 
 1 - 2   3   5   6  
 1   2 - 3   5   6  
--------------------
 1   2   3   5   6  

パス3: 
 1 - 2   3   5   6  
--------------------
 1   2   3   5   6  

昇順にソートしました
x[ 0] = 1
x[ 1] = 2
x[ 2] = 3
x[ 3] = 5
x[ 4] = 6

比較は 10 回でした。
移動は 3 回でした。
交換は 1 回でした。

ソースコード(第2版)

打ち切りを導入
あるパス内で、一度も交換が行われない  ⇒ ソートは完了している
  • パス内での交換回数を記録するローカル変数を追加
static void bubbleSort(int[] a, int n) {
	for (int i = 0; i < n - 1; i++) {
		int exchg = 0;
		for (int j = n - 1; j > i; j--) {
			if (a[j - 1] > a[j]) {
				swap(a, j - 1, j);
				exchg++;
			}
		}
		if (exchg == 0) {
			break;
		}
	}
}
  • 途中経過表示版
import java.util.Scanner;

class BubbleSort2 {
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1];
		a[idx1] = a[idx2];
		a[idx2] = t;
	}

	static void bubbleSort(int[] a, int n) {
		// パスのループ
		for (int i = 0; i < n - 1; i++) {
			int exchg = 0;
			System.out.println("パス : " + i);
			
			// パス内での、隣接要素比較のループ
			for (int j = n - 1; j > i; j--) {
				// 比較対象要素の直前までを標準出力
				for (int k = 0; k < j - 1; k++) {
					System.out.printf("%2d  ", a[k]);
				}
				if (a[j - 1] > a[j]) {
					System.out.printf("%2d +%2d  ", a[j-1], a[j]); // 交換前の比較対象要素を出力
					for (int k = j + 1; k < n; k++) {
						System.out.printf("%2d  ", a[k]);
					}
					System.out.println();
					swap(a, j - 1, j);
					exchg++;
				} else {
					System.out.printf("%2d -%2d  ", a[j-1], a[j]); // 交換前の比較対象要素を出力
					for (int k = j + 1; k < n; k++) {
						System.out.printf("%2d  ", a[k]);
					}
					System.out.println();
				}
			}
			if (exchg == 0) {
				break;
			}
			for (int e : a) {
				System.out.printf("%2d  ", e); // パス完了時の並びを出力
			}
			System.out.print("\n\n");
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("単純交換ソート(バブルソート)");
		System.out.print("要素数: ");
		int nx = stdIn.nextInt();
		;
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		bubbleSort(x, nx);

		System.out.println("昇順にソートしました");
		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "] = " + x[i]);
		}
	}
}
単純交換ソート(バブルソート)
要素数: 5
x[0]:
9
x[1]:
1
x[2]:
5
x[3]:
8
x[4]:
4
パス : 0
 9   1   5   8 + 4  
 9   1   5 + 4   8  
 9   1 - 4   5   8  
 9 + 1   4   5   8  
 1   9   4   5   8  

パス : 1
 1   9   4   5 - 8  
 1   9   4 - 5   8  
 1   9 + 4   5   8  
 1   4   9   5   8  

パス : 2
 1   4   9   5 - 8  
 1   4   9 + 5   8  
 1   4   5   9   8  

パス : 3
 1   4   5   9 + 8  
 1   4   5   8   9  

昇順にソートしました
x[0] = 1
x[1] = 4
x[2] = 5
x[3] = 8
x[4] = 9

ソースコード(第3版)

前パスで既にソート済みの要素比較をスキップ
static void bubbleSort(int[] a, int n) {
	int k = 0; // ソート済インデックスのマーカー(パスをまたぐ変数)
	while (k < n - 1) {
		int last = n - 1; // 前パスで最後に交換したインデックス
		for (int j = n - 1; j > k; j--) {
			if (a[j - 1] > a[j]) {
				swap(a, j - 1, j);
				last = j;
			}
		}
		k = last;
	}
}
疑問
  • line8 と line11 でそれぞれ代入する意味ってなんだ?
    • k に逐次 j 突っ込むんじゃダメだっけ?
static void bubbleSort(int[] a, int n) {
	int k = 0; // ソート済インデックスのマーカー(パスをまたぐ変数)
	while (k < n - 1) {
		for (int j = n - 1; j > k; j--) {
			if (a[j - 1] > a[j]) {
				swap(a, j - 1, j);
				k = j;
			}
		}
	}
}
回答
  • 挙動正しく理解できてなかった。
    • for 内で k = j 突っ込んじゃうと、swap対象な j の場合、forから即出ちゃうね・・・
    • k は漸減していくパラメータであり、1パス完走した時の最小値が意味を持つ。
    • よって、for ループに関係しない変数で j を確保しといて、for ループ回りきった後に、その最小値を k に代入する仕組みが必要。
途中経過表示版
import java.util.Scanner;

class BubbleSort3 {
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1];
		a[idx1] = a[idx2];
		a[idx2] = t;
	}

	static void bubbleSort(int[] a, int n) {
		int k = 0; // ソート済インデックスのマーカー(パスをまたぐ変数)
		while (k < n - 1) {
			System.out.println("パス: " + (k+1));
			int last = n - 1; // 最後に交換したインデックス

			for (int j = n - 1; j > k; j--) {

				// 比較対象以前の要素を表示
				for (int l = 0; l < j - 1; l++) {
					System.out.printf("%2d  ", a[l]);
				}

				// 比較
				if (a[j - 1] > a[j]) {
					// 交換対象箇所を表示
					System.out.printf("%2d +%2d  ", a[j-1], a[j]);
					swap(a, j - 1, j);
					last = j;

				} else {
					// 非交換対象箇所を表示
					System.out.printf("%2d -%2d  ", a[j-1], a[j]);
				}

				// 比較対象箇所以降の要素を表示
				for (int l = j + 1; l < n; l++) {
					System.out.printf("%2d  ", a[l]);
				}
				System.out.println();
			}
			for (int e : a) {
				System.out.printf("%2d  ", e);
			}
			System.out.println();
			k = last;
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("単純交換ソート(バブルソート)");
		System.out.print("要素数: ");
		int nx = stdIn.nextInt();
		;
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		bubbleSort(x, nx);

		System.out.println("昇順にソートしました");
		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "] = " + x[i]);
		}
	}
}
単純交換ソート(バブルソート)
要素数: 5
x[0]:
1
x[1]:
2
x[2]:
3
x[3]:
5
x[4]:
4
パス: 1
 1   2   3   5 + 4  
 1   2   3 - 4   5  
 1   2 - 3   4   5  
 1 - 2   3   4   5  
 1   2   3   4   5  
昇順にソートしました
x[0] = 1
x[1] = 2
x[2] = 3
x[3] = 4
x[4] = 5

双方向バブルソート bidirection bubble sort / シェーカーソート shaker sort

  • 『最小要素を先頭側に』と『最大要素を末尾側に』を組み合わせたソート法
    • ほぼほぼソートされているが、大きめの値がぽつんと先頭付近にあったりする際(あるいはその逆)の場合有効
ソースコード
  • shakerSort()がデブっててかっこ悪いので、あれな感じだな
import java.util.Scanner;

class ShakerSort {
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1];
		a[idx1] = a[idx2];
		a[idx2] = t;
	}

	static void shakerSort(int[] a, int n) {
		int k = 0; // ソート済インデックスのマーカー
		int l = n - 1; // ソート済インデックスのマーカー
		int pathNo = 1; // 現在のパス数
		boolean topSorted = false; // トップソートの完了フラグ
		boolean bottomSorted = false; // ボトムソートの完了フラグ
		
		while (!topSorted && !bottomSorted) {
			System.out.println("パス: " + pathNo);

			// 奇数パス -> 最小値を先頭へ
			if (pathNo % 2 == 1) {
				int last = n - 1; // 最後に交換したインデックス
				for (int j = n - 1; j > k; j--) {
					// 比較対象以前の要素を表示
					for (int m = 0; m < j - 1; m++) {
						System.out.printf("%2d  ", a[m]);
					}
					// 比較
					if (a[j - 1] > a[j]) {
						// 交換対象箇所を表示
						System.out.printf("%2d +%2d  ", a[j - 1], a[j]);
						swap(a, j - 1, j);
						last = j;
					} else {
						// 非交換対象箇所を表示
						System.out.printf("%2d -%2d  ", a[j - 1], a[j]);
					}
					// 比較対象箇所以降の要素を表示
					for (int m = j + 1; m < n; m++) {
						System.out.printf("%2d  ", a[m]);
					}
					System.out.println();
				}
				k = last;

			// 偶数パス -> 最大値を末尾へ
			} else {
				int last = 0; // 最後に交換したインデックス
				for (int j = 0; j < n - k; j++) {
					// 比較対象以前の要素を表示
					for (int m = 0; m < j; m++) {
						System.out.printf("%2d  ", a[m]);
					}
					// 比較
					if (a[j] > a[j + 1]) {
						// 交換対象箇所を表示
						System.out.printf("%2d +%2d  ", a[j], a[j + 1]);
						swap(a, j, j + 1);
						last = j;
					} else {
						// 非交換対象箇所を表示
						System.out.printf("%2d -%2d  ", a[j], a[j + 1]);
					}
					// 比較対象箇所以降の要素を表示
					for (int m = j + 2; m < n; m++) {
						System.out.printf("%2d  ", a[m]);
					}
					System.out.println();
				}
				l = last;
				
			}

			// 各パスでのソート結果表示
			for (int e : a) {
				System.out.printf("%2d  ", e);
			}
			System.out.println();

			// ボトムソート・トップソート完了判定
			if (k >= n - 1)
				topSorted = true;
			if (l <= 1) {
				bottomSorted = true;
			}
			
			pathNo++;
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("単純交換ソート(バブルソート)");
		System.out.print("要素数: ");
		int nx = stdIn.nextInt();
		;
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		shakerSort(x, nx);

		System.out.println("昇順にソートしました");
		for (int i = 0; i < nx; i++) {
			System.out.println("x[" + i + "] = " + x[i]);
		}
	}
}
参考) 第三版とシェーカーソートとの差分
  • 第三版
単純交換ソート(バブルソート)
要素数: 7
x[0]:
9
x[1]:
1
x[2]:
3
x[3]:
4
x[4]:
6
x[5]:
7
x[6]:
8
パス: 1
 9   1   3   4   6   7 - 8  
 9   1   3   4   6 - 7   8  
 9   1   3   4 - 6   7   8  
 9   1   3 - 4   6   7   8  
 9   1 - 3   4   6   7   8  
 9 + 1   3   4   6   7   8  
 1   9   3   4   6   7   8  
パス: 2
 1   9   3   4   6   7 - 8  
 1   9   3   4   6 - 7   8  
 1   9   3   4 - 6   7   8  
 1   9   3 - 4   6   7   8  
 1   9 + 3   4   6   7   8  
 1   3   9   4   6   7   8  
パス: 3
 1   3   9   4   6   7 - 8  
 1   3   9   4   6 - 7   8  
 1   3   9   4 - 6   7   8  
 1   3   9 + 4   6   7   8  
 1   3   4   9   6   7   8  
パス: 4
 1   3   4   9   6   7 - 8  
 1   3   4   9   6 - 7   8  
 1   3   4   9 + 6   7   8  
 1   3   4   6   9   7   8  
パス: 5
 1   3   4   6   9   7 - 8  
 1   3   4   6   9 + 7   8  
 1   3   4   6   7   9   8  
パス: 6
 1   3   4   6   7   9 + 8  
 1   3   4   6   7   8   9  
昇順にソートしました
x[0] = 1
x[1] = 3
x[2] = 4
x[3] = 6
x[4] = 7
x[5] = 8
x[6] = 9
  • シェイカー
単純交換ソート(バブルソート)
要素数: 7
x[0]:
9
x[1]:
1
x[2]:
3
x[3]:
4
x[4]:
6
x[5]:
7
x[6]:
8
パス: 1
 9   1   3   4   6   7 - 8  
 9   1   3   4   6 - 7   8  
 9   1   3   4 - 6   7   8  
 9   1   3 - 4   6   7   8  
 9   1 - 3   4   6   7   8  
 9 + 1   3   4   6   7   8  
 1   9   3   4   6   7   8  
パス: 2
 1 - 9   3   4   6   7   8  
 1   9 + 3   4   6   7   8  
 1   3   9 + 4   6   7   8  
 1   3   4   9 + 6   7   8  
 1   3   4   6   9 + 7   8  
 1   3   4   6   7   9 + 8  
 1   3   4   6   7   8   9  
パス: 3
 1   3   4   6   7   8 - 9  
 1   3   4   6   7 - 8   9  
 1   3   4   6 - 7   8   9  
 1   3   4 - 6   7   8   9  
 1   3 - 4   6   7   8   9  
 1   3   4   6   7   8   9  
パス: 4
 1 - 3   4   6   7   8   9  
 1   3   4   6   7   8   9  
昇順にソートしました
x[0] = 1
x[1] = 3
x[2] = 4
x[3] = 6
x[4] = 7
x[5] = 8
x[6] = 9
//--- 双方向バブルソート(シェーカーソート)---//
static void shakerSort(int[] a, int n) {
	int left = 0;
	int right = n - 1;
	int last = right;

	 while (left < right){
		for (int j = right; j > left; j--){
			if (a[j - 1] > a[j]){
				swap(a, j - 1, j);
				last = j;
			}
		}
		left = last;

		for (int j = left; j < right; j++){
			if (a[j] > a[j + 1]){
				swap(a, j, j + 1);
				last = j;
			}
		}
		right = last;
	}
}
明解 Javaによるアルゴリズムとデータ構造

明解 Javaによるアルゴリズムとデータ構造

はてなブログへ Syntax Highlighter の Java 用定義を導入した

はてなブログへ、Syntax Highlighter の Java 用定義を導入した。
詳しい解説があったので、それを参考に。

  1. はてなブログへとりあえず導入
  2. Java用定義の書き換え(『行ごとに背景色変え』を適用する)
  3. pre タグ内での、はてなキーワードへの自動リンク禁止


この3ステップでだいじょーぶ。

明解 Javaによるアルゴリズムとデータ構造 6-1_ソートとは

6-1 ソートとは

安定性
  • 安定 stable
    • 同一キーを持つ要素の順序関係が、ソート前後で必ず維持される
  • 不安定
    • 同一キーを持つ要素の順序関係が、ソート前後で維持されるとは限らない

ソートの種類
  • 内部ソート
    • ソート対象データが、1配列上に展開可能な場合に用いるアルゴリズム
  • 外部ソート
    • ソート対象データが大量で、一度で処理しきれない場合のアルゴリズム

ソートの考え方


明解 Javaによるアルゴリズムとデータ構造

明解 Javaによるアルゴリズムとデータ構造

空気清浄機を買った (ダイキン: ACK55N-W)

空気清浄機購入に至る流れと、ネット通販で、ふと思ったこと。

なんで買ったの?

  • 空気中のホコリが多い気がする
    • 部屋が狭い。一方で寝具の容積は一般的なもの。
      • 結果、部屋の容積に対して、ホコリ発生源の体積比率が大きく、フローリングにすぐホコリが溜まる・・・
  • 冬場に、空気がすごく乾燥して、気管支が辛い

要は、部屋の空気があまり快適ではなかったのです。

で、空気清浄機買うことにしました。
田舎じゃあ部屋広くてこんなん要らんかったから、初購入ですわー(ドヤ

メーカー選び

メーカーはダイキンにした。

エアコン何回か買ってるんだけど、他メーカーより明らかにクオリティ違った。
業者のおっちゃんの印象とも合致していて、空調関連機器はダイキン選んどけば安パイだよね~、って感じ。

3秒で決定。

機種選定

2012年9月モデルのこれになった。


なんせ初で、機能も商品のリリースサイクルもよくわからんので、そのへんざっくり見た。
ダイキンは毎年9月にモデルチェンジがある模様。
んでもって、シングル世帯あるいはそれほど大きくない部屋向きと思われ、今回の購入候補となるMCK55/ACK55モデルにおいては、2012 -> 2013 のモデルチェンジはマイナーチェンジに相当し、大幅な機能追加は無し。
2012 モデルと 2013 モデルの価格差は \10,000 程度。
デザインも変わらんし、じゃあ2012年モデルでいいやー、っと決定。

高々2万ぐらいの商品なんで、長々時間かけても得する額が大きいわけでもない。
30分でエイヤッと決定。

店選び

店頭はめんどいのでナシ。
いちいち出向いて値切りとか、タルいっす。
少なくとも要した時間分は、値引きして返してほしいよね、とか思うけど、たいていそんなとこまで下がんないのでネット通販です。

ヒッキーだからじゃないんだからねっ!


購入した通販サイトは、YAHOO!ショッピングの小規模通販サイト楽天 - 23,400円 にも YAHOO!ショッピング - 21,863円 にも系列店持ってるようだが、YAHOO!ショッピングのほうが、楽天よりちょっと安いね。
2,000円弱。
手数料として楽天により多く納めてる分を、価格転嫁してるんかな。

楽天最弱説

楽天でほしいもんあった場合、最安ではない可能性が高いから、amazonYAHOO!ショッピングでも検索かけると幸せになれるね。
あとは、クレカのWEBサイト経由で買いもんすることで、ちょっとポイントもらえるぐらいかな。
まあ今回のケースだとクレカWEBサイト経由で得するのは500円弱だったので、マクド一回分に満たないぐらい。
面倒さとペイするかは微妙。

つーか、amazonには品揃えで負けるし、値段ではYAHOO!に負けるし、楽天のほうが勝つ領域って何かなー。
出店してる業者も情弱色が強い。
amazonマケプレとかの勝手にやってくれ感に較べて、縦長で糞うざい楽天テンプレだったり、なんやかんやでネット進出のハードル下げることについては一番親切なのかな。

手数料ガッチリとって、厚いサポート体制を用意して、その結果、最も恩恵を蒙る登場人物が、出店者でも消費者でなく、おそらく楽天であるあたりが非常に日本的だなあと思ったり。
日本的というか、前時代的というべきなのかな。

だからダメとは言わないが、尻窄んでいく気がするな。

所感

今週ぐらいに届くだろうから、後で記録するぜー。


(2013/01/11追記)
いらっしゃいました。
f:id:darao:20140111204743j:plain

とりあえず初回稼働。
f:id:darao:20140111211515j:plain

意外と風つえー。
コーって言ってる。
動作面では特に問題なさそうー。
ハウスダストの量を、インジケータ表示してくれるから、掃除サボれなくなるのがいいね!

湿度インジケータ見たら、だいぶ乾燥してる。
全力で加湿してもすぐには上がってこないあたり、相当やな。
冬の間は全力加湿でいこうと思うのねん。
気管支やられる割合を下げたい。
体調管理はホント大事。

明解 Javaによるアルゴリズムとデータ構造 5_再帰的アルゴリズム

5-1 再帰の基本

メソッド内で自分を呼びだす

5-2 再帰アルゴリズムの解析

5-3 ハノイの塔

非再帰への書き下し解答があんまりしっくりこないので、あとで振り返る

5-4 8王妃問題

8x8チェス盤上に、互いに取れないように合計8クイーンを配置する問題

クイーンは縦横斜めに移動できる(飛車と角の動きを併せ持つ感じ)

列ごとに1王妃配置する場合の全配置パターン書き下しアルゴ。
なんてことない再帰なんだけど、すぐには挙動読み取れんかった。

  • 分岐 branching
  • 分割統治法 devide and conquer
class QueenB {
	static int[] pos = new int[8];
	
	static void print() {
		for (int i = 0; i < 8; i++) {
			System.out.printf("%2d", pos[i]);
		}
		System.out.println();
	}
	
	/**
	 * 女王のいる行数: j=0 からスタート
	 * 
	 * 女王j=0  で固定したまま列数をインクリメントして、潜る。
	 * 列i=7  まで来たら、結果を出力
	 * 列i=7 のまま 女王j++ -> j=7 まできたら、列i=7のセットは終わり。i=6にpopしてくる。
	 * 
	 * 列i=6における、j=0が完了し、j++ -> j=7 まできたら、列i=6のセットは終わり。i=5にpopしてくる。
	 * 列i=5における、j=0が完了し、j++ -> j=7 まできたら、列i=5のセットは終わり。i=4にpopしてくる。
	 * :
	 * :
	 * :
	 * 列i=1における、j=0が完了し、j++ -> j=7 まできたら、列i=1のセットは終わり。done!
	 * 
	 * @param i 王妃配置対象の列
	 */
	static void set(int i) { // i は
		for (int j = 0; j < 8; j++) { // j は女王を配置する行数
			pos[i] = j;
			if (i == 7) {
				print();
			} else {
				set(i+1); // 考慮する対象列をインクリメント
			}
		}
	}
	
	public static void main(String[] args) {
		set(0);
	}
}
以下、挙動のメモ。
i=0 j=0から始まる
	1 0
		2 0
			3 0
				4 0
					// set(5)
					5 0
						// set(6)
						6 0
							// set(7)
							7 0	print
							
							7 1	print
							7 2	print
							7 3	print
							7 4	print
							7 5	print
							7 6	print
							7 7	print
						6 1	
							// set(7)
							7 0	print
							
							7 1	print
							7 2	print
							7 3	print
							7 4	print
							7 5	print
							7 6	print
							7 7	print
						6 2
							// set(7)
							7 0	print
							
							7 1	print
							7 2	print
							7 3	print
							7 4	print
							7 5	print
							7 6	print
							7 7	print
						6 3
						6 4
						6 5
						6 6
						6 7
					5 1
					:
					:
					:
8王妃問題のソルバー(解を盤面表示する)。 なお、このソルバーは
  • 限定 bounding
  • 分枝限定法 branching and bounding method
を用いていることに留意。
class EightQueen {
	static boolean[] flag_a = new boolean[8];
	static boolean[] flag_b = new boolean[15];
	static boolean[] flag_c = new boolean[15];
	static int[] pos = new int[8];

	static void print() {
		String[] board = new String[8];
		for (int j = 0; j < 8; j++) { // j行
			board[j] = "";
			for (int i = 0; i < 8; i++) { // i列
				board[j] += (pos[i] == j) ? "■" : "□";
			}
			System.out.println(board[j]);
		}
		System.out.println();
	}

	static void set(int i) {
		for (int j = 0; j < 8; j++) {
			if (flag_a[j] == false && flag_b[i + j] == false
					&& flag_c[i - j + 7] == false) {
				pos[i] = j;
				if (i == 7) {
					print();
				} else {
					flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = true;
					set(i + 1);
					flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = false;
				}
			}
		}
	}

	public static void main(String[] args) {
		set(0);
	}
}
上記ソルバーを走らせると、以下の様な盤が92面出力される。
■□□□□□□□
□□□□□□■□
□□□□■□□□
□□□□□□□■
□■□□□□□□
□□□■□□□□
□□□□□■□□
□□■□□□□□

■□□□□□□□
□□□□□□■□
□□□■□□□□
□□□□□■□□
□□□□□□□■
□■□□□□□□
□□□□■□□□
□□■□□□□□

■□□□□□□□
□□□□□■□□
□□□□□□□■
□□■□□□□□
□□□□□□■□
□□□■□□□□
□■□□□□□□
□□□□■□□□
再帰化、ちょっと考え中。
明解 Javaによるアルゴリズムとデータ構造

明解 Javaによるアルゴリズムとデータ構造

明解 Javaによるアルゴリズムとデータ構造 4_スタックとキュー

4-1 スタック

フィールド: スタックの容量・ポインタ。後入れ先出し(LIFO - Last In First Out)。

  • push, pop, peek, dump
  • indexOf, size, clear, capacity
  • isEmpty, isFull
疑問
  • 総称クラスが java.lang.Throwable をサブクラス化できないのってなんでだっけ?
    • 例) hogeクラス内に、例外クラス FugaException extends RuntimeException を作れない。
    • static class FugaException extends RuntimeException だと OK
  • staticだといけるということは、インスタンス化に問題有りということなんかな?

4-2 キュー

フィールド: キューの容量・ポインタ。先入れ先出し(FIFO - First In First Out)。

  • 配列キュー
    • enqueue, dequeue, peek, dump
    • indexOf, size, clear, capacity
    • isEmpty, isFull
  • リングバッファ(ring buffer)キュー
    • 要素ピックアップ時の残要素シフトコストが、配列キューよりも下がるのがメリット
    • max, front, rear, num


明解 Javaによるアルゴリズムとデータ構造

明解 Javaによるアルゴリズムとデータ構造