【问题标题】:Check if HUGE dictionary contains a string and get all elemets that match element检查 HUGE 字典是否包含字符串并获取与元素匹配的所有元素
【发布时间】:2020-02-08 01:07:05
【问题描述】:

我有两个庞大的字典,一个名为 DictHashesSource 有 2256001 行,另一个字典名为 DictHashesTarget 有 2061735 行。

Dictionary<int, string> DictHashesSource = new Dictionary<int, string>();
Dictionary<int, string> DictHashesTarget = new Dictionary<int, string>();

我想要做的是,对于 DictHashesSource 的每个元素,检索 DictHashesTarget 中匹配的所有元素,并以相反的方式执行完全相同的操作。 为此,我使用了如下所示的 LINQ:

IEnumerable<string> interceptedRowsSource = DictHashesSource.Values.Where(x => DictHashesTarget.Values.Contains(x)).ToList();
IEnumerable<string> interceptedRowsTarget = DictHashesTarget.Values.Where(x => DictHashesSource.Values.Contains(x)).ToList();

问题是,由于两个字典真的很大,每次操作都需要1个多小时,有什么办法可以降低这个算法的复杂度?

注意:我真的必须使用两个字典,因为我将不得不在进一步的操作中使用这些键。

另一个注意事项:相同的值在两个字典中没有相同的键

【问题讨论】:

  • 请提供更多信息。 DictHashesSource 定义为什么? DictHashesTarget 定义为什么?是否需要在其他操作之前实现它(.ToList())?
  • 每次调用 Values 的时间复杂度为 O(1),因此使用 Contains 的每个语句都是 O(n2*2)
  • dict1.Values.Intersect(dict2.Values);?
  • 字典不适用于这个,你需要像这样的树型搜索:github.com/gmamaladze/trienet
  • 字典是如何填充的?

标签: c# linq


【解决方案1】:

一种方法可能是制作一个反向字典。所以你有更稳定的结果。所以你的值变成了键,反之亦然。

        Dictionary<int, string> source = new Dictionary<int, string>();
        Dictionary<int, string> target = new Dictionary<int, string>();

        source.Add(1, "a");
        source.Add(2, "b");
        source.Add(3, "c");

        target.Add(4, "c");
        target.Add(5, "d");
        target.Add(6, "e");

        // Reverse index:
        var reverseSource = source.Reverse();
        var reverseTarget = target.Reverse();

        foreach (var sourceItem in reverseSource)
        {
            if (reverseTarget.ContainsKey(sourceItem.Key)){
                Console.WriteLine($"Source and Target contains {sourceItem.Key}");
            }
        }

具有以下反向字典功能。

    public static Dictionary<TValue, TKey> Reverse<TKey, TValue>(this IDictionary<TKey, TValue> source)
    {
        var dictionary = new Dictionary<TValue, TKey>();
        foreach (var entry in source)
        {
            if (!dictionary.ContainsKey(entry.Value))
                dictionary.Add(entry.Value, entry.Key);
        }
        return dictionary;
    }

如果需要,您可以将所有键添加为逗号分隔列表吗?

【讨论】:

  • 是的,这是有道理的,但是做这个比较仍然需要一个多小时
  • 我将添加代码 sn-p 来帮助 :) 你仍然需要运行 1 次。对于这些数据,它不应该花费很长时间。 2sek
  • 这是二次的。它不需要是二次的:你可以让它成为线性的。
  • 重做整个方法以使用反向字典。
  • 如何让它变得更好?
【解决方案2】:

您可以使用两个字典中的值创建 HashSet。

HashSet<string> HashesSourceSet;

HashSet<string> HashesTargetSet;

然后做这样的事情:

var result1 = HashesSourceSet.Where(x => HashesTargetSet.Contains(x)).ToList();
var result2 = HashesTargetSet.Where(x => HashesSourceSet.Contains(x)).ToList();

这会将复杂度降低到 O(n)

----------------- 更新 --------------------------------

正如您提到的,您需要计算哈希值,您可以执行以下操作:


Dictionary<string, int> HashesWithCount = new Dictionary<string, int>();

foreach (var x in DictHashesSource.Values)
{   
    HashesWithCount[x] = HashesWithCount.ContainsKey(x) ? (HashesWithCount [x] + 1) : 1;
}


现在您有了重复值的计数。

【讨论】:

  • 问题是我有一些重复值,据我所知HashSet不允许重复值吗?
  • 是的,hashset 只会保留不同的值。如果您需要它们的计数,您可以使用您的哈希值和它们的计数创建 Dictionary。在向字典添加哈希时,如果键已经存在,则增加值。
猜你喜欢
  • 2013-09-17
  • 2021-10-31
  • 2017-11-20
  • 2010-10-04
  • 1970-01-01
  • 2020-02-06
  • 2015-12-24
  • 2019-10-22
  • 1970-01-01
相关资源
最近更新 更多