Java에서 집합의 powerset 얻기


86

의 powerset {1, 2, 3}은 다음과 같습니다.

{{}, {2}, {3}, {2, 3}, {1, 2}, {1, 3}, {1, 2, 3}, {1}}

내가 SetJava에 있다고 가정 해 봅시다 .

Set<Integer> mySet = new HashSet<Integer>();
mySet.add(1);
mySet.add(2);
mySet.add(3);
Set<Set<Integer>> powerSet = getPowerset(mySet);

가능한 가장 복잡한 순서로 getPowerset 함수를 어떻게 작성합니까? (O (2 ^ n)일지도 모르겠네요.)


7
모델을 매개 변수화하는 데 사용할 수있는 구성 집합 (예 : "A", "B"및 "C")이 있고 어떤 하위 집합이 최상의 결과를 산출하는지 확인하려는 경우 (예 : "A") ". 가능한 해결책은 powerset의 각 구성원을 테스트하는 것입니다.
— João Silva

7
소프트웨어 개발자를위한 Google 인터뷰 질문입니다. 마음의 민첩성을 테스트하는 것은 인위적인 문제입니다.
— Eric Leschinski

이것은 합리적인 질문입니다. 예를 들어 크리비지에 대한 스코어링 기능을 구현하려면 powerset의 요소가 15까지 더해지는 지 테스트해야합니다.
— John Henckel

답변:


101

예, 가능한 조합 O(2^n)을 생성해야하기 때문에 실제로 그렇습니다 2^n. 다음은 제네릭 및 세트를 사용하는 작업 구현입니다.

public static <T> Set<Set<T>> powerSet(Set<T> originalSet) {
    Set<Set<T>> sets = new HashSet<Set<T>>();
    if (originalSet.isEmpty()) {
        sets.add(new HashSet<T>());
        return sets;
    }
    List<T> list = new ArrayList<T>(originalSet);
    T head = list.get(0);
    Set<T> rest = new HashSet<T>(list.subList(1, list.size())); 
    for (Set<T> set : powerSet(rest)) {
        Set<T> newSet = new HashSet<T>();
        newSet.add(head);
        newSet.addAll(set);
        sets.add(newSet);
        sets.add(set);
    }       
    return sets;
}  

예제 입력이 주어진 경우 테스트 :

 Set<Integer> mySet = new HashSet<Integer>();
 mySet.add(1);
 mySet.add(2);
 mySet.add(3);
 for (Set<Integer> s : SetUtils.powerSet(mySet)) {
     System.out.println(s);
 }

1
목록을 사용하는 대신 반복자를 사용하는 것이 더 빠를까요? 예 : Set <T> rest = new HashSet <T> (originalSet); 반복자 <T> i = rest.iterator (); T 머리 = i.next (); i.remove (); ?
— Dimath

1
@CosminVacaroiu ... 그 밖에 무엇을 할 수 있습니까?
— user253751

3
확실 O(2^n)합니까? 그것은 파워 세트의 세트 수이지만 각 세트는 메모리에 생성되어야하므로 최소한 세트 크기에 비례하는 시간이 걸립니다. wolfram alpha에 따르면, 다음과 O(n * 2^n)같습니다 : wolfram alpha query
— fabian

1
세트의 크기가 10 ^ 5 정도라도 작동할까요?
— bane19

1
@GauravShankar 2 ^ 100 = 2 ^ (10 ^ 2)는 이미 10 ^ 30보다 큽니다. 계산할 튜링 머신에 관계없이 계산이 완료되는 것을 목격하지 못할 것입니다.
— Karl Richter

31

사실, 저는 O (1)에서 당신이 요구하는 것을하는 코드를 작성했습니다. 문제는 당신이 계획 무엇을 할 다음 설정으로. 그냥 전화를 걸면 size()O (1)이되지만 반복하려면 분명히O(2^n) .

contains() 될 것이다 O(n)등 .

정말 필요한가요?

편집하다:

이 코드는 이제 Guava 에서 사용할 수 있으며 메서드를 통해 노출됩니다 Sets.powerSet(set).


나는 모든 부분 집합 반복 할 필요가
— 마누엘 Araoz

그러나 모든 하위 집합 을 저장 해야 합니까?
— finnw 2009


정확히 k 개의 요소가있는 powerset을 원하면 어떻게해야합니까? 코드가 효율적입니까?
— Eyal

새로운 링크 (구아바는 Github에서 이동)
— yiwei

12

여기에 발전기를 사용하는 솔루션이 있습니다. 장점은 전체 전력 세트가 한 번에 저장되지 않는다는 것입니다. 따라서 메모리에 저장할 필요없이 하나씩 반복 할 수 있습니다. 더 나은 옵션이라고 생각하고 싶습니다 ... 복잡성은 동일합니다. O (2 ^ n), 그러나 메모리 요구 사항은 감소합니다 (가비지 수집기가 작동한다고 가정하면!;)).

/**
 *
 */
