【问题标题】:Get index of List.Max?获取 List.Max 的索引?
【发布时间】:2019-04-17 21:39:08
【问题描述】:

我有清单

List<double> nums = new List<double> {0.2, 5.0, 12.0};

我正在尝试找到element-element^2 最大的元素。我可以跑

nums.Max(num => num - Math.Pow(num, 2))

要找到最大值,但是在评估num - Math.Pow(num, 2) 时,我如何才能获得标识为产生最大值的该元素的索引? IndexOf 不起作用,因为Max 返回的值不是应用时产生最大值的实际元素的值,而是整个表达式的值。

我查看了Obtain the Index of the Maximum Element,但在它返回实际元素并因此IndexOf 有效的情况下,这似乎涵盖了Max。我只能想象有一种简单的方法可以解决我的问题,因为Max 无论如何都会迭代列表。

【问题讨论】:

标签: c# list max


【解决方案1】:

有时使用一个好的旧循环更容易做到这一点

double maxValue = Double.MinValue;
int indexOfMax = -1;
for (int i = 0; i < nums.Count; i++) {
    double x = nums[i];
    double y = x - x * x;
    if (y > maxValue) {
        maxValue = y;
        indexOfMax = i;
    }
}
double element = nums[indexOfMax];

【讨论】:

  • 但与Max 代码涵盖了这么多问题的事实相比,这太冗长了:/
  • 我的意思是,Max 确实已经迭代了整个列表并找到了应用表达式的最大值 - 然后你只需要获取值最大的元素的索引......
  • 它的编写并不比使用有效的 LINQ 解决方案多,更容易理解并且可能更快。但这是一个品味问题。您的 Max 解决方案更短但不起作用。
【解决方案2】:

在一个 Linq 查询中(但不保证具有最佳性能)

List<double> nums = new List<double> { 0.2, 5.0, 12.0 };
var index = nums.Select((n, i) => new { Result = n - n * n, Index = i })
                .Aggregate(new { Result = Double.MinValue, Index = -1 }, (a, b) => (a.Result > b.Result) ? a : b)
                .Index;

仅在列表的一次解析中执行此操作的更有效方法是创建自己的 .Max() 扩展,然后在列表中调用它。

public static int MaxIndexByCustomRule(this IEnumerable<double> sequence)
    {
        int index = 0;
        int maxIndex = -1;
        double maxResult = double.MinValue;
        foreach (var value in sequence)
        {
            var tempResult = value - value * value;

            if (tempResult > maxResult)
            {
                maxResult = tempResult;
                maxIndex = index;
            }

            index++;
        }
        return maxIndex;
    }

呼叫:

List<double> nums = new List<double> { 0.2, 5.0, 12.0 };
nums.MaxIndexByCustomRule();

【讨论】:

    【解决方案3】:

    您可以使用 Linq。选择元素及其索引并计算每个元素的值。然后排序(在您的情况下按降序排列)并取顶部元素。与代码相同的文本:

    List<double> nums = new List<double> { 0.2, 5.0, 12.0 };
    
    var index = nums.Select((d, i) => new {index = i, value = d - d * d})
        .OrderByDescending(x => x.value)
        .Take(1)
        .Select(x => (int?) x.index)
        .FirstOrDefault();
    
    if (index == null)
        Console.WriteLine("nums is empty");
    else
        Console.WriteLine($"index is {index}");
    

    【讨论】:

    • 不错的解决方案@Aleks
    • 这本质上是低效的。对整个列表进行排序以选择一个元素。
    • @TheodorZoulias 据我所知,版本 >= 4 之后的 .NET 如果在 OrderBy 之后有 Take,则使用 quickselect 而不是完整排序
    • @Aleks Andreev 我检查了source code 并进行了一些性能测试,但我无法确认此优化(.NET Framework 4.7.2)。你有什么参考吗?
    • @TheodorZoulias 我的错。此优化是在 .NetCore 中引入的,不适用于常规 .NET 框架。源代码为here
    【解决方案4】:
        private static int index = -1;
        private static double value = Double.MinValue;
        [TestMethod]
        public void MaxTest()
        {
    
            List<double> nums = new List<double> { 0.2, 5.0, 12.0 };
            var x = nums.Max(num => Transform(num));
        }
    
    
        public double Transform(double n)
        {
            var r = n - Math.Pow(n, 2);
            if (r > value)
            {
                value = r;
                index++;
            }
    
            return r;
        }
    

    如果您打算捕获最大元素的索引,我建议您通过使用传统循环来使用本文中的第一个解决方案。如果您仍想通过委托使用 max 函数,可以执行以下操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-01
      • 2011-10-08
      • 2011-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多