Отримання набору потужностей набору в Java


86

Набір потужностей {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 ". Можливим рішенням буде тестування кожного члена блоку живлення.
Жоао Сільва,

7
Це запитання співбесіди в Google для розробників програмного забезпечення. Це надумана проблема - перевірити свою спритність розуму.
Ерік Лещинський

Це розумне запитання. Наприклад, щоб реалізувати функцію оцінки для cribbage, вам потрібно перевірити, чи не додає якийсь елемент набору потужностей до 15.
Джон Хенкель,

Відповіді:


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
Чи швидше було б використовувати Iterator замість списку? Наприклад: встановити <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. Ви не побачите завершення обчислення, незалежно від того, на якій машині Тьюрінга ви збираєтесь його обчислити.
Карл Ріхтер,

31

Насправді я написав код, який виконує те, про що ви просите, в O (1). Питання в тому, що ви плануєте робити з набором далі. Якщо ви просто збираєтеся зателефонувати size(), це O (1), але якщо ви збираєтеся повторити це, це очевидно O(2^n).

contains() був би O(n) і т.д.

Вам справді це потрібно?

РЕДАГУВАТИ:

Цей код тепер доступний у Гуаві , виставлений за допомогою методу Sets.powerSet(set).


Мені потрібно переглядати кожну підмножину
Мануель Араоз,

Але чи потрібно зберігати кожну підмножину?
finnw

2
Зараз цей метод є в Гуаві
Кевін Бурріллон,

Що робити, якщо я просто хочу агрегати з точно k елементами? Чи ефективний ваш код для цього?
Еял

Нове посилання (Гуава переїхав до 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);
        }

Це з моєї бібліотеки проекту Euler ... :)


Гуава працює майже так само, як цей, але обмежений 32 елементами. Це нерозумно, оскільки 2 ** 32 - це, мабуть, занадто багато ітерацій. Він використовує навіть менше пам'яті, ніж ваша, оскільки генерує AbstractSet лише за потреби. Спробуйте свій код проти коду гуави, де ви надрукуєте лише 1 із 10 000 елементів, і наведіть великий приклад. Б'юсь об заклад, що гуавський буде швидшим.
Еял,

@Eyal, я впевнений, що так, я ніколи не стверджував інакше. Я написав це сам, він не призначений для виробничого коду. Це була вправа в алгоритмах.
st0le

1
Незначне зауваження: ваш 'returnSet' - це TreeSet, який вимагає, щоб його елементи були порівнянними. Це може бути не так. Подумайте про його заміну на HashSet або LinkedHashSet
Джоріс Кінінел

10

Якщо n <63, що є обґрунтованим припущенням, оскільки у вас закінчиться пам'ять (якщо ви не використовуєте реалізацію ітератора), намагаючись побудувати набір потужності в будь-якому випадку, це більш стислий спосіб це зробити. Бінарні операції набагато швидші, ніж Math.pow()масиви для масок, але користувачі Java їх якось бояться ...

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 <(2 << n - 1) замість i <(1 << n - 1).
bazeusz

Дякую @bazeusz, я змінив його на i < (1 << n)еквівалент.
Ендрю Мао,

9

Ось підручник, що описує саме те, що ви хочете, включаючи код. Ви маєте рацію в тому, що складність становить O (2 ^ n).


2
Чи не складність (n * 2 ^ n)? Оскільки двійковий рядок має довжину n, і в кожній ітерації основного циклу ми перебираємо весь двійковий рядок.
Меггі,

1
Підручник чудовий, АЛЕ я використав цей метод для вирішення проблеми HackerRank: він пройшов лише половину тестових випадків, а інша половина не вдалася через час очікування або спричинила помилку виконання.
Євгенія Озірна

7

Я придумав ще одне рішення, засноване на ідеях @Harry He. Мабуть, не найелегантніший, але тут все йде так, як я розумію:

