分类排序优化

时间:2015-12-28 20:10:13

标签: c# algorithm linq sorting parallel-processing

问题:将项目(T)排序到存储桶(ConcurrentBag)的最佳方法是什么?

好的,所以我还没有参加过算法课程,所以我不确定我遇到的问题的最佳方法。

前提条件:

  • 每个存储桶都有一个唯一的标识符(在每个sBucket中)。
  • 每个sBucket都有一个唯一的标识符。
  • 每个项目都有唯一的标识符。
  • 每个项目都有一个与其对应的属性(bucketId) 属于。
  • 每个项目都有一个与之对应的属性(sBucketId) superBucket它属于。
  • Bucket和sBucket id是独一无二的。
  • 我有一个ConcurrentBag项目,我希望将它们分类 桶中。
  • 有几百个项目。
  • 有几十个桶。
  • 有3个超级水桶包含水桶。
  • 每个超级桶都包含相同的桶,但不同 桶内的物品。

我目前正通过项目集合上的Parallel.foreach循环使用暴力来使用linq将项目的bucketId与每个桶进行比较。这是非常缓慢和麻烦的,所以我想找到一个更好的方法。

我已经考虑过根据他们的superBucket然后Bucket对项目进行排序,然后遍历每个superbucket->桶来插入项目。这应该是我走的路吗?

感谢您提供的任何帮助。

当前代码示例

ConcurrentBag<Item> items ...
List<SuperBuckets> ListOfSuperBuckets ...


Parallel.ForEach(items, item =>
{
   ListOfSuperBuckets
       .Where(sBucket => sBucket.id == item.sBucketId)
       .First()
       .buckets
       .Where(bucket => bucket.id == item.bucketId)
       .First()
       .items
       .Add(item);
});

2 个答案:

答案 0 :(得分:0)

我不会为此使用并行性,但有很多选项。

var groupedBySBucket = ListOfSuperBuckets
    .GroupJoin(items, a => a.id, b => b.sBucketId, (a,b) => new
        {
            sBucket = a,
            buckets = a.buckets
                .GroupJoin(b, c => c.id, x => x.bucketId, (c, x) => new
                    {
                        bucket = c,
                        items = x
                    });
        });

foreach (var g in groupedBySBucket)
{
    // We benefit here from that the collection types are passed by reference.

    foreach (var b in g.buckets)
    {
        b.bucket.AddRange(b.items);
    }
}

或者,如果你的代码太多,这是可以比较的。

var groupedByBucket = ListOfSuperBuckets
    .SelectMany(c => c.buckets, (a,b) => new { sBucketId = a.id, bucket = b })
    .GroupJoin(items, a => new { a.sBucketId, bucketId = a.bucket.id }, b => new { b.sBucketId, b.bucketId }, (a, b) => new
            {
                bucket = a.bucket,
                items = b
            }));

foreach (var g in groupedByBucket)
{
    // We benefit here from that the collection types are passed by reference.

    g.bucket.AddRange(b.items);
}

这也假设ListOfSuperBuckets是给定的。如果这只是您实现的工件,那么即使是更简单的方法。这构建了列表。

当然要注意,因为这些是不同的 - 这个没有任何数据的空桶,但第一个实现可以。我们也在创建新的存储桶,第一个实现没有;好的,如果我们需要的话,如果你已经在其他地方创建过它们那么糟糕当然,第一个可以轻松修改以创建它们。

var ListOfSuperBuckets = items
    .GroupBy(c => new { c.bucketId, c.sBucketId })
    .GroupBy(c => c.Key.sBucketId)
    .Select(c => new SuperBucket
        {
            id = c.Key,
            buckets = c.Select(b => new Bucket
                {
                    id = b.Key.bucketId,
                    items = b.ToList()
                }).ToList()
        })
    .ToList();

对于它的价值,所有这些ToList电话都是为了保留我认为你拥有的合同。如果您不需要它们,您可以通过将它们关闭而从LINQ的延迟执行中受益。这真的是你如何使用代码的问题,但值得考虑。

答案 1 :(得分:0)

您应该使用Dictionary,这样您就可以按ID查找存储桶和SuperBuckets,而不是搜索它们。

SuperBucket应该有一个Dictionary<id_type,Bucket>可以用来按ID查找存储桶,并且应该将SuperBuckets保持在Dictionary<id_type,SuperBucket>。 (id_type是您的ID类型 - 可能是字符串或整数,但我无法从您的代码中说出来)

如果您不想修改现有的类,请构建Dictionary<id_type, Dictionary<id_type, Bucket>>并使用它。