从字典中删除n个随机值

时间:2018-10-18 23:37:36

标签: c#

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

例如,从总初始计数为100到期望的最终计数为10,该计数可能会降至20。我可以在生成的Dictionary上运行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());
}

2 个答案:

答案 0 :(得分:1)

您可以执行以下几项操作来确保尊重keepN值的大小。

首先,我将摆脱while (tempDict.Count() < 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设为无限序列,因为一旦临时字典的计数达到期望的目标,我们就会退出循环。

keepNdictionary.Count小得多时,这应该是相当快的,但是如果N大且keepN较近,您将随机选择剩下的很少的值之一。

答案 1 :(得分:1)

问题是在add第一次迭代之后,您可能最终将keepN-1项目添加到您的临时字典中。但是对于下一次迭代,还会检查并添加另一个keepN * 2个新键,其结果将比在第二次或第三次迭代中向您的临时集合中添加的KeepN号更多。

即使在第一次迭代中,由于选中了keepN * 2键,您最终可能还不止要添加keepN项目。

修复很容易。

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