Nhận quyền hạn của một tập hợp trong Java


86

Bộ quyền hạn của {1, 2, 3}là:

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

Giả sử tôi có một Settrong Java:

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

Làm cách nào để viết hàm getPowerset, với thứ tự phức tạp nhất có thể? (Tôi nghĩ nó có thể là O (2 ^ n).)


7
Giả sử bạn có một tập hợp các cấu hình - giả sử "A", "B" và "C" -, có thể được sử dụng để tham số hóa một mô hình và bạn muốn xem tập hợp con nào mang lại kết quả tốt nhất - ví dụ: chỉ "A ". Một giải pháp khả thi là kiểm tra từng thành viên của bộ poweret.
João Silva

7
Đó là một câu hỏi phỏng vấn của Google dành cho các nhà phát triển phần mềm. Đó là một vấn đề phức tạp để kiểm tra sự nhanh nhẹn của trí óc bạn.
Eric Leschinski

Đây là một câu hỏi hợp lý. Ví dụ: để triển khai chức năng tính điểm cho cribbage, bạn phải kiểm tra xem có bất kỳ phần tử nào của bộ poweret thêm đến 15 hay không
John Henckel

Câu trả lời:


101

Vâng, đúng là như O(2^n)vậy, vì bạn cần phải tạo ra 2^ncác kết hợp có thể có. Đây là một cách triển khai hoạt động, sử dụng generic và bộ:

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

Và một bài kiểm tra, với đầu vào ví dụ của bạn:

 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
Sẽ nhanh hơn nếu sử dụng Iterator thay vì sử dụng danh sách? Vd: Đặt <T> rest = new HashSet <T> (originalSet); Lặp lại <T> i = rest.iterator (); T head = i.next (); i.remove (); ?
Dimath

1
@CosminVacaroiu ... nó có thể làm gì khác?
dùng253751

3
Bạn có chắc là nó O(2^n)không? Đó là số bộ trong bộ nguồn, nhưng mỗi bộ phải được tạo trong bộ nhớ, điều này cần thời gian tỷ lệ với kích thước bộ ít nhất. Theo wolfram alpha, nó nằm trong O(n * 2^n): wolfram alpha query
fabian

1
Điều này có hoạt động ngay cả khi kích thước của tập hợp theo thứ tự 10 ^ 5 không?
bane19

1
@GauravShankar 2 ^ 100 = 2 ^ (10 ^ 2) đã lớn hơn 10 ^ 30. Bạn sẽ không chứng kiến ​​kết thúc tính toán cho dù bạn sẽ tính toán nó bằng máy turing nào.
Karl Richter

31

Trên thực tế, tôi đã viết mã thực hiện những gì bạn đang yêu cầu trong O (1). Câu hỏi là bạn dự định làm gì với Set tiếp theo. Nếu bạn chỉ định gọi size()nó, đó là O (1), nhưng nếu bạn định lặp lại thì rõ ràng là như vậy O(2^n).

contains()sẽ là O(n), v.v.

Bạn có thực sự cần cái này không?

BIÊN TẬP:

Mã này hiện đã có trong Guava , được hiển thị thông qua phương pháp Sets.powerSet(set).


Tôi cần phải lặp qua tất cả các tập hợp con
Manuel Araoz

Nhưng bạn có cần lưu trữ mọi tập hợp con không?
finnw


Điều gì sẽ xảy ra nếu tôi chỉ muốn các bộ lũy thừa có chính xác k phần tử? Mã của bạn có hiệu quả cho việc đó không?
Eyal

Liên kết mới (Ổi được chuyển sang Github)
yiwei

12

Đây là một giải pháp mà tôi sử dụng máy phát điện, ưu điểm là, toàn bộ bộ nguồn không bao giờ được lưu trữ cùng một lúc ... Vì vậy, bạn có thể lặp lại từng cái một mà không cần lưu trữ trong bộ nhớ. Tôi muốn nghĩ rằng đó là một lựa chọn tốt hơn ... Lưu ý rằng độ phức tạp là như nhau, O (2 ^ n), nhưng yêu cầu bộ nhớ được giảm bớt (giả sử bộ thu gom rác hoạt động!;))

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

}

Để gọi nó, hãy sử dụng mẫu này:

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

Nó từ Thư viện Project Euler của tôi ... :)


Guava một hoạt động giống như cái này, nhưng bị hạn chế ở 32 phần tử. Điều đó không phải là không hợp lý vì 2 ** 32 có lẽ là quá nhiều lần lặp lại. Nó thậm chí còn sử dụng ít bộ nhớ hơn của bạn vì nó chỉ tạo ra AbstractSet khi cần thiết. Hãy thử mã của bạn với mã Guava mà bạn chỉ in được 1 trong 10.000 phần tử và làm một ví dụ lớn. Tôi cá rằng ổi sẽ nhanh hơn.
Eyal

