4

I'm trying to grab a single item from each of the Lists here, and combine them to make a unique name. This is just for kicks. :)

Here are the lists:

List<string> FirstNames = new List<string>()
{
    "Sergio",
    "Daniel",
    "Carolina",
    "David",
    "Reina",
    "Saul",
    "Bernard",
    "Danny",
    "Dimas",
    "Yuri",
    "Ivan",
    "Laura"
};

List<string> LastNamesA = new List<string>()
{
    "Tapia",
    "Gutierrez",
    "Rueda",
    "Galviz",
    "Yuli",
    "Rivera",
    "Mamami",
    "Saucedo",
    "Dominguez",
    "Escobar",
    "Martin",
    "Crespo"
};

List<string> LastNamesB = new List<string>()
{
    "Johnson",
    "Williams",
    "Jones",
    "Brown",
    "David",
    "Miller",
    "Wilson",
    "Anderson",
    "Thomas",
    "Jackson",
    "White",
    "Robinson"
};

I know I get a single item via an index, and I also know that I can use the Random class to generate a random number from 0 to ListFoo.Count.

What I don't know is how to check if a random permutation has already been drawn from the collections.

I've thought about using the tuple class:

List<Tuple<int,int,int>> permutations = new List<Tuple<int,int,int>>();

But I'm having a brainfart here. ;) Any guidance? I'm not really looking for the entire code to this simple problem, just a suggestion or hint.


EDIT

Thanks to the suggestions given here, here what I've come up with. Any room for improvements?

static void Main(string[] args)
{
    List<string> FirstNames = new List<string>()
    {
        "Sergio",
        "Daniel",
        "Carolina",
        "David",
        "Reina",
        "Saul",
        "Bernard",
        "Danny",
        "Dimas",
        "Yuri",
        "Ivan",
        "Laura"
    };

    List<string> LastNamesA = new List<string>()
    {
        "Tapia",
        "Gutierrez",
        "Rueda",
        "Galviz",
        "Yuli",
        "Rivera",
        "Mamami",
        "Saucedo",
        "Dominguez",
        "Escobar",
        "Martin",
        "Crespo"
    };

    List<string> LastNamesB = new List<string>()
    {
        "Johnson",
        "Williams",
        "Jones",
        "Brown",
        "David",
        "Miller",
        "Wilson",
        "Anderson",
        "Thomas",
        "Jackson",
        "White",
        "Robinson"
    };

    var permutations = new List<Tuple<int, int, int>>();
    List<string> generatedNames = new List<string>();

    Random random = new Random();
    int a, b, c;

    //We want to generate 500 names.
    while (permutations.Count < 500)
    {
        a = random.Next(0, FirstNames.Count);
        b = random.Next(0, FirstNames.Count);
        c = random.Next(0, FirstNames.Count);

        Tuple<int, int, int> tuple = new Tuple<int, int, int>(a, b, c);

        if (!permutations.Contains(tuple))
        {
            permutations.Add(tuple);
        }
    }

    foreach (var tuple in permutations)
    {
        generatedNames.Add(string.Format("{0} {1} {2}", FirstNames[tuple.Item1], 
                                                        LastNamesA[tuple.Item2],
                                                        LastNamesB[tuple.Item3])
        );
    }

    foreach (var n in generatedNames)
    {
        Console.WriteLine(n);
    }

    Console.ReadKey();
}
  • If you start excluding pairs, it is no longer random, no? Unless duplicates are an explicit fail... if there isn't an absolute need to preclude duplicates... let them. – Marc Gravell Mar 20 '11 at 17:21
  • There is a need to not let the same permutations exist. They need to be unique names. Since it's 12 x 12 x 12, I can get 1600+ unique names, no? Unless my math is off. –  Mar 20 '11 at 17:26
  • @sergio Tapia, Thats correct 1728 unique possibilities. Do you plan to generate all of them? or just a few? – MerickOWA Mar 20 '11 at 17:29
  • @MerickOWA: I've decided to just generate 500. If I were to generate all I could just iterate one by one and get all of them. Not really a good exercise. :) –  Mar 20 '11 at 18:11

6 Answers6

4

You are on the right track! Every time you generate a name, add it to your tuple list

//Create the tuple
Tuple <int, int, int> tuple = new Tuple<int, int, int>(index1, index2, index3)

if(!permutations.Contains(tuple))
{ 
    permutations.Add(tuple);
    //Do something else
}
Varun Chatterji
  • 5,059
  • 1
  • 23
  • 24
  • This sounds great. I'm assuming permutations in a Tuple as well, correct? –  Mar 20 '11 at 17:48
2

I would think the simplest solution is to just the stuff the assembled name into a HashSet<string> which will ensure the list of created names is unique.

Thomas
  • 63,911
  • 12
  • 95
  • 141
  • Do you think there might be performance gain with `HashSet` (where int is a representation of the random numbers used to index the arrays) over `HashSet`? – Hogan Mar 20 '11 at 17:27
  • @Hogan: For this small of a resultset, I can't imagine a performance difference between strings and ints. In my opinion, it's worth the extra microseconds anyway in order to guarantee your algorithm. For example, in your answer, 010305 for {1,3,5} means that the code will break if there are more than 100 entries. The only ways I can see to guarantee success are with strings, or the Tuple (or similar class or struct). I don't know if it's possible to use `int` as your key and have it work universally. – Joe Enos Mar 20 '11 at 17:39
  • You don't need to check against it though, just delete them once they've been picked. –  Mar 20 '11 at 18:11
  • @Hogan - If what you are trying to build is a unique list of names, a `Hashset` has the advantage of providing the final output you need without the additional contains check. – Thomas Mar 20 '11 at 19:56
  • True. @JonB : That would require generating all possible combinations first -- which is crazy expensive. – Hogan Mar 20 '11 at 19:57
1

An alternative to the HashSet answer is to build all of the possible combinations in advance, shuffle them, then store them in a Queue, where you can retrieve them one at a time. This will avoid having to check the existing ones every time you build a new one, and will still be random.

This only works if you don't have a large set to begin with, since the work involved in creating the complete list and shuffling it would be huge for a large set of data.

Joe Enos
  • 39,478
  • 11
  • 80
  • 136
1

It's really easy to generate them all using LINQ:

var combs =
(from first in FirstNames
from second in LastNamesA
from third in LastNamesB
select new Tuple<string, string, string>(first, second, third)).ToList();

After this, if you need to take unique elements from the list randomly, just shuffle the list and then pick them one-by-one in order.

You can use the Knuth-Fisher-Yates algorithm (that's an in-place shuffle):

Random rand = new Random();

for (int i = combs.Count - 1; i > 0; i--)
{
    int n = rand.Next(i + 1);
    var mem = combs[i];
    combs[i] = combs[n];
    combs[n] = mem;
}
digEmAll
  • 56,430
  • 9
  • 115
  • 140
0
  1. Create a new tuple with 3 random digits
  2. Check if permutations contains your new tuple
  3. If not => Add new tuple to the list. If yes, start with point 1 again.
eldarerathis
  • 35,455
  • 10
  • 90
  • 93
Tokk
  • 4,499
  • 2
  • 31
  • 47
0

I would create a HashSet<int> and store the numeric representation of the picks (eg 135 for first, third and 5th or use 010305) and then check if they are in the set.

Hogan
  • 69,564
  • 10
  • 76
  • 117