2013-03-19 13 views
6

Come si scrive un metodo ricorsivo PowerSet (input stringa) che stampa tutte le possibili combinazioni di una stringa passata a esso?Generazione ricorsiva di energia senza loop

Ad esempio: PowerSet ("abc") stamperà abc, ab, ac, bc, a, b, c

Ho visto alcune soluzioni ricorsive con i cicli, ma in questo caso non sono ammesse loop .

Qualche idea?

Modifica: il metodo richiesto ha solo un parametro, vale a dire ingresso stringa.

+1

questo caso? quale caso? – SudoRahul

+0

Penso che ci siano ** alcuni ** algoritmi là fuori che possono risolvere questo problema, nel caso tu voglia usare Google per trovarne uno. – Matten

+2

E quasi ogni ciclo può essere sostituito da una funzione ricorsiva. – Matten

risposta

13

La powerset del abcd è l'unione dei power-set di abc, abd, acd (più set abcd stessa *).

P(`abcd`) = {`abcd`} + P(`abc`) + P(`abd`) + P(`acd`) + P(`bcd`) 

* Si noti che l'insieme vuoto , che è un membro di P (abcd) è anche un membro di P (abc), P (abd), ... così l'equivalenza sopra indicato tiene .

ricorsivo, P (abc) = {abc} + P (ab) + P (ac), e così via

Un primo approccio, in pseudocodice, potrebbe essere:

powerset(string) { 
    add string to set; 
    for each char in string { 
    let substring = string excluding char, 
    add powerset(substring) to set 
    } 
    return set;  
} 

La ricorsione termina quando la stringa è vuota (perché non entra mai nel ciclo).

Se si desidera realmente i loop no, sarà necessario convertirlo in un'altra ricorsione. Ora vogliamo generare ab, ac e cb da abc

powerset(string) { 
    add string to set; 
    add powerset2(string,0) to set; 
    return set 
} 

powerset2(string,pos) { 
    if pos<length(string) then 
    let substring = (string excluding the char at pos) 
    add powerset(substring) to set 
    add powerset2(string,pos+1) to set 
    else 
    add "" to set 
    endif 
    return set 
} 

Un altro approccio implementare una funzione ricorsiva P che o rimuove il primo carattere da suo argomento, o non lo fa. (Qui + significa insieme unione, . significa concatenazione e λ è la stringa vuota)

P(abcd) = P(bcd) + a.P(bcd) 
P(bcd) = P(cd) + b.P(cd) 
P(cd) = P(d) + c.P(d) 
P(d) = λ+d //particular case 

Poi

P(d) = λ+d 
R(cd) = P(d) + c.P(d) = λ + d + c.(λ+d) = λ + d + c + cd 
R(bcd) = P(cd) + b.P(cd) = λ + d + c + cd + b.(λ + d + c + cd) 
          = λ + d + c + cd + b + bd + bc + bcd 
P(abcd) = λ + d + c + cd + b + bd + bc + bcd 
     + aλ + ad + ac + acd + ab + abd + abc + abcd 

Se cicli sono stati ammessi, quindi P è fuori funzione power-impostata. In caso contrario, avremmo bisogno di una funzione loopless a un parametro per concatenare un determinato carattere a un determinato set di stringhe (che ovviamente sono due elementi).

Alcuni aggiustamento potrebbe essere possibile giocando con String.replace (se un risultato String è desiderato, o sostituendo Set con List (in modo che il parametro "supplementare" è in realtà il primo elemento della lista).

+0

Impressionante, ho pensato all'algoritmo nel tuo pseudocodice. Ma mi sono bloccato a svolgere questo compito: lasciare substring = stringa escludendo char. C'è qualche funzione integrata nell'API per farlo? – uohzxela

+1

's.substring (0, pos)' restituirà la sottostringa da '0' a' pos-1', e 's.substring (pos)' restituirà la sottostringa da 'pos' alla fine della stringa. – Javier

+0

Grazie. Capisco. Ad ogni modo, sono pedante perché la domanda parlava solo di un parametro. Sai come implementare il metodo con un solo parametro che è l'input di String? – uohzxela

2

Bene se non si hanno cicli, si emula uno con ricorsione, usando iteratori questo è abbastanza semplice.

public final Set<Set<Integer>> powerSet(Set<Integer> set) { 
     Set<Set<Integer>> powerSet = new HashSet<>(); 
     powerSet(set, powerSet, set.iterator()); 
     return powerSet; 
    } 
    public final void powerSet(Set<Integer> set, Set<Set<Integer>> powerSet, Iterator<Integer> iterator) { 
     if(iterator.hasNext()) { 
      Integer exlude = iterator.next(); 
      Set<Integer> powThis = new HashSet<Integer>(); 
      powThis.addAll(set); 
      powThis.remove(exlude); 
      powerSet.add(powThis); 
      powerSet(powThis, powerSet, powThis.iterator()); 
      powerSet(set, powerSet, iterator); 
     } 
    } 
//usage 
     Set<Integer> set = new HashSet<>(); 
     set.add(1); 
     set.add(2); 
     set.add(3); 
     set.add(4); 
     log.error(powerSet(set).toString()); 
1

A versione ricorsiva della generic solution proposta da João Silva:

public static <T> Set<Set<T>> powerSet2(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())); 
    addSets(sets, powerSet(rest), head); 
    return sets; 
} 

private static <T> void addSets(Set<Set<T>> sets, Set<Set<T>> setsToAdd, T head) { 
    Iterator<Set<T>> iterator = setsToAdd.iterator(); 
    if (iterator.hasNext()) { 
     Set<T> set = iterator.next(); 
     iterator.remove(); 
     Set<T> newSet = new HashSet<T>(); 
     newSet.add(head); 
     newSet.addAll(set); 
     sets.add(newSet); 
     sets.add(set); 
     addSets(sets, setsToAdd, head); 
    } 
} 

