如何生成给定List的幂集?

时间:2014-01-05 15:24:13

标签: java algorithm list combinations

我试图生成长度为N的给定List的所有2 ^ N - 1种可能组合的集合。该集合将组合中的元素数量映射到包含组合的有序组合列表具体长度。例如,对于List:

[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)) ,并尽快运行。

我整天都在努力处理一些递归代码(我知道实现应该是递归的)但是无法找到它的底部。

是否有我可以使用的参考/这种算法的现成实现?

8 个答案:

答案 0 :(得分:12)

您正在寻找的内容基本上是power set(减去可能是空集)。番石榴实际上有一个方法:Sets.powerSet()。如果您想自己编写方法,可以查看source of the Sets class以了解该方法的实现方式;您可能需要修改它以返回List而不是Set,因为您希望保留顺序,尽管此更改不应过于激烈。一旦你获得了权力集,迭代它并构建你想要的地图应该是微不足道的。

答案 1 :(得分:7)

您要问的是生成集合的所有可能子集。您可以将其视为迭代所有可能的大小为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_arraybs上进行迭代,并选择s[current]b[current] = 1所在的元素。

以上将打印出所有现有的子集。您可以调整此算法,以获得您在问题中要求的地图。

答案 2 :(得分:3)

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);
}

<强>输出

1 -> [[1], [2], [3]]
2 -> [[2, 3], [1, 2], [1, 3]]
3 -> [[1, 2, 3]]

答案 3 :(得分:2)

这是我测试的代码,用于生成给定数组中的所有可能组合:

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;
}
}

答案 4 :(得分:1)

有多种方法可以解决这个问题,但最简单的方法是使用递归。下面我提供了迭代递归方法来解决生成集合的所有组合的问题。两种方法的一般思想是生成属于您要选择的元素的索引集。

对于递归方法,您需要跟踪三条信息:一个布尔数组,指示是否选择了某个项目,您在项目列表中的位置以及跟踪该项目的变量r剩余要选择的元素数量。然后只要r!= 0(意味着你仍然需要选择r元素来完成这个组合),你回溯选择一个你尚未选择的元素并在数组中向前移动。

下面显示的代码来自我的github repo

exit_code == "No"
while exit_code == "No":
    index_of_pixels_with_excess_volume = numpy.argwhere(ds2> 0.05) # find location of pixels where excess volume is greater than 0.05

    if not index_of_pixels_with_excess_volume.size:
        exit_code = "Yes"
    else:
        for pixel in index_of_pixels_with_excess_volume:
            # spread those excess volumes to the neighbours and
            # change the values of ds2

答案 5 :(得分:0)

我测试了Elist提出的代码,我发现了错误。

这是一个建议的修正:(在函数getPermutation()的最后一个方面,我做了两处修改)

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);
}

}

答案 6 :(得分:0)

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 Set是SICP“计算机程序设计的结构和解释”一书中的练习之一。每个程序员都应该阅读它。

答案 7 :(得分:0)

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);
    }
}