Расчет всех подмножеств набора чисел
Вопрос
Я хочу найти подмножества набора целых чисел. Это первый шаг алгоритма «суммы подмножеств» с отступом. Я написал следующий код, но он не возвращает правильный ответ:
BTSum(0, nums);
///**************
ArrayList<Integer> list = new ArrayList<Integer>();
public static ArrayList<Integer> BTSum(int n, ArrayList<Integer> numbers) {
if (n == numbers.size()) {
for (Integer integer : list) {
System.out.print(integer+", ");
}
System.out.println("********************");
list.removeAll(list);
System.out.println();
} else {
for (int i = n; i < numbers.size(); i++) {
if (i == numbers.size() - 1) {
list.add(numbers.get(i));
BTSum(i + 1, numbers);
} else {
list.add(numbers.get(i));
for (int j = i+1; j < numbers.size(); j++)
BTSum(j, numbers);
}
}
}
return null;
}
Например, если я хочу рассчитать подмножество SET = {1, 3, 5} результатом моего метода:
1, 3, 5, ********************
5, ********************
3, 5, ********************
5, ********************
3, 5, ********************
5, ********************
Я хочу, чтобы это произвести:
1, 3, 5
1, 5
3, 5
5
Я думаю, что проблема из списка деталей .removealL (список); Но я не знаю, как это исправить.
Решение
То, что вы хотите, называется Пауза. Отказ Вот простая реализация этого:
public static Set<Set<Integer>> powerSet(Set<Integer> originalSet) {
Set<Set<Integer>> sets = new HashSet<Set<Integer>>();
if (originalSet.isEmpty()) {
sets.add(new HashSet<Integer>());
return sets;
}
List<Integer> list = new ArrayList<Integer>(originalSet);
Integer head = list.get(0);
Set<Integer> rest = new HashSet<Integer>(list.subList(1, list.size()));
for (Set<Integer> set : powerSet(rest)) {
Set<Integer> newSet = new HashSet<Integer>();
newSet.add(head);
newSet.addAll(set);
sets.add(newSet);
sets.add(set);
}
return sets;
}
Я дам вам пример, чтобы объяснить, как работает алгоритм для PowerSet {1, 2, 3}
:
- Удалять
{1}
, и выполнить PowerSet для{2, 3}
;- Удалять
{2}
, и выполнить PowerSet для{3}
;- Удалять
{3}
, и выполнить PowerSet для{}
;- Пауэрс
{}
является{{}}
;
- Пауэрс
- Пауэрс
{3}
является3
в сочетании с{{}}
={ {}, {3} }
;
- Удалять
- Пауэрс
{2, 3}
является{2}
в сочетании с{ {}, {3} }
={ {}, {3}, {2}, {2, 3} }
;
- Удалять
- Пауэрс
{1, 2, 3}
является{1}
в сочетании с{ {}, {3}, {2}, {2, 3} }
={ {}, {3}, {2}, {2, 3}, {1}, {3, 1}, {2, 1}, {2, 3, 1} }
.
Другие советы
Просто грунтовка, как вы мог решать проблему:
Подход 1
- Возьмите первый элемент вашего номера
- генерация все Подвеска из оставшегося списка номеров (т. Е. Номерный список без выбранного) => Рекурсия!
- Для каждого подмножества, найденного на предыдущем шаге, добавьте саму подмножество и подмножество соединено с элементом, выбранным на шаге 1 к выходу.
Конечно, вы должны проверить базовый чехол, то есть, если ваш список номеров пуст.
Подход 2
Это хорошо известный факт, что набор с n
элементы имеет 2^n
Подвеска. Таким образом, вы можете рассчитывать в двоичном 0
к 2^n
и интерпретировать бинарный номер в качестве соответствующего подмножества. Обратите внимание, что этот подход требует двоичного номера с достаточным количеством цифр для представления всего набора.
Это должна быть не слишком большая проблема, чтобы преобразовать один из двух подходов в код.
Ваш код действительно запутается, и нет объяснения.
Вы можете сделать итеративно с растровой мажокой, который определяет, какие цифры в наборе. Каждое число от 0 до 2 ^ n дает уникальное подмножество в своем двоичном представлении, например
для n = 3:
I = 5 -> 101 в двоичном, выберите первые и последние элементы I = 7 -> 111 в двоичном, выберите первые 3 элемента
Предположим, что есть n элементов (n <64, в конце концов, если n больше 64, вы запускаете это навсегда).
for(long i = 0; i < (1<<n); i++){
ArrayList<Integer> subset = new ArrayList<Integer>();
for(int j = 0; j < n; j++){
if((i>>j) & 1) == 1){ // bit j is on
subset.add(numbers.get(j));
}
}
// print subset
}
Учитывая посетитель нуба (благодаря Google) на этот вопрос - как я
Вот рекурсивное решение, которое работает на простом принципе:
Set = {a, b, c, d, e}
Тогда мы можем сломать его {a}
+ Subset of {b,c,d,e}
public class Powerset{
String str = "abcd"; //our string
public static void main(String []args){
Powerset ps = new Powerset();
for(int i = 0; i< ps.str.length();i++){ //traverse through all characters
ps.subs("",i);
}
}
void subs(String substr,int index)
{
String s = ""+str.charAt(index); //very important, create a variable on each stack
s = substr+s; //append the subset so far
System.out.println(s); //print
for(int i=index+1;i<str.length();i++)
subs(s,i); //call recursively
}
}
ВЫХОД
a
ab
abc
abcd
abd
ac
acd
ad
b
bc
bcd
bd
c
cd
d
Понятно, что общее количество подмножеств любого заданного набора равна 2 ^ (количество элементов в комплекте). Если установлен
A = {1, 2, 3}
Затем подмножество A:
{ }, { 1 }, { 2 }, { 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 1, 2, 3 }
Если мы посмотрим, как двоичные номера.
{ 000 }, { 001 }, { 010 }, { 011 }, { 100 }, { 101 }, { 110 }, { 111 }
Если мы учитываем приведенные выше:
static void subSet(char[] set) {
int c = set.length;
for (int i = 0; i < (1 << c); i++) {
System.out.print("{");
for (int j = 0; j < c; j++) {
if ((i & (1 << j)) > 0) {
System.out.print(set[j] + " ");
}
}
System.out.println("}");
}
}
public static void main(String[] args) {
char c[] = {'a', 'b', 'c'};
subSet(c);
}
private static void findSubsets(int array[])
{
int numOfSubsets = 1 << array.length;
for(int i = 0; i < numOfSubsets; i++)
{
int pos = array.length - 1;
int bitmask = i;
System.out.print("{");
while(bitmask > 0)
{
if((bitmask & 1) == 1)
System.out.print(array[pos]+",");
bitmask >>= 1;
pos--;
}
System.out.print("}");
}
}
На основании того, что я узнал сегодня, вот решение Java основано на recursion
public class Powerset {
public static void main(String[] args) {
final List<List<String>> allSubsets = powerSet(Arrays.asList(1, 2, 3, 4), 0);
for (List<String> subsets : allSubsets) {
System.out.println(subsets);
}
}
private static List<List<String>> powerSet(final List<Integer> values,
int index) {
if (index == values.size()) {
return new ArrayList<>();
}
int val = values.get(index);
List<List<String>> subset = powerSet(values, index + 1);
List<List<String>> returnList = new ArrayList<>();
returnList.add(Arrays.asList(String.valueOf(val)));
returnList.addAll(subset);
for (final List<String> subsetValues : subset) {
for (final String subsetValue : subsetValues) {
returnList.add(Arrays.asList(val + "," + subsetValue));
}
}
return returnList;
}
}
Работает он даст результаты как
[1]
[2]
[3]
[4]
[3,4]
[2,3]
[2,4]
[2,3,4]
[1,2]
[1,3]
[1,4]
[1,3,4]
[1,2,3]
[1,2,4]
[1,2,3,4]
Я на самом деле пытался решить этот и получил алгоритм @phimuemue на предыдущем посте. Здесь я реализовал. Надеюсь, это работает.
/**
*@Sherin Syriac
*
*/
import java.util.ArrayList;
import java.util.List;
public class SubSet {
ArrayList<List<Integer>> allSubset = new ArrayList<List<Integer>>();
/**
* @param args
*/
public static void main(String[] args) {
SubSet subSet = new SubSet();
ArrayList<Integer> set = new ArrayList<Integer>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
subSet.getSubSet(set, 0);
for (List<Integer> list : subSet.allSubset) {
System.out.print("{");
for (Integer element : list) {
System.out.print(element);
}
System.out.println("}");
}
}
public void getSubSet(ArrayList<Integer> set, int index) {
if (set.size() == index) {
ArrayList<Integer> temp = new ArrayList<Integer>();
allSubset.add(temp);
} else {
getSubSet(set, index + 1);
ArrayList<List<Integer>> tempAllSubsets = new ArrayList<List<Integer>>();
for (List subset : allSubset) {
ArrayList<Integer> newList = new ArrayList<Integer>();
newList.addAll(subset);
newList.add(set.get(index));
tempAllSubsets.add(newList);
}
allSubset.addAll(tempAllSubsets);
}
}
}
// subsets for the set of 5,9,8
import java.util.ArrayList;
import java.util.List;
public class Subset {
public static void main(String[] args) {
List<Integer> s = new ArrayList<Integer>();
s.add(9);
s.add(5);
s.add(8);
int setSize = s.size();
int finalValue = (int) (Math.pow(2, setSize));
String bValue = "";
for (int i = 0; i < finalValue; i++) {
bValue = Integer.toBinaryString(i);
int bValueSize = bValue.length();
for (int k = 0; k < (setSize - bValueSize); k++) {
bValue = "0" + bValue;
}
System.out.print("{ ");
for (int j = 0; j < setSize; j++) {
if (bValue.charAt(j) == '1') {
System.out.print((s.get(j)) + " ");
}
}
System.out.print("} ");
}
}
}
//Output : { } { 8 } { 5 } { 5 8 } { 9 } { 9 8 } { 9 5 } { 9 5 8 }
public static ArrayList<ArrayList<Integer>> powerSet(List<Integer> intList) {
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
result.add(new ArrayList<Integer>());
for (int i : intList) {
ArrayList<ArrayList<Integer>> temp = new ArrayList<ArrayList<Integer>>();
for (ArrayList<Integer> innerList : result) {
innerList = new ArrayList<Integer>(innerList);
innerList.add(i);
temp.add(innerList);
}
result.addAll(temp);
}
return result;
}
Если вы имеете дело с большой коллекцией элементов, вы можете (хотя вполне вероятно) столкнуться с проблемами с переполнением стека. Я признаю, что вы с большей вероятностью выбегаете из памяти, прежде чем переполнить стек, но в любом случае я приложу этот нерекурсный метод.
public static final <T> Set<Set<T>> powerSet(final Iterable<T> original) {
Set<Set<T>> sets = new HashSet<>();
sets.add(new HashSet<>());
for (final T value : original) {
final Set<Set<T>> newSets = new HashSet<>(sets);
for (final Set<T> set : sets) {
final Set<T> newSet = new HashSet<>(set);
newSet.add(value);
newSets.add(newSet);
}
sets = newSets;
}
return sets;
}
Или если вы бы предпочли иметь дело с массивами:
@SuppressWarnings("unchecked")
public static final <T> T[][] powerSet(final T... original) {
T[][] sets = (T[][]) Array.newInstance(original.getClass(), 1);
sets[0] = Arrays.copyOf(original, 0);
for (final T value : original) {
final int oldLength = sets.length;
sets = Arrays.copyOf(sets, oldLength * 2);
for (int i = 0; i < oldLength; i++) {
final T[] oldArray = sets[i];
final T[] newArray = Arrays.copyOf(oldArray, oldArray.length + 1);
newArray[oldArray.length] = value;
sets[i + oldLength] = newArray;
}
}
return sets;
}
Вот какой-то псевдокод. Вы можете резать те же рекурсивные звонки, сохраняя значения для каждого вызова, как вы идете и до проверки рекурсивной проверки вызова, если значение вызова уже присутствует.
Следующий алгоритм будет иметь все подмножества, исключая пустой набор.
list * subsets(string s, list * v){
if(s.length() == 1){
list.add(s);
return v;
}
else
{
list * temp = subsets(s[1 to length-1], v);
int length = temp->size();
for(int i=0;i<length;i++){
temp.add(s[0]+temp[i]);
}
list.add(s[0]);
return temp;
}
}
Вот логика для печати всех подмножеств данного набора номеров. Это также называется PowerSet набора. Я использовал простой рекурсивный подход для решения этого с помощью Java, но вы также можете соответственно кодировать на других языках.
import java.util.Scanner;
public class PowerSubset {
public static void main(String[] args) {
// HardCoded Input
int arr[] = { 1, 2, 3 };//original array whose subset is to be found
int n=3; //size of array
// Dynamic Input
/*Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int arr[] = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}*/
int data[] = new int[arr.length]; // temporary array
printSubset(arr, data, n, 0, 0);
}
public static void printSubset(int arr[], int data[], int n, int dataIndex, int arrIndex) {
if (arrIndex == n) { //comparing with n since now you are at the leaf node
System.out.print("[");//watch pictorial chart in the below video link
for (int j = 0; j < n; j++) {
System.out.print(data[j] == 0 ? "" : data[j]);
}
System.out.print("]");
System.out.println();
return;
}
data[dataIndex] = arr[arrIndex];
printSubset(arr, data, n, dataIndex + 1, arrIndex + 1);//recursive call 1
data[dataIndex] = 0;
printSubset(arr, data, n, dataIndex, arrIndex + 1);//recursive call 2
}
}
Выход вышеуказанного кода:
[123]
[12]
[13]
[1]
[23]
[2]
[3]
[]
Чтобы получить концепцию, вы можете посмотреть следующее видео YouTube, которое четко объясняет, какой подход используется позади кода.https://www.youtube.com/watch?v=vel15c3vbve.
Простое рекурсивное решение Java -
private static List<List<Integer>> allsubSet(List<Integer> integers, int start, int end) {
//Base case if there is only one element so there would be two subset
// empty list and that element
if(start == end) {
List<List<Integer>> result = new ArrayList<>();
List<Integer> emptyList = new ArrayList<>();
result.add(emptyList);
List<Integer> element = new ArrayList<>();
element.add(integers.get(start));
result.add(element );
return result;
}
//I know if by recursion we can expect that we'll get the n-1 correct result
List<List<Integer>> lists = allsubSet(integers, start, end-1);
//here i copy all the n-1 results and just added the nth element in expected results
List<List<Integer>> copyList = new ArrayList<>(lists);
for (List<Integer> list : lists) {
List<Integer> copy= new ArrayList<>(list);
copy.add(integers.get(end));
copyList.add(copy);
}
return copyList;
}
Чтобы избежать избыточности мы можем просто использовать набор в месте списка