package org.mechaevil.util.Algorithms;

import java.util.BitSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author st0le
 *
 */
public class PowerSet<E> implements Iterator<Set<E>>,Iterable<Set<E>>{
    private E[] arr = null;
    private BitSet bset = null;

    @SuppressWarnings("unchecked")
    public PowerSet(Set<E> set)
    {
        arr = (E[])set.toArray();
        bset = new BitSet(arr.length + 1);
    }

    @Override
    public boolean hasNext() {
        return !bset.get(arr.length);
    }

    @Override
    public Set<E> next() {
        Set<E> returnSet = new TreeSet<E>();
        for(int i = 0; i < arr.length; i++)
        {
            if(bset.get(i))
                returnSet.add(arr[i]);
        }
        //increment bset
        for(int i = 0; i < bset.size(); i++)
        {
            if(!bset.get(i))
            {
                bset.set(i);
                break;
            }else
                bset.clear(i);
        }

        return returnSet;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Not Supported!");
    }

    @Override
    public Iterator<Set<E>> iterator() {
        return this;
    }

}

이를 호출하려면 다음 패턴을 사용하십시오.

        Set<Character> set = new TreeSet<Character> ();
        for(int i = 0; i < 5; i++)
            set.add((char) (i + 'A'));

        PowerSet<Character> pset = new PowerSet<Character>(set);
        for(Set<Character> s:pset)
        {
            System.out.println(s);
        }

내 프로젝트 오일러 라이브러리에서 가져온 것입니다 ... :)


구아바는 이것과 매우 유사하게 작동하지만 32 개 요소로 제한됩니다. 2 ** 32는 아마도 너무 많은 반복이기 때문에 불합리하지 않습니다. 필요할 때만 AbstractSet을 생성하기 때문에 당신보다 적은 메모리를 사용합니다. 10,000 개의 요소 중 1 개만 인쇄하는 Guava 코드에 대해 코드를 시도하고 큰 예를 만드십시오. 나는 구아바가 더 빠를 것이라고 장담한다.
— Eyal 2014 년

@Eyal, 나는 그것을 확신합니다, 나는 달리 주장하지 않았습니다. 직접 작성했는데 프로덕션 코드 용이 아닙니다. 그것은 알고리즘의 연습이었습니다.
— st0le

1
사소한 말 : 'returnSet'은 TreeSet이며 항목이 비교 가능해야합니다. 그렇지 않을 수도 있습니다. HashSet의 또는 LinkedHashSet의 그것을 교환 고려
— 요리스 Kinable

10

어쨌든 전력 세트를 구성하려고 시도하는 메모리가 부족하기 때문에 (반복기 구현을 사용하지 않는 한) 합리적인 가정 인 n <63이면 이것은 더 간결한 방법입니다. 바이너리 연산은 Math.pow()마스크 배열 보다 훨씬 빠르지 만 , 자바 사용자는이를 두려워합니다.

List<T> list = new ArrayList<T>(originalSet);
int n = list.size();

Set<Set<T>> powerSet = new HashSet<Set<T>>();

for( long i = 0; i < (1 << n); i++) {
    Set<T> element = new HashSet<T>();
    for( int j = 0; j < n; j++ )
        if( (i >> j) % 2 == 1 ) element.add(list.get(j));
    powerSet.add(element); 
}

return powerSet;

for 루프의 종료 조건은 i <(1 << n-1) 대신 i <(2 << n-1)이어야합니다.
— bazeusz 2014 년

감사합니다 @bazeusz, 나는 그것을 i < (1 << n)동등한 것으로 변경했습니다 .
— Andrew Mao

비트 현명한 연산을 사용하기 때문에 ((i >> j) &1) == 1대신 (i >> j) % 2 == 1 사용할 수 있다고 생각 합니다 . 또한 long서명되어 있으므로 오버플로 확인이 합리적이라고 생각하십니까?
— Ravi Tiwari

9

다음 은 코드를 포함하여 원하는 것을 정확히 설명하는 자습서입니다. 복잡성이 O (2 ^ n)이라는 점에서 맞습니다.


2
복잡하지 않습니까 (n * 2 ^ n)? 이진 문자열의 길이는 n이고 메인 루프의 각 반복에서 전체 이진 문자열을 반복합니다.
— Maggie

1
튜토리얼은 훌륭하지만 HackerRank 문제를 해결하는 데이 기술을 사용했습니다. 테스트 케이스의 절반 만 통과했고 나머지 절반은 시간 초과로 인해 실패했거나 런타임 오류가 발생했습니다.
— Eugenia Ozirna

7

@Harry He의 아이디어를 기반으로 다른 솔루션을 생각해 냈습니다. 아마도 가장 우아하지는 않지만 내가 이해하는대로 여기에 있습니다.

SP (S) = {{1}, {2}, {3}}의 고전적인 간단한 예제 PowerSet을 보겠습니다. 부분 집합의 수를 구하는 공식은 2 ^ n (7 + 빈 집합)입니다. 이 예에서는 2 ^ 3 = 8 개의 하위 집합입니다.

