配列(再帰的および非再帰的)アルゴリズムおよびインターフェース設計


本論文では、まず再帰的アルゴリズムと非再帰的アルゴリズムを用いて、整数配列の全配列を実現し、インターフェースを精製することによって、このアルゴリズムを他のデータタイプの全配列、long配列、リスト、文字列などに適用する.
 
以下のコードは、整数配列の配列を実現するためのアルゴリズムであり、具体的な原理はここですを参照してください.
	public static List<int[]> perm(int[] a) {
		return perm(a, 0, a.length);
	}
	public static List<int[]> perm(int[] a, int fromIndex, int toIndex) {
		List<int[]> result = new ArrayList<int[]>();
		perm(a, fromIndex, toIndex, result);
		return result;
	}
	
	private static void perm(int[] a, int fromIndex, int toIndex, List<int[]> result) {
		if (toIndex - fromIndex <= 1) {
			result.add(a.clone());
		} else {
			for (int i = fromIndex; i < toIndex; i++) {
				swap(a, fromIndex, i);
				perm(a, fromIndex+1, toIndex, result);
				swap(a, fromIndex, i);
			}
		}
	}
	
	private static void swap(int[] a, int i, int j) {
		int temp = a[i]; a[i] = a[j]; a[j] = temp;
	}
 
ここではすべての結果をListに置いて、小さいデータに対してはいいですが、大きなデータに対してはOutOfMemoryがあります.私のマシンでデータの大きさにだまされます.私はそれを非再帰的アルゴリズムに変換し、Iteratorに戻ります.このようにメモリに保存されているデータはとても少なく、OutOfMemoryではありません.このプログラムを非再帰的アルゴリズムに変換する場合、先に再帰樹を描き、具体的にどうすればいいですか?ここでは詳しく説明しません.コードを参照してください.
	
	public static Iterator<int[]> permIterator(int[] a, int fromIndex, int toIndex) {
		return new IntArrayPermIterator(a, fromIndex, toIndex);
	}
	public static Iterator<int[]> permIterator(int[] a) {
		return new IntArrayPermIterator(a, 0, a.length);
	}
	public static Iterable<int[]> permIterable(int[] a, int fromIndex, int toIndex) {
		return new IntArrayPermIterator(a, fromIndex, toIndex);
	}
	public static Iterable<int[]> permIterable(int[] a) {
		return new IntArrayPermIterator(a, 0, a.length);
	}
	
	//          
	private static class IntArrayPermIterator implements Iterator<int[]>, Iterable<int[]> {
		private int[] array;
		private int toIndex;
		private LinkedList<StackFrame> frames = new LinkedList<StackFrame>();
		
		public IntArrayPermIterator(int[] a, int fromIndex, int toIndex) {
			this.array = a;
			this.toIndex = toIndex;
			frames.add(new StackFrame(fromIndex));
		}

		public boolean hasNext() {
			return frames.size() > 0;
		}

		public int[] next() {
			StackFrame frame = frames.getLast();
			swap(array, frame.fromIndex, frame.currentIndex);
			for (int i = frame.fromIndex + 1; i < toIndex; i++) {
				frames.addLast(new StackFrame(i));
			}
			int[] result = array.clone();
			frame = frames.removeLast();
			while (!frames.isEmpty()) {
				frame = frames.getLast();
				swap(array, frame.fromIndex, frame.currentIndex);
				if (frame.currentIndex != toIndex - 1) {
					frame.currentIndex++; break;
				}
				frames.removeLast();
			}
			
			return result;
		}
		
		public void remove() {
			throw new UnsupportedOperationException();
		}

		public Iterator<int[]> iterator() {
			return this;
		}
	}

	
	private static class StackFrame {
		int fromIndex;
		int currentIndex;
		
		public StackFrame(int fromIndex) {
			this.fromIndex = fromIndex;
			this.currentIndex = fromIndex;
		}
		public String toString() {
			return String.format("[%d, %d]", fromIndex, currentIndex);
		}
	}
  IntArayPermIteratorがIteratorとIterableインターフェースを実現し、Iterableインターフェースを実現するのはJDK 5を利用してforサイクルを強化するためである.
		for (int[] a: permIterable(new int[]{1, 2, 3})) {
			System.out.println(Arrays.toString(a));
		}
 
