-2

THI QUESTION CONTAINS VB.NET CODE BUT I CAN ACCEPT A SOLUTION EXPLAINED IN C#.


SCENARIO


In the past, and with some help from StackOverflow helper users, I built this function that makes permutations of characters:

<DebuggerStepThrough>
Public Shared Function PermuteCharacters(ByVal charSet As IEnumerable(Of Char),
                                         ByVal length As Integer,
                                         ByVal allowRepetition As Boolean) As IEnumerable(Of String)

    If (charSet.Count <> charSet.Distinct.Count) Then
        Throw New ArgumentException("Char-set contains duplicated characters.", "charSet")
        Return Nothing
    End If

    If (length = 1) Then
        Return charSet.Select(Function(c As Char)
                                  Return New String(New Char() {c})
                              End Function)
    End If

    If (allowRepetition) Then
        Return PermuteCharacters(charSet:=charSet, length:=length - 1, allowRepetition:=True).
               SelectMany(Function(str As String)
                              Return charSet
                          End Function,
                          Function(str As String, c As Char)
                              Return str & c
                          End Function)

    Else
        Return PermuteCharacters(charSet:=charSet, length:=length - 1, allowRepetition:=False).
           SelectMany(Function(x As String) charSet,
                      Function(str As String, c As Char)
                          If Not str.Contains(c) Then
                              Return str & c
                          Else
                              Return Nothing
                          End If
                      End Function).
        Where(Function(value As String) value IsNot Nothing)

    End If

End Function

PROBLEM


Now, In C# or Vb.Net, I would like to permutate all the possible combinations between the elements of a collection (only a collection of numeric types, or strings).

This is what I have done so far, it is incomplete and is not returning me the expected results:

Public Shared Function PermuteItems(ByVal items As IEnumerable(Of String)) As IEnumerable(Of IEnumerable(Of String))

    Return items.SelectMany(
        Function(x As String) As IEnumerable(Of String)
            Return items
        End Function,
        Function(str1 As String, str2 As String) As IEnumerable(Of String)
            Return {str1}.Concat({str2})
        End Function)

End Function

Public Shared Function PermuteItems(ByVal items As IEnumerable(Of Integer)) As IEnumerable(Of IEnumerable(Of String))

    Return PermuteItems((From item As Integer In items Select Convert.ToString(item)))

End Function

I need help to fix the resulting value that I'm getting, I'm getting collections of only two elements inside.

Is important for me to preserve the LINQ-to-Object approach.

C# online (and untested) translation:

public static IEnumerable<IEnumerable<string>> PermuteItems(IEnumerable<string> items) {
    return items.SelectMany((string x) => { return items; }, (string str1, string str2) => { return { str1 }.Concat({ str2 }); });
}

public static IEnumerable<IEnumerable<string>> PermuteItems(IEnumerable<int> items) {
    return PermuteItems((from item in itemsConvert.ToString(item)));
}

//=======================================================
//Service provided by Telerik (www.telerik.com)
//=======================================================

EXPECTATIONS


If I pass an array to the function containing these elements: {10, 2, 30} then it should return me an IEnumerable(Of IEnumerable(Of Integer)) containing these collections:

  • Item 1: {2, 10, 30}
  • Item 2: {2, 30, 10}
  • Item 3: {10, 2, 30}
  • Item 4: {10, 30, 2}
  • Item 5: {30, 10, 2}
  • Item 6: {30, 2, 10}

If I pass an array to the function containing these elements: {"abc", "", "abc"} then it should return me an IEnumerable(Of IEnumerable(Of String)) containing these collections:

  • Item 1: { "", "abc", "abc"}
  • Item 2: {"abc", "", "abc"}
  • Item 3: {"abc", "abc", ""}

The elements can be equals, the collections not.