각 부분 집합을 찾으려면 아래 변환 표에 표시된 10 진수 0-7을 이진 표현으로 변환해야합니다.

ConversionTable

행 단위로 테이블을 탐색하면 각 행은 하위 집합이되고 각 하위 집합의 값은 활성화 된 비트에서 나옵니다.

Bin Value 섹션의 각 열은 원래 입력 Set의 인덱스 위치에 해당합니다.

여기 내 코드 :

public class PowerSet {

/**
 * @param args
 */
public static void main(String[] args) {
    PowerSet ps = new PowerSet();
    Set<Integer> set = new HashSet<Integer>();
    set.add(1);
    set.add(2);
    set.add(3);
    for (Set<Integer> s : ps.powerSet(set)) {
        System.out.println(s);
    }
}

public Set<Set<Integer>> powerSet(Set<Integer> originalSet) {
    // Original set size e.g. 3
    int size = originalSet.size();
    // Number of subsets 2^n, e.g 2^3 = 8
    int numberOfSubSets = (int) Math.pow(2, size);
    Set<Set<Integer>> sets = new HashSet<Set<Integer>>();
    ArrayList<Integer> originalList = new ArrayList<Integer>(originalSet);
    for (int i = 0; i < numberOfSubSets; i++) {
        // Get binary representation of this index e.g. 010 = 2 for n = 3
        String bin = getPaddedBinString(i, size);
        //Get sub-set
        Set<Integer> set = getSet(bin, originalList));
        sets.add(set);
    }
    return sets;
}

//Gets a sub-set based on the binary representation. E.g. for 010 where n = 3 it will bring a new Set with value 2
private Set<Integer> getSet(String bin, List<Integer> origValues){
    Set<Integer> result = new HashSet<Integer>();
    for(int i = bin.length()-1; i >= 0; i--){
        //Only get sub-sets where bool flag is on
        if(bin.charAt(i) == '1'){
            int val = origValues.get(i);
            result.add(val);
        }
    }
    return result;
}

//Converts an int to Bin and adds left padding to zero's based on size
private String getPaddedBinString(int i, int size) {
    String bin = Integer.toBinaryString(i);
    bin = String.format("%0" + size + "d", Integer.parseInt(bin));
    return bin;
}

}

5

당신이 사용하는 경우 이클립스 컬렉션 (구 GS 컬렉션 ), 당신은 사용할 수있는 powerSet()모든 SetIterables에 방법을.

MutableSet<Integer> set = UnifiedSet.newSetWith(1, 2, 3);
System.out.println("powerSet = " + set.powerSet());
// prints: powerSet = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

참고 : 저는 Eclipse Collections의 커미터입니다.


솔루션 코드를 공유하고 설명 할 수 있습니까?
— Konrad Höffner 2014

3
여기에서 코드를 볼 수 있습니다. github.com/goldmansachs/gs-collections/blob/…
— Craig P. Motlin

4

여기에 게시 된 것만 큼 크지 않은 솔루션을 찾고있었습니다. 이것은 Java 7을 대상으로하므로 버전 5 및 6에 대해 몇 가지 붙여 넣기가 필요합니다.

