-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRecommendAnime.cpp
235 lines (187 loc) · 7.75 KB
/
RecommendAnime.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
//
// Created by chlaw on 11/14/2023.
//
#include "RecommendAnime.h"
#include <algorithm>
#include <unordered_set>
RecommendAnime::RecommendAnime() {
data1.loadData();
}
void RecommendAnime::calculateRecommendations(vector<Anime *> inputtedAnimes, int numRecommendations)
{
vector<string> inputtedShowGenres;
float normalizedGenre;
float normalizedRating;
float normalizedEpisodeCount;
for(int i = 0; i < inputtedAnimes.size(); i++)
{
normalizedRating += inputtedAnimes.at(i)->rating;
for(int j = 0; j < inputtedAnimes.at(i)->genres.size(); j++)
{
inputtedShowGenres.push_back(inputtedAnimes.at(i)->genres.at(j));
}
normalizedEpisodeCount += inputtedAnimes.at(i)->normEpisodes;
}
normalizedRating = normalizedRating / inputtedAnimes.size();
normalizedEpisodeCount = normalizedEpisodeCount / inputtedAnimes.size();
printRecommendations(normalizedEpisodeCount, normalizedRating, inputtedShowGenres, inputtedAnimes, numRecommendations);
}
void
RecommendAnime::printRecommendations(float normalizedEpisodes, float normalizedRating, vector<string> inputtedGenres, vector<Anime *> inputtedAnimes, int numRecommendations) {
//recommendationPrioritizations[0] = genre
//recommendationPrioritizations[1] = rating
//recommendationPrioritizations[2] = episodeCount
vector<Anime *> recommendationList;
float weight1;
float weight2;
float compareWeights;
float compareWeightsOld;
bool first = true;
if (recommendationPrioritizations[0]) {
// genre selected
recommendationList = getAnimeWithSameGenre(inputtedAnimes, inputtedGenres);
} else {
// genre not selected
recommendationList = getAnimeWithSimilarGenre(inputtedAnimes, inputtedGenres);
}
if (!recommendationPrioritizations[1] && !recommendationPrioritizations[2] ||
recommendationPrioritizations[1] && !recommendationPrioritizations[2]) {
// only genre or all three selected
// weight them the same
weight1 = getOverallWeight(normalizedEpisodes, normalizedRating, 0.5, 0.5);
cout << "Overall weight based on input: " << weight1 << endl;
// set the recommendation list to only animes with the same genrees
compare(recommendationList, 0.5, 0.5, weight1);
// sort the compareWeights in ascending order
} else if (!recommendationPrioritizations[1] && recommendationPrioritizations[2]) {
//episode and genre
weight1 = getOverallWeight(normalizedEpisodes, normalizedRating, 0.9, 0.1);
cout << "Overall weight based on input: " << weight1 << endl;
compare(recommendationList, 0.9, 0.1, weight1);
} else if (recommendationPrioritizations[1] && !recommendationPrioritizations[2]) {
// genre and rating
weight1 = getOverallWeight(normalizedEpisodes, normalizedRating, 0.4, 0.6);
cout << "Overall weight based on input: " << weight1 << endl;
compare(recommendationList, 0.4, 0.6, weight1);
}
if(sortMethod == "quick"){
quickSort(recommendationList, 0, recommendationList.size() - 1);
} else if(sortMethod == "shell"){
shellSort(recommendationList);
} else{
cout << "No sorting method used" << endl;
}
for (int i = 0; i < numRecommendations; i++) {
ReadData::printAnimeInfo(recommendationList[i]);
}
}
vector<Anime *> RecommendAnime::getAnimeWithSameGenre(vector<Anime *> inputtedAnimes,vector<string> inputtedGenres) {
// this will return a list of all animes that contain the genres found in the input genres parameter.
// used if genre is selected as preference
vector<Anime*> recommendationList;
for(int i = 0; i < data1.animeObj.size(); i++)
{
std::sort(data1.animeObj[i]->genres.begin(), data1.animeObj[i]->genres.end());
std::sort(inputtedGenres.begin(), inputtedGenres.end());
// this will check if the genre list of an Anime in the dataset is a subset of the inputtedGenres.
bool isSubset = std::includes(inputtedGenres.begin(), inputtedGenres.end(), data1.animeObj[i]->genres.begin(), data1.animeObj[i]->genres.end());
if(isSubset){
bool isRepeat = false;
for(int j = 0; j < inputtedAnimes.size(); j++){
if(inputtedAnimes[j]->id == data1.animeObj[i]->id){
isRepeat = true;
break;
}
}
if(!isRepeat){
recommendationList.push_back(data1.animeObj[i]);
}
}
}
return recommendationList;
}
float
RecommendAnime::getOverallWeight(float normalizedEpisodes, float normalizedRating, float epWeight, float ratingWeight) {
return (epWeight * normalizedEpisodes) + (ratingWeight * normalizedRating);
}
void RecommendAnime::swap(vector<Anime*> &vect, int i, int j) {
Anime* temp = vect[i];
vect[i] = vect[j];
vect[j] = temp;
}
int RecommendAnime::partition(vector<Anime*> &vect, int low, int high) {
// pivot is choosen as the last element
float pivot = vect[high]->compareWeight;
// Index of smaller element
int i = (low - 1);
for (int j = low; j < high; j++) {
if (vect[j]->compareWeight <= pivot) {
// If current element is smaller than or
// equal to pivot
i++;
// swap arr[i] and arr[j]
swap(vect, i, j);
}
}
// Swap the pivot element with arr[i + 1]
swap(vect, i+1, high);
// Return the position from where partition
// is done
return (i + 1);
}
void RecommendAnime::quickSort(vector<Anime*> &vect, int low, int high) {
if (low < high) {
/* pi is the partitioning index, arr[pi] is
now at right place */
int pi = partition(vect, low, high);
// Separately sort elements before
// partition and after partition
quickSort(vect, low, pi - 1);
quickSort(vect, pi + 1, high);
}
}
void RecommendAnime::compare(vector<Anime *> &vect, float epWeight, float ratingWeight, float weight1) {
for (int i = 0; i < vect.size(); i++) {
float weight2 = getOverallWeight(vect[i]->normEpisodes, vect[i]->rating, epWeight, ratingWeight);
vect[i]->weight = weight2;
float compareWeights = abs(weight2 - weight1);
vect[i]->compareWeight = compareWeights;
// smaller value means better recommendation
}
}
vector<Anime *>
RecommendAnime::getAnimeWithSimilarGenre(vector<Anime *> inputtedAnimes, vector<string> inputtedGenres) {
// this will return a list of all animes that contain at least 1 genre found in the input genres parameter.
// Used if genre is NOT selected as preference.
unordered_set<string> inputGenresSet(inputtedGenres.begin(), inputtedGenres.end());
unordered_set<int> inputAnimesSet;
for (const auto& anime : inputtedAnimes) {
inputAnimesSet.insert(anime->id);
}
vector<Anime*> recommendationList;
for (const auto& anime : data1.animeObj) {
for (const auto& genre : anime->genres) {
if (inputGenresSet.count(genre) > 0 && inputAnimesSet.count(anime->id) == 0) {
recommendationList.push_back(anime);
break; // No need to check other genres for this anime
}
}
}
return recommendationList;
}
int RecommendAnime::shellSort(vector<Anime *> &vect) {
int n = vect.size();
for (int gap = n/2; gap > 0; gap /= 2)
{
for (int i = gap; i < n; i += 1)
{
Anime* temp = vect[i];
int j;
for (j = i; j >= gap && vect[j - gap]->compareWeight > temp->compareWeight; j -= gap)
vect[j] = vect[j - gap];
// put temp (the original a[i]) in its correct location
vect[j] = temp;
}
}
return 0;
}