The order matters, but I understand that is a different question, and I think that I can solve that by myself with LINQ grouping or sorting.

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • Thanks for colaboration but please read the questions before retag. I'm very displeased feeling the need to put a disclaimer on the header of each question about searching a solution for both C# or VB.Net. – ElektroStudios Mar 22 '16 at 21:42
  • 4
    Simply saying you'll accept C# code doesn't make this a question that should be tagged as C#. – juharr Mar 22 '16 at 21:44
  • Sorry but that is not True, If I'm searching for a solution in **C#** language of .Net platform then what tag I should use, **Pascal**?, hehe. – ElektroStudios Mar 22 '16 at 21:45
  • 1
    Refer to [this](http://meta.stackoverflow.com/questions/270101/can-a-question-have-both-c-and-vb-net-tags-when-relevant-to-both). I personally believe it's best to stick with one language and since you're code is in vb.net adding the C# tag just seems like spamming. – juharr Mar 22 '16 at 21:48
  • @juharr Thanks for comment. Did you read the first comment of the question that you linked?, it says soething which i'm totaly agreed: **For a question to which the reader of the question really doesn't care which language an answer uses, using both can be appropriate. When the reader is going to be expecting a particular language then no.** – ElektroStudios Mar 22 '16 at 21:55
  • sounds like a good time to start using the `Debugger` and findout where you are going wrong.. it's not up to us to step through your code.. this is up to you and with a rather high `reputation` I would think that you would know how to do the basics in regards to debugging and reading your own code.. if you want a C# solution then go to [VB -to C# Converter](http://converter.telerik.com/) and step through the C# converted code to figure out where you went wrong.. `SO` is not a `Code Factory Site` – MethodMan Mar 22 '16 at 21:56
  • 3
    @ElektroStudios But you didn't say you're searching for a solution in C#. You said you "can accept a solution explained in C#." And adding two lines of C# doesn't make it a C# question, either, especially adding it **just so you can also use that tag.** The vast majority of your code is in vb.net and I have to say, your question as it is written is rather confusing. – Frozenthia Mar 22 '16 at 21:58
  • @MethodMan Thanks for comment, but please, stop judging, it is not a question of debugging inexperience, I know what I'm doing wrong, the second lamda recives one element in both parameters (str1, str2) so I'm comparing one element with one other, then I'm always bulding a collection of two elements/strings. The problem Is, I don't know how to properly build the entire collection (of treee elements in the example I gave) using the **SelectMany**. – ElektroStudios Mar 22 '16 at 21:58
  • Where did you get the C# code, it wont compile for me. – Ňɏssa Pøngjǣrdenlarp Mar 22 '16 at 22:05
  • 1
    Might be of interest/help: http://stackoverflow.com/a/35631701/1070452 AndAlso ( && in C#): http://stackoverflow.com/a/21090635/1070452 – Ňɏssa Pøngjǣrdenlarp Mar 22 '16 at 22:08
  • 1
    It is very gratifiying for everyone, this kind of welcome from users that are limited to criticize, judge without knowing the person, vote negative, flag, everything, everything except help the OP, those users or programmers who think they know everything and they are gods to judge every thing. Really it is very disappointing what people like you makes become every day to this page, in some years this could be named "**StackTrolling**", really... I'm gonna stop trying to justify each of my actions to people who can not use reasoning. Free moderation to those persons is a virus. – ElektroStudios Mar 22 '16 at 22:08
  • Thankyou, @Plutonix, I will chek it. As usual, you are one of the few nice people one can still find in StackOverflow, a person who really wants to offer help to others. – ElektroStudios Mar 22 '16 at 22:10
  • 1
    Also check this: http://stackoverflow.com/questions/4319049/generating-permutations-using-linq – JamesFaix Mar 22 '16 at 22:19
  • 1
    Take a look at https://www.nuget.org/packages/Combinatorics/ - a nuget package (and source code) for computing permutations and combinations. – Ian Mercer Mar 23 '16 at 00:04
  • @Ian Mercer Amazing, just amazing!!! It's the fastest and better solution that I tried, thankyou so much for share that "unknown" library, please feel free to publish it inside an answer to mark it as accepted!. – ElektroStudios Mar 23 '16 at 11:57
  • Definitelly does not exists any approach more simple and efficient than **Combinatronics** in all the world wide web for .Net, the source-code and the model is a delight, thanks again @Ian Mercer. – ElektroStudios Mar 23 '16 at 12:07

3 Answers3

2

This works for me:

public IEnumerable<IEnumerable<T>> Permutate<T>(IEnumerable<T> source)
{
    var xs = source.ToArray();
    return
        xs.Length == 1
            ? new [] { xs }
            : (
                from n in Enumerable.Range(0, xs.Length)
                let cs = xs.Skip(n).Take(1)
                let dss = Permutate<T>(xs.Take(n).Concat(xs.Skip(n + 1)))
                from ds in dss
                select cs.Concat(ds)
            ).Distinct(new EnumerableEqualityComparer<T>());
}

private class EnumerableEqualityComparer<T> : IEqualityComparer<IEnumerable<T>>
{
    public bool Equals(IEnumerable<T> a, IEnumerable<T> b)
    {
        return a.SequenceEqual(b);
    }

    public int GetHashCode(IEnumerable<T> t)
    {
        return t.Take(1).Aggregate(0, (a, x) => a ^ x.GetHashCode());
    }
}

With var source = new[] { 10, 2, 30 }; I get:

{10, 2, 30} 
{10, 30, 2} 
{2, 10, 30} 
{2, 30, 10} 
{30, 10, 2} 
{30, 2, 10} 

And with var source = new[] { "abc", "", "abc" }; I get:

{"abc", "", "abc"} 
{"abc", "abc", ""} 
{"", "abc", "abc"} 

Vb.Net equivalent:

Public Function Permutate(Of T)(source As IEnumerable(Of T)) As IEnumerable(Of IEnumerable(Of T))

    Dim xs As T() = source.ToArray()

    Return If(xs.Length = 1,
              {xs},
              (From n In Enumerable.Range(0, xs.Length)
               Let cs = xs.Skip(n).Take(1)
               Let dss = Permutate(Of T)(xs.Take(n).Concat(xs.Skip(n + 1)))
               From ds In dss Select cs.Concat(ds))
              ).Distinct(New EnumerableEqualityComparer(Of T)())

End Function

Public Class EnumerableEqualityComparer(Of T) : Implements IEqualityComparer(Of IEnumerable(Of T))

    Public Shadows Function Equals(a As IEnumerable(Of T), b As IEnumerable(Of T)) As Boolean _
    Implements IEqualityComparer(Of IEnumerable(Of T)).Equals
        Return a.SequenceEqual(b)
    End Function

    Public Shadows Function GetHashCode(t As IEnumerable(Of T)) As Integer Implements _
    IEqualityComparer(Of IEnumerable(Of T)).GetHashCode
        Return t.Take(1).Aggregate(0, Function(a, x) a Xor x.GetHashCode())
    End Function

End Class
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • I really want to give a big THANKYOU for the code that you've provided, because this is better than having nothing, thanks!, however, maybe you could improve the algorithm to maintain the approach of the first character permutation function that I provided in my question, please?. – ElektroStudios Mar 23 '16 at 10:55
  • To understand, your function is not doing too much projections/iterations in the collection?. See what I mean, it has a performance overkill. The method that I provided which works for characters takes 10 sec. approx to return and count the length of a collection of 3.628.800 elements. The method that you provided, without using the custom comparer, takes 50 sec. approx to return and count the length of a collection of 3.628.800 elements, and, using the custom comparer, the total time increases in +1 Hours!. Maybe could you do some important improvement, please?, because it's a pitty. – ElektroStudios Mar 23 '16 at 11:27
  • 1
    @ElektroStudios - If you change the line `select cs.Concat(ds)` to `select cs.Concat(ds).ToArray()` I got a 4x factor of speed improvement in one test I made. But, yes, this is a slow a memory intensive algorithm. – Enigmativity Mar 23 '16 at 13:13
1

Take a look at Combinatorics - a nuget package (and source code) for computing permutations and combinations.

Ian Mercer
  • 38,490
  • 8
  • 97
  • 133
0

Not sure about performance. But compact

var permutation = list.SelectMany((item,i) => list.Skip(i + 1).Select((another) => (item, another)));
Thaina Yu
  • 1,372
  • 2
  • 16
  • 27