我最近开始使用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());
}
答案 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设为无限序列,因为一旦临时字典的计数达到期望的目标,我们就会退出循环。
当keepN
比dictionary.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]] )