Set<Set<Object>> powerSetofNodes(Set<Object> orig) {
    Set<Set<Object>> powerSet = new HashSet<>(),
        runSet = new HashSet<>(),
        thisSet = new HashSet<>();

    while (powerSet.size() < (Math.pow(2, orig.size())-1)) {
        if (powerSet.isEmpty()) {
            for (Object o : orig) {
                Set<Object> s = new TreeSet<>();
                s.add(o);
                runSet.add(s);
                powerSet.add(s);
            }
            continue;
        }
        for (Object o : orig) {
            for (Set<Object> s : runSet) {
                Set<Object> s2 = new TreeSet<>();
                s2.addAll(s);
                s2.add(o);
                powerSet.add(s2);
                thisSet.add(s2);
            }
        }
        runSet.clear();
        runSet.addAll(thisSet);
        thisSet.clear();
    }
    powerSet.add(new TreeSet());
    return powerSet;

다음은 테스트 할 몇 가지 예제 코드입니다.

Set<Object> hs = new HashSet<>();
hs.add(1);
hs.add(2);
hs.add(3);
hs.add(4);
for(Set<Object> s : powerSetofNodes(hs)) {
    System.out.println(Arrays.toString(s.toArray()));
}

powerSetofNodes () 끝에 "}"가 누락되지 않았습니까?
— Peter Mortensen 2014 년

3

위의 솔루션 중 일부는 수집 할 개체 가비지를 많이 생성하고 데이터를 복사해야하기 때문에 집합의 크기가 클 때 어려움을 겪습니다. 그것을 어떻게 피할 수 있습니까? 우리는 결과 집합 크기가 (2 ^ n) 얼마나 클지 알고 있다는 사실을 활용할 수 있고, 그렇게 큰 배열을 미리 할당하고 복사하지 않고 그 끝에 추가 할 수 있습니다.

속도 향상은 n과 함께 빠르게 증가합니다. 위의 João Silva의 솔루션과 비교했습니다. 내 컴퓨터에서 (모든 측정은 대략) n = 13은 5 배, n = 14는 7 배, n = 15는 12 배, n = 16은 25 배, n = 17은 75 배, n = 18은 140 배입니다. 따라서 가비지 생성 / 수집 및 복사는 유사한 big-O 솔루션으로 보이는 것에서 지배적입니다.

처음에 어레이를 사전 할당하는 것이 동적으로 성장하는 것에 비해 유리한 것 같습니다. n = 18이면 동적 성장은 전체적으로 약 2 배 더 오래 걸립니다.

public static <T> List<List<T>> powerSet(List<T> originalSet) {
    // result size will be 2^n, where n=size(originalset)
    // good to initialize the array size to avoid dynamic growing
    int resultSize = (int) Math.pow(2, originalSet.size());
    // resultPowerSet is what we will return
    List<List<T>> resultPowerSet = new ArrayList<List<T>>(resultSize);

    // Initialize result with the empty set, which powersets contain by definition
    resultPowerSet.add(new ArrayList<T>(0)); 

    // for every item in the original list
    for (T itemFromOriginalSet : originalSet) {

        // iterate through the existing powerset result
        // loop through subset and append to the resultPowerset as we go
        // must remember size at the beginning, before we append new elements
        int startingResultSize = resultPowerSet.size();
        for (int i=0; i<startingResultSize; i++) {
            // start with an existing element of the powerset
            List<T> oldSubset = resultPowerSet.get(i);

            // create a new element by adding a new item from the original list
            List<T> newSubset = new ArrayList<T>(oldSubset);
            newSubset.add(itemFromOriginalSet);

            // add this element to the result powerset (past startingResultSize)
            resultPowerSet.add(newSubset);
        }
    }
    return resultPowerSet;
}

3

다음 솔루션은 내 책 " 코딩 인터뷰 : 질문, 분석 및 솔루션 " 에서 차용 한 것입니다 .

조합을 구성하는 배열의 일부 정수가 선택됩니다. 비트 세트가 사용되며 각 비트는 배열의 정수를 나타냅니다. 경우] , i 번째 문자가 조합을 선택하면, i 번째 비트는 1이고; 그렇지 않으면 0입니다. 예를 들어 배열 [1, 2, 3]의 조합에 3 비트가 사용됩니다. 처음 두 정수 1과 2를 선택하여 조합 [1, 2]를 구성하면 해당 비트는 {1, 1, 0}입니다. 마찬가지로 다른 조합 [1, 3]에 해당하는 비트는 {1, 0, 1}입니다. n 비트의 가능한 모든 조합을 얻을 수 있다면 길이가 n 인 배열의 모든 조합을 얻을 수 있습니다 .

숫자는 비트 세트로 구성됩니다. n 비트 의 가능한 모든 조합은 1에서 2 ^ n -1 사이의 숫자에 해당합니다 . 따라서 1과 2 ^ n -1 범위의 각 숫자는 길이가 n 인 배열의 조합에 해당합니다 . 예를 들어, 숫자 6은 비트 {1, 1, 0}로 구성되어 있으므로 첫 번째 및 두 번째 문자는 배열 [1, 2, 3]에서 선택되어 [1, 2] 조합을 생성합니다. 마찬가지로 비트가 {1, 0, 1} 인 숫자 5는 [1, 3] 조합에 해당합니다.

이 솔루션을 구현하는 Java 코드는 다음과 같습니다.

public static ArrayList<ArrayList<Integer>> powerSet(int[] numbers) {
    ArrayList<ArrayList<Integer>> combinations = new ArrayList<ArrayList<Integer>>(); 
    BitSet bits = new BitSet(numbers.length);
    do{
        combinations.add(getCombination(numbers, bits));
    }while(increment(bits, numbers.length));

    return combinations;
}

private static boolean increment(BitSet bits, int length) {
    int index = length - 1;

    while(index >= 0 && bits.get(index)) {
        bits.clear(index);
        --index;
    }

    if(index < 0)
        return false;

    bits.set(index);
    return true;
}

private static ArrayList<Integer> getCombination(int[] numbers, BitSet bits){
    ArrayList<Integer> combination = new ArrayList<Integer>();
    for(int i = 0; i < numbers.length; ++i) {
        if(bits.get(i))
            combination.add(numbers[i]);
    }

    return combination;
}

메서드 증분은 비트 세트로 표현되는 숫자를 증가시킵니다. 알고리즘은 0 비트를 찾을 때까지 맨 오른쪽 비트에서 1 비트를 지 웁니다. 그런 다음 맨 오른쪽 0 비트를 1로 설정합니다. 예를 들어 비트 {1, 0, 1}로 숫자 5를 늘리려면 오른쪽에서 1 비트를 지우고 맨 오른쪽 0 비트를 1로 설정합니다. 숫자 6의 경우 {1, 1, 0}, 5를 1 씩 늘린 결과입니다.


