1

I've searched stackoverflow and the web for similar questions, but i couldn't found the solution that i need.

I'm programming a code-list-generator.

so for example i have a List of chars like List<char> { 'a', 'b', 'c' };.

and i have a couple of settings like (int)minLength of 2 and a (int)maxLength of 3.

and i want this output :

aa
ab
ac
ba
bb
bc
ca
cb
cc

aaa
aab
aac
aba
abb
abc
aca
acb
acc

baa
bab
bac
bba
bbb
bbc
bca
bcb
bcc

caa
cab
cac
cba
cbb
cbc
cca
ccb
ccc

in gereral i would just kreate multidimensional loops, but i have to do this dynamically because of the diferent minLength, maxLength & charList values.

so i was going with a "self-calling-function" like this example:

    private void loop() {
        for( int i = 0; i < num; i++ ) {
            // stuff
            loop();
        }
    }

so far i've made following bunch of code, but in this stage i get stuck... :

    Thread mainThread;

    List<char> azlower;
    List<char> azupper;
    List<char> nullnine;

    List<char> totalChars;

    int totalNum;

    int levelCounter;

    bool running;

    public Form1() {
        InitializeComponent();
    }

    private void init() {
        azlower = new List<char> { 'a', 'b', 'c' , 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
        azupper = new List<char> { 'A', 'B', 'C' , 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
        nullnine = new List<char> { '0', '1', '2' /* , '3', '4', '5', '6', '7', '8', '9' */ };

        totalChars = new List<char> ();

        running = false;
    }

    private void button1_Click( object sender, EventArgs e ) {
        if( !running ) {
            init();

            // Start
            if( checkBoxAZ1.Checked ) {
                foreach( char character in azlower ) {
                    totalChars.Add( character );
                }
            }
            if( checkBoxAZ2.Checked ) {
                foreach( char character in azupper ) {
                    totalChars.Add( character );
                }
            }
            if( checkBox09.Checked ) {
                foreach( char character in nullnine ) {
                    totalChars.Add( character );
                }
            }
            if( checkBoxS.Checked && textBoxSpec.Text != "" ) {
                char[] specArray = textBoxSpec.Text.ToCharArray();
                foreach( char character in specArray ) {
                    totalChars.Add( character );
                }
            }
            totalNum = totalChars.Count;
            levelCounter = Int32.Parse( textBoxMinLength.Text );
            mainThread = new Thread( new ThreadStart( run ) );
            button1.Text = "Stop";
            running = true;
            mainThread.Start();
        } else {
            mainThread.Abort();
            button1.Text = "Start";
            running = false;
        }
    }

    private void run() {
        for( int i = 0; i < totalNum; i++ ) {
            Invoke( ( MethodInvoker ) delegate {
                write( totalChars[ i ].ToString() );
                if( i == totalNum - 1 && levelCounter == Int32.Parse( textBoxMaxLength.Text ) ) {
                    write( "\n" );
                }
            } );
            if( levelCounter < Int32.Parse( textBoxMaxLength.Text ) ) {
                levelCounter++;
                run();
            }
        }
        return;
    }

    private void write( string line ) {
        richTextBox1.Text += line;
    }

but with the setup above, and my code, the output looks like this:

aabc
bc

i think i've just made a thinking mistake, didn't i ?

so guys, do you have anny suggestions for me ?

i've also looked at the Cartesian Product, but i thought that it would not work with only one array...

thx for any help.

Ace
  • 1,437
  • 6
  • 28
  • 46
  • possible duplicate of [All possible combinations of an array](http://stackoverflow.com/questions/5162254/all-possible-combinations-of-an-array) – Reed Copsey Mar 26 '13 at 15:22
  • 2
    @ReedCopsey although that question is tagged C# all the answers are java based – Scott Chamberlain Mar 26 '13 at 15:24
  • @ScottChamberlain There are [tons of duplicates](http://stackoverflow.com/search?q=combinations+array+C%23) for this already - such as the one I posted, but also http://stackoverflow.com/questions/7643885/different-combinations-of-an-array-c and http://stackoverflow.com/questions/5777208/array-of-array-combinations A search on this site will easily pull up plenty of dups – Reed Copsey Mar 26 '13 at 15:27
  • @ReedCopsey i've tryed this solution, but it gives me a diferent output as i need. also the question says "2."ted williams" and "williams ted" are considered the same, so I want "ted williams" only"... this ould be NOT "ALL possible combinations" – Ace Mar 26 '13 at 15:27
  • also in most of the duplicates, the solution is not dynamic. as i said, i need to be able to change diferent settings later on. – Ace Mar 26 '13 at 15:29
  • @Ace - So what were your testing results? – dcp Mar 28 '13 at 21:50

4 Answers4

1

Here is an example that would generate it using recursion.

IEnumerable<string> GenerateCode(int length, int min, IEnumerable<char> chars)
{
    if (length == 0)
    {
        yield return string.Empty;
        yield break;
    }

    foreach (var mid in GenerateCode(length - 1, min, chars))
    {
        foreach (var c in chars)
        {
            var t = mid + c;
            if (length >= min)
                Console.WriteLine(t); // replace with where you want to put the results

            yield return t;
        }
    }
}

// now call this method:
GenerateCode(3 /*max length*/, 2 /*min length*/, new [] { 'a', 'b', 'c'  });

But unless this is an exercise, why would you generate all possible variations? There might be a much better solution if you provide the actual requirement.

Knaģis
  • 20,827
  • 7
  • 66
  • 80
  • again, i have the problem that i'm calling the function as a new thread, where i can't pass arguments for the method – Ace Mar 26 '13 at 15:32
  • yes, you can. create a new method with single `object` argument, use `ParameterizedThreadStart`, pass in `Tuple.Create(3,2,char[])` and cast the object argument to the Tuple in the method. – Knaģis Mar 26 '13 at 15:34
  • alright, i'll test it after work. thanks for this example. also i want to notice that i need to "sort" the output in the given order in my question. – Ace Mar 26 '13 at 15:37
  • to sort, create a separate `foreach()` for the `yield return t` part - so first you will generate all options for particular length and just then return them. – Knaģis Mar 26 '13 at 15:42
  • i need to generate combinations with up to 24 chars length, so performance is the primary condition for this, and i'll test both methods. i'll response if i tested it. thx – Ace Mar 26 '13 at 15:45
  • @Ace With up to 24 chars length, you're talking 24! or 620448401733239439360000 combinations. That's just for the ones of length 24 (e.g. I didn't count 23!, 22!, ...) Are you planning to run this on a Cray Supercomputer? – dcp Mar 26 '13 at 15:47
  • haha no XD i'll let the application run for exactly 1h, and proportionally calculate the total time that it would need. also i should npotice that i'll put logics in the code, that'll prevent unnatural combinations, to generate words that COULD be real. also the 16, 20 and 24 chars length generated codes will only contain numbers. in a next step i'll let 50 computing units do the work, which will execute each 3 to 10 threads, to generate the list of 16 to 24 chars length codes that contains a-zA-z0-9 and some special characters. – Ace Mar 26 '13 at 15:56
  • @Ace I don't think 1h is going to be enough. 620448401733239439360000 combinations could take years to process (that's not an exaggeration). – dcp Mar 26 '13 at 15:59
  • 1h is just for calculating some things to spread the threads to the other computers ^^ also the list must NOT be complete, my customer is only about to have a list of lets say 1.000.000.000 generated codes. – Ace Mar 26 '13 at 16:14
  • also my logics that i'll implement will down the total count to about 30 % of the original. ALSO the application is NOT calculating every combination from 1 to 16, so only 16 to 20 AND/OR 24 chars length. trust me, i definitly know what you're talking about. i'm not even thinking about generating all these codes.. ( 24! would be a textfile with about 564294534099 TB of data HAHAHA incredible... ) – Ace Mar 26 '13 at 16:30
  • If you need just 1 billion random codes then generate random 1 billion codes (for each code verify that the same code was not generated using a Hashtable)... There is no need to iterate through every single code this way. Even if you need to know the position of a random code in the whole list, you can calculate that instead of going through the list. – Knaģis Mar 26 '13 at 18:03
0

You can use a backtracking algorithm, code is below. Output is also included to prove the code works.

If you need to pass parameters to a thread, see this answer.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        const int MIN_LENGTH = 2;
        const int MAX_LENGTH = 3;
        static IList<string> vals;
        static IList<string> results;

    static void go(string cur)
    {
        if (cur.Length > MAX_LENGTH)
        {
            return;
        }
        if (cur.Length >= MIN_LENGTH && cur.Length <= MAX_LENGTH)
        {
            results.Add(cur);
        }

        foreach (string t in vals)
        {
            cur += t;
            go(cur);
            cur = cur.Substring(0, cur.Length - 1);
        }
    }
    static int Main(string[] args)
    {
        vals = new List<string>();
        vals.Add("a");
        vals.Add("b");
        vals.Add("c");

        results = new List<string>();
        go("");
        results = results.OrderBy(x => x.Length).ToList();

        foreach (string r in results)
        {
            Console.WriteLine(r);
        }

        return 0;
    }
}

output

aa
ab
ac
ba
bb
bc
ca
cb
cc
aaa
aab
aac
aba
abb
abc
aca
acb
acc
baa
bab
bac
bba
bbb
bbc
bca
bcb
bcc
caa
cab
cac
cba
cbb
cbc
cca
ccb
ccc
Community
  • 1
  • 1
dcp
  • 54,410
  • 22
  • 144
  • 164
  • how can i pass attributes in a method when i'm calling it in a new thread ? – Ace Mar 26 '13 at 15:31
  • @Ace - Also, I modified it to sort in the order you requested. – dcp Mar 26 '13 at 15:42
  • thank you, i also will test your solution after work. also i'll test the performance, because i'll use a max length of up to 24 chars. – Ace Mar 26 '13 at 15:46
  • 1
    @Ace 24 is going to be way too much (see my comment in other question). If you get much over 10, you're going to have problems since the number of combinations increases exponentially. If this makes no sense to you, I'd recommend studying this: http://en.wikipedia.org/wiki/Permutation – dcp Mar 26 '13 at 15:50
  • i've explained my idea down below ;) – Ace Mar 26 '13 at 15:57
0
public static IEnumerable<IEnumerable<T>> Permutations<T>(IEnumerable<T> source)
{
    if (source == null)
        throw new ArgumentNullException("source");
    return permutations(source.ToArray());
}

private static IEnumerable<IEnumerable<T>> permutations<T>(IEnumerable<T> source)
{
    IEnumerable<T> enumerable = source as List<T> ?? source.ToList();
    var c = enumerable.Count();
    if (c == 1)
        yield return enumerable;
    else
        for (int i = 0; i < c; i++)
            foreach (var p in permutations(enumerable.Take(i).Concat(enumerable.Skip(i + 1))))
                yield return enumerable.Skip(i).Take(1).Concat(p);
}

private static IEnumerable<string> Subsets(char[] chars)
{
    List<string> subsets = new List<string>();

    for (int i = 1; i < chars.Length; i++)
    {
        subsets.Add(chars[i - 1].ToString(CultureInfo.InvariantCulture));
        int i1 = i;
        List<string> newSubsets = subsets.Select(t => t + chars[i1]).ToList();
        subsets.AddRange(newSubsets);
    }
    subsets.Add(chars[chars.Length - 1].ToString(CultureInfo.InvariantCulture));
    return subsets;
}
private static void Main()
{
    char[] chars = new[]{'a','b','c'};
    var subsets = Subsets(chars);
    List<string> allPossibleCombPerm = 
        subsets.SelectMany(Permutations).Select(permut => string.Join("", permut)).ToList();
    allPossibleCombPerm.ForEach(Console.WriteLine);
}
Ahmed KRAIEM
  • 10,267
  • 4
  • 30
  • 33
0

You can use the function below. T is the type of elements that will be combined into the string (ToString() will be called on each item). You can optionally use a prefix and separator for the strings representing the combination, so they look like myprefix_A_1, myprefix_A_2, etc, where _ will be the separator.

You must call this function with level=1 and tmpList=null.

 public static IEnumerable<string> Combine<T>(string prefix, 
                                              string separator, 
                                              List<List<T>> collections, 
                                              int level, List<string> tmpList)
    {
        if (separator == null)
            separator = "";

        if (prefix == null)
            prefix = "";

        List<string> nextTmpList  = new List<string>();
        int length = collections.Count();

        if (tmpList == null || tmpList.Count == 0)
        {
            tmpList = new List<string>();
            foreach (var ob in collections.Last())
                tmpList.Add(ob.ToString());
        }

        if(length == level)
        {
            foreach (string s in tmpList)
                nextTmpList.Add(prefix + separator + s.ToString());
            return nextTmpList;
        }


        foreach (var comb in tmpList)
            foreach(var ob in collections[length - level - 1])
                nextTmpList.Add(ob.ToString() + separator + comb);
        return Combine(prefix, separator, collections, level + 1, nextTmpList);

    }

Example:

Combine<int>("G1", "_", new List<List<int>>() { new List<int>() { 1, 3}, new List<int>() {2, 4 } }, 1, null);

OUTPUT:

G1_1_2     G1_3_2    G1_1_4    G1_3_4
VMh
  • 1,300
  • 1
  • 13
  • 19