0

我有一个Dictionary<Key, <Quality,Item>>正在跟踪质量和项目之间的关系。质量是一种对象类型,项目是一种对象类型,在其他地方我有有效质量和有效项目的列表。物品有一个固定的品质列表,总是不止一个。质量可以由任意数量的项目持有,包括 0,具体取决于程序的状态。

目前,项目对象还在列表中跟踪它们自己的质量,这是我解决此问题的失败策略之一。我不知道这是否有用,它现在肯定对我没有帮助,如果证明没用,可能会被淘汰。

我已经有一个 LINQ 自联接,它收集了成功共享至少一个质量的独特项目对。

var r = from KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_1
        in QualitiesToItems
        join KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_2
        in QualitiesToItems
        on virtQ2I_1.Value.Item1.name equals virtQ2I_2.Value.Item1.name
        where (virtQ2I_1.Value.Item2.name != virtQ2I_2.Value.Item2.name)
        select new List<Item>
        {
            virtQ2I_1.Value.Item2, 
            virtQ2I_2.Value.Item2
        };

之后我使用另一个字典来清理 <ItemA, ItemB> 被认为与 <ItemB, ItemA> 相同的小打嗝。

需要什么:每个三元组的唯一项目的列表,这些项目至少与三元组中的至少一个其他项目共享一个品质。毛茸茸的大并发症:三元组中的第三项不能共享现有的共享品质之一;它必须给这段关系带来一些新的东西。而且我需要从几百个项目的列表中快速获得结果——我现有的解决方案不满足最后一个要求。

例子:

  • ItemA 是毛茸茸的、金发的、四足的、训练有素的
  • ItemB 是毛茸茸的、罗文的、六足的、训练有素的
  • ItemC 是有羽毛的、蓝色的、两条腿的、训练有素的
  • ItemD 是 Scaled、Rowan、Slithers 和未经训练的

    • ItemA 和 ItemB 已经被选为有效的一对,具有 Furry 和 Trained 的品质。(B:D 当然是另一个有效的配对,A:C 和 B:C 也是如此)

    • ItemA、ItemB 和 ItemC 不构成有效的三元组,因为 A:B 已经训练过了,并且 ItemC 与 ItemA 或 ItemB 没有其他共同点;A:B:C 与 A:B 具有相同的 Qualities 列表,因此 C 被视为“多余”或“冗余”而被拒绝。

    • ItemA、ItemB 和 ItemD 构成一个有效的三元组,因为 ItemD 与 ItemB 在 Rowan 周围形成一对。A:B:D 的结果是 Furry、Rowan、Trained... 我需要 A:B:D 的组合进入我的返回结果列表。

从我获得我的双胞胎的方式到我需要以一种在合理的时间内处理数百件物品的方式获得我的三胞胎的方式,我遇到了问题。

当我编写一个方法来查找两个项目之间的共享品质并将其用于我的新 LINQ 查询时,我认为我非常聪明,但结果是......当用于超过一个分数左右的项目时非常慢,并且与将要运行的某些机器相比,我的计算机功能过于强大。

var r = from KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_1 
        in QualitiesToItems 
        join KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_2 
        in QualitiesToItems
        on virtQ2I_1.Value.Item1.name equals virtQ2I_2.Value.Item1.name
        join KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_3
        in QualitiesToItems
        on virtQ2I_2.Value.Item1.name equals virtQ2I_3.Value.Item1.name
        where (virtQ2I_1.Value.Item2.name != virtQ2I_2.Value.Item2.name &&
        virtQ2I_1.Value.Item2.name != virtQ2I_3.Value.Item2.name &&
        virtQ2I_2.Value.Item2.name != virtQ2I_3.Value.Item2.name &&
        Item.SharedQualities(this, new Item[2] { virtQ2I_1.Value.Item2, virtQ2I_2.Value.Item2 }).Count !=
        Item.SharedQualities(this, new Item[3] { virtQ2I_1.Value.Item2, virtQ2I_2.Value.Item2, virtQ2I_3.Value.Item2 }).Count)
        select new List<Item>
        {
            virtQ2I_1.Value.Item2, 
            virtQ2I_2.Value.Item2, 
            virtQ2I_3.Value.Item2
        };

所以:这行得通,但我不喜欢它。有没有办法用纯 LINQ 替换我的函数调用(和新项目数组)中间查询?必须有。

4

1 回答 1

0

对这个问题进行更多的抨击提供了一个解决方案,它在 LINQ 中完成了最糟糕的繁重工作,并且比我在原始帖子中尝试的性能要好得多。

//collect two-pair items
var result = from KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_1
                    in QualitiesToItems
         join KeyValuePair<int, Tuple<Quality, Item>> virtQ2I_2
                 in QualitiesToItems
         on virtQ2I_1.Value.Item1.name equals virtQ2I_2.Value.Item1.name
         where (virtQ2I_1.Value.Item2.name != virtQ2I_2.Value.Item2.name)
         select new List<Item> {
                    virtQ2I_1.Value.Item2, 
                    virtQ2I_2.Value.Item2
                    };
List<List<Item>> ItemsForSets = result.ToList();

// self-join raw two-pair item list to generate three-set items
result =    from List<Item> leftSide in ItemsForSets 
        join List<Item> rightSide in ItemsForSets
        on leftSide[1] equals rightSide[0]
        where (leftSide[0] != rightSide[1])
        select new List<Item> {
                    leftSide[0], 
                    leftSide[1],
                    rightSide[1]
                    };

ItemsForSets.AddRange(result.ToList());

// clean up results - preventing A:B and B:A from being considered unique,
//    and ensuring all third ingredients actually contribute to a relationship.
foreach (List<Item> items in ItemsForSets)
{
    List<Quality> sharedQualities = Item.SharedQualities(this, items.ToArray());
    sharedQualities.Sort();
    List<String> sortedItems = items.ConvertAll(item => item.name); // I need the string names elsewhere 
    // TODO: I should rewrite to work directly with Items and convert after confirming I actually need the item.
    sortedItems.Sort(); // first part of preventing A:B B:A problems
    if (!Sets.ContainsKey(String.Join(", ", sortedItems))) // Dictionary provides second part.
    {
        if (items.Count == 3)
        {
            List<Quality> leftPairQualities = Item.SharedQualities(this, items.GetRange(0, 2).ToArray());
            leftPairQualities.Sort();
            if (leftPairQualities.SequenceEqual(sharedQualities))
            { // if the third item does not add a new quality
                continue; // short circuit out to the next item
            }
        }
        // otherwise add to the list.
        Sets.Add(String.Join(", ", sortedItems), new Potion(items, sharedQualities));
    }
}

我可以做更多的清理工作,我可能会用另一个 LINQ 查询替换 foreach ,但这会消除大障碍并显着提高性能。

于 2012-12-25T02:42:04.433 回答