내가 수정 한 두 가지 : numbers.length (또는 bits.size ()) 대신 getCombination에서 반복하면 bits.length ()로 반복하여 생성 속도를 약간 높일 수 있습니다. 마지막으로 내 문제에 대해 크기별로 하위 집합을 정렬했습니다.
— BoLe

3

다음은 쉬운 반복 O (2 ^ n) 솔루션입니다.

public static Set<Set<Integer>> powerSet(List<Integer> intList){

    Set<Set<Integer>> result = new HashSet();
    result.add(new HashSet());

    for (Integer i : intList){

        Set<Set<Integer>> temp = new HashSet();

        for(Set<Integer> intSet : result){

            intSet = new HashSet(intSet);
            intSet.add(i);                
            temp.add(intSet);
        }
        result.addAll(temp);
    }
    return result;
}

이 솔루션은 또한 큰 입력 세트에 너무 많은 O (2 ^ n) 공간을 사용합니다. 재귀 대신 스택이나 큐를 사용하여 재귀 정의를 따르는 것이 좋습니다.
— rossb83

2
import java.util.Set;
import com.google.common.collect.*;

Set<Set<Integer>> sets = Sets.powerSet(ImmutableSet.of(1, 2, 3));

1

S가 N 개의 요소가있는 유한 집합이면 S의 거듭 제곱 집합에는 2 ^ N 개의 요소가 포함됩니다. 전력 집합의 요소를 단순히 열거하는 시간은 2 ^ N이므로 전력 집합 O(2^N)을 (열심히) 구성하는 시간 복잡도의 하한이됩니다.

간단히 말해서, 파워 셋 생성을 포함하는 모든 계산은 N의 큰 값으로 확장되지 않습니다. 영리한 알고리즘은 도움이되지 않습니다.


1

재귀가없는 한 가지 방법은 다음과 같습니다. 이진 마스크를 사용하고 가능한 모든 조합을 만듭니다.

public HashSet<HashSet> createPowerSet(Object[] array)
{
    HashSet<HashSet> powerSet=new HashSet();
    boolean[] mask= new boolean[array.length];

    for(int i=0;i<Math.pow(2, array.length);i++)
    {
        HashSet set=new HashSet();
        for(int j=0;j<mask.length;j++)
        {
            if(mask[i])
                set.add(array[j]);
        }
        powerSet.add(set);      

        increaseMask(mask);
    }

    return powerSet;
}

public void increaseMask(boolean[] mask)
{
    boolean carry=false;

    if(mask[0])
        {
            mask[0]=false;
            carry=true;
        }
    else
        mask[0]=true;

    for(int i=1;i<mask.length;i++)
    {
        if(mask[i]==true && carry==true)
        mask[i]=false;
        else if (mask[i]==false && carry==true)
        {
            mask[i]=true;
            carry=false;
        }
        else 
            break;

    }

}

1

연산:

입력 : Set [], set_size 1. power set의 크기를 가져옵니다. powet_set_size = pow (2, set_size) 2 0에서 pow_set_size까지 카운터에 대한 루프 이 부분 집합에 대한 집합에서 i 번째 요소를 인쇄합니다. (b) 부분 집합에 대한 구분자 인쇄, 즉 개행

#include <stdio.h>
#include <math.h>
 
void printPowerSet(char *set, int set_size)
{
    /*set_size of power set of a set with set_size
      n is (2**n -1)*/
    unsigned int pow_set_size = pow(2, set_size);
    int counter, j;
 
    /*Run from counter 000..0 to 111..1*/
    for(counter = 0; counter < pow_set_size; counter++)
    {
      for(j = 0; j < set_size; j++)
       {
          /* Check if jth bit in the counter is set
             If set then pront jth element from set */
          if(counter & (1<<j))
            printf("%c", set[j]);
       }
       printf("\n");
    }
}
 
/*Driver program to test printPowerSet*/
int main()
{
    char set[] = {'a','b','c'};
    printPowerSet(set, 3);
 
    getchar();
    return 0;
}


1

이것은 Java Generics를 사용하여 모든 세트의 파워 세트를 얻을 수있는 내 재귀 솔루션입니다. 주요 아이디어는 다음과 같이 입력 배열의 헤드를 나머지 배열의 가능한 모든 솔루션과 결합하는 것입니다.

import java.util.LinkedHashSet;
import java.util.Set;

public class SetUtil {
    private static<T>  Set<Set<T>> combine(T head, Set<Set<T>> set) {
        Set<Set<T>> all = new LinkedHashSet<>();

        for (Set<T> currentSet : set) {
            Set<T> outputSet = new LinkedHashSet<>();

            outputSet.add(head);
            outputSet.addAll(currentSet);

            all.add(outputSet);
        }

        all.addAll(set);        

        return all;
    }

