0

Dictionary<Key, <Quality,Item>>品質とアイテムの関係を追跡している があります。Qualities はオブジェクト タイプで、Items はオブジェクト タイプです。その他の場所には、有効な Quality と有効な Items のリストがあります。アイテムには、常に複数の品質の固定リストがあります。プログラムの状態に応じて、0 を含む任意の数の品質を保持できます。

現在、これを解決するための私の失敗した戦略の 1 つとして、Item オブジェクトはリスト内の独自の品質も追跡します。これが役立つかどうかはわかりませんが、今のところ役に立たないことは確かであり、役に立たないことが判明した場合はおそらく取り除かれます。

少なくとも 1 つの品質を正常に共有する一意のアイテムのペアを収集する 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> と同じと見なされる小さなげっぷをクリーンアップします。

必要なもの:トリプレット内の少なくとも 1 つの他のアイテムと少なくとも 1 つの品質を共有する固有のアイテムの各トリプレットのリスト。毛むくじゃらの複雑な問題:トリプルの 3 番目の項目は、既存の共有品質の 1 つだけを共有することはできません。それは関係に何か新しいものをもたらさなければなりません。そして、数百の項目のリストからすぐに結果を取得する必要があります。既存のソリューションは、この最後の要件を満たしていません。

例:

  • ItemA は毛むくじゃらで、金髪で、四本足で、訓練を受けています
  • アイテムBは毛皮、ナナカマド、6本足、訓練済み
  • アイテム C は、羽毛、青、2 本足、訓練済みです。
  • ItemD は Scaled、Rowan、Slithers、および Untrained です

    • ItemA と ItemB はすでに有効なペアとして選択されており、Furry と Trained の品質を共有しています。(もちろん、B:D は別の有効なペアであり、A:C と B:C も同様です)

    • ItemA、ItemB、および ItemC は有効なトリプレットを作成しません。A:B は既にトレーニング済みであり、ItemC は ItemA または ItemB と他に共通点がないためです。A:B:C は A:B と同じ品質リストを持っているため、C は「過剰」または「重複」として拒否されます。

    • ItemA、ItemB、および ItemD は、ItemD が Rowan と ItemB の周りにペアを形成するため、有効なトリプレットを作成します。A:B:D の結果は Furry、Rowan、Trained です。返された結果のリストに入れるには、A:B:D の組み合わせが必要です。

ペアを取得する方法から、適切な時間内に数百のアイテムで機能する方法でトリプレットを取得する必要がある方法まで、問題が発生しています。

2 つのアイテム間で共有される品質を検索するメソッドを作成し、それを新しい LINQ クエリで使用したとき、私は非常に賢いと思っていましたが、結果は... 10 点以上のアイテムで使用すると非常に遅くなり、私のコンピューターは、これが実行されるいくつかのマシンと比較して圧倒的です.

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));
    }
}

より多くのクリーンアップを行うことができ、おそらく foreach を別の LINQ クエリに置き換えることができますが、それによって大きな障害が解消され、パフォーマンスが大幅に向上します。

于 2012-12-25T02:42:04.433 に答える