長さNの指定されたリストの2 ^ N-1の可能なすべての組み合わせのコレクションを生成しようとしています。コレクションは、組み合わせの要素数を特定の長さの組み合わせを含む組み合わせの順序付きリストにマップします。たとえば、リストの場合:
[A, B, C, D]
マップを生成したい:
{
1 -> [{A}, {B}, {C}, {D}]
2 -> [{A, B}, {A, C}, {A, D}, {B, C}, {B, D}, {C, D}]
3 -> [{A, B, C}, {A, B, D}, {A, C, D}, {B, C, D}]
4 -> [{A, B, C, D}]
}
生成されたデータベースは元の順序を維持する必要があります([]
は順序付きシリーズ(List
)を表し、{}
は順序付けられていないグループ(Set
)を表します)できるだけ速く実行します。
私は1日中再帰的なコードに苦労していました(実装が再帰的であるべきだということは知っています)が、その根本に到達することができませんでした。
私が使用できるリファレンス/そのようなアルゴリズムのすぐに使える実装はありますか?
探しているのは、本質的に power set (マイナスの空のセット)です。グアバには実際にこのためのメソッドがあります: Sets.powerSet()
。 Sets
クラスのソース を表示して、自分でメソッドを記述したい場合のメソッドの実装方法を確認できます。順序を維持したいので、List
の代わりにSet
を返すように変更する必要があるかもしれませんが、この変更はそれほど大きくないはずです。パワーを設定したら、それを反復して必要なマップを作成するのは簡単です。
あなたが求めているのは、セットのすべての可能なサブセットを生成することです。これは、サイズN(リストのサイズ)のすべての可能なバイナリ配列を反復処理するものと考えることができます。
000000...000
000000...001
000000...010
000000...011
etc.
何故ですか?答えは簡単です。1は要素がサブセットに存在することを示し、0は要素が存在しないことを示します。
したがって、基本的なアルゴリズムは明らかです。
s = [A, B, C, D]
for i=0 to 2^N-1:
b = convert_number_to_bin_array(i)
ss = calculate_subset_based_on_bin_array(s, b)
print ss
calculate_subset_based_on_bin_array
はb
とs
を反復処理し、s[current]
where b[current] = 1
から要素を選択します。
上記は、すべての既存のサブセットを出力します。質問で要求したマップを取得するために、このアルゴリズムを適応できます。
static Map<Integer, List<LinkedList<Integer>>> powerset = new HashMap<>();
public static void main(String[] args) throws IOException {
powerset(Arrays.asList(1, 2, 3));
for (Integer key : powerset.keySet()) {
System.out.print(key + " -> ");
System.out.println(Arrays.toString(powerset.get(key).toArray()));
}
}
static void powerset(List<Integer> src) {
powerset(new LinkedList<>(), src);
}
private static void powerset(LinkedList<Integer> prefix, List<Integer> src) {
if (src.size() > 0) {
prefix = new LinkedList<>(prefix); //create a copy to not modify the orig
src = new LinkedList<>(src); //copy
Integer curr = src.remove(0);
collectResult(prefix, curr);
powerset(prefix, src);
prefix.add(curr);
powerset(prefix, src);
}
}
private static void collectResult(LinkedList<Integer> prefix, Integer curr) {
prefix = new LinkedList<>(prefix); //copy
prefix.add(curr);
List<LinkedList<Integer>> addTo;
if (powerset.get(prefix.size()) == null) {
List<LinkedList<Integer>> newList = new LinkedList<>();
addTo = newList;
} else {
addTo = powerset.get(prefix.size());
}
addTo.add(prefix);
powerset.put(prefix.size(), addTo);
}
[〜#〜] output [〜#〜]
1 -> [[1], [2], [3]]
2 -> [[2, 3], [1, 2], [1, 3]]
3 -> [[1, 2, 3]]
これは、特定の配列からすべての可能な組み合わせを生成するためにテストしたコードです。
enter code here
import Java.util.Arrays;
public class PasswordGen {
static String[] options = { "a", "b", "c", "d" };
static String[] places = new String[options.length];
static int count;
public static void main(String[] args) {
// Starting with initial position of a i.e. 0
sequence(0, places.clone());
}
private static void sequence(int level, String[] holder) {
if (level >= options.length) {
// combination complete
System.out.println("" + (++count) + " Combination "
+ Arrays.toString(holder));
return;
}
String val = options[level];
String[] inrHolder = null;
for (int c = 0; c < holder.length; c++) {
inrHolder = holder.clone();
if (inrHolder[c] == null) {
inrHolder[c] = val;
sequence(level + 1, inrHolder.clone());
}
}
return;
}
}
この問題を解決するには複数の方法がありますが、最も簡単な方法は再帰を使用することです。以下では、iterativeおよびrecursiveの問題を解決するアプローチを提供していますセットのすべての組み合わせを生成します。両方のアプローチの一般的な考え方は、選択する要素に属するインデックスのセットを生成することです。
再帰的なアプローチでは、3つの情報を追跡する必要があります。アイテムが選択されたかどうかを示すブール配列、アイテムのリスト内の位置、残りの要素の数を追跡する変数r選択する。次に、r!= 0(この組み合わせを完了するためにr個の要素を選択する必要があることを意味します)であれば、まだ選択していない要素の選択をバックトラックし、配列内を前方に移動します。
以下に示すコードは、私の github repo から引用したものです。
/**
* Here we present two methods (recursive and iterative) of generating all
* the combinations of a set by choosing only r of n elements.
*
* Time Complexity: O( n choose r )
*
* @author William Fiset, Micah Stairs
**/
public class Combinations {
// This method finds all the combinations of size r in a set
public static void combinationsChooseR(int[] set, int r) {
if (r < 0) return;
if (set == null) return;
boolean [] used = new boolean[set.length];
combinations(set, r, 0, used);
}
// To find all the combinations of size r we need to recurse until we have
// selected r elements (aka r = 0), otherwise if r != 0 then we need to select
// an element which is found after the position of our last selected element
private static void combinations(int[] set, int r, int at, boolean[] used) {
final int N = set.length;
// If there are more elements left to select than what are available
// This is a short circuiting optimization we can take advantage of
int elementsLeftToPick = N - at;
if (elementsLeftToPick < r) return;
// We selected 'r' elements so we found a valid subset!
if (r == 0) {
System.out.print("{ ");
for (int i = 0; i < N; i++)
if (used[i])
System.out.print(set[i] + " ");
System.out.println("}");
} else {
for (int i = at; i < N; i++) {
// Try including this element
used[i] = true;
combinations(set, r - 1, i + 1, used);
// Backtrack and try the instance where we did not include this element
used[i] = false;
}
}
}
// Use this method in combination with a do while loop to generate all the
// combinations of a set choose r in a iterative fashion. This method returns
// false once the last combination has been generated.
// NOTE: Originally the selection needs to be initialized to {0,1,2,3 ... r-1}
public static boolean nextCombination(int[] selection, int N, int r) {
if (r > N) throw new IllegalArgumentException("r must be <= N");
int i = r - 1;
while (selection[i] == N - r + i)
if (--i < 0) return false;
selection[i]++;
for (int j = i + 1; j < r; j++) selection[j] = selection[i] + j - i;
return true;
}
public static void main(String[] args) {
// Recursive approach
int R = 3;
int[] set = {1,2,3,4,5};
combinationsChooseR(set, R);
// prints:
// { 1 2 3 }
// { 1 2 4 }
// { 1 2 5 }
// { 1 3 4 }
// { 1 3 5 }
// { 1 4 5 }
// { 2 3 4 }
// { 2 3 5 }
// { 2 4 5 }
// { 3 4 5 }
// Suppose we want to select all combinations of colors where R = 3
String[] colors = {"red", "purple", "green", "yellow", "blue", "pink"};
R = 3;
// Initialize the selection to be {0, 1, ... , R-1}
int[] selection = { 0, 1, 2 };
do {
// Print each combination
for (int index : selection)
System.out.print(colors[index] + " ");
System.out.println();
} while( nextCombination(selection, colors.length, R) );
// prints:
// red purple green
// red purple yellow
// red purple blue
// red purple pink
// red green yellow
// red green blue
// red green pink
// red yellow blue
// red yellow pink
// red blue pink
// purple green yellow
// purple green blue
// purple green pink
// purple yellow blue
// purple yellow pink
// purple blue pink
// green yellow blue
// green yellow pink
// green blue pink
// yellow blue pink
}
}
OPのソリューションは質問から回答に移行しました。
以前の回答のおかげで、次の実装を思いつきました。
public class OrderedPowerSet<E> {
private static final int ELEMENT_LIMIT = 12;
private List<E> inputList;
public int N;
private Map<Integer, List<LinkedHashSet<E>>> map =
new HashMap<Integer, List<LinkedHashSet<E>>>();
public OrderedPowerSet(List<E> list) {
inputList = list;
N = list.size();
if (N > ELEMENT_LIMIT) {
throw new RuntimeException(
"List with more then " + ELEMENT_LIMIT + " elements is too long...");
}
}
public List<LinkedHashSet<E>> getPermutationsList(int elementCount) {
if (elementCount < 1 || elementCount > N) {
throw new IndexOutOfBoundsException(
"Can only generate permutations for a count between 1 to " + N);
}
if (map.containsKey(elementCount)) {
return map.get(elementCount);
}
ArrayList<LinkedHashSet<E>> list = new ArrayList<LinkedHashSet<E>>();
if (elementCount == N) {
list.add(new LinkedHashSet<E>(inputList));
} else if (elementCount == 1) {
for (int i = 0 ; i < N ; i++) {
LinkedHashSet<E> set = new LinkedHashSet<E>();
set.add(inputList.get(i));
list.add(set);
}
} else {
list = new ArrayList<LinkedHashSet<E>>();
for (int i = 0 ; i <= N - elementCount ; i++) {
@SuppressWarnings("unchecked")
ArrayList<E> subList = (ArrayList<E>)((ArrayList<E>)inputList).clone();
for (int j = i ; j >= 0 ; j--) {
subList.remove(j);
}
OrderedPowerSet<E> subPowerSet =
new OrderedPowerSet<E>(subList);
List<LinkedHashSet<E>> pList =
subPowerSet.getPermutationsList(elementCount-1);
for (LinkedHashSet<E> s : pList) {
LinkedHashSet<E> set = new LinkedHashSet<E>();
set.add(inputList.get(i));
set.addAll(s);
list.add(set);
}
}
}
map.put(elementCount, list);
return map.get(elementCount);
}
}
Elistが提案したコードをテストしましたが、エラーが見つかりました。
ここに提案された修正があります:(関数getPermutation()の最後のほかに、私は2つの変更を行いました)
public class OrderedPowerSet<E> {
private ArrayList<E> inputList;
public int N;
private Map<Integer, List<Set<E>>> map =
new HashMap<Integer, List<Set<E>>>();
public OrderedPowerSet(ArrayList<E> list) {
inputList = list;
N = list.size();
}
public List<Set<E>> getPermutationsList(int elementCount) {
if (elementCount < 1 || elementCount > N) {
throw new IndexOutOfBoundsException(
"Can only generate permutations for a count between 1 to " + N);
}
if (map.containsKey(elementCount)) {
return map.get(elementCount);
}
ArrayList<Set<E>> list = new ArrayList<Set<E>>();
if (elementCount == N) {
list.add(new HashSet<E>(inputList));
} else if (elementCount == 1) {
for (int i = 0 ; i < N ; i++) {
Set<E> set = new HashSet<E>();
set.add(inputList.get(i));
list.add(set);
}
} else {
for (int i = 0 ; i < N-elementCount ; i++) {
@SuppressWarnings("unchecked")
ArrayList<E> subList = (ArrayList<E>)inputList.clone(); // one change
subList.remove(0);
OrderedPowerSet<E> subPowerSet =
new OrderedPowerSet<E>(subList);
for (Set<E> s : subPowerSet.getPermutationsList(elementCount-1)) {
Set<E> set = new HashSet<E>();
set.add(inputList.get(i));
set.addAll(s);
list.add(set); // second change
}
}
}
map.put(elementCount, list);
return map.get(elementCount);
}
}
public static List<String> getCombinationsLists(List<String> elements)
{
//return list with empty String
if(elements.size() == 0){
List<String> allLists = new ArrayList<String>();
allLists.add("");
return allLists ;
}
String first_ele = elements.remove(0);
List<String> rest = getCobminationLists(elements);
int restsize = rest.size();
//Mapping the first_ele with each of the rest of the elements.
for (int i = 0; i < restsize; i++) {
String ele = first_ele + rest.get(i);
rest.add(ele);
}
return rest;
}
このPowerセットは、SICPの「コンピュータープログラミングの構造と解釈」という本の課題の1つです。すべてのプログラマーが読んでください。