13

有没有办法找到比这更有效/更快的最小值索引?

int minimumValueIndex = List.IndexOf(List.Min());
4

10 回答 10

10

List.IndexOf()是的,您可以通过构建自定义Min()扩展来消除开销。(真的,Enumerable.Min()应该有一个扩展,通过键选择原始元素而不是选择转换。这种疏忽在这种情况下特别痛苦。)

public static int IndexOfMin(this IList<int> self)
{
    if (self == null) {
        throw new ArgumentNullException("self");
    }

    if (self.Count == 0) {
        throw new ArgumentException("List is empty.", "self");
    }

    int min = self[0];
    int minIndex = 0;

    for (int i = 1; i < self.Count; ++i) {
        if (self[i] < min) {
            min = self[i];
            minIndex = i;
        }
    }

    return minIndex;
}
于 2013-05-01T17:47:23.533 回答
5

以我自己的经验,诸如 Array.Max() 和 Array.Min() 之类的 LINQ 聚合方法通常比手动 for 循环慢。因此,您可以考虑这样的替代方法:

int minima=0;
int mindex=0;

for(int i=0;i<List.Count;i++)
{
    if (List[i]<minima)
        {minima=List[i]; mindex=i;}
}

您始终可以使用 System.Diagnostics.StopWatch 在您的环境中测试这两种方法的速度。

于 2013-05-01T17:47:10.297 回答
2

@cdhowie 发布的答案存在问题,因为它假设 anIList<T>可以通过其索引器有效地访问特定项目。虽然对于数组 和 确实如此List[T],但绝对不能保证(以实现 的单链表为例Ilist<T>)。

如果我要以通用的 Linqy 方式执行此操作,我会执行以下操作:

public static IndexOfMinValue<T>( this IList<T> list ) where T:IComparable
{
  if ( list == null ) throw new ArgumentNullException("list") ;
  int? offset = null ;
  T    min    = default(T) ;

  int i = 0 ;
  foreach ( T item in list )
  {
    if ( !offset.HasValue || item.CompareTo(min) < 0 )
    {
       offset = i ;
       min    = item ;
    }
    ++i ;
  }

  if ( !offset.HasValue ) throw new ArgumentOutOfRangeException("list","list is empty") ;
  return offset.Value ;
}

或者,可以说更干净,因为我们在循环体中摆脱了无关的初始化和无关的比较:

public static int IndexOfMin<T>( this IList<T> list ) where T:IComparable
{
  if ( list == null ) throw new ArgumentNullException("list") ;

  IEnumerator<T> enumerator  = list.GetEnumerator() ;
  bool           isEmptyList = ! enumerator.MoveNext() ;

  if ( isEmptyList ) throw new ArgumentOutOfRangeException("list","list is empty") ;

  int minOffset = 0 ;
  T   minValue  = enumerator.Current ;
  for ( int i = 1 ; enumerator.MoveNext() ; ++i )
  {
    if ( enumerator.Current.CompareTo(minValue) >= 0 ) continue ;
    minValue  = enumerator.Current ;
    minOffset = i ;
  }

  return minOffset ;
}

您也可以使用库存的 LinqAggregate()重载,尽管它并不比蛮力方法更清洁或更简单(恕我直言,效率可能也较低):

IList<int> = GetSomeIntegers() ;

int minIndex = list.Aggregate( (Tuple<int,int,int>)null,
  ( acc , item ) => {
    int offset     = 0    ;
    int minValue   = item ;
    int minOffset  = 0    ;
    if ( acc != null )
    {
      offset    = acc.Item3 + 1 ;
      minValue  = item < acc.Item1 ? item   : acc.Item1 ;
      minOffset = item < acc.Item1 ? offset : acc.Item2 ;
    }
    return new Tuple<int, int, int>( minValue , minOffset , offset ) ;
  }).Item2 ;
于 2013-05-01T19:47:01.517 回答
2

目前 .NET 支持允许简单解决方案的值元组:

var index = List.Select((item, index) => (item, index)).Max().index;
于 2020-09-09T17:49:12.573 回答
1

如果可能,在将值放入列表时跟踪最小值/索引,因此您无需遍历完整列表。然后,如果将新值添加到列表中,请根据您保存的最小值检查它们,并根据需要更改新的最小值。

当然,这可能不适合您的情况。

于 2013-05-01T18:07:00.773 回答
1

我稍微改进了@cdhowie 的答案以使其更强大。如果有多个最小元素,则此方法将返回第一个。

public static T GetMin<T, TOrder>(this IEnumerable<T> self, Func<T, TOrder> orderFunc, 
                                  out int minIndex, IComparer<TOrder> cmp = null)
{
    if (self == null) throw new ArgumentNullException("self");            

    IEnumerator<T> selfEnumerator = self.GetEnumerator();
    if (!selfEnumerator.MoveNext()) throw new ArgumentException("List is empty.", "self");

    if (cmp == null) cmp = Comparer<TOrder>.Default;

    T min = selfEnumerator.Current;
    minIndex = 0;
    int intCount = 1;
    while (selfEnumerator.MoveNext ())
    {
        if (cmp.Compare(orderFunc(selfEnumerator.Current), orderFunc(min)) < 0)
        {
            min = selfEnumerator.Current;
            minIndex = intCount;
        }
        intCount++;
    }

    return min;
}
于 2015-01-14T11:02:56.097 回答
0

最小计算:在集合中查找Min值不能比 O(n) 更快,因此它可能没有比这更好的方法,只是代码风格不同。

查找步骤:根据您的问题,您可能会使用一些特殊的数据结构(例如二叉树、堆树等),这样您可以更快地找到索引。

使用诸如最小堆树之类的东西,您可以通过 O(1) 获得最小值,而无需使用一些特殊的添加、删除功能。

于 2013-05-01T17:43:06.820 回答
0

当然。只需编写自己的Min函数,而不是使用 LINQ,来跟踪当前最小值的索引。通过这样做,您可以避免需要根据它的最小值查找项目的索引。

于 2013-05-01T17:47:18.110 回答
0

微软似乎不想休息,所以他们实施了另一种方式来实现目标。

请参阅.NET 6.0 中的MaxBy()

它允许通过某个键的最大值来查找项目。

于 2022-01-02T20:33:25.893 回答
-2

我很懒所以我会使用:

List.Sort();/* sorts the values least to greatest */
List[0];/* first item has the least value */
于 2015-12-01T22:24:29.473 回答