@Eyal, tôi chắc chắn là có, tôi chưa bao giờ tuyên bố khác. Tôi đã tự viết cái này, nó không dành cho mã sản xuất. Đó là một bài tập về thuật toán.
st0le

1
Nhận xét nhỏ: 'returnSet' của bạn là một TreeSet, đòi hỏi các mục của nó phải tương đương. Điều này có thể không phải là trường hợp. Xem xét trao đổi nó cho một HashSet hoặc LinkedHashSet
Joris Kinable

10

Nếu n <63, đó là một giả định hợp lý vì bạn sẽ hết bộ nhớ (trừ khi sử dụng triển khai trình lặp) đang cố gắng xây dựng bộ công suất, đây là cách ngắn gọn hơn để thực hiện. Các hoạt động nhị phân nhanh hơn Math.pow()và mảng cho mặt nạ, nhưng bằng cách nào đó người dùng Java lại sợ chúng ...

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;

Điều kiện kết thúc trong vòng lặp for phải là i <(2 << n - 1) thay vì i <(1 << n - 1).
bazeusz

Cảm ơn @bazeusz, tôi đã thay đổi nó thành i < (1 << n)tương đương.
Andrew Mao

Vì các phép toán bit khôn ngoan được sử dụng, tôi nghĩ rằng ((i >> j) &1) == 1thay vì (i >> j) % 2 == 1 có thể được sử dụng. Ngoài ra, longđược ký, vì vậy, bạn có nghĩ rằng kiểm tra tràn có hợp lý không?
Ravi Tiwari

9

Đây là một hướng dẫn mô tả chính xác những gì bạn muốn, bao gồm cả mã. Bạn đúng ở chỗ độ phức tạp là O (2 ^ n).


2
Không phải là độ phức tạp (n * 2 ^ n)? Bởi vì chuỗi nhị phân có độ dài n, và trong mỗi lần lặp của vòng lặp chính, chúng ta lặp lại toàn bộ chuỗi nhị phân.
Maggie

1
Hướng dẫn rất hay NHƯNG tôi đã sử dụng kỹ thuật này để giải quyết vấn đề HackerRank: nó chỉ vượt qua một nửa số trường hợp thử nghiệm và nửa còn lại không thành công do hết thời gian chờ hoặc gây ra lỗi thời gian chạy.
Eugenia Ozirna

7

Tôi đã đưa ra một giải pháp khác dựa trên ý tưởng của @Harry He. Có lẽ không phải là thanh lịch nhất nhưng ở đây nó diễn ra như tôi hiểu nó:

Hãy lấy ví dụ đơn giản cổ điển PowerSet của SP (S) = {{1}, {2}, {3}}. Ta biết công thức nhận số tập con là 2 ^ n (7 + tập rỗng). Đối với ví dụ này 2 ^ 3 = 8 tập con.

Để tìm từng tập hợp con, chúng ta cần chuyển đổi 0-7 từ thập phân sang biểu diễn nhị phân được hiển thị trong bảng chuyển đổi dưới đây:

ConversionTable

Nếu chúng ta duyệt từng hàng trong bảng, mỗi hàng sẽ dẫn đến một tập hợp con và giá trị của mỗi tập hợp con sẽ đến từ các bit được kích hoạt.

Mỗi cột trong phần Giá trị thùng tương ứng với vị trí chỉ mục trong Tập đầu vào ban đầu.

Đây là mã của tôi:

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

Nếu bạn đang sử dụng Bộ sưu tập Eclipse (trước đây là Bộ sưu tập GS ), bạn có thể sử dụng powerSet()phương pháp này trên tất cả các 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]]

Lưu ý: Tôi là người cam kết cho Bộ sưu tập Eclipse.


Bạn có thể chia sẻ và giải thích mã của giải pháp của bạn?
Konrad Höffner

3
Bạn có thể xem qua đoạn code ở đây: github.com/goldmansachs/gs-collections/blob/...
Craig P. Motlin

4

Tôi đang tìm kiếm một giải pháp không quá lớn như những giải pháp được đăng ở đây. Điều này nhắm mục tiêu đến Java 7, vì vậy nó sẽ yêu cầu một số ít bột nhão cho các phiên bản 5 và 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;

Đây là một số mã ví dụ để kiểm tra:

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

Không phải powerSetofNodes () thiếu dấu "}" ở cuối sao?
Peter Mortensen

3

