4

2 つのオブジェクトを表示するページがあり、ユーザーはそのうちの 1 つを選択します。好みと組み合わせを MSSQL データベースに記録し、最終的に次のようなデータを保存します。

UserId=1, BetterObjectId=1, WorseObjectId=2

ここで、オブジェクトの組み合わせ (1,2 / 2,1) を二度と表示したくありません。

では、ランダムな組み合わせを生成して、以前に表示した組み合わせを除外してユーザーに表示するにはどうすればよいでしょうか?

これは本当に簡単な質問のように思えますが、ほとんどのプログラマーと同じように、私は睡眠とコーヒーが不足しているので、あなたの助けに感謝します:-)

非常に単純なアプローチは次のようなものです (この関数へのすべての呼び出しは、ユーザーが既に nCr (n はアイテム数、r は 2) と同じ回数評価しているかどうかを確認するためにチェックでラップする必要があります):

public List<Item> GetTwoRandomItems(int userId)
{
    Item i = null, i2 = null;
    List<Item> r = null;

    while (i == null || i2 == null)
    {
        r = GetTwoRandomItemsRaw();
        i = r[0];
        i2 = r[1];
        if (GetRating(i.Id, i2.Id, userId) != null) /* Checks if viewed */
        {
            i = null;
            i2 = null;
        }
    }
    return r;
}

private List<Item> GetTwoRandomItemsRaw()
{
    return Items.ToList().OrderBy(i => Guid.NewGuid()).Take(2).ToList();
}

編集

一部の SQL を使用して、完全ではないすべてのアイテムのリストを生成できます (つまり、ユーザーが見たことのないアイテムを含む組み合わせがあります) が、特に役立つとは思いません。

2 つのランダムな項目を選択する前に、すべての可能な組み合わせを生成し、既に表示されているものを削除することも想像できますが、これは別のひどい解決策です。

1 つの可能性 (大きな n に対してメモリを大量に消費する) は、考えられるすべての組み合わせを生成し、combinationId を評価に格納することです。次に、組み合わせの対称関係を反映するためにいくつかの変更を加えて、すべての組み合わせの SELECT を行うことができます。

4

4 に答える 4

1
Table Item: ItemId
Table Rating: UserId, ItemId1, ItemId2, WinnerId

評価テーブルでItemId1<ItemId2が必要な場合は、評価テーブルを1回だけ確認する必要があります。

var pair = db.Items.Join(db.Items,
  i1 => i1.ItemId,
  i2 => i2.ItemId,
  (i1, i2) => new {i1, i2}
)  //produce all pairs
.Where(x => x.i1.ItemId < x.i2.ItemId) //filter diagonal to unique pairs
.Where(x => 
  !db.Ratings
  .Where(r => r.UserId == userId
    && r.ItemId1 == x.i1.ItemId
    && r.ItemId2 == x.i2.ItemId)
  .Any() //not any ratings for this user and pair
)
.OrderBy(x => db.GetNewId()) //in-database random ordering
.First();  // just give me the first one

return new List<Item>() {pair.i1, pair.i2 };

これは、「ランダム」をデータベースに翻訳することについてのブログです。

于 2009-03-03T00:15:05.513 に答える
1

1つの解決策はこれです:

SELECT TOP 1 i.id item1, i2.id item2 from item i, item i2 
WHERE i.id <> i2.id 
AND (SELECT COUNT(*) FROM Rating WHERE userId=@userId AND FK_ItemBetter=i.id AND FK_ItemWorse=i2.id) = 0
AND (SELECT COUNT(*) FROM Rating WHERE userId=@userId AND FK_ItemBetter=i2.id AND FK_ItemWorse=i.id) = 0
ORDER BY NEWID()

複数のFROMテーブルを並べるだけのクロスジョインの方法は、以前は知りませんでした。

于 2009-03-02T05:32:01.003 に答える
0

利用可能なアイテムのリストがデータベースにあると仮定すると、この問題は完全にデータベースで処理されます。何があっても、すでにデータベースにアクセスしています。

于 2009-03-02T05:05:15.043 に答える
0

すべてのオブジェクトをキューまたはスタックに入れ、2 と 2 を空になるまでポップするのはどうでしょうか?

于 2009-03-02T05:16:24.597 に答える