【问题标题】:Find a combination of two elements that have not been viewed together (LINQ, SQL or C#)查找未一起查看的两个元素的组合(LINQ、SQL 或 C#)
【发布时间】:2009-03-02 02:57:32
【问题描述】:

我有一个显示两个对象的页面,然后用户选择其中一个。我将偏好和组合记录在 MSSQL 数据库中,并最终存储如下数据:

UserId=1, BetterObjectId=1, WorseObjectId=2

现在我想避免再次显示对象组合 (1,2 / 2,1)。

那么我如何生成随机组合来向用户展示不包括以前查看过的组合?

这似乎应该是一个非常简单的问题,但像大多数程序员一样,我睡眠不足和咖啡不足,因此非常感谢您的帮助 :-)

very 天真的方法是这样的(并且所有对该函数的调用都必须包含在检查中,以查看用户是否已经对 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 个随机项目之前生成所有可能的组合并消除已经查看过的组合,但这是另一个糟糕的解决方案。

一种可能性(大 n 的内存密集型)是生成所有可能的组合并将组合 ID 存储在评级中。然后我可以对所有组合进行 SELECT WHERE combinationId IS NOT IN (SELECT combinationId FROM rating WHERE userId=x),并进行一些更改以反映组合的对称关系。

【问题讨论】:

    标签: c# sql linq linq-to-sql combinations


    【解决方案1】:
    Table Item: ItemId
    Table Rating: UserId, ItemId1, ItemId2, WinnerId
    

    如果您要求 Rating 表中的 ItemId1

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

    这是一个blog,关于将“随机”翻译到数据库中。

    【讨论】:

    • 感谢您的意见!我想知道单独存储获胜者 - 目前它是 WinnerId/LoserId,而不是 Item1、Item2、Winner。
    • 这是空间和排序能力之间的权衡。
    【解决方案2】:

    一个解决方案是这样的:

    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()
    

    我之前不知道 cross join 方法只是列出多个 FROM 表。

    【讨论】:

      【解决方案3】:

      假设可用项目列表在数据库中,我将完全在数据库中处理这个问题。无论如何,您已经在访问数据库了,为什么不在那里完成呢?

      【讨论】:

      • 是的,这就是为什么我将 SQL 列为可能的解决方法之一。
      【解决方案4】:

      将所有对象放入队列或堆栈中,然后将 2 和 2 弹出,直到它们为空?

      【讨论】:

      • 你会为每个用户保存堆栈吗?也不确定这是否会产生所有组合。
      • 哦,这是网络内容和跨会话?我只是在想普通的 C#。不会产生所有可能的组合,但认为其中一个目标是没有两次看到一个物体。在我的解决方案中就是这种情况。如果是网站和跨会话,则 sql 很可能是要走的路 =)
      • 抱歉,忽略了跨会话部分。是的,你的对于停止物体两次很有用——我只需要包含每种组合的东西。不过谢谢:-)
      猜你喜欢
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-20
      • 1970-01-01
      • 1970-01-01
      • 2019-12-06
      • 1970-01-01
      相关资源
      最近更新 更多