    //Assuming that T[] is an array with no repeated elements ...
    public static<T> Set<Set<T>> powerSet(T[] input) {
        if (input.length == 0) {
            Set <Set<T>>emptySet = new LinkedHashSet<>();

            emptySet.add(new LinkedHashSet<T>());

            return emptySet;
        }

        T head = input[0];
        T[] newInputSet = (T[]) new Object[input.length - 1];

        for (int i = 1; i < input.length; ++i) {
            newInputSet[i - 1] = input[i];
        }

        Set<Set<T>> all = combine(head, powerSet(newInputSet));

        return all;
    }

    public static void main(String[] args) {            
        Set<Set<Integer>> set = SetUtil.powerSet(new Integer[] {1, 2, 3, 4, 5, 6});

        System.out.println(set);
    }
}

그러면 다음이 출력됩니다.

[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 4], [1, 2, 3, 5, 6], [1, 2, 3, 5], [1, 2, 3, 6], [1, 2, 3], [1, 2, 4, 5, 6], [1, 2, 4, 5], [1, 2, 4, 6], [1, 2, 4], [1, 2, 5, 6], [1, 2, 5], [1, 2, 6], [1, 2], [1, 3, 4, 5, 6], [1, 3, 4, 5], [1, 3, 4, 6], [1, 3, 4], [1, 3, 5, 6], [1, 3, 5], [1, 3, 6], [1, 3], [1, 4, 5, 6], [1, 4, 5], [1, 4, 6], [1, 4], [1, 5, 6], [1, 5], [1, 6], [1], [2, 3, 4, 5, 6], [2, 3, 4, 5], [2, 3, 4, 6], [2, 3, 4], [2, 3, 5, 6], [2, 3, 5], [2, 3, 6], [2, 3], [2, 4, 5, 6], [2, 4, 5], [2, 4, 6], [2, 4], [2, 5, 6], [2, 5], [2, 6], [2], [3, 4, 5, 6], [3, 4, 5], [3, 4, 6], [3, 4], [3, 5, 6], [3, 5], [3, 6], [3], [4, 5, 6], [4, 5], [4, 6], [4], [5, 6], [5], [6], []]

1

다른 샘플 구현 :

 public static void main(String args[])
    {
        int[] arr = new int[]{1,2,3,4};
        // Assuming that number of sets are in integer range
        int totalSets = (int)Math.pow(2,arr.length);
        for(int i=0;i<totalSets;i++)
        {
            String binaryRep = Integer.toBinaryString(i);      
            for(int j=0;j<binaryRep.length();j++)
            {
                int index=binaryRep.length()-1-j;
                if(binaryRep.charAt(index)=='1')
                System.out.print(arr[j] +" ");       
            }
            System.out.println();
        }
    }

1

이것은 람다에 대한 나의 접근 방식입니다.

public static <T> Set<Set<T>> powerSet(T[] set) {
      return IntStream
            .range(0, (int) Math.pow(2, set.length))
            .parallel() //performance improvement
            .mapToObj(e -> IntStream.range(0, set.length).filter(i -> (e & (0b1 << i)) != 0).mapToObj(i -> set[i]).collect(Collectors.toSet()))
            .map(Function.identity())
            .collect(Collectors.toSet());
        }

또는 병렬로 (parallel () 주석 참조) :

입력 세트 크기 : 18

논리 프로세서 : 8 à 3.4GHz

성능 향상 : 30 %


1

t의 하위 집합은 0 개 이상의 t 요소를 제거하여 만들 수있는 모든 집합입니다. withoutFirst 서브 세트는 첫 번째 요소가 누락 된 t의 서브 세트를 추가하고 for 루프는 첫 번째 요소가있는 서브 세트 추가를 처리합니다. 예를 들어 t에 [ "1", "2", "3"] 요소가 포함 된 경우 missingFirst는 [[ ""], [ "2"], [ "3"], [ "2", "3 "]] 및 for 루프는 이러한 요소 앞에"1 "을 붙이고이를 newSet에 추가합니다. 따라서 [[ ""], [ "1"], [ "2"], [ "3"], [ "1", "2"], [ "1", "3"]로 끝납니다. , [ "2", "3"], [ "1", "2", "3"]].

public static Set<Set<String>> allSubsets(Set<String> t) {
        Set<Set<String>> powerSet = new TreeSet<>();
        if(t.isEmpty()) {
            powerSet.add(new TreeSet<>());
            return powerSet;
        }
        String first = t.get(0);
        Set<Set<String>> withoutFirst = allSubsets(t.subSet(1, t.size()));
        for (List<String> 1st : withoutFirst) {
            Set<String> newSet = new TreeSet<>();
            newSet.add(first);
            newSet.addAll(lst);
            powerSet.add(newSet);
        }
        powerSet.addAll(withoutFirst);
        return powerSet;
    }

제공 한 코드와 함께 간단한 설명을 추가해보세요.
— Mirza Sisic 2017

