【问题标题】:Dropping n Random Values from Dictionary从字典中删除 n 个随机值
【发布时间】:2018-10-18 23:37:36
【问题描述】:

我最近开始使用 C# 并正在开发一个程序,我需要从字典中删除 n 随机值。我从this previous q&a 中提取了一些代码并添加了DropRandom 以删除n 值,但是当我运行DropRandom(比如keepN = 10)时,字典不会直接下降到10 个值。

例如,从最初的总计数 100 到期望的最终计数 10,计数可能会下降到 20。我可以在结果字典上运行 DropRandom,然后它会下降到 12,最后是第三次(或第四次或第五次......)迭代将减少到 10 次。

所以我的问题是,我该怎么做才能让DropRandom 立即下降到所需的计数?

第一部分只是设置随机密钥(改编自上述问答):

public static IEnumerable<TKey> RandomKeys<TKey, TValue>(IDictionary<TKey,TValue> dictionary)
{
    Random random = new Random();
    List<TKey> keys = Enumerable.ToList(dictionary.Keys);
    int size = dictionary.Count();

    while (true) //returns random order of keys from dictionary using infinite loop
    {
        yield return keys[random.Next(size)];
    }
}

第二部分删除除n 之外的所有值:

public static void DropRandom<TKey, TValue>(int keepN, ref Dictionary<TKey, TValue> dictionary)
{
    if(dictionary.Count() < keepN)
    {
        throw new Exception("Trying to keep more items than in dictionary");
    }

    Console.WriteLine("old dict size = " + dictionary.Count());
    Dictionary<TKey, TValue> tempDict = new Dictionary<TKey, TValue>();

    //inelegant way to get extra values...
    IEnumerable<TKey> randK = RandomKeys(dictionary).Take(keepN * 2);

    while (tempDict.Count() < keepN)
    {
        foreach(TKey key in randK)
        {
            if (!tempDict.ContainsKey(key))
            {
                tempDict.Add(key, dictionary[key]);
                Console.WriteLine("key = " + key.ToString());
            }
        }
    }
    dictionary = null;
    dictionary = tempDict;
    Console.WriteLine("New dict size = " + dictionary.Count());
}

【问题讨论】:

  • 你是内在的if 还需要对keepN 进行计数检查,否则你添加来自randK 的所有键。我建议使用Fisher-Yates shuffle 来获得随机顺序的键,而不是希望取两倍的键不会导致太多重复。
  • KeepN 会比 N 小很多吗?
  • @juharr,谢谢你的建议,我会研究一下洗牌。另外,keepN 总是比 N 小很多。

标签: c#


【解决方案1】:

您可以采取一些措施来确保您的 keepN 值在最终尺寸方面得到尊重。

首先,我将摆脱while (tempDict.Count() &lt; keepN) 循环。然后,我会从 randk 分配中删除 Take(keepN * 2),给您留下如下所示的内容:

IEnumerable<TKey> randK = RandomKeys(dictionary); // Lazy infinite sequence

foreach (TKey key in randK)
{
    if(tempDict.Count >= keepN) // Break from the loop once we hit our target count
        break;
    if (!tempDict.ContainsKey(key))
    {
        tempDict.Add(key, dictionary[key]);
        Console.WriteLine("key = " + key.ToString());
    }
}

在这种情况下,我们可以将 randK 作为无限序列,因为一旦临时字典的计数达到所需目标,我们就会跳出循环。

keepN 远小于dictionary.Count 时,这应该会相当快,但如果N 较大且keepN 很接近,您将随机选择极少数剩余值之一。

【讨论】:

  • 很好,这对我有用。您关于 keepN 与 dictionary.Count 相比的大小的观点也是一个很好的观点,我必须牢记前进。谢谢!
【解决方案2】:

问题是在第一次迭代 add 之后,您最终可能会将 keepN-1 项添加到临时字典中。但是对于下一次迭代,还有另一个 keepN * 2 个新键被检查和添加,这最终将超过 KeepN 编号在第二次或第三次迭代中添加到您的临时集合中。

即使在第一次迭代中,您最终可能会添加超过 keepN 项,因为您检查了 keepN * 2 键。

修复很简单。

       foreach(TKey key in randK)
        {
            if (!tempDict.ContainsKey(key))
            {
                tempDict.Add(key, dictionary[key]);
                Console.WriteLine("key = " + key.ToString());
            }
            //------------------------------------------
            // the easy fix it here
            if(tempDict.Count() == keepN) break;
            //-----------------------------------
        }

但你可以做得更好

1. Generating keepN random, unique values!
https://stackoverflow.com/questions/14473321/generating-random-unique-values-c-sharp
2. var sourceDicKeyArray = dictionary.Keys.ToArray();
3. foreach index in keepNRandomValues
   newDic.Add(sourceDicKeyArray[index], dictionary[sourceDicKeyArray[index]] )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-16
    • 2015-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-14
    相关资源
    最近更新 更多