Java'da bir kümenin güç kümesini elde etme


86

Güç kümesi {1, 2, 3}:

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

Diyelim ki Java'm var Set:

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

GetPowerset işlevini mümkün olan en iyi karmaşıklık sırasına göre nasıl yazarım? (Bence O (2 ^ n) olabilir.)


7
Bir modeli parametrize etmek için kullanılabilecek "A", "B" ve "C" gibi bir dizi yapılandırmanız olduğunu ve hangi alt kümenin en iyi sonucu verdiğini görmek istediğinizi varsayalım - ör. Sadece "A ". Olası bir çözüm, güç kümesinin her bir üyesini test etmek olacaktır.
João Silva

7
Yazılım geliştiricileri için bir Google röportaj sorusu. Zihin çevikliğinizi test etmek, uydurma bir problem.
Eric Leschinski

Bu makul bir sorudur. Örneğin, cribbage için puanlama işlevini uygulamak için, güç kümesinin herhangi bir öğesinin toplamının 15 olup olmadığını test etmeniz gerekir.
John Henckel

Yanıtlar:


101

Evet, O(2^n)gerçekten de öyle, çünkü 2^nolası kombinasyonları oluşturmanız gerekiyor . İşte jenerikler ve kümeler kullanılarak çalışan bir uygulama:

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

Ve örnek girdinize göre bir test:

 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
Listeyi kullanmak yerine Yineleyici kullanmak daha mı hızlı olur? Örneğin: <T> rest = new HashSet <T> (originalSet) ayarlayın; Yineleyici <T> i = rest.iterator (); T kafa = i.sonraki (); i. kaldır (); ?
Dimath

1
@CosminVacaroiu ... başka ne yapabilirdi ki?
user253751

3
Emin misin O(2^n)? Bu, güç setindeki set sayısıdır, ancak her setin hafızada oluşturulması gerekir ve bu, en azından set boyutuyla orantılı olarak zaman alır. Wolfram alpha'ya göre, giriş O(n * 2^n): wolfram alfa sorgusu
fabian

1
Setin boyutu 10 ^ 5 mertebesinde olsa bile bu çalışır mı?
bane19

1
@GauravShankar 2 ^ 100 = 2 ^ (10 ^ 2) zaten 10 ^ 30'dan büyük. Hangi turing makinesini hesaplayacak olursanız olun, hesaplamanın bitişine şahit olmayacaksınız.
Karl Richter

31

Aslında, O (1) 'de istediğinizi yapan bir kod yazdım. Soru plan budur yapmak yanındaki Set ile. Eğer onu sadece arayacaksan size(), bu O (1), ama eğer onu yineleyeceksen, bu açıkça O(2^n).

contains()olurdu O(n), vb.

Buna gerçekten ihtiyacın var mı?

DÜZENLE:

Bu kod artık Guava'da mevcut ve yöntemle açığa çıkıyor Sets.powerSet(set).


Her alt kümeyi yinelemem gerekiyor
Manuel Araoz

Ama gerekiyor depolamak her alt kümesini?
finnw

2
Bu yöntem artık Guava'da
Kevin Bourrillion

Ya tam olarak k öğeli güç setlerini istersem? Kodunuz bunun için verimli mi?
Eyal

Yeni bağlantı (Guava Github'a taşındı)
yiwei

12

İşte bir jeneratör kullandığım bir çözüm, avantajı, tüm güç setinin asla aynı anda depolanmaması ... Böylece hafızada saklanmasına gerek kalmadan tek tek yineleyebilirsiniz. Bunun daha iyi bir seçenek olduğunu düşünmek isterim ... Karmaşıklığın aynı olduğunu unutmayın, O (2 ^ n), ancak bellek gereksinimleri azalır (çöp toplayıcının davrandığı varsayılarak!;))

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

}

Onu aramak için şu kalıbı kullanın:

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

Project Euler Kitaplığımdan ... :)


Guava, buna çok benzer şekilde çalışır, ancak 32 element ile sınırlıdır. Bu mantıksız değil çünkü 2 ** 32 muhtemelen çok fazla yineleme. Sizinkinden bile daha az bellek kullanır çünkü AbstractSet'i yalnızca ihtiyaç duyulduğunda oluşturur. Kodunuzu 10.000 öğeden yalnızca 1'ini yazdırdığınız Guava'ya karşı deneyin ve büyük bir örnek yapın. Guava'nın daha hızlı olacağına bahse girerim.
Eyal

