2010-04-29 4 views
32

J'essaie d'écrire une instruction LINQ qui me renvoie toutes les combinaisons possibles de nombres (j'en ai besoin pour un test et j'ai été inspiré par ce article of Eric Lippert). J'appelle le prototype de la méthode ressemble à:Générer des séquences de nombres avec LINQ

IEnumerable<Collection<int>> AllSequences(int start, int end, int size); 

Les règles sont les suivantes:

  • tous retournés collections ont une longueur de size
  • valeurs numériques dans une collection doivent augmenter
  • chaque numéro entre start et end doivent être utilisés

appelant Ainsi, le AllSequences(1, 5, 3) devrait se traduire par 10 collections, chacune de la taille 3:

1 2 3 
1 2 4 
1 2 5 
1 3 4 
1 3 5 
1 4 5 
2 3 4 
2 3 5 
2 4 5 
3 4 5 

Maintenant, en quelque sorte, je voudrais vraiment voir un LINQ pur solution. Je suis capable d'écrire une solution non LINQ par moi-même, alors ne mettez pas d'effort dans une solution sans LINQ.
Mes essais jusqu'à présent terminée à un point où je dois rejoindre un numéro avec le résultat d'un appel récursif de ma méthode - quelque chose comme:

return from i in Enumerable.Range(start, end - size + 1) 
     select BuildCollection(i, AllSequences(i, end, size -1)); 

Mais je ne peux pas le gérer à mettre en œuvre sur une BuildCollection() LINQ base - ou même ignorer cet appel de méthode. Pouvez-vous m'aider ici?

+0

@Noldorin, @Fede: Merci pour les grandes réponses - je dois certainement regarder de plus près les méthodes de 'Enumerable' (comme' répétition() '' ou concat() ') – tanascius

Répondre

32

Pensez que je l'ai.

IEnumerable<List<int>> AllSequences(int start, int end, int size) 
{ 
    if (size == 0) 
     return Enumerable.Repeat<List<int>>(new List<int>(), 1); 

    return from i in Enumerable.Range(start, end - size - start + 2) 
      from seq in AllSequences(i + 1, end, size - 1) 
      select new List<int>{i}.Concat(seq).ToList(); 
} 
+0

'if (size == 0) return new int [] {} .ToList()'? – Spook

13

Quelque chose comme ce qui suit devrait faire l'affaire, je pense.

public static IEnumerable<IEnumerable<int>> AllSequences(int start, int end, 
    int size) 
{ 
    return size <= 0 ? new[] { new int[0] } : 
      from i in Enumerable.Range(start, end - size - start + 2) 
      from seq in AllSequences(i + 1, end, size - 1) 
      select Enumerable.Concat(new[] { i }, seq); 
} 

La clé de la solution est le compound from clause, ce qui est très pratique pour faire face à enumerables imbriquées.

Notez que j'ai légèrement modifié la signature de méthode en IEnumerable<IEnumerable<int>>, car cela est plus pratique lors de l'utilisation de LINQ (pur). Vous pouvez toujours le convertir facilement en un IEnumerable<ICollection<int>> à la fin si vous aimez, cependant. Faites-moi savoir si le code a besoin d'explications, mais j'espère que la syntaxe LINQ le rend raisonnablement clair.

Édition 1: Correction d'un bug et amélioration de la concision.

Edit 2: Parce que je suis ennuyé et avoir rien de mieux à faire (non, pas vraiment), je pensais que je serais écrire une méthode d'extension qui calcule les combinaisons d'une liste donnée d'éléments, en utilisant de la méthode AllSequences.

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IList<T> source, 
    int num) 
{ 
    return AllSequences(0, source.Count - 1, num).Select(
     seq => seq.Select(i => source[i])); 
} 

Peut-être pas la façon la plus efficace de calculer des combinaisons, mais certainement un code assez compact!

+3

Vous avez volé mon tonnerre! Je travaillais juste ceci = D +1 – Tejs

+1

Je l'ai juste couru et il provoque un stackoverflow :-(, peut-être avec une taille où 0, juste avant la seconde de? –

+1

Il doit être 'à partir de seq dans AllSequences (i +1, fin, taille-1) ' A part cela, je viens juste d'en écrire un, mais le vôtre est beaucoup plus concis +1 – tzaman

41
Enumerable.Range(1, 12) 
      .Select(x => (x - 1) + 1); 
+2

n'a pas répondu à la question, mais elle m'a été utile, alors +1 – Kell

+1

Qu'est-ce que cela fait? Le '.Select 'n'est-il pas redondant? –

+0

@MateenUlhaq Oui, cela semble inutile. –

Questions connexes