Візьмемо класичний простий приклад PowerSet SP (S) = {{1}, {2}, {3}}. Ми знаємо, що формула для отримання кількості підмножин дорівнює 2 ^ n (7 + порожній набір). У цьому прикладі 2 ^ 3 = 8 підмножин.

Для того, щоб знайти кожну підмножину, нам потрібно перетворити 0-7 десяткових у двійкове представлення, показано в таблиці перетворень нижче:

Таблиця перетворення

Якщо ми обходимо таблицю рядок за рядком, кожен рядок призведе до підмножини, і значення кожного підмножини будуть надходити з увімкнених бітів.

Кожен стовпець у розділі Значення кошика відповідає позиції індексу у вихідному наборі вводу.

Ось мій код:

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

Якщо ви використовуєте колекції Eclipse (раніше GS Collections ), ви можете використовувати 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.


Чи можете ви поділитися та пояснити код свого рішення?
Конрад Геффнер

3
Ви можете переглянути код тут: github.com/goldmansachs/gs-collections/blob/…
Крейг П. Мотлін

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 () відсутня "}"?
Пітер Мортенсен

3

Деякі з наведених вище рішень страждають, коли розмір набору є великим, оскільки вони створюють багато об’єктного сміття для збирання та вимагають копіювання даних. Як ми можемо цього уникнути? Ми можемо скористатися тим фактом, що знаємо, наскільки великим буде розмір набору результатів (2 ^ n), попередньо розподілити масив, який є великим, і просто додати його в кінець, ніколи не копіюючи.

Прискорення швидко зростає з n. Я порівняв це з рішенням Жоао Сільви вище. На моїй машині (усі вимірювання приблизні) n = 13 в 5 разів швидше, n = 14 дорівнює 7x, n = 15 дорівнює 12x, n = 16 дорівнює 25x, n = 17 дорівнює 75x, n = 18 дорівнює 140x. Отже, створення / збирання та копіювання сміття є головним у тих, що інакше здаються схожими на великі рішення.

Попереднє розподіл масиву на початку здається виграшним порівняно з тим, щоб дати йому динамічно зростати. При n = 18 динамічне вирощування займає приблизно вдвічі більше часу.

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]. Якщо перші два цілих числа 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]. Так само число 5 з бітами {1, 0, 1} відповідає комбінації [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;
}

Інкремент методу збільшує число, представлене у наборі бітів. Алгоритм очищає 1 біт від самого правого біта, поки не буде знайдено 0 біт. Потім він встановлює крайній правий 0 біт на 1. Наприклад, щоб збільшити число 5 за допомогою бітів {1, 0, 1}, він очищає 1 біт з правого боку і встановлює крайній правий 0 біт до 1. Біти стають {1, 1, 0} для числа 6, що є результатом збільшення 5 на 1.


Дві речі, які я змінив: цикл у getCombination, замість numbers.length (або bits.size ()), можна виконати ітерацію до 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_size 1. Отримати розмір потужності set powet_set_size = pow (2, set_size) 2 Цикл для лічильника від 0 до pow_set_size (a) Цикл для i = 0 до set_size (i) Якщо i-й біт у лічильнику встановити Друк 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());
        }

Або паралельно (див. Паралельний () коментар):

Розмір вхідного набору: 18

Логічні процесори: 8 на 3,4 ГГц

Покращення продуктивності: 30%


1

Підмножина t - це будь-яка множина, яку можна створити, видаливши нуль або більше елементів 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;
    }

Будь ласка, додайте коротке пояснення до коду, який ви надали.
Мірза Сісіч,

Це навіть не компілюється, здається, це написано у якійсь фантастичній версії Java. 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

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

Ще одне рішення - з потоковим api java8 + Це ліниво і впорядковано, тому повертає правильні підмножини, коли використовується з "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] .

Обчисліть усі підмножини меншого набору та розмістіть їх у всіх підмножинах.

Для кожного підмножини у всіх підмножинах клонуйте його та додайте спочатку до підмножини.

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>();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.