![](/img/trans.png)
[英]Most efficient way to randomly "sort" (Shuffle) a list of integers in C#
[英]What is the Most Efficient way to compare large List of integers to smaller List of integers?
目前我有一個100萬integers
的list
,我檢查每個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
調用 - 我已將其包含在內以實現結果,因此可以便宜地多次檢查它們。 如果你只是迭代, Intersect
或Except
的返回值將只是流式傳輸結果,使其在內存使用方面更便宜。
選項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)
您的方法需要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) ,其中k是MillionInterList
找到的每個黑名單整數的平均重復數。
使用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.