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.