我正在尝试获取中的最大值的键Dictionary<string, double> results
。
这是我到目前为止所拥有的:
double max = results.Max(kvp => kvp.Value);
return results.Where(kvp => kvp.Value == max).Select(kvp => kvp.Key).First();
但是,由于这似乎效率不高,所以我想知道是否有更好的方法可以做到这一点。
我正在尝试获取中的最大值的键Dictionary<string, double> results
。
这是我到目前为止所拥有的:
double max = results.Max(kvp => kvp.Value);
return results.Where(kvp => kvp.Value == max).Select(kvp => kvp.Key).First();
但是,由于这似乎效率不高,所以我想知道是否有更好的方法可以做到这一点。
.First().Key;
来获取密钥。
Answers:
我认为这是使用标准LINQ最易读的O(n)答案。
var max = results.Aggregate((l, r) => l.Value > r.Value ? l : r).Key;
编辑:CoffeeAddict的说明
Aggregate
是常用功能概念Fold的LINQ名称
它遍历集合的每个元素并应用您提供的任何功能。在这里,我提供的函数是一个比较函数,它返回较大的值。循环时,Aggregate
记住上次调用我的函数时的返回结果。它将其作为变量输入到我的比较函数中l
。该变量r
是当前选定的元素。
因此,在aggregate遍历整个集合之后,它将返回上一次调用比较函数的结果。然后我.Key
从中读取了成员,因为我知道它是字典条目
这是查看它的另一种方法[我不保证会编译;)]
var l = results[0];
for(int i=1; i<results.Count(); ++i)
{
var r = results[i];
if(r.Value > l.Value)
l = r;
}
var max = l.Key;
阅读各种建议后,我决定对它们进行基准测试并分享结果。
经过测试的代码:
// TEST 1
for (int i = 0; i < 999999; i++)
{
KeyValuePair<GameMove, int> bestMove1 = possibleMoves.First();
foreach (KeyValuePair<GameMove, int> move in possibleMoves)
{
if (move.Value > bestMove1.Value) bestMove1 = move;
}
}
// TEST 2
for (int i = 0; i < 999999; i++)
{
KeyValuePair<GameMove, int> bestMove2 = possibleMoves.Aggregate((a, b) => a.Value > b.Value ? a : b);
}
// TEST 3
for (int i = 0; i < 999999; i++)
{
KeyValuePair<GameMove, int> bestMove3 = (from move in possibleMoves orderby move.Value descending select move).First();
}
// TEST 4
for (int i = 0; i < 999999; i++)
{
KeyValuePair<GameMove, int> bestMove4 = possibleMoves.OrderByDescending(entry => entry.Value).First();
}
结果:
Average Seconds Test 1 = 2.6
Average Seconds Test 2 = 4.4
Average Seconds Test 3 = 11.2
Average Seconds Test 4 = 11.2
这仅仅是为了让他们对它们的相对性能有所了解。
如果优化“ foreach”最快,但是LINQ紧凑而灵活。
也许这不是LINQ的好用法。我使用LINQ解决方案对字典进行了2次完整扫描(1次获取最大值,然后进行另一次查找kvp以返回字符串。
您可以使用“老式” foreach进行1遍操作:
KeyValuePair<string, double> max = new KeyValuePair<string, double>();
foreach (var kvp in results)
{
if (kvp.Value > max.Value)
max = kvp;
}
return max.Key;
var max = default(KeyValuePair<string, double>);
您可以使用OrderBy(用于查找最小值)或OrderByDescending(用于最大值)对字典进行排序,然后获取第一个元素。当您需要查找第二个最大/最小元素时,它也有帮助
通过最大值获取字典键:
double min = results.OrderByDescending(x => x.Value).First().Key;
按最小值获取字典键:
double min = results.OrderBy(x => x.Value).First().Key;
通过第二个最大值获取字典键:
double min = results.OrderByDescending(x => x.Value).Skip(1).First().Key;
通过第二个最小值获取字典键:
double min = results.OrderBy(x => x.Value).Skip(1).First().Key;
OrderBy
执行了比实际需要更多的计算。
小扩展方法:
public static KeyValuePair<K, V> GetMaxValuePair<K,V>(this Dictionary<K, V> source)
where V : IComparable
{
KeyValuePair<K, V> maxPair = source.First();
foreach (KeyValuePair<K, V> pair in source)
{
if (pair.Value.CompareTo(maxPair.Value) > 0)
maxPair = pair;
}
return maxPair;
}
然后:
int keyOfMax = myDictionary.GetMaxValuePair().Key;
检查这些:
result.Where(x => x.Value == result.Values.Max())。Select(x => x.Key).ToList()
为了确保线程安全,如何使用Interlocked.Exchange并行执行此操作:)请记住,Interlocked.Exchange仅适用于引用类型。(即,结构或键值对(除非包装在类中)将不起作用)最大值。
这是我自己的代码中的一个示例:
//Parallel O(n) solution for finding max kvp in a dictionary...
ClassificationResult maxValue = new ClassificationResult(-1,-1,double.MinValue);
Parallel.ForEach(pTotals, pTotal =>
{
if(pTotal.Value > maxValue.score)
{
Interlocked.Exchange(ref maxValue, new
ClassificationResult(mhSet.sequenceId,pTotal.Key,pTotal.Value));
}
});
编辑(更新的代码,以避免上面可能出现的争用情况):
这是一个更健壮的模式,它还显示了并行选择最小值。我认为这解决了以下评论中提到的有关可能的比赛条件的担忧:
int minVal = int.MaxValue;
Parallel.ForEach(dictionary.Values, curVal =>
{
int oldVal = Volatile.Read(ref minVal);
//val can equal anything but the oldVal
int val = ~oldVal;
//Keep trying the atomic update until we are sure that either:
//1. CompareExchange successfully changed the value.
//2. Another thread has updated minVal with a smaller number than curVal.
// (in the case of #2, the update is no longer needed)
while (oldval > curVal && oldval != val)
{
val = oldval;
oldval = Interlocked.CompareExchange(ref minVal, curVal, oldval);
}
});
我的版本基于当前的Enumerable.Max实现,带有可选的比较器:
public static TSource MaxValue<TSource, TConversionResult>(this IEnumerable<TSource> source, Func<TSource, TConversionResult> function, IComparer<TConversionResult> comparer = null)
{
comparer = comparer ?? Comparer<TConversionResult>.Default;
if (source == null) throw new ArgumentNullException(nameof(source));
TSource max = default;
TConversionResult maxFx = default;
if ( (object)maxFx == null) //nullable stuff
{
foreach (var x in source)
{
var fx = function(x);
if (fx == null || (maxFx != null && comparer.Compare(fx, maxFx) <= 0)) continue;
maxFx = fx;
max = x;
}
return max;
}
//valuetypes
var notFirst = false;
foreach (var x in source)
{
var fx = function(x);
if (notFirst)
{
if (comparer.Compare(fx, maxFx) <= 0) continue;
maxFx = fx;
max = x;
}
else
{
maxFx = fx;
max = x;
notFirst = true;
}
}
if (notFirst)
return max;
throw new InvalidOperationException("Sequence contains no elements");
}
用法示例:
class Wrapper
{
public int Value { get; set; }
}
[TestMethod]
public void TestMaxValue()
{
var dictionary = new Dictionary<string, Wrapper>();
for (var i = 0; i < 19; i++)
{
dictionary[$"s:{i}"] = new Wrapper{Value = (i % 10) * 10 } ;
}
var m = dictionary.Keys.MaxValue(x => dictionary[x].Value);
Assert.AreEqual(m, "s:9");
}