Một số giải pháp ở trên bị ảnh hưởng khi kích thước của tập hợp lớn vì chúng đang tạo ra rất nhiều đối tượng rác cần được thu thập và yêu cầu sao chép dữ liệu. Làm thế nào chúng ta có thể tránh điều đó? Chúng ta có thể tận dụng lợi thế của thực tế là chúng ta biết kích thước tập kết quả sẽ lớn như thế nào (2 ^ n), định vị trước một mảng lớn như thế nào và chỉ thêm vào cuối mảng, không bao giờ sao chép.

Tốc độ phát triển nhanh chóng với n. Tôi đã so sánh nó với giải pháp của João Silva ở trên. Trên máy của tôi (tất cả các phép đo gần đúng), n = 13 nhanh hơn 5x, n = 14 là 7x, n = 15 là 12x, n = 16 là 25x, n = 17 là 75x, n = 18 là 140x. Vì vậy, việc tạo / thu thập và sao chép rác đang chiếm ưu thế trong các giải pháp tương tự như big-O.

Việc phân bổ trước mảng ngay từ đầu dường như là một chiến thắng so với việc để nó tự phát triển. Với n = 18, thời gian tăng trưởng động mất khoảng gấp đôi thời gian tổng thể.

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

Giải pháp sau đây được mượn từ cuốn sách " Phỏng vấn viết mã: Câu hỏi, Phân tích & Giải pháp " của tôi:

Một số số nguyên trong một mảng được chọn để tạo thành một tổ hợp. Một tập hợp các bit được sử dụng, trong đó mỗi bit đại diện cho một số nguyên trong mảng. Nếu ký tự thứ i được chọn cho một tổ hợp, bit thứ i là 1; nếu không, nó là 0. Ví dụ, ba bit được sử dụng cho các kết hợp của mảng [1, 2, 3]. Nếu hai số nguyên đầu tiên 1 và 2 được chọn để tạo tổ hợp [1, 2], các bit tương ứng là {1, 1, 0}. Tương tự, các bit tương ứng với một tổ hợp khác [1, 3] là {1, 0, 1}. Chúng ta có thể nhận được tất cả các kết hợp của một mảng có độ dài n nếu chúng ta có thể nhận được tất cả các kết hợp có thể có của n bit.

Một số bao gồm một tập hợp các bit. Tất cả các kết hợp có thể có của n bit tương ứng với các số từ 1 đến 2 ^ n -1. Do đó, mỗi số trong phạm vi từ 1 đến 2 ^ n -1 tương ứng với một tổ hợp của mảng có độ dài n . Ví dụ, số 6 được bao gồm các bit {1, 1, 0}, do đó các ký tự đầu tiên và thứ hai được chọn trong mảng [1, 2, 3] để tạo ra tổ hợp [1, 2]. Tương tự, số 5 với các bit {1, 0, 1} tương ứng với tổ hợp [1, 3].

Mã Java để triển khai giải pháp này trông như sau:

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

Phương thức gia tăng một số được biểu diễn trong một tập hợp các bit. Thuật toán xóa 1 bit từ bit ngoài cùng bên phải cho đến khi tìm thấy bit 0. Sau đó, nó đặt bit 0 ngoài cùng bên phải thành 1. Ví dụ, để tăng số 5 với các bit {1, 0, 1}, nó xóa 1 bit từ phía bên phải và đặt bit 0 ngoài cùng bên phải thành 1. Các bit trở nên {1, 1, 0} cho số 6, là kết quả của việc tăng 5 lên 1.


Hai điều tôi đã sửa đổi: lặp trong getCombination, thay vì number.length (hoặc bits.size ()), người ta có thể lặp lại thành bits.length (), điều này làm tăng tốc độ tạo một chút. Cuối cùng, tôi đã sắp xếp các tập hợp con theo kích thước cho vấn đề của tôi yêu cầu điều đó.
BoLe

3

Đây là một giải pháp O (2 ^ n) lặp lại dễ dàng:

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

Giải pháp này cũng sử dụng không gian O (2 ^ n) sẽ là quá nhiều cho các bộ đầu vào lớn. Tốt hơn hết là tuân theo định nghĩa đệ quy, sử dụng một ngăn xếp hoặc một hàng đợi thay cho đệ quy.
rossb83

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

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

1

Nếu S là tập hữu hạn có N phần tử thì tập lũy thừa của S chứa 2 ^ N phần tử. Thời gian để liệt kê một cách đơn giản các phần tử của tập lũy thừa là 2 ^ N, do đó, O(2^N)giới hạn thấp hơn về độ phức tạp về thời gian của việc xây dựng tập lũy thừa (hăng hái).

