List1: {"123456", "432978", "321675", …} // containing 100,000 members
List2: {"7674543897", "1234568897", "8899776644",…} // containing 500,000 members
我想在List2中提取其前6位数字来自List1成员的所有项目,因此字符串“1234568897”有效,因为它的前6位数字来自List1的第一项。 这是最快的方式吗?
foreach(string id in List1)
{
string result = List2.FirstOrDefault(x => x.Contains(id));
if(result!=null)
{
//some works here
}
}
这适用于少于1000的组,但当List2项目增长时,这需要太长时间
答案 0 :(得分:3)
您可以使用Enumerable.Join
{/ 3>} quite efficient
var match = from str1 in List1
join str2 in List2
on str1 equals (str2.Length < 6 ? str2 : str2.Substring(0, 6))
select str2;
修改强>
由于@Oleksandr Pshenychnyy认为使用如此大的集合会非常慢,这里是一个demo1,其中list1中有100000个随机字符串,list2中有500000个字符串,其范围与问题相同。它在ideone上执行600毫秒:
答案 1 :(得分:0)
这在很大程度上取决于您运行的硬件。 您可能正在进行过早优化。它可能足够快,只是粗暴强迫它。 500,000 * 100,000是您的最大比较数(即,如果没有匹配)在合理的规格台式PC上真的不会花很长时间。
如果您发现这个目的太慢,那么您可以使用一些技巧来提高性能:
并行化。这只会对多核硬件产生很大的好处。从本质上讲,您需要一个调度程序类,它将List2中的数字提供给执行搜索List1中匹配项的线程。请参阅Task Parellel Library。
通过更聪明地减少比较次数。对您的集合进行一些预分析,以改善其比较步骤的特征。例如将List1中的项放入“桶”列表中,其中每个桶包含具有相同前2个数字的所有序列。例如bucket 1将包含那些以“00”,bucket 2“01”等开头的内容。当你进行实际比较时,你只需要比较少量的字符串。在您的示例中,对于“1234568897”,我们将检查存储桶为“12”,然后我们知道我们只需要对该存储桶中的字符串进行完整的字符串比较。
这种预处理实际上可能会使事情变慢,因此请确保您对代码进行分析以确定。
答案 2 :(得分:0)
实现所需内容的最有效方法是Aho-Corasick的算法 - 如果需要动态处理List 2的新元素。它基于前缀树,这是字符串搜索算法中常用的技术。那些算法会给你复杂的O(所有字符串长度之和)
另一个选项是Knuth-Morris-Pratt算法,它会给你相同的复杂性,但最初使用单个字符串进行搜索。
但如果你对O((list2.Count*log(list2.Count) + list1.Count*log(list1.Count))*AverageStrLength)
没问题,我可以提出我的简单实现:
对两个列表排序。然后同时进行并选择匹配。
更新:当您已对列表进行排序时,此实现很好。然后它在大约100ms内执行。但是排序本身需要3.5秒,因此实现并不像我预期的那样好。至于简单的实现,Tim的解决方案更快。
list1.Sort();
list2.Sort();
var result = new List<string>();
for(int i=0, j=0; i<list1.Count; i++)
{
var l1Val = list1[i];
for (; j < list2.Count && string.CompareOrdinal(l1Val, list2[j]) > 0; j++) ;
for (; j < list2.Count && list2[j].StartsWith(l1Val); j++)
{
result.Add(list2[j]);
}
}
这是我可以提出的最简单有效的实施方案。