有没有办法找到比这更有效/更快的最小价值指数?
int minimumValueIndex = List.IndexOf(List.Min());
答案 0 :(得分:8)
是的,您可以通过构建自定义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;
}
答案 1 :(得分:4)
根据我自己的经验,LINQ聚合方法(如Array.Max()和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.Diagnostics.StopWatch测试环境中两种方法的速度。
答案 2 :(得分:2)
@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()
重载,虽然它不比蛮力方法更清晰或更简单(可能效率也不高,恕我直言):
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 ;
答案 3 :(得分:1)
如果可能,跟踪最小值/索引,因为值已放入列表中,因此您无需遍历完整列表。然后,如果将新值添加到列表中,请根据您保存的最小值检查它们,并根据需要更改新的最小值。
当然,这可能不适合您的情况。
答案 4 :(得分:1)
我稍微改进了@ cdhowie的answer以使其更强大。如果有多个最小元素,则此方法将返回第一个元素。
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;
}
答案 5 :(得分:1)
当前.NET支持值元组,该值元组允许简单的解决方案:
var index = List.Select((item, index) => (item, index)).Max().index;
答案 6 :(得分:0)
最小计算:查找集合中的Min
值不能比O(n)更快完成,所以它可能没有比这更好的方法,但只是代码风格的不同之处
查找步骤:根据您的问题,您可以使用一些特殊的数据结构(例如二叉树,堆树等),这样您就可以更快地找到索引。
使用像min-heap tree这样的东西你可以用O(1)获得最小值,而不需要使用一些特殊的Add,Remove函数。
答案 7 :(得分:0)
不确定。只需编写自己的Min
函数,而不是使用LINQ,它跟踪当前最小值的索引。通过这样做,您可以避免需要根据项目的最小值找到项目的索引。
答案 8 :(得分:-1)
我很懒,所以我会用:
List.Sort();/* sorts the values least to greatest */
List[0];/* first item has the least value */