본문 바로가기

기타

음식 추천 알고리즘

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define food_count 4                                                                  //음식 수
#define load_user_database {{0, 5, 2, 8}, {1, 2, 0, 0}, {0, 0, 4, 9}, {1, 4, 0, 10}}; //실제로는 데이터 베이스에서 정보를 받아온다
using namespace std;

class FooRe
{
private:
    int **user_data;     //점수
    int user_count;      //사람수
    double **similality; //유사도
    int save_date;
    int system_time;
    int save_time;
    int days[12];
    double pearson(int, int);
    double prediction(int, int);
    int collaborative_filtering(int);
    int cac_remain_food(int);
    int evaluated_FoodRecommendation(int);
    int evaluated_FoodRecommendation_function(int, int[], int);
    int evaluate_can_exe(int);
    int rand_recommend(int);
    int check_data(int);
    int reco_time(int);
    int cac_system_time();
    int cac_user_time();
    int cac_time(int, int);

public:
    FooRe();
    ~FooRe();
    int FoodRecommendation(int);
    int return_time(int, int);
};

FooRe::FooRe()
{
    int user_database[][food_count] = load_user_database;
    user_count = sizeof(user_database) / food_count / 4;
    user_data = new int *[user_count];
    days[0] = 31, days[1] = 28, days[2] = 31, days[3] = 30, days[4] = 31, days[5] = 30, days[6] = 31, days[7] = 31, days[8] = 30, days[9] = 31, days[10] = 30, days[11] = 31;
    int current_time = time(NULL);
    int day = current_time / 60 / 60 / 24;
    int year = 1970;
    int mon = 0;
    while (day > 365)
    {
        if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
        {
            day -= 366;
        }
        else
        {
            day -= 365;
        }
        year++;
    }
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) //윤년
    {
        for (int i = 0; day - days[i] > 1; i++)
        {
            day = day - days[i];
            mon = i + 1;
        }
        day--;
    }
    else // NOT윤년
    {
        for (int i = 0; day - days[i] - 1 > 0; i++)
        {
            day = day - days[i];
            mon = i + 1;
        }
    }
    day++;
    mon++;
    save_time = year % 100 * 1000000 + mon * 10000 + day * 100;

    system_time = 0;
    for (int j = year % 100; j > 0; j--)
    {
        if (j % 4 == 0)
        {
            system_time += 366;
        }
        else
        {
            system_time += 365;
        }
    }
    if (year % 100 != 0)
    {
        for (int j = mon; j > 1; j--)
        {
            system_time += days[j - 2];
        }
    }
    else if (year % 100 == 0 && mon > 2)
    {
        for (int j = mon; j > 1; j--)
        {
            system_time += days[j - 2];
        }
        system_time++;
    }
    system_time += day;

    for (int i = 0; i < user_count; i++) // user data 동적 할당
    {
        user_data[i] = new int[food_count];
    }
    for (int i = 0; i < user_count; i++) //복사
        for (int j = 0; j < food_count; j++)
        {
            user_data[i][j] = user_database[i][j];
        }
    similality = new double *[user_count]; //유사도 동적 할당
    for (int i = 0; i < user_count; i++)
    {
        similality[i] = new double[user_count];
    }
    for (int i = 0; i < user_count; i++) // 0으로 초기화
    {
        for (int j = 0; j < user_count; j++)
        {
            similality[i][j] = 0;
        }
    }
}

FooRe::~FooRe()
{
    for (int i = 0; i < user_count; i++)
        delete[] user_data[i];
    delete[] user_data;
    for (int i = 0; i < user_count; i++)
        delete[] similality[i];
    delete[] similality;
}

double FooRe::pearson(int user1, int user2) //유저1, 유저2 pearson연산
{
    double left_bot = 0, right_bot = 0, top = 0, result;
    for (int i = 0; i < food_count; i++)
    {
        top += (user_data[user1][i] % 100) * (user_data[user2][i] % 100);
        if ((user_data[user1][i] % 100) != 0 && (user_data[user2][i] % 100) != 0)
        {
            left_bot += pow(user_data[user1][i] % 100, 2);
            right_bot += pow(user_data[user2][i] % 100, 2);
        }
    }
    left_bot = sqrt(left_bot);
    right_bot = sqrt(right_bot);
    result = (double)top / (left_bot * right_bot);
    if(left_bot*right_bot==0)
    return 0;
    return result;
}

double FooRe::prediction(int user, int food)
{
    double top = 0, bot = 0, result = 0;
    for (int i = 0; i < user_count; i++)
    {
        if(user_data[i][food]>0&&i!=user){
        top += similality[user][i] * (user_data[i][food] % 100);
        bot += similality[user][i];
        }
    }
    result = top / bot;
    return result;
}

int FooRe::collaborative_filtering(int user) //협업 필터링 알고리즘, 추천 음식 반환
{
    double expect_score[food_count] = {};
    double top = 0, bot = 0, result = 0, max = 0;
    int r_max = -1;
    for (int i = 0; i < user_count; i++) //유사도 계산
    {
        for (int j = 0; j < user_count; j++)
        {
            if (i == j)
            {
                similality[i][j] = 1;
            }
            else
            {
                similality[i][j] = pearson(i, j);
                
            }
        }
    }
    for (int i = 0; i < food_count; i++)
    {
        if (user_data[user][i] % 100 == 0)
        {
            expect_score[i] = prediction(user, i);
        }
    }
    for (int i = 0; i < food_count; i++)
    {
        if (expect_score[i] > max)
        {
            max = expect_score[i];
            r_max = i;
        }
    }
    return r_max;
}

