private static Tuple <int, double> SelectNextBook(List <int> currentlySelectedBookIDs, List <Tuple <int, double> > currentlyAvailableBookIDAndWeightsForSelection, double lambda, BooksSimilarityModel model) { if (currentlyAvailableBookIDAndWeightsForSelection.Count == 0) { return(null); } double theBestSimilarity = Double.MinValue; Tuple <int, double> theBestBookIDAndWeight = null; foreach (Tuple <int, double> bookIDAndWeightI in currentlyAvailableBookIDAndWeightsForSelection) { int bookIDI = bookIDAndWeightI.Item1; double relevancyI = bookIDAndWeightI.Item2; double similarityMaxI = model.GetSimilarityMax( currentlySelectedBookIDs, bookIDI); double similarityI = lambda * relevancyI - (1 - lambda) * similarityMaxI; if (similarityI > theBestSimilarity) { theBestSimilarity = similarityI; theBestBookIDAndWeight = bookIDAndWeightI; } } return(theBestBookIDAndWeight); }
public static List <int> Recommend(List <int> recCoreOfBookIDs, List <Tuple <int, double> > bookIDsAvailableForAdding, BooksSimilarityModel model, double lambda, int howMany) { // iterative addition available books to rec. core List <int> currentlySelectedBookIDs = recCoreOfBookIDs; while (currentlySelectedBookIDs.Count() < howMany) { Tuple <int, double> nextBookIDAndWeight = SelectNextBook( currentlySelectedBookIDs, bookIDsAvailableForAdding, lambda, model); if (nextBookIDAndWeight == null) { return(currentlySelectedBookIDs); } currentlySelectedBookIDs.Add(nextBookIDAndWeight.Item1); bookIDsAvailableForAdding.Remove(nextBookIDAndWeight); } return(currentlySelectedBookIDs.Take(howMany).ToList()); }
/// <summary> /// Recommender algorithm Maximal Marginal Relevance (MMR) searchs books similar to one other, /// at the same time strives for diversity of returned books. /// Algorithm is based od ContentBasedBookSimilarity recommender. In the first step, the algorithm /// requires a list of all similar books to one other. Then it select the first 10 (CORE_SIZE) /// books as a core of resulting recommendation list. The other 90 (CANDIDATES_SIZE) books from /// the list are taken as candidates. The algorithm then iteratively adds one candidate to the /// result list. Another element of the resulting list is selected to maximize diversity /// (minimizing the similarity of books in the resulting list). /// The similarity of books is measured by Jaccard's similarity to the authors and the genres. /// /// </summary> /// <param name="bookId">Id of book on which will the recommendation be based</param> /// <param name="userId">Id of the signed in user</param> /// <param name="howMany">How many books to return</param> /// <returns>Maximal Marginal Relevance List of books (bookIDs) based on results of /// RecommenderBookSimilar algorithm /// /// </returns> public static List <int> Recommend(int bookId, string userId = null, double lambda = 0.2, int howMany = 6) { List <Tuple <int, int> > bookIDsAndTheirQuantitiesAll = RecommenderContentBasedBookSimilarity.RecommendWeightedList(bookId, userId); // creating core and candidates of books List <Tuple <int, int> > recCoreWList = bookIDsAndTheirQuantitiesAll.Take(CORE_SIZE).ToList(); List <Tuple <int, int> > recCandidatesWList = bookIDsAndTheirQuantitiesAll.Skip(CORE_SIZE).Take(CANDIDATES_SIZE).ToList(); // creating similarity model of books var db = new BookRecommenderContext(); db.ChangeTracker.QueryTrackingBehavior = Microsoft.EntityFrameworkCore.QueryTrackingBehavior.NoTracking; List <int> boodIdsForModel = new List <int>(); boodIdsForModel.AddRange(recCoreWList.Select(b => b.Item1).ToList()); boodIdsForModel.AddRange(recCandidatesWList.Select(b => b.Item1).ToList()); BooksSimilarityModel model = new BooksSimilarityModel(boodIdsForModel); model.CountSimilarity(db); // run diversity enhanced recommender List <int> recCoreList = recCoreWList.Select(b => b.Item1).ToList(); List <Tuple <int, double> > recCandidatesNormWList = NormBookIDsAndTheirQuantities(recCandidatesWList); return(RecommenderDiversityEnhanced.Recommend(recCoreList, recCandidatesNormWList, model, lambda, howMany).ToList()); }