6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaAdvent Calendar 2024

Day 13

Javaで学ぶアルゴリズム -最適化バブルソート

Posted at

最適化(打ち切りありの)バブルソート

  • 隣り合う要素の大小を比較しながら整列させるバブルソートに、「打ち切り」の処理を加えたもの
  • 完全ランダムな配列にはほぼ効果がないが、一部ソートされているものには効果を発揮する

実行環境

  • JUnit 5
  • Java 8

テストコード

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class OptimizedBubbleSortTest {

	@DisplayName("配列を渡すとソートされた配列が返ること")
	@Test
	void testStraightExchange() {
		int[] array = { 5, 12, 7, 0, 1, 14, 6, 9, 8, 3, 2, 11, 4, 13, 10 };
		int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };

		int[] actual = OptimizedBubbleSort.bubbleSortWithEarlyStop(array);
		assertThat(actual, is(expected));
	}

	@DisplayName("要素が1つしかない配列を渡すと要素が1つしかない配列が返ること")
	@Test
	void testStraightExchangeOneElement() {
		int[] array = { 5 };
		int[] expected = { 5 };

		int[] actual = OptimizedBubbleSort.bubbleSortWithEarlyStop(array);
		assertThat(actual, is(expected));
	}

	@DisplayName("空の配列を渡すと空の配列が返ること")
	@Test
	void testStraightExchangeEmpty() {
		int[] array = {};
		int[] expected = {};

		int[] actual = OptimizedBubbleSort.bubbleSortWithEarlyStop(array);
		assertThat(actual, is(expected));
	}

	@DisplayName("すでに昇順でソートされた配列を渡すとソートされた配列が返ること")
	@Test
	void testStraightExchangeAlreadySorted() {
		int[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
		int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };

		int[] actual = OptimizedBubbleSort.bubbleSortWithEarlyStop(array);
		assertThat(actual, is(expected));
	}

	@DisplayName("要素が重複した配列を渡すとソートされた配列が返ること")
	@Test
	void testStraightExchangeDuplicateElement() {
		int[] array = { 5, 3, 5, 2, 1, 5, 3, 2, 5, 3, 5 };
		int[] expected = { 1, 2, 2, 3, 3, 3, 5, 5, 5, 5, 5 };

		int[] actual = OptimizedBubbleSort.bubbleSortWithEarlyStop(array);
		assertThat(actual, is(expected));
	}
}

バブルソート(打ち切り)アルゴリズム

public class OptimizedBubbleSort {

	/**
	 * 最適化バブルソート
	 * 先頭から末尾に向かって隣接する要素を比較して整列させる
	 * ソートの過程で交換が行われなくなった時点で早期に終了する
	 * @param array
	 * @return sortedArray
	 */
	public static int[] bubbleSortWithEarlyStop(int[] array) {
		int[] sortedArray = array.clone();
		int size = sortedArray.length;

		for (int i = 0; i < size - 1; i++) {
			int swapped = 0;
			for (int j = 0; j < size - 1 - i; j++) {
				if (sortedArray[j] > sortedArray[j + 1]) {
					int temp = sortedArray[j];
					sortedArray[j] = sortedArray[j + 1];
					sortedArray[j + 1] = temp;
					swapped++;
				}
			}
			if (swapped == 0) {
				break;
			}
		}
		return sortedArray;
	}
}

完全ランダム配列には、ほぼ効果がない

(テストコード)
	@DisplayName("単純交換ソートと最適化単純交換ソートの交換回数の違いをカウントする")
	@Test
	void testStraightExchangeCount() {
		int[] array1 = { 5, 12, 7, 0, 1, 14, 6, 9, 8, 3, 2, 11, 4, 13, 10, 18, 19, 17, 15, 16 };
		int[] array2 = { 3, 0, 15, 6, 17, 11, 9, 14, 2, 1, 10, 8, 7, 5, 4, 13, 19, 18, 12, 16 };
		int[] array3 = { 10, 14, 6, 3, 4, 18, 15, 12, 1, 17, 19, 2, 8, 5, 11, 9, 16, 0, 7, 13 };
		int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };

		int[] actual = OptimizedBubbleSort.bubbleSortWithEarlyStop(array1);
		assertThat(actual, is(expected));
		int[] actual2 = OptimizedBubbleSort.bubbleSortWithEarlyStop(array2);
		assertThat(actual2, is(expected));
		int[] actual3 = OptimizedBubbleSort.bubbleSortWithEarlyStop(array3);
		assertThat(actual3, is(expected));

		int[] actual4 = OptimizedBubbleSort.straightExchange(array1);
		assertThat(actual4, is(expected));
		int[] actual5 = OptimizedBubbleSort.straightExchange(array2);
		assertThat(actual5, is(expected));
		int[] actual6 = OptimizedBubbleSort.straightExchange(array3);
		assertThat(actual6, is(expected));
	}
public class OptimizedBubbleSort {

	/**
	 * バブルソート(打ち切りアリ)
	 * 先頭から末尾に向かって隣接する要素を比較して整列させる
	 * ソートの過程で交換が行われなくなった時点で早期に終了する
	 * @param array
	 * @return sortedArray
	 */
	public static int[] bubbleSortWithEarlyStop(int[] array) {
		int[] sortedArray = array.clone();
		int size = sortedArray.length;
		int exchangeCount = 0; // 交換回数のカウント用

		for (int i = 0; i < size - 1; i++) {
			int swapped = 0;
			for (int j = 0; j < size - 1 - i; j++) {
				if (sortedArray[j] > sortedArray[j + 1]) {
					int temp = sortedArray[j];
					sortedArray[j] = sortedArray[j + 1];
					sortedArray[j + 1] = temp;
					swapped++;
					exchangeCount++; // 交換が行われたらカウントを増やす
				}
			}
			if (swapped == 0) {
				break;
			}
		}
		System.out.println("打ち切りアリの交換回数: " + exchangeCount); // 交換回数を出力
		return sortedArray;
	}

	/**
	 * バブルソート(単純交換ソート)
	 * 先頭から末尾に向かって隣接する要素を比較して整列させる
	 * @param array
	 * @return sortedArray
	 */
	static int[] straightExchange(int[] array) {
		int[] sortedArray = array.clone();
		int size = sortedArray.length;
		int exchangeCount = 0; // 交換回数のカウント用

		for (int i = 0; i < size - 1; i++) {
			for (int j = 0; j < size - 1 - i; j++) {
				if (sortedArray[j] > sortedArray[j + 1]) {
					int temp = sortedArray[j];
					sortedArray[j] = sortedArray[j + 1];
					sortedArray[j + 1] = temp;
					exchangeCount++; // 交換が行われたらカウントを増やす
				}
			}
		}
		System.out.println("打ち切り無しの交換回数: " + exchangeCount); // 交換回数を出力
		return sortedArray;
	}
}
結果
打ち切りアリの交換回数: 53
打ち切りアリの交換回数: 73
打ち切りアリの交換回数: 98
打ち切り無しの交換回数: 53
打ち切り無しの交換回数: 73
打ち切り無しの交換回数: 98

完全ランダムな配列では特に最適化されてませんでした。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?