配列(再帰的および非再帰的)アルゴリズムおよびインターフェース設計
本論文では、まず再帰的アルゴリズムと非再帰的アルゴリズムを用いて、整数配列の全配列を実現し、インターフェースを精製することによって、このアルゴリズムを他のデータタイプの全配列、long配列、リスト、文字列などに適用する.
以下のコードは、整数配列の配列を実現するためのアルゴリズムであり、具体的な原理はここですを参照してください.
ここではすべての結果をListに置いて、小さいデータに対してはいいですが、大きなデータに対してはOutOfMemoryがあります.私のマシンでデータの大きさにだまされます.私はそれを非再帰的アルゴリズムに変換し、Iteratorに戻ります.このようにメモリに保存されているデータはとても少なく、OutOfMemoryではありません.このプログラムを非再帰的アルゴリズムに変換する場合、先に再帰樹を描き、具体的にどうすればいいですか?ここでは詳しく説明しません.コードを参照してください.
このようにする問題は、long配列、char配列、Listデータ、文字列の配列を実現するためには、それらをほとんど書き換える必要があります.これらのアルゴリズムを再利用したいですが、どうすればいいですか?この時になると、インターフェースを取りたいです.再帰バージョンのperm(int[]a,int from Index,int toIndex,Listresult)メソッド(再帰バージョンと類似しない)を観察して、配列aを操作する方法はswap方法とa.clone()方法しかないので、一つのインターフェースをPermableといいます.toIndexを指定しないように、新しい方法を追加しました.またObjectでclone()と競合しないようにcopyという名前を使いました.インターフェースは以下の通りです
文字列、long配列の配列を取得します.
参考記事:
全配列アルゴリズム原理と実装:http://www.blogjava.net/nokiaguy/archive/2008/05/11/199838.html
再帰的および非再帰的な変換をスタックでどのようにするか:http://www.chinaunix.net/jh/23/331522.html、この記事は、再帰的アルゴリズムを非再帰的アルゴリズムに変換する際に、再帰的ツリーを描く必要があることを示唆している.
以下のコードは、整数配列の配列を実現するためのアルゴリズムであり、具体的な原理はここですを参照してください.
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,List
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、この記事は、再帰的アルゴリズムを非再帰的アルゴリズムに変換する際に、再帰的ツリーを描く必要があることを示唆している.