Nói một cách đơn giản, bất kỳ phép tính nào liên quan đến việc tạo các bộ lũy thừa sẽ không mở rộng quy mô cho các giá trị lớn của N. Không có thuật toán thông minh nào giúp bạn ... ngoài việc tránh phải tạo bộ lũy thừa!


1

Một cách mà không cần đệ quy như sau: Sử dụng mặt nạ nhị phân và tạo tất cả các kết hợp có thể.

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

Thuật toán:

Đầu vào: Set [], set_size 1. Lấy kích thước của power set powet_set_size = pow (2, set_size) 2 Vòng lặp cho bộ đếm từ 0 đến pow_set_size (a) Vòng lặp cho i = 0 đến set_size (i) Nếu bit thứ i trong bộ đếm là đặt In phần tử thứ i từ tập hợp cho tập hợp con này (b) Trình tách phần tử in cho tập hợp con tức là dòng mới

#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

Đây là giải pháp đệ quy của tôi có thể lấy bộ nguồn của bất kỳ bộ nào bằng Java Generics. Ý tưởng chính của nó là kết hợp phần đầu của mảng đầu vào với tất cả các giải pháp khả thi của phần còn lại của mảng như sau.

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

Điều này sẽ xuất ra:

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

Một triển khai mẫu khác:

 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

Đây là cách tiếp cận của tôi với lambdas.

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

Hoặc song song (xem nhận xét song song ()):

Kích thước của bộ đầu vào: 18

Bộ xử lý logic: 8 à 3,4 GHz

Cải thiện hiệu suất: 30%


1

Tập hợp con của t là bất kỳ tập hợp nào có thể được tạo ra bằng cách loại bỏ không hoặc nhiều phần tử của t. Tập con withoutFirst thêm các tập con của t bị thiếu phần tử đầu tiên và vòng lặp for sẽ giải quyết việc thêm các tập con với phần tử đầu tiên. Ví dụ: nếu t chứa các phần tử ["1", "2", "3"], thì missingFirst sẽ thêm [[""], ["2"], ["3"], ["2", "3 "]] và vòng lặp for sẽ gắn" 1 "vào trước phần tử này và thêm nó vào newSet. Vì vậy, chúng ta sẽ kết thúc với [[""], ["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;
    }

Vui lòng xem xét thêm một giải thích ngắn cùng với mã bạn đã cung cấp.
Mirza Sisic

Điều này thậm chí không biên dịch, dường như được viết trong một số phiên bản Java giả tưởng. Setkhông có getphương thức với chỉ mục, cũng không có subSetphương thức; 1stkhông phải là một định danh hợp lệ (tôi cho lstlà có ý nghĩa). Thay đổi tất cả các bộ các danh sách và nó hầu như biên dịch ...
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;
    }

}

Chào mừng bạn đến với Stack Overflow. Bạn có thể muốn làm rõ câu trả lời này một chút với một số văn bản mô tả những gì nó đang làm và cách nó giải quyết vấn đề của người đặt câu hỏi.
Lachlan Goodhew-Cook

0

Gần đây tôi đã phải sử dụng một cái gì đó như thế này, nhưng cần các danh sách con nhỏ nhất (với 1 phần tử, sau đó là 2 phần tử, ...) trước. Tôi không muốn đưa vào danh sách trống hoặc toàn bộ. Ngoài ra, tôi không cần danh sách tất cả các danh sách phụ được trả về, tôi chỉ cần làm một số thứ với mỗi danh sách.

Muốn thực hiện việc này mà không cần đệ quy, và đã nghĩ ra cách sau (với "việc thực hiện" được tóm tắt thành một giao diện chức năng):

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

Bằng cách này, bạn cũng dễ dàng giới hạn nó trong danh sách phụ có độ dài cụ thể.


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

Tuy nhiên, một giải pháp khác - với java8 + streaming api Nó lười biếng và có thứ tự nên nó trả về các tập hợp con chính xác khi nó được sử dụng với "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()));
}

Và mã khách hàng là

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

}

/ * Bản in: [] [a] [b] [c] [d] [e] [a, b] [a, c] [b, c] * /


0

Chúng ta có thể viết bộ lũy thừa có hoặc không sử dụng đệ quy. Đây là một nỗ lực mà không cần đệ quy:

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

Đây là để tạo ra một bộ nguồn. Ý tưởng là đầu tiên = S[0]và các bộ nhỏ hơn được S[1,...n].

Tính toán tất cả các tập con của SmallSet và đặt chúng vào allsubsets.

Đối với mỗi tập hợp con trong tất cả các tập hợp con, sao chép nó và thêm đầu tiên vào tập hợp con.

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>();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.