簡體   English   中英

將大型整數列表與較小的整數列表進行比較的最有效方法是什么?

[英]What is the Most Efficient way to compare large List of integers to smaller List of integers?

目前我有一個100萬integerslist ,我檢查每個integer與2000 integer s的黑名單。 這大約需要2分鍾。

for(int i = 0; i< MillionIntegerList.Length ; i++)
{
    for(int blacklisted = 0; blacklisted < TwoThousandIntegerList.Length ; blacklisted++)
        if(i==blacklisted)
            i = 0; //Zero is a sentinel value 
}

這樣就可以完成2,000,000,000次迭代(循環)。 有沒有更好的方式我沒有看到? 謝謝

現在有三個選項 - 前兩個更通用,因為它們不依賴於MillionIntegerList的排序(最初未指定)。 在已經對大列表進行排序的情況下,第三個是優選的。

選項1

是的,使用LINQ肯定有更好的方法:

var common = MillionIntegerList.Intersect(TwoThousandIntegerList).ToList();

這將在內部使用通過TwoThousandIntegerList構建的HashSet<int> ,然后查找其中的MillionIntegerList每個元素 - 這TwoThousandIntegerList每次遍歷整個TwoThousandIntegerList更有效。

如果您只想要非黑名單的,您需要:

var valid = MillionIntegerList.Except(TwoThousandIntegerList).ToList();

請注意,如果您只需要迭代結果一次,則應該刪除ToList調用 - 我已將其包含在內以實現結果,因此可以便宜地多次檢查它們。 如果你只是迭代, IntersectExcept的返回值將只是流式傳輸結果,使其在內存使用方面更便宜。

選項2

如果您不想依賴LINQ to Objects的實現細節,但仍需要基於散列的方法:

var hashSet = new HashSet<int>(TwoThousandIntegerList);
hashSet.IntersectWith(MillionIntegerList);
// Now use hashSet

選項3

使用大型列表排序這一事實的方法肯定是有用的。

假設您不介意首先排序列入黑名單的列表,您可以編寫像這樣的流式(和通用)實現(未經測試):

// Note: to use this, you'd need to make sure that *both* sequences are sorted.
// You could either sort TwoThousandIntegerList in place, or use LINQ's OrderBy
// method.

public IEnumerable<T> SortedIntersect<T>(this IEnumerable<T> first,
    IEnumerable<T> second) where T : IComparable<T>
{
    using (var firstIterator = first.GetEnumerator())
    {
        if (!firstIterator.MoveNext())
        {
            yield break;
        }

        using (var secondIterator = second.GetEnumerator())
        {
            if (!secondIterator.MoveNext())
            {
                yield break;
            }
            T firstValue = firstIterator.Current;
            T secondValue = secondIterator.Current;

            while (true)
            {
                int comparison = firstValue.CompareTo(secondValue);
                if (comparison == 0) // firstValue == secondValue
                {
                    yield return firstValue;
                }
                else if (comparison < 0) // firstValue < secondValue
                {
                    if (!firstIterator.MoveNext())
                    {
                        yield break;
                    }
                    firstValue = firstIterator.Current;
                }
                else // firstValue > secondValue
                {
                    if (!secondIterator.MoveNext())
                    {
                        yield break;
                    }
                    secondValue = secondIterator.Current;
                }  
            }                
        }
    }
}

(如果你想要的話,可以選擇IComparer<T> ,而不是依賴於T的可比性。)

由於大型列表已排序。 您可以通過排序小列表(非常快)然后進行線性合並來獲得最佳結果。 您只需要查看大(和小)列表中的每個項目一次,並且不需要在后台創建Hashtable。

請參閱MergeSort的合並功能部分,了解如何執行此操作。

在我看來,你需要的是Enumerable.Except方法(IEnumerable,IEnumerable)

點擊這里http://msdn.microsoft.com/en-us/library/bb300779.aspx

您的方法需要O(n * n)時間。 考慮這些優化:

  • 1)

    如果你的整數不是太大,你可以使用bool數組(例如,如果最大可能的整數是1000000,則使用bool [] b = new bool [1000000])。 現在要將數字K添加到黑名單,請使用b [K] = true。 檢查是微不足道的。 這適用於O(n)。 您也可以使用BitArray

  • 2)

    整數可以足夠大。 使用二叉搜索樹存儲黑名單(例如SortedSet)。 它有O(logN)插入和檢索時間。 所以它總是O(N * logN)。 語法與List(Add(int K),Contains(int K))的語法相同,忽略重復項

我認為最好的解決方案是使用Bloom過濾器 ,然后Bloom過濾器說一個元素可能在黑名單中,只檢查是否為誤報(如果黑名單可以在O(Log(n))中完成排序)。 這個解決方案是節省時間的,並且幾乎不使用額外的空間,這使得它比使用hashset要好得多。

這是Google用於Chrome中黑名單的解決方案。

如何在較長的列表上進行二進制搜索,因為它已經排序。

foreach(integer blacklisted in TwoThousandIntegerList)
{
    integer i  = MillionIntegerList.binarySearch(blacklisted)
    if(i==blacklisted){
          //Do your stuff
    } 
}

此解決方案僅花費O(m log n)時間,其中m是小列表的大小,n是較長列表的大小。 警告:此解決方案假定MillionIntegerList沒有重復值。

如果不是這種情況,那么你可以迭代重復,因為它們必須位於一個連續的塊中。 為此,我將假設MillionInterList是一個記錄列表,每個記錄都有一個value和一個index

foreach(integer blacklisted in TwoThousandIntegerList)
{
    integer index = MillionIntegerList.binarySearch(blacklisted)
    //Find the index of the first occurrence of blacklisted value
    while(index > 0 && MillionIntegerList[index - 1].value == blacklisted){
          --index;
    }
    while(MillionIntegerList[index].value == blacklisted){
          //Do your stuff
          ++index;
    } 
}

該解決方案花費O(m log n + mk) ,其中kMillionInterList找到的每個黑名單整數的平均重復數。

使用HashSet作為阻止列表。

foreach(integer i in MillionIntegerList)
{
        //check if blockedlist contains i
        //do what ever you like. 
}

使用List的Except方法。 這會奏效

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM