Быстрый / эффективный способ получить индекс минимального значения в списке?
есть ли способ найти индекс минимального значения более эффективным / быстрее, чем это?
int minimumValueIndex = List.IndexOf(List.Min());
8 ответов
по моему собственному опыту методы агрегации LINQ, такие как Array.Макс() и Array.Min () обычно медленнее, чем руководство для цикла. Таким образом, вы можете рассмотреть что-то вроде этого в качестве альтернативного подхода:
int minima=0;
int mindex=0;
for(int i=0;i<List.Count;i++)
{
if (List[i]<minima)
{minima=List[i]; mindex=i;}
}
вы всегда можете проверить скорость обоих подходов в своей среде с помощью System.Диагностика.секундомер.
есть проблема с ответом, опубликованным @cdhowie в том, что он предполагает, что IList<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 ;
}
вы также можете использовать запас Linq Aggregate()
перегрузка, хотя это не чище или проще, чем метод грубой силы (вероятно, менее эффективный, тоже IMHO):
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 ;
Если возможно, следите за минимальным значением / индексом, поскольку значения помещаются в список, поэтому вам не нужно перебирать полный список. Затем, если в список добавляются новые значения, сверьте их с сохраненным значением min и при необходимости измените новое значение min.
конечно, это может быть не подходит для вашей ситуации.
я улучшил @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;
}
конечно. Просто напишите свой собственный Min
функции, вместо использования LINQ, которая отслеживает индекс текущего минимального значения. Делая это, вы можете избежать необходимости поиска индекса элемента на основе его минимального значения.
Мин Расчет: найти Min
значение в коллекции не может быть сделано быстрее, чем O (n), поэтому это может быть не лучше, чем это, но просто отличается в стиле Кода.
Найти Шаг: в зависимости от вашей проблемы вы можете использовать некоторую специальную структуру данных (например, двоичное дерево, дерево кучи,...) таким образом, вы можете найти индекс быстрее.
Используя что-то вроде дерева мин-кучи, вы можете получить минимальное значение с O (1) за счет некоторые специальные функции добавления, удаления.
я ленив, поэтому я бы использовал:
List.Sort();/* sorts the values least to greatest */
List[0];/* first item has the least value */