@Eyal, eminim öyledir, aksini asla iddia etmedim. Bunu kendim yazdım, üretim kodu için tasarlanmadı. Bu bir algoritma alıştırmasıydı.
st0le

1
Küçük açıklama: 'returnSet'iniz', öğelerinin karşılaştırılabilir olmasını gerektiren bir TreeSet'tir. Durum bu olmayabilir. Bir HashSet veya LinkedHashSet için değiştirmeyi düşünün
Joris Kinable

10

N <63 ise, ki bu makul bir varsayımdır, çünkü yine de güç kümesini oluşturmaya çalışırken (bir yineleyici uygulaması kullanmadığınız sürece) belleğiniz tükenirse, bu bunu yapmanın daha özlü bir yoludur. İkili işlemler, Math.pow()maskeler için dizilerden çok daha hızlıdır , ancak bir şekilde Java kullanıcıları onlardan korkar ...

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;

Bir for döngüsündeki sonlandırma koşulu, i <(1 << n - 1) yerine i <(2 << n - 1) olmalıdır.
bazeusz

Teşekkürler @bazeusz, i < (1 << n)eşdeğerini değiştirdim .
Andrew Mao

Biraz akıllıca işlemler kullanıldığı için ((i >> j) &1) == 1yerine kullanılabilir diye düşünüyorum (i >> j) % 2 == 1 . Ayrıca, longimzalandı, yani taşma kontrolü mantıklı mı?
Ravi Tiwari

9

İşte kod dahil tam olarak ne istediğinizi açıklayan bir eğitim. Karmaşıklığın O (2 ^ n) olduğu konusunda haklısınız.


2
Karmaşıklık (n * 2 ^ n) değil mi? İkili dizge n uzunluğunda olduğundan ve ana döngünün her yinelemesinde tüm ikili dizeyi yineliyoruz.
Maggie

1
Öğretici harika AMA HackerRank problemini çözmede bu tekniği kullandım: test durumlarının sadece yarısını geçti ve diğer yarısı zaman aşımına uğradı veya çalışma zamanı hatasına neden oldu.
Eugenia Ozirna

7

@Harry He'nin fikirlerine dayanarak başka bir çözüm buldum. Muhtemelen en zarif değil ama anladığım kadarıyla işte burada:

Klasik basit örnek PowerSet of SP (S) = {{1}, {2}, {3}} 'yi ele alalım. Alt kümelerin sayısını elde etmek için formülün 2 ^ n (7 + boş küme) olduğunu biliyoruz. Bu örnek için 2 ^ 3 = 8 alt küme.

Her bir alt kümeyi bulmak için 0-7 ondalık sayıyı aşağıdaki dönüştürme tablosunda gösterilen ikili gösterime dönüştürmemiz gerekir:

Dönüşüm tablosu

Tabloyu satır satır geçersek, her satır bir alt küme ile sonuçlanacak ve her alt kümenin değerleri etkinleştirilen bitlerden gelecektir.

Bölme Değeri bölümündeki her sütun, orijinal giriş Kümesindeki dizin konumuna karşılık gelir.

İşte kodum:

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

Eğer kullanıyorsanız Eclipse Koleksiyonları (eski GS Koleksiyonları ), kullanabilirsiniz powerSet()tüm SetIterables üzerinde yöntemini.

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]]

Not: Eclipse Collections için bir komisyoncuyum.


Çözümünüzün kodunu paylaşıp açıklayabilir misiniz?
Konrad Höffner


4

Burada yayınlananlar kadar büyük olmayan bir çözüm arıyordum. Bu, Java 7'yi hedeflediğinden, sürüm 5 ve 6 için bir avuç dolusu yapıştırma gerektirecektir.

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;

İşte test edilecek bazı örnek kod:

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 () sonunda "}" eksik değil mi?
Peter Mortensen

3

Yukarıdaki çözümlerden bazıları, kümenin boyutu büyük olduğunda, toplanacak çok fazla nesne çöpü oluşturduğundan ve verilerin kopyalanmasını gerektirdiğinden zarar görür. Bundan nasıl kaçınabiliriz? Sonuç kümesi boyutunun (2 ^ n) ne kadar büyük olacağını bildiğimiz gerçeğinden faydalanabiliriz, bu kadar büyük bir diziyi önceden tahsis edebiliriz ve hiçbir zaman kopyalamadan sonuna kadar ekleyebiliriz.

