2013-05-01 28 views
8

Có cách nào để tìm chỉ số giá trị tối thiểu hiệu quả hơn/nhanh hơn điều này không?Cách nhanh/hiệu quả để lấy chỉ mục giá trị tối thiểu trong Danh sách <T>?

int minimumValueIndex = List.IndexOf(List.Min()); 
+3

chắc. Viết một vòng lặp đơn giản để tìm hai giá trị: giá trị tối thiểu trong danh sách và chỉ mục mà tại đó bạn tìm thấy nó. Làm xong. Nó vẫn là thời gian tuyến tính. Bạn có lý do để tin rằng đây là nút cổ chai của bạn? –

Trả lời

8

Có, bạn có thể xóa phí trên List.IndexOf() bằng cách tạo tiện ích mở rộng tùy chỉnh Min(). (Thực sự, Enumerable.Min() nên có một phần mở rộng lựa chọn ban đầu yếu tố bằng phím thay vì chọn một chuyển đổi. Giám sát Điều này đặc biệt đau đớn trong những tình huống như thế này.)

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; 
} 
+0

Cảm ơn bạn đã mở rộng. Tôi tự hỏi tại sao không có phương pháp như vậy trong .NET. – Kamil

+0

@Kamil Đó là một trường hợp cạnh tranh. Tuy nhiên, nếu ['Min()'] (http://msdn.microsoft.com/en-us/library/bb548741.aspx) trả về 'TSource' thay vì' TResult' bạn có thể làm điều này với vanilla LINQ. – cdhowie

+0

Xin lỗi vì câu hỏi ngớ ngẩn, nhưng nếu tôi muốn sử dụng câu hỏi này với 'Danh sách 'và' Danh sách 'thì sao? Tôi cần hai phần mở rộng (một số quá tải), hoặc có một cách để tạo phần mở rộng sẽ làm việc với bất kỳ kiểu dữ liệu số nào? – Kamil

0

Min tính: Tìm giá trị Min trong một bộ sưu tập không thể được thực hiện nhanh hơn so với O (n) để nó có thể là không có cách nào tốt hơn nhưng chỉ là một khác nhau trong phong cách mã.

Tìm bước: tùy thuộc vào sự cố của bạn, bạn có thể sử dụng một số cấu trúc dữ liệu đặc biệt (như cây nhị phân, cây heap, ...) để bạn có thể tìm chỉ mục nhanh hơn.

Sử dụng thứ gì đó như cây min-heap bạn có thể nhận giá trị tối thiểu bằng O (1) trong một số chức năng Add, Remove đặc biệt.

+5

Cách tiếp cận này yêu cầu hai lần lặp lại. – dasblinkenlight

+1

Có. việc tìm kiếm chỉ mục có thể không hoàn toàn là quét, tuy nhiên trong trường hợp xấu nhất, nó có thể là O (n). –

+2

@HosseinNarimaniRad Vấn đề là mã trong OP sẽ luôn dài hơn ~ 2x so với mức cần thiết. Nếu bạn đã viết hàm Min của riêng bạn, nó có thể trả về chỉ mục ngay lập tức. – Servy

4

Theo kinh nghiệm của riêng tôi, các phương pháp tổng hợp LINQ như Array.Max() và Array.Min() thường chậm hơn hướng dẫn sử dụng cho vòng lặp. Vì vậy, bạn có thể xem xét một cái gì đó như thế này như một cách tiếp cận khác:

int minima=0; 
int mindex=0; 

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

Bạn luôn có thể kiểm tra tốc độ của cả hai phương pháp trên môi trường của bạn bằng cách sử dụng System.Diagnostics.StopWatch.

+0

Điều này không nhận được chỉ mục, nhưng giá trị tối thiểu thực tế. – juharr

+0

Tôi đã đọc sai phần đó. Thay đổi câu trả lời của tôi cho phù hợp. Nhưng nó vẫn là một vòng lặp đơn. –

0

Chắc chắn. Chỉ cần viết hàm Min của riêng bạn, thay vì sử dụng LINQ, theo dõi chỉ mục của giá trị tối thiểu hiện tại. Bằng cách đó, bạn có thể tránh phải tìm chỉ mục của mục dựa trên giá trị tối thiểu của nó.

1

Nếu có thể, theo dõi các giá trị min/index là các giá trị được đặt trong danh sách, vì vậy bạn không cần lặp qua danh sách đầy đủ. Sau đó, nếu các giá trị mới được thêm vào danh sách, hãy kiểm tra chúng dựa vào giá trị tối thiểu mà bạn đã lưu và thay đổi giá trị tối thiểu mới nếu cần.

Tất nhiên điều đó có thể không phù hợp với hoàn cảnh của bạn.

2

Có vấn đề với câu trả lời được đăng bởi @cdhowie ở chỗ nó giả định rằng một IList<T> có thể truy cập hiệu quả một mục cụ thể thông qua chỉ mục của nó. Trong khi nó đúng với các mảng và List[T], nó được bảo đảm theo cách không đảm bảo (lấy ví dụ, một danh sách được liên kết đơn lẻ thực hiện Ilist<T>).

Nếu tôi đã đi để làm điều này trong một, LINQy cách chung chung, tôi muốn làm một cái gì đó như:

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 ; 
} 

Hoặc, cho là sạch hơn, vì chúng ta thoát khỏi khởi tạo không liên quan và không liên quan một so sánh trong thân của vòng lặp:

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 ; 
} 

Bạn cũng có thể sử dụng chứng khoán LINQ Aggregate() quá tải, mặc dù nó không có bụi hoặc đơn giản hơn so với phương pháp brute force (có thể là kém hiệu quả, quá, 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 ; 
1

Tôi đã cải thiện @ cdhowie answer một chút để làm cho nó mạnh mẽ hơn. Nếu có nhiều hơn một phần tử tối thiểu, thì phương thức này sẽ trả về phần tử đầu tiên.

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; 
} 
+0

Và bạn sử dụng nó như thế nào? Tôi không hiểu tại sao tôi cần một chức năng so sánh và đặt hàng? – weston

+0

Ví dụ, chúng ta có một danh sách 'Người'. 'Person' có thuộc tính' Location' và 'Location' bao gồm' X' và 'Y'. Bây giờ chúng tôi muốn có được người có tối thiểu 'Y', và nếu có nhiều hơn một người có cùng mức tối thiểu' Y', thì người có tối thiểu 'X' là những gì chúng tôi muốn. Trong trường hợp này, chúng ta cần hàm order để xác định 'Location' là thuộc tính mà chúng ta muốn so sánh, và chúng ta cần so sánh để nhận ra sự so sánh này. Lời giải thích của tôi có hợp lý không? – pengdlzn

+0

Không, tôi không hiểu tại sao điều đó không thể thực hiện trong một 'ICompare'. – weston

-1

tôi là lười biếng vì vậy tôi sẽ sử dụng:

List.Sort();/* sorts the values least to greatest */ 
List[0];/* first item has the least value */ 
+0

Tại sao điều này sai? nó được giá trị tối giản – SSpoke

+1

Nó không phải là nó sai, nhưng để có được giá trị tối thiểu từ danh sách, hầu hết bạn phải làm là quét danh sách n mục n lần. Điều đó có độ phức tạp O (n). Nhưng phân loại có độ phức tạp O (n log n), vì vậy nó chậm hơn. Và cuối cùng, nếu bạn không thể sắp xếp mảng - thì bạn phải tạo một bản sao của một mảng mới và sắp xếp nó, điều này làm tăng thêm chi phí. – zumalifeguard

Các vấn đề liên quan