이것은 컴파일조차하지 않고, 판타지 자바 버전으로 작성된 것 같습니다. 인덱스 Set가있는 get메서드 나 메서드 가 없습니다 subSet. 1st유효한 식별자가 아닙니다 ( lst의미 된 것 같습니다). ... 목록에있는 모든 설정을 변경하고 거의 컴파일
— john16384

0
// input: S
// output: P
// S = [1,2]
// P = [], [1], [2], [1,2]

public static void main(String[] args) {
    String input = args[0];
    String[] S = input.split(",");
    String[] P = getPowerSet(S);
    if (P.length == Math.pow(2, S.length)) {
        for (String s : P) {
            System.out.print("[" + s + "],");
        }
    } else {
        System.out.println("Results are incorrect");
    }
}

private static String[] getPowerSet(String[] s) {
    if (s.length == 1) {
        return new String[] { "", s[0] };
    } else {
        String[] subP1 = getPowerSet(Arrays.copyOfRange(s, 1, s.length));
        String[] subP2 = new String[subP1.length];
        for (int i = 0; i < subP1.length; i++) {
            subP2[i] = s[0] + subP1[i];
        }
        String[] P = new String[subP1.length + subP2.length];
        System.arraycopy(subP1, 0, P, 0, subP1.length);
        System.arraycopy(subP2, 0, P, subP1.length, subP2.length);
        return P;
    }

}

Stack Overflow에 오신 것을 환영합니다. 이 답변이 무엇을하고 있는지 그리고 질문자가 문제를 어떻게 해결하는지 설명하는 텍스트로이 답변을 조금 구체화 할 수 있습니다.
— Lachlan Goodhew-Cook 2015

0

나는 최근에 이와 같은 것을 사용해야했지만 먼저 가장 작은 하위 목록 (1 요소, 2 요소, ...)이 필요했습니다. 비어 있거나 전체 목록을 포함하고 싶지 않았습니다. 또한 반환 된 모든 하위 목록의 목록이 필요하지 않았으며 각각에 대해 몇 가지 작업 만 수행하면되었습니다.

재귀없이이 작업을 수행하고 싶었고 다음을 생각해 냈습니다 ( "작업 수행"이 기능 인터페이스로 추상화 됨).

@FunctionalInterface interface ListHandler<T> {
    void handle(List<T> list);
}


public static <T> void forAllSubLists(final List<T> list, ListHandler handler) {
    int     ll = list.size();   // Length of original list
    int     ci[] = new int[ll]; // Array for list indices
    List<T> sub = new ArrayList<>(ll);  // The sublist
    List<T> uml = Collections.unmodifiableList(sub);    // For passing to handler

    for (int gl = 1, gm; gl <= ll; gl++) {  // Subgroup length 1 .. n-1
        gm = 0; ci[0] = -1; sub.add(null);  // Some inits, and ensure sublist is at least gl items long

        do {
                ci[gm]++;                       // Get the next item for this member

                if (ci[gm] > ll - gl + gm) {    // Exhausted all possibilities for this position
                        gm--; continue;         // Continue with the next value for the previous member
                }

                sub.set(gm, list.get(ci[gm]));  // Set the corresponding member in the sublist

                if (gm == gl - 1) {             // Ok, a sublist with length gl
                        handler.handle(uml);    // Handle it
                } else {
                        ci[gm + 1] = ci[gm];    // Starting value for next member is this 
                        gm++;                   // Continue with the next member
                }
        } while (gm >= 0);  // Finished cycling through all possibilities
    }   // Next subgroup length
}

이런 식으로 특정 길이의 하위 목록으로 제한하는 것도 쉽습니다.


0
public class PowerSet {
    public static List<HashSet<Integer>> powerset(int[] a) {
        LinkedList<HashSet<Integer>> sets = new LinkedList<HashSet<Integer>>();
        int n = a.length;
        for (int i = 0; i < 1 << n; i++) {
            HashSet<Integer> set = new HashSet<Integer>();
            for (int j = 0; j < n; j++) {
                if ((1 << j & i) > 0)
                    set.add(a[j]);
            }
            sets.add(set);
        }
        return sets;
    }

    public static void main(String[] args) {
        List<HashSet<Integer>> sets = PowerSet.powerset(new int[]{ 1, 2, 3 });
        for (HashSet<Integer> set : sets) {
            for (int i : set)
                System.out.print(i);
            System.out.println();
        } 
    }
}

0

또 다른 해결책-java8 + 스트리밍 API를 사용하면 "limit ()"와 함께 사용할 때 올바른 하위 집합을 반환하도록 지연되고 정렬됩니다.

 public long bitRangeMin(int size, int bitCount){
    BitSet bs = new BitSet(size);
    bs.set(0, bitCount);
    return bs.toLongArray()[0];
}

public long bitRangeMax(int size, int bitCount){
    BitSet bs = BitSet.valueOf(new long[]{0});
    bs.set(size - bitCount, size);
    return bs.toLongArray()[0];
}

