0

I am trying to work with Random array list and here is the code

private static string getSearchEngine()
{
    ArrayList url = new ArrayList();
    url.Add("www.google.com");
    url.Add("www.bing.com");
    url.Add("www.yahoo.com"); 
    Random rnd = new Random();
    int i = rnd.Next(0, url.Count);
    return url[i].ToString();
}

private static void DoMore_Functionality_Using_engName()
{
    for(int i = 0; i < 300; i++)
        string engName = getSearchEngine();
}

I want each search engine to be used only 100 times. If I use the above code, the randomizer could pick any one of them more than 100 times. How can I modify the code to do this?

Thanks Rashmi

14
  • 1
    So in short: a random search engine should be used and it should be used 100 times in total so for example: google 50, bing 25 and yahoo 25. Or they should all execute 100 times, so a total of 300? Commented Mar 18, 2014 at 15:08
  • @MaxMommersteeg: My reading would be the later. They want each search engine to be used only 100 times. Commented Mar 18, 2014 at 15:09
  • You have 3 engines, from which you want to randomly generate 300 engines, at most 100 of each? I think I have a non-random solution for you... Commented Mar 18, 2014 at 15:09
  • What if you used the i % 3th engine each time you need one? This would be well distributed, albeit not random. Commented Mar 18, 2014 at 15:09
  • 4
    Create a list of 300 ints with 100 0's, 100 1's and 100 2's. Then shuffle that list (use Fisher-Yates, for example). Now iterate over that list taking the engine at the index on each iteration. Commented Mar 18, 2014 at 15:12

3 Answers 3

3

I think you can do this by defining your collection outside of your method.Add each search engine for 100 times to your list, then each time you pick a random engine, remove that item from your collection.Something like this:

static List<string> engines = new List<string> (Enumerable.Repeat("www.google.com", 100)
        .Concat(Enumerable.Repeat("www.bing.com", 100))
        .Concat(Enumerable.Repeat("www.yahoo.com", 100)));

static Random rnd = new Random();
private static string getSearchEngine()
{
    int i = rnd.Next(0, engines.Count);
    var temp = engines[i];
    engines.RemoveAt(i);
    return temp;
}
Sign up to request clarification or add additional context in comments.

3 Comments

You should really use a proper shuffling algorithm instead, as much more efficient options are not much more work.
I really like your proposition. I would write something like you. Fill the list with 300 item and shuffle it.
Removal from List<string> is O(n), you do it n times, so overall you are O(n2). Versus an O(n) shuffle and then iterating through that list.
1

Here's a way to do it with a shuffle:

List<string> urls = new List<string> { "www.google.com", "www.bing.com", "www.yahoo.com" };
List<int> randomIdx = new List<int> (Enumerable.Repeat(0, 100)
                                     .Concat(Enumerable.Repeat(1, 100))
                                     .Concat(Enumerable.Repeat(2, 100)));

Random r = new Random();
// This is the Fisher-Yates Shuffle
// see: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
for (int i = randomIdx.Count - 1; i > 0; i--) 
{
    int j = r.Next(0,i);
    int tmp = randomIdx[i];
    randomIdx[i] = randomIdx[j];
    randomIdx[j] = tmp;
}

// Iterate through our random list and write each one out
for (int i = 0; i < randomIdx.Count; i++)
{
    Console.WriteLine(urls[randomIdx[i]]);
}

So to work this into your DoMore_Functionality_Using_engName() function, you would have an initialization in your class that sets up the list and shuffles it and then sets an index property that will keep track of where you are in the list. Then in DoMore_Functionality_Using_engName(), you would get the next result and then increment the index.

Comments

0

You need to associate each search engine with a counter of how many times it was used. To do so, you'll need to keep the collection in memory and not create it each time.

First, create a class to represent your data:

class SearchEngine
{
    public SearchEngine(string url, int counter)
    {
        Url = url;
        Counter = counter;
    }

    public string Url { get; set; }
    public int Counter { get; set; }
}

Second, create a collection of your engines as a private field somewhere:

private Random _random = new Random();
private IEnumerable<SearchEngine> _engines = new[]
{
    new SearchEngine("www.google.com", 0),
    new SearchEngine("www.bing.com", 0),
    new SearchEngine("www.yahoo.com", 0)
};

And the method to get a random search engine should be like this:

private string GetRandomSearchEngine()
{
    var searchEngine = _engines
    // randomize the collection
        .OrderBy(x => _random.Next())
    // skip items with invalid counter
        .SkipWhile(t => t.Counter >= 100)
        .First();
    // update the counter
    searchEngine.Counter++;
    return searchEngine.Url;
}

EDIT

As @Matt Burland suggests using _random.Next() is a better approach to randomizing the collection. He also raises another valid point: what happens when all the counters reach 100?

In such case, the code above will throw an exception (namely, the First() method). Assuming you don't want that you can use FirstOrDefault() and check for null. If the returned item is null then all counters have reached 100.

private bool TryGetRandomSearchEngine(out string url)
{
    var searchEngine = _engines
    // randomize the collection
        .OrderBy(x => _random.Next())
    // skip items with invalid counter
        .SkipWhile(t => t.Counter >= 100)
        .FirstOrDefault();
    if(searchEngine != null)
    {        
        // update the counter
        searchEngine.Counter++;
        url = searchEngine.Url;
        return true;
    }
    url = String.Empty;
    return false;
} 

And somewhere in your code you can use the method above like this:

string searchEngineUrl;
while(TryGetRandomSearchEngine(out searchEngineUrl))
{
    PerformSearch(searchEngineUrl, searchTerms);
}

4 Comments

Guids are unique, not necessarily random, and should not be used in place of random numbers.
@Servy, Guids aren't used as random numbers but rather for ensuring random order.
Which is a task it is not capable of ensuring. You're using it as if it were a random number, which you're using to try and generate a random number. Since your "random" number is not in fact random, your "random" ordering is not in fact random.
I also fail to see why you are using a Guid versus just Random.Next(). Otherwise, it's kind of a clever solution, but you should probably use FirstOrDefault and check for null which would indicate that all the counters are >= 100.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.