このようにする問題は、long配列、char配列、Listデータ、文字列の配列を実現するためには、それらをほとんど書き換える必要があります.これらのアルゴリズムを再利用したいですが、どうすればいいですか?この時になると、インターフェースを取りたいです.再帰バージョンのperm(int[]a,int from Index,int toIndex,Listresult)メソッド(再帰バージョンと類似しない)を観察して、配列aを操作する方法はswap方法とa.clone()方法しかないので、一つのインターフェースをPermableといいます.toIndexを指定しないように、新しい方法を追加しました.またObjectでclone()と競合しないようにcopyという名前を使いました.インターフェースは以下の通りです
	public interface Permable<T> {
		void swap(int i, int j);
		T copy();
		int size();
	}
 このインターフェースは、タイプパラメータTを使用して、戻りのデータタイプを表しています.このインターフェースを使用して、再帰的なバージョンの配列アルゴリズムは以下の通りである.
	public static <T> List<T> perm(Permable<T> data) {
		return perm(data, 0, data.size());
	}
	
	public static <T> List<T> perm(Permable<T> data, int fromIndex, int toIndex) {
		List<T> result = new ArrayList<T>();
		perm(data, fromIndex, toIndex, result);
		return result;
	}
	
	private static <T> void perm(Permable<T> data, int fromIndex, int toIndex, List<T> result) {
		if (toIndex - fromIndex <= 1) {
			result.add(data.copy());
		} else {
			for (int i = fromIndex; i < toIndex; i++) {
				data.swap(fromIndex, i);
				perm(data, fromIndex+1, toIndex, result);
				data.swap(fromIndex, i);
			}
		}
	}
 long配列、文字列、Listなどを並べ替えるには、自分たちのPermableインターフェースを提供するだけで実現されます.
	public static class LongArrayPermable implements Permable<long[]> {
		private long[] data;
		public LongArrayPermable(long[] data) {
			this.data = data;
		}
		public void swap(int i, int j) {
			long temp = data[i]; data[i] = data[j]; data[j] = temp;
		}
		public long[] copy() {
			return data.clone();
		}
		public int size() {
			return data.length;
		}
	}

	public static class CharArrayPermable implements Permable<char[]> {
		private char[] data;
		public CharArrayPermable(char[] data) {
			this.data = data;
		}
		public void swap(int i, int j) {
			char temp = data[i]; data[i] = data[j]; data[j] = temp;
		}
		public char[] copy() {
			return data.clone();
		}
		public int size() {
			return data.length;
		}
	}
	
	public static class StringPermable implements Permable<String> {
		private char[] data;
		public StringPermable(String str) {
			this.data = str.toCharArray();
		}
		public StringPermable(char[] data) {
			this.data = data;
		}
		public String copy() {
			return new String(data);
		}
		public int size() {
			return data.length;
		}
		public void swap(int i, int j) {
			char temp = data[i]; data[i] = data[j]; data[j] = temp;
		}
	}

	public static class ObjectArrayPermable implements Permable<Object[]> {
		private Object[] data;
		public ObjectArrayPermable(Object[] data) {
			this.data = data;
		}
		public void swap(int i, int j) {
			Object temp = data[i]; data[i] = data[j]; data[j] = temp;
		}
		public Object[] copy() {
			return data.clone();
		}
		public int size() {
			return data.length;
		}
	}
	
	public static class ListPermable<E> implements Permable<List<E>> {
		private List<E> data;
		private Class<?> implementation;
		
		public ListPermable(List<E> data, Class<?> listImplementation) {
			this.data = data;
			this.implementation = listImplementation;
		}
		public ListPermable(List<E> data) {
			this(data, data.getClass());
		}
		
		@SuppressWarnings("unchecked")
		public List<E> copy() {
			if (ArrayList.class == implementation) {
				return new ArrayList<E>(data);
			} else {
				try {
					List<E> list = (List<E>) implementation.newInstance();
					list.addAll(data);
					return list;
				} catch (InstantiationException e) {
					throw new IllegalStateException(e);
				} catch (IllegalAccessException e) {
					throw new IllegalStateException(e);
				}
			}
		}

		public int size() {
			return data.size();
		}

		public void swap(int i, int j) {
			E temp = data.get(i); data.set(i, data.get(j)); data.set(j, temp);
		}
	}
 
文字列、long配列の配列を取得します.
		for (long[] a : perm(new LongArrayPermable(new long[]{1, 2, 3}))) {
			System.out.println(Arrays.toString(a));
		}
		for (String a : perm(new StringPermable("abc"))) {
			System.out.println(a);
		}
このインターフェースは再帰版ではない配置アルゴリズムでも簡単です.いくつかの簡単な交換をすればいいです.ここでは列挙しません.添付ファイルを参照してください.
 
参考記事:
全配列アルゴリズム原理と実装:http://www.blogjava.net/nokiaguy/archive/2008/05/11/199838.html
再帰的および非再帰的な変換をスタックでどのようにするか:http://www.chinaunix.net/jh/23/331522.html、この記事は、再帰的アルゴリズムを非再帰的アルゴリズムに変換する際に、再帰的ツリーを描く必要があることを示唆している.