public <T> Stream<List<T>> powerSet(Collection<T> data)
{
    List<T> list = new LinkedHashSet<>(data).stream().collect(Collectors.toList());
    Stream<BitSet> head = LongStream.of(0).mapToObj( i -> BitSet.valueOf(new long[]{i}));
    Stream<BitSet> tail = IntStream.rangeClosed(1, list.size())
            .boxed()
            .flatMap( v1 -> LongStream.rangeClosed( bitRangeMin(list.size(), v1), bitRangeMax(list.size(), v1))
                    .mapToObj(v2 -> BitSet.valueOf(new long[]{v2}))
                    .filter( bs -> bs.cardinality() == v1));

    return Stream.concat(head, tail)
            .map( bs -> bs
                    .stream()
                    .mapToObj(list::get)
                    .collect(Collectors.toList()));
}

그리고 클라이언트 코드는

@Test
public void testPowerSetOfGivenCollection(){
    List<Character> data = new LinkedList<>();
    for(char i = 'a'; i < 'a'+5; i++ ){
        data.add(i);
    }
    powerSet(data)
            .limit(9)
            .forEach(System.out::print);

}

/ * 인쇄 : [] [a] [b] [c] [d] [e] [a, b] [a, c] [b, c] * /


0

재귀를 사용하거나 사용하지 않고 거듭 제곱 세트를 작성할 수 있습니다. 다음은 재귀없는 시도입니다.

public List<List<Integer>> getPowerSet(List<Integer> set) {
    List<List<Integer>> powerSet = new ArrayList<List<Integer>>();
    int max = 1 << set.size();
    for(int i=0; i < max; i++) {
        List<Integer> subSet = getSubSet(i, set);
        powerSet.add(subSet);
    }
    return powerSet;
}

private List<Integer> getSubSet(int p, List<Integer> set) {
    List<Integer> subSet = new ArrayList<Integer>();
    int position = 0;
    for(int i=p; i > 0; i >>= 1) {
        if((i & 1) == 1) {
            subSet.add(set.get(position));
        }
        position++;
    }
    return subSet;
}

0

여기에 파워 세트를 생성하는 것입니다. 아이디어는 첫 번째 = S[0]이고 더 작은 세트는S[1,...n] .

smallSet의 모든 하위 집합을 계산하고 모든 하위 집합에 넣습니다.

모든 하위 집합의 각 하위 집합에 대해 복제하고 먼저 하위 집합에 추가합니다.

ArrayList<ArrayList<Integer>> getSubsets(ArrayList<Integer> set, int index){
    ArrayList<ArrayList<Integer>> allsubsets;
    if(set.size() == index){
        allsubsets = new ArrayList<ArrayList<Integer>>();
        allsubsets.add(new ArrayList<Integer>()); // the empty set 
    }else{
        allsubsets = getSubsets(set, index+1);
        int item = set.get(index);

        ArrayList<ArrayList<Integer>> moresubsets = new ArrayList<ArrayList<Integer>>();

        for(ArrayList<Integer> subset: allsubsets){
            ArrayList<Integer> newsubset = new ArrayList<Integer>();

            newsubset.addAll(subset);
            newsubset.add(item);
            moresubsets.add(newsubset);

        }

        moresubsets.addAll(moresubsets);

    }

    return allsubsets;
}

0
package problems;

import java.util.ArrayList;
import java.util.List;

public class SubsetFinderRecursive {
    public static void main(String[] args) {
        //input
        int[] input = new int[3];
        for(int i=0; i<input.length; i++) {
            input[i] = i+1;
        }
        // root node of the tree
        Node root = new Node();

        // insert values into tree
        for(int i=0; i<input.length; i++) {
            insertIntoTree(root, input[i]);
        }

        // print leaf nodes for subsets
        printLeafNodes(root);
    }

    static void printLeafNodes(Node root) {

        if(root == null) {
            return;
        }

        // Its a leaf node
        if(root.left == null && root.right == null) {
            System.out.println(root.values);
            return;
        }

        // if we are not at a leaf node, then explore left and right

        if(root.left !=null) {
            printLeafNodes(root.left);
        }

        if(root.right != null) {
            printLeafNodes(root.right);
        }
    }

    static void insertIntoTree(Node root, int value) {

        // Error handling
        if(root == null) {
            return;
        }

        // if there is a sub tree then go down
        if(root.left !=null && root.right != null) {
            insertIntoTree(root.left, value);
            insertIntoTree(root.right, value);
        }

        // if we are at the leaf node, then we have 2 choices
        // Either exclude or include
        if(root.left == null && root.right == null) {
            // exclude
            root.left = new Node();
            root.left.values.addAll(root.values);
            // include
            root.right = new Node();
            root.right.values.addAll(root.values);
            root.right.values.add(value);
            return;
        }
    }

}

class Node {
    Node left;
    Node right;
    List<Integer> values = new ArrayList<Integer>();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.