Hızlanma n ile hızla büyüyor. Bunu, João Silva'nın yukarıdaki çözümüyle karşılaştırdım. Makinemde (tüm ölçümler yaklaşık olarak), n = 13 5 kat daha hızlı, n = 14 7x, n = 15 12x, n = 16 25x, n = 17 75x, n = 18 140x. Öyle ki, çöp oluşturma / toplama ve kopyalama, başka türlü benzer büyük çözümler gibi görünen şeylerde hakimdir.

Diziyi başlangıçta önceden tahsis etmek, dinamik olarak büyümesine izin vermekle karşılaştırıldığında bir kazanç gibi görünüyor. N = 18 ile dinamik büyüme toplamda yaklaşık iki kat daha uzun sürer.

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

Aşağıdaki çözüm, " Coding Interviews: Questions, Analysis & Solutions " adlı kitabımdan ödünç alınmıştır. " :

Bir dizide bir kombinasyon oluşturan bazı tam sayılar seçilir. Her bitin dizide bir tamsayı anlamına geldiği bir dizi bit kullanılır. Eğer i-inci karakter kombinasyonu için seçilmiş, i-inci biti 1 olduğu; aksi takdirde 0'dır. Örneğin, [1, 2, 3] dizisinin kombinasyonları için üç bit kullanılır. Bir kombinasyon [1, 2] oluşturmak için ilk iki tamsayı 1 ve 2 seçilirse, karşılık gelen bitler {1, 1, 0} 'dir. Benzer şekilde, başka bir kombinasyona [1, 3] karşılık gelen bitler {1, 0, 1} 'dir. Tüm olası n bit kombinasyonlarını elde edebilirsek , uzunluğu n olan bir dizinin tüm kombinasyonlarını elde edebiliriz .

Bir sayı, bir dizi bitten oluşur. Tüm olası n bit kombinasyonları 1'den 2 ^ n -1'e kadar sayılara karşılık gelir . Bu nedenle, Şekil 1 ve 2'de ^ aralığında her bir numara , n uzunluğu ile bir dizi bir kombinasyonu -1 tekabül n . Örneğin, 6 sayısı {1, 1, 0} bitlerinden oluşur, bu nedenle [1, 2] kombinasyonunu oluşturmak için [1, 2, 3] dizisindeki birinci ve ikinci karakterler seçilir. Benzer şekilde, bit {1, 0, 1} olan 5 sayısı [1, 3] kombinasyonuna karşılık gelir.

Bu çözümü uygulamak için Java kodu aşağıdaki gibidir:

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

Yöntem artışı, bir bit kümesinde temsil edilen bir sayıyı artırır. Algoritma, 0 bit bulunana kadar en sağdaki bitten 1 biti temizler. Daha sonra en sağdaki 0 ​​biti 1'e ayarlar. Örneğin, 5 sayısını {1, 0, 1} bitleriyle artırmak için, sağ taraftan 1 biti temizler ve en sağdaki 0 ​​biti 1'e ayarlar. 6 sayısı için {1, 1, 0}, 5'i 1 artırmanın sonucudur.


Değiştirdiğim iki şey: numbers.length (veya bits.size ()) yerine getCombination'da döngü yapmak, üretimi biraz hızlandıran bits.length () 'e yinelenebilir. Son olarak, sorunum için alt kümeleri boyuta göre sıraladım bunu gerektirir.
bolus

3

İşte kolay yinelemeli O (2 ^ n) çözümü:

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

Bu çözüm ayrıca, büyük girdi kümeleri için çok fazla olan O (2 ^ n) alanını kullanır. Özyineleme yerine bir yığın veya kuyruk kullanarak özyinelemeli tanımı takip etmek daha iyidir.
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 elemanlı sonlu bir küme ise, S'nin kuvvet kümesi 2 ^ N eleman içerir. Güç kümesinin öğelerini basitçe sıralamanın zamanı 2 ^ N'dir, bu nedenleO(2^N) güç kümesini (hevesle) oluşturmanın zaman karmaşıklığı için alt sınırdır.