int FooRe::cac_remain_food(int user)
{
    srand((unsigned int)time(NULL));
    int rand_num = 0;
    int evaluation = 0;
    for (int i = 0; i < food_count; i++)
        if ((user_data[user][i] % 100) != 0)
            evaluation++;
    rand_num = rand() % food_count + 1;
    if (rand_num <= evaluation)
    {
        return 1; //왼쪽 분기
    }
    else
    {
        return 0; //오른쪽 분기
    }
}

int FooRe::evaluated_FoodRecommendation_function(int user, int re_food_arr[], int num)
{
    srand((unsigned int)time(NULL));
    int total_score = 0, com_score = 0;
    int rand_num;
    for (int i = 0; i < num; i++)
        total_score += (user_data[user][re_food_arr[i]] % 100);
    rand_num = rand() % total_score + 1;
    for (int i = 0; i < num; i++)
    {
        com_score += (user_data[user][re_food_arr[i]] % 100);
        if (rand_num <= com_score)
            return re_food_arr[i];
    }
    return 0;
}

int FooRe::evaluate_can_exe(int user)
{
    int same_data_exist, not_same_data_exist;
    for (int i = 0; i < user_count; i++)
    {
        same_data_exist = 0, not_same_data_exist = 0;
        for (int j = 0; j < food_count; j++)
        {
            if ((user_data[user][j] % 100) != 0 && (user_data[i][j] % 100) != 0)
                same_data_exist = 1;
            if ((user_data[user][j] % 100) == 0 && (user_data[i][j] % 100) != 0)
                not_same_data_exist = 1;
        }
        if (same_data_exist + not_same_data_exist == 2)
            return 1; //협업 필터링 알고리즘 사용가능
    }
    return 0; //협업 필터링 알고리즘 사용 불가능
}

int FooRe::rand_recommend(int user)
{
    srand((unsigned int)time(NULL));
    int rand_num;
    int zero_arr[food_count] = {}, zero_arr_count = 0;

    for (int i = 0; i < food_count; i++)
    {
        if ((user_data[user][i] % 100) == 0)
        {
            zero_arr[zero_arr_count++] = i;
        }
    }
    rand_num = rand() % zero_arr_count;
    return zero_arr[rand_num];
}

int FooRe::FoodRecommendation(int user) //음식 추천을 받을 user입력시 추천 음식 반환
{
    if (check_data(user))
    {
        if (cac_remain_food(user))
        {
            return evaluated_FoodRecommendation(user);
        }
        else
        {
            if (evaluate_can_exe(user))
            {
                return collaborative_filtering(user);
            }
            else
            {
                return rand_recommend(user);
            }
        }
    }
    else
    {
        return -1;
    }
}

int FooRe::check_data(int user)
{
    if (user < user_count)
    {
        for (int i = 0; i < user_count; i++)
            for (int j = 0; j < food_count; j++)
                if (user_data[i][j] / 10 < 0 || user_data[i][j] % 100 / 10 > 10)
                {
                    cout << "ERROR: DATA BASE" << endl;
                    return 0;
                }
        return 1;
    }
    else
    {
        cout << "ERROR: USER NUMBER" << endl;
    }
    return 0;
}

int FooRe::cac_time(int user, int item)
{
    int user_time;
    user_time = 0;
    for (int j = user_data[user][item] / 1000000; j > 0; j--)
    {
        if (j % 4 == 0)
        {
            user_time += 366;
        }
        else
        {
            user_time += 365;
        }
    }
    if (user_data[user][item] / 1000000 % 4 != 0)
    {
        for (int j = user_data[user][item] % 1000000 / 10000; j > 1; j--)
        {
            user_time += days[j - 2];
        }
    }
    else if (user_data[user][item] / 1000000 % 4 == 0 && user_data[user][item] % 1000000 / 10000 > 2)
    {
        for (int j = user_data[user][item] % 1000000 / 10000; j > 1; j--)
        {
            user_time += days[j - 2];
        }
        user_time++;
    }
    user_time += user_data[user][item] % 10000 / 100;

    return system_time - user_time;
}

int FooRe::evaluated_FoodRecommendation(int user)
{
    int recommended_food[food_count] = {};
    int count_num = 0, zero_count = 0;
    int *user_data_copy = new int[food_count];
    for (int i = 0; i < food_count; i++)
        user_data_copy[i] = i;
    for (int i = 0; i < food_count; i++)
        if (cac_time(user, i) >= 7 && user_data[user][i] % 100 != 0)
            recommended_food[count_num++] = i;
    if (count_num != 0)
    {
        return evaluated_FoodRecommendation_function(user, recommended_food, count_num);
    }
    else
    {
        for (int i = 0; i < food_count; i++)
            if (user_data[user][i] % 100 == 0)
                zero_count++;
        if (zero_count != 0)
        {
            if (evaluate_can_exe(user))
            {
                return collaborative_filtering(user);
            }
            else
            {
                return rand_recommend(user);
            }
        }
        else
        {
            return evaluated_FoodRecommendation_function(user, user_data_copy, food_count);
        }
    }
}

int FooRe::return_time(int food, int user)
{
    return save_time+user_data[user][food];
}

int main(void)
{
    FooRe test;
    int rf;
    int save_database;
    if ((rf = test.FoodRecommendation(1)) != -1)
    {
        cout << "recommended food is : " << rf << endl;
        save_database = test.return_time(rf, 1);
    }
    else
    {
        cout << "failed to recommend food" << endl;
    }
    cout << save_database;
    return 0;
}