estraggo il metodo addSets ricorsive per trasformare il for loop originale:

for (Set<T> set : powerSet(rest)) { 
    Set<T> newSet = new HashSet<T>(); 
    newSet.add(head); 
    newSet.addAll(set); 
    sets.add(newSet); 
    sets.add(set); 
} 
2

Questo sarà anche il trucco:

var powerset = function(arr, prefix, subsets) { 
    subsets = subsets || []; 
    prefix = prefix || []; 
    if (arr.length) { 
    powerset(arr.slice(1), prefix.concat(arr[0]), subsets); 
    powerset(arr.slice(1), prefix, subsets); 
    } else { 
    subsets.push(prefix); 
    } 
    return subsets; 
}; 

powerset('abc'); 
0
void powerSet(int * ar, int *temp, int n, int level,int index) 
{ 
    if(index==n) return; 

    int i,j; 
    for(i=index;i<n;i++) 
    { 
    temp[level]=ar[i]; 

    for(j=0;j<=level;j++) 
    printf("%d ",temp[j]); 
    printf(" - - - t\n"); 

    powerSet(ar, temp, n, level+1,i+1); 
    } 
} 

int main() 
{ 
    int price[] = {1,2,3,7}; 
    int temp[4] ={0}; 

    int n = sizeof(price)/sizeof(price[0]); 

    powerSet(price, temp, n, 0,0); 


    return 0; 
} 
0

Soluzione semplice ma con scarsa complessità temporale (2^n) è la seguente (basta tenere una cosa in mente, una volta dobbiamo evitare (vale a dire 0) e una volta dobbiamo prendere esso (cioè 1):.

public HashSet<int[]> powerSet(int n) { 
    return calcPowerSet(n-1, new HashSet<int[]>(), new int[n]); 
} 

private HashSet<int[]> calcPowerSet(int n, HashSet<int[]> result, int []set) { 
    if(n < 0) { 
     result.add(set.clone()); 
     return null; 
    } 
    else { 
     set[n] = 0; 
     calcPowerSet(n-1, result, set); 
     set[n] = 1; 
     calcPowerSet(n-1, result, set); 
     return result; 
    } 
} 
+0

Non è possibile ottenere una complessità migliore di 2^n poiché ci sono 2^n sottoinsiemi. – fons

+0

sì, è vero –

0

Solo per divertimento, una versione che fa powerset di qualsiasi set memorizzati in un LinkedList (per rendere più facile per rimuovere l'elemento di testa). Java 8 flussi fanno la parte funzionale:

static <T> LinkedList<LinkedList<T>> powerset(LinkedList<T> elements) { 
    if (elements.isEmpty()) 
    return copyWithAddedElement(new LinkedList<>(), new LinkedList<>()); 
    T first = elements.pop(); 
    LinkedList<LinkedList<T>> powersetOfRest = powerset(elements); 
    return Stream.concat(
     powersetOfRest.stream(), 
     powersetOfRest.stream().map(list -> copyWithAddedElement(list, first))) 
      .collect(Collectors.toCollection(LinkedList::new)); 
} 

static <T> LinkedList<T> copyWithAddedElement(LinkedList<T> list, T elt) { 
    list = new LinkedList<>(list); 
    list.push(elt); 
    return list; 
} 

Questo è ispirata dal seguente Common Lisp, il che dimostra che il linguaggio giusto può rendere le cose più semplici:

(defun powerset (set) 
    (cond ((null set) '(())) 
     (t (let ((powerset-of-rest (powerset (cdr set)))) 
      (append powerset-of-rest 
        (mapcar #'(lambda (x) (cons (car set) x)) 
          powerset-of-rest)))))) 
0

Sulla base di informazioni here, qui è soluzione in C#.

NOTA: il ciclo nella funzione principale è solo per stampare il risultato nel valore della console. Nessun loop utilizzato nel metodo PowerSet.

public static void Main(string[] args) 
    { 

     string input = "abbcdd"; 


     Dictionary < string, string> resultSet = new Dictionary<string, string>(); 

     PowerSet(input, "", 0, resultSet); 

     //apply sorting 
     var resultSorted = resultSet.OrderBy(l => l.Key.Length).ThenBy(l=>l.Key); 

     //print values 
     foreach(var keyValue in resultSorted) 
     { 
      Console.Write("{{{0}}}, ",keyValue.Key); 
     } 


    } 

    /// <summary> 
    /// Computes the powerset of a string recursively 
    /// based on the Algorithm http://www.ideserve.co.in/learn/generate-all-subsets-of-a-set-recursion 
    /// </summary> 
    /// <param name="input">Original input string</param> 
    /// <param name="temp">Temporary variable to store the current char for the curr call</param> 
    /// <param name="depth">The character position we are evaluating to add to the set</param> 
    /// <param name="resultSet">A hash list to store the result</param> 
    public static void PowerSet(string input, string temp, int depth, Dictionary<string, string> resultSet) 
    { 

     //base case 
     if(input.Length == depth) 
     { 
      //remove duplicate characters 
      string key = new string(temp.ToCharArray().Distinct().ToArray()); 

      //if the character/combination is already in the result, skip it 
      if (!resultSet.ContainsKey(key)) 
       resultSet.Add(key, key); 

      return;//exit 
     } 

     //left 
     PowerSet(input, temp, depth + 1, resultSet); 

     //right 
     PowerSet(input, temp + input[depth], depth + 1, resultSet); 

    } 
Problemi correlati