Найти комбинацию двух элементов, которые не были просмотрены вместе (LINQ, SQL или C #) - PullRequest
4 голосов
/ 02 марта 2009

У меня есть страница, на которой отображаются два объекта, а затем пользователь выбирает один из них. Я записываю предпочтение и комбинацию в базу данных MSSQL и в итоге сохраняю данные следующим образом:

UserId=1, BetterObjectId=1, WorseObjectId=2

Теперь я бы хотел больше не показывать эту комбинацию объектов (1,2 / 2,1).

Итак, как мне генерировать случайные комбинации, чтобы показать пользователю исключая ранее просмотренные комбинации?

Кажется, это должен быть очень простой вопрос, но, как и большинство программистов, мне не хватает сна и кофе, поэтому ваша помощь очень ценится: -)

Очень наивный подход очень подобен этому (и все вызовы этой функции должны быть обернуты в проверку, чтобы увидеть, оценивал ли пользователь уже столько раз, сколько nCr, где n - элемент число и г 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) состоит в том, чтобы сгенерировать все возможные комбинации и сохранить код комбинации в рейтинге. Затем я могу просто выполнить ВЫБОР всех комбинаций, ГДЕ комбинационного идентификатора нет (ВЫБРАТЬ комбинацию идентификаторов из рейтингов, ГДЕ userId = x) с некоторыми изменениями, чтобы отразить симметричное соотношение комбинаций.

Ответы [ 4 ]

1 голос
/ 03 марта 2009
Table Item: ItemId
Table Rating: UserId, ItemId1, ItemId2, WinnerId

Если вам требуется, чтобы 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 };

Вот блог о переводе "random" в базу данных.

1 голос
/ 02 марта 2009

Одно из решений таково:

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.

0 голосов
/ 02 марта 2009

Как насчет размещения всех объектов в очереди или в стеке, а затем выкидывания 2 и 2, пока они не станут пустыми?

0 голосов
/ 02 марта 2009

Предполагая, что список доступных элементов находится в базе данных, я бы полностью решил эту проблему в базе данных. Вы уже попали в базу данных, несмотря ни на что, так почему бы не сделать это там?

...