【问题标题】:Random array list随机数组列表
【发布时间】:2014-03-18 15:05:51
【问题描述】:

我正在尝试使用随机数组列表,这是代码

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();
}

我希望每个搜索引擎只被使用 100 次。如果我使用上面的代码,随机发生器可以选择其中任何一个超过 100 次。我该如何修改代码来做到这一点?

谢谢 拉什米

【问题讨论】:

  • 所以简而言之:应该使用随机搜索引擎,并且应该总共使用 100 次,例如:google 50、bing 25 和 yahoo 25。或者它们都应该执行 100 次,所以一共300个?
  • @MaxMommersteeg:我的阅读时间会晚一些。他们希望每个搜索引擎只被使用 100 次。
  • 你有3个引擎,你想从中随机生成300个引擎,每个最多100个?我想我有一个非随机的解决方案给你......
  • 如果您每次需要一个时都使用i % 3th 引擎怎么办?虽然不是随机的,但这会很好地分布。
  • 创建一个包含 300 个 ints 的列表,其中包含 100 个 0、100 个 1 和 100 个 2。然后打乱该列表(例如,使用 Fisher-Yates)。现在遍历该列表,在每次迭代时将引擎置于索引处。

标签: c# random arraylist


【解决方案1】:

我认为您可以通过在您的方法之外定义您的集合来做到这一点。将每个搜索引擎添加到您的列表中 100 次,然后每次选择一个随机引擎时,从您的集合中删除该项目。像这样:

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;
}

【讨论】:

  • 你应该真正使用适当的洗牌算法,因为更有效的选择并没有更多的工作。
  • 我真的很喜欢你的提议。我会写一些像你一样的东西。用 300 项和 shuffle it 填写列表。
  • List&lt;string&gt; 中删除是 O(n),你这样做 n 次,所以总的来说你是 O(n2)。与 O(n) 洗牌然后遍历该列表相比。
【解决方案2】:

这是一种使用随机播放的方法:

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]]);
}

因此,要将其应用到您的 DoMore_Functionality_Using_engName() 函数中,您将在您的类中进行初始化,设置列表并将其打乱,然后设置一个索引属性,该属性将跟踪您在列表中的位置。然后在DoMore_Functionality_Using_engName(),你会得到下一个结果,然后增加索引。

【讨论】:

    【解决方案3】:

    您需要将每个搜索引擎与使用次数的计数器相关联。为此,您需要将集合保存在内存中,而不是每次都创建它。

    首先,创建一个类来表示您的数据:

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

    其次,在某处创建一个引擎集合作为私有字段:

    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)
    };
    

    而获取随机搜索引擎的方法应该是这样的:

    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;
    }
    

    编辑

    正如 @Matt Burland 所建议的那样,使用 _random.Next() 是随机化集合的更好方法。 他还提出了另一个有效的观点:当所有计数器达到 100 时会发生什么

    在这种情况下,上面的代码会抛出异常(即First()方法)。假设您不希望您可以使用FirstOrDefault() 并检查null。如果返回的项目是null,那么所有的计数器都达到了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;
    } 
    

    在你的代码中的某个地方,你可以像这样使用上面的方法:

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

    【讨论】:

    • Guid 是唯一的,不一定是随机的,也不应该用来代替随机数。
    • @Servy, Guids 不用作随机数,而是用于确保random order
    • 这是一项它无法确保的任务。您使用它就像它是一个随机数一样,您正在使用它来尝试生成一个随机数。由于您的“随机”数字实际上不是随机的,因此您的“随机”排序实际上也不是随机的。
    • 我也看不出你为什么使用Guid 而不是Random.Next()。否则,这是一个聪明的解决方案,但您可能应该使用 FirstOrDefault 并检查 null 这表明所有计数器都是 &gt;= 100
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 2013-09-24
    • 1970-01-01
    • 2018-11-06
    相关资源
    最近更新 更多