Basitçe ifade etmek gerekirse, güç kümeleri oluşturmayı içeren herhangi bir hesaplama, büyük N değerleri için ölçeklenmeyecektir. Hiçbir akıllı algoritma, güç kümelerini yaratma ihtiyacından kaçınmanın dışında size yardımcı olmaz!


1

Özyinelemesiz bir yol şudur: Bir ikili maske kullanın ve tüm olası kombinasyonları yapın.

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

Algoritma:

Giriş: Set [], set_size 1. Güç setinin boyutunu alın powet_set_size = pow (2, set_size) 2 Sayaç için 0'dan pow_set_size'a döngü (a) i = 0'dan set_size'a (i) set_size (i) ise Bu alt küme için kümeden Print ith öğesini ayarlayın (b) Alt kümeler için yazdırma ayırıcısı, yani yeni satır

#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

Bu, Java Generics kullanarak herhangi bir setin güç setini alabilen özyinelemeli çözümüm. Ana fikri, girdi dizisinin başını, dizinin geri kalanının tüm olası çözümleriyle aşağıdaki gibi birleştirmektir.

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

Bu çıktı:

[[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

Başka bir örnek uygulama:

 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

Lambdas ile yaklaşımım budur.

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

Veya paralel olarak (paralel () açıklamaya bakın):

Giriş kümesi boyutu: 18

Mantıksal işlemciler: 8 - 3,4 GHz

Performans iyileştirmesi:% 30


1

T'nin bir alt kümesi, t'nin sıfır veya daha fazla elemanının çıkarılmasıyla yapılabilen herhangi bir kümedir. WithoutFirst altkümesi, t'nin birinci elementi eksik olan altkümelerini ekler ve for döngüsü, ilk elementle birlikte altkümelerin eklenmesiyle ilgilenir. Örneğin, t ["1", "2", "3"] öğelerini içeriyorsa, missingFirst [[""], ["2"], ["3"], ["2", "3 "]] ve for döngüsü bu öğelerin önüne" 1 "yapıştıracak ve onu yeni kümeye ekleyecektir. Sonuç olarak [[""], ["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;
    }

Lütfen sağladığınız koda kısa bir açıklama eklemeyi düşünün.
Mirza Sisic

Bu derlenmiyor bile, bazı fantezi Java sürümünde yazılmış gibi görünüyor. SetBir yoktur getbir dizin, ne de bir yöntemi ile subSetyöntemiyle; 1stgeçerli bir tanımlayıcı değil (sanırım lstkastedildi). Tüm setleri listelere çevirin ve neredeyse
derlenecek

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'a hoş geldiniz. Ne yaptığını ve soruyu soranın problemini nasıl çözdüğünü açıklayan bir metinle bu yanıtı biraz detaylandırmak isteyebilirsiniz.
Lachlan Goodhew-Cook

0

Geçenlerde bunun gibi bir şey kullanmak zorunda kaldım, ancak önce en küçük alt listelere ihtiyacım vardı (1 eleman, sonra 2 eleman, ...). Ne boş ne de tüm listeyi dahil etmek istemedim. Ayrıca, döndürülen tüm alt listelerin bir listesine ihtiyacım yoktu, sadece her biri için bazı şeyler yapmam gerekiyordu.

Bunu özyineleme olmadan yapmak istedim ve aşağıdakileri ortaya çıkardı ("işler" işlevsel bir arayüze soyutlanmış olarak):

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

Bu şekilde, belirli uzunluklardaki alt listelerle sınırlamak da kolaydır.


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

Yine başka bir çözüm - java8 + akış api ile Bu tembel ve sıralı olduğundan "limit ()" ile kullanıldığında doğru alt kümeleri döndürür.

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

Ve müşteri kodu

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

}

/ * Baskılar: [] [a] [b] [c] [d] [e] [a, b] [a, c] [b, c] * /


0

Güç kümesini özyineleme ile veya kullanmadan yazabiliriz. İşte özyinelemesiz bir deneme:

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

İşte bir güç seti oluşturmak için. İlk fikir = S[0]ve daha küçük kümeler olabilir S[1,...n].

SmallSet'in tüm alt kümelerini hesaplayın ve bunları tüm alt kümelere koyun.

Tüm alt kümelerdeki her alt küme için, onu klonlayın ve önce alt kümeye ekleyin.

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>();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.