Найдите комбинацию двух элементов, которые не просматривались вместе (LINQ, SQL или C#).

StackOverflow https://stackoverflow.com/questions/601134

Вопрос

У меня есть страница, на которой отображаются два объекта, а затем пользователь выбирает один из них.Я записываю предпочтение и комбинацию в базу данных 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 случайных предмета, но это еще одно ужасное решение.

Есть возможность (интенсивная память для больших n) — сгенерировать все возможные комбинации и сохранить в рейтинге идентификатор комбинации.Затем я могу просто выполнить ВЫБОР всех комбинаций, ГДЕ комбинацияId НЕТ (ВЫБРАТЬ идентификатор комбинации из рейтингов, ГДЕ userId=x) с некоторыми изменениями, чтобы отразить симметричное соотношение комбинаций.

Это было полезно?

Решение

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

Если вам требуется, чтобы ItemId1 < ItemId2 в таблице рейтингов, вам нужно проверить таблицу рейтингов только один раз.

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

Вот блог о получении «случайного» перевода в базу данных.

Другие советы

Одно из решений заключается в следующем:

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 раньше.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top