微信抢红包C语言版-线段切割法

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//#define     demo            // 范例程序,此范例程序没有考虑公平性,总金额方差巨大,仅供主框架调试,请写出自己的抢红包算法

// 函数功能 : 生成切割点(需要生成人数减1个切割点)
// 输入 : amount    : 总金额 (单位: 分)
//      : peoples   : 总人数
// 输出 : cut_point : 切割点
static void Make_Cut_Point(int *cut_point, int amount, int peoples)
{
    char repeat_flag;       //重复切割点标志位,1:生成了重复切割点;0:不重复
    int i, j;

    for (i = 0; i < (peoples - 1); i++) {
        do {
            repeat_flag = 0;
            cut_point[i] = rand() % amount;
            if (cut_point[i] == 0) {    //至少要有1分钱
                cut_point[i] = 1;
            } else if (cut_point[i] == amount) {    //要给最后一个人留一分钱
                cut_point[i] = amount - 1;
            }
            for (j = 0; j < i; j++) {
                if (cut_point[i] == cut_point[j]) { //若切割点已生成,则重新生成
                    repeat_flag = 1;
                    break;
                }
            }
        } while(repeat_flag);
    }
}

// 函数功能 : 对切割点进行排序(从小到大排)
// 输入 :  num      : 切割点数
// 输出 : cut_point : 从小到大排列的切割点
static void Sort_Cut_Point(int *cut_point, int num)
{
    int temp;
    int i, j;

    for (i = 0; i < num; i++) {
        for (j = i + 1; j < num; j++) {
            if (cut_point[i] > cut_point[j]) {
                temp = cut_point[i];
                cut_point[i] = cut_point[j];
                cut_point[j] = temp;
            }
        }
    }
}

// 函数功能 : 生成红包
// 输入 : amount  : 总金额 (单位: 分)
//      : peoples : 总人数
// 输出 : money   : 红包
static void Make_Red_Package(int *money, int amount, int peoples)
{
#ifdef demo
    int i;

    for (i = 0 ; i < (peoples - 1) ; i++) {
        money[i] = rand() % amount;
        amount -= money[i];
    }

    money[i] = amount;
#endif

    int *cut_point;           // 记录切割点
    int i;

    cut_point = (int *)malloc((peoples - 1) * sizeof(int));

    /* 1.生成切割点 */
    Make_Cut_Point(cut_point, amount, peoples);
    /*
    for (i = 0; i < (peoples - 1); i++) {
        printf("a[%d] %d\n", i, cut_point[i]);
    }*/

    /* 2.将切割点按从小到大进行排序 */
    Sort_Cut_Point(cut_point, (peoples - 1));
    /*
    for (i = 0; i < (peoples - 1); i++) {
        printf("b[%d] %d\n", i, cut_point[i]);
    }*/

    /* 3.分红包,按切割的段数分 */
    for (i = 0; i < peoples; i++) {
        if (i == 0) {
            money[i] = cut_point[i];
        } else if  (i == (peoples - 1)) {
            money[i] = amount - cut_point[peoples - 2];
        } else {
            money[i] = cut_point[i] - cut_point[i - 1];
        }
    }
}

/******* 请在这里描述你的算法 *************************************************
算法描述:
    将红包总金额视为一段长度,从中随机取(人数减1)个切割点将线段切割为(等同人数)的份数然后分配



******************************************************************************/




// ---------------- 上面的函数为抢红包算法,不要修改函数接口,函数体内部的程序请自己实现 --------------------

// ---------------- 下面的代码为抢红包算法调试框架 ----------------------------------------------------------






// 函数功能 : 累加每个人的总收入
// 输入 : peoples : 总人数
//      : money   : 本次每个人抢到的红包金额
// 输出 : total   : 每个人累计抢到的红包金额
static void cal_total_money(int *total, int peoples, int const *money)
{
    int i;

    for (i = 0 ; i < peoples ; i++) {
        total[i] += money[i];                                       // 累加上本次抢到的红包金额
    }
}

// 函数功能 : 查找人气王
// 输入 : peoples : 总人数
//      : money   : 每个人抢到的红包金额
// 返回 : 人气王编号
static int Find_lucky_king(int peoples, int const *money)
{
    int i, max_money, max_idx;

    max_idx = 0;                                                    // 先假设第一个人是人气王
    max_money = money[max_idx];                                     // 记录人气王抢到的钱

    for (i = 1 ; i < peoples ; i++) {                               // 从第二个人开始比较
        if (money[i] > max_money) {                                 // 发现抢到更多钱的人
            max_idx = i;                                            // 变更人气王编号
            max_money = money[max_idx];                             // 记录人气王抢到的钱
        }
    }

    return max_idx;                                                 // 返回人气王编号
}

// 函数功能 : 查找衰神
// 输入 : peoples : 总人数
//      : money   : 每个人抢到的红包金额
// 返回 : 衰神编号
static int Find_Decadence(int peoples, int const *money)
{
    int i, min_money, min_idx;

    min_idx = 0;                                                    // 先假设第一个人是衰神
    min_money = money[min_idx];                                     // 记录衰神抢到的钱

    for (i = 1 ; i < peoples ; i++) {                               // 从第二个人开始比较
        if (money[i] < min_money) {                                 // 发现抢到更少钱的人
            min_idx = i;                                            // 变更衰神编号
            min_money = money[min_idx];                             // 记录衰神抢到的钱
        }
    }

    return min_idx;                                                 // 返回衰神编号
}

// 函数功能 : 更新每个人曾经获取的最大红包
// 输入 : peoples   : 总人数
//      : money     : 每个人抢到的红包金额
// 输出 : max_money : 每个人历史上抢到的最大红包值
static void Change_Max_Package(int *max_money, int peoples, int const *money)
{
    int i;

    for (i = 0 ; i < peoples ; i++) {
        if (money[i] > max_money[i]) {                               // 本次抢到的红包大于历史上的最大红包
            max_money[i] = money[i];                                 // 更新历史最大红包
        }
    }
}

// 函数功能 : 更新每个人曾经获取的最小红包
// 输入 : peoples   : 总人数
//      : money     : 每个人抢到的红包金额
// 输出 : min_money : 每个人历史上抢到的最小红包值
static void Change_Min_Package(int *min_money, int peoples, int const *money)
{
    int i;

    for (i = 0 ; i < peoples ; i++) {
        if (money[i] < min_money[i]) {                               // 本次抢到的红包小于历史上的最小红包
            min_money[i] = money[i];                                 // 更新历史最小红包
        }
    }
}

// 函数功能 : 计算方差
// 输入     : count : 总人数
//          : val_list : 每个人的总金额
//          : div : 用于把单位转换成元
// 返回 : 方差
static double Cal_variance(int count, int const *val_list, int div)
{
    double variance, average_total;
    int i;

    variance = 0;
    average_total = 0;

    for (i = 0; i < count; i++) {
        average_total += val_list[i];                               // 计算累加和
    }

    average_total /= count;                                         // 计算平均值

    for (i = 0; i < count; i++) {
        double temp;                                                // 用于计算当前值 和 平均值 的差值 的绝对值

        if (val_list[i] > average_total) {
            temp = val_list[i] - average_total;
        } else {
            temp = average_total - val_list[i];
        }

        temp /= div;                                                // 把单位转换为元
        variance += temp * temp;
    }

    variance /= count;
    return variance;
}

#define CENT_PER_YUAN       100             // 1元 == 100分

// 函数功能 : 发n次红包
// 输入 : amount  : 发一次红包的金额
//        peoples : 参与发红包的人数
//        times   : 发红包次数
static void Red_Packet_nTimes(int amount, int peoples, int times)
{
    int i;
    int *money;                 // 抢一次红包的金额
    int *total_money;           // 抢n次红包的总金额
    int *lucky_king;            // 幸运王次数
    int *Decadence;             // 衰神次数
    int *max_money;             // n次抢红包中,抢到的最大值
    int *min_money;             // n次抢红包中,抢到的最小值

    if ((amount < peoples) || (peoples < 1) || (times < 1)) {       // 检查输入参数合法性
        printf("input error\r\n");
        return;
    }

    srand((unsigned)time(NULL));                                    // 初始化随机数发生器
    money = (int *)malloc(peoples * sizeof(int));
    total_money = (int *)malloc(peoples * sizeof(int));
    lucky_king = (int *)malloc(peoples * sizeof(int));
    Decadence = (int *)malloc(peoples * sizeof(int));
    max_money = (int *)malloc(peoples * sizeof(int));
    min_money = (int *)malloc(peoples * sizeof(int));

    for (i = 0; i < times; i++) {
        Make_Red_Package(money, amount, peoples);                   // 生成单次红包

        if (i == 0) {                                               // 第一次抢红包初始化一些参数
            int j;

            for (j = 0 ; j < peoples ; j++) {
                total_money[j] = 0;
                lucky_king[j] = 0;
                Decadence[j] = 0;
                max_money[j] = money[j];
                min_money[j] = money[j];
            }
        }

        cal_total_money(total_money, peoples, money);               // 累计每个人的总收入
        lucky_king[Find_lucky_king(peoples, money)]++;              // 人气王次数加1
        Decadence[Find_Decadence(peoples, money)]++;                // 衰神次数加1
        Change_Max_Package(max_money, peoples, money);              // 更新每个人曾经获取的最大红包
        Change_Min_Package(min_money, peoples, money);              // 更新每个人曾经获取的最小红包
    }

    printf("\r\n序号\t  总金额\t最大一包\t最小一包\t运气王次数\t衰神次数\r\n");

    for (i = 0; i < peoples; i++) {
        printf("%3d\t", i + 1);
        printf("%5d.%02d元\t", total_money[i] / CENT_PER_YUAN, total_money[i] % CENT_PER_YUAN);
        printf("%3d.%02d元\t", max_money[i] / CENT_PER_YUAN, max_money[i] % CENT_PER_YUAN);
        printf("%2d.%02d元\t", min_money[i] / CENT_PER_YUAN, min_money[i] % CENT_PER_YUAN);
        printf("\t%6d\t\t%6d\r\n\r\n", lucky_king[i], Decadence[i]);
    }

    printf("总金额方差:%lf\r\n", Cal_variance(peoples, total_money, CENT_PER_YUAN));
    printf("最大红包方差:%lf\r\n", Cal_variance(peoples, max_money, CENT_PER_YUAN));
    printf("最小红包方差:%lf\r\n", Cal_variance(peoples, min_money, CENT_PER_YUAN));
    printf("运气王方差:%lf\r\n", Cal_variance(peoples, lucky_king, 1));
    printf("衰神方差:%lf\r\n\r\n", Cal_variance(peoples, Decadence, 1));

    free(money);
    free(total_money);
    free(lucky_king);
    free(Decadence);
    free(max_money);
    free(min_money);
}

int main(int argc, char *argv[])
{
    float amount;
    int peoples, times;

    if (argc > 1) {
        amount = atof(argv[1]);
    } else {
        amount = 100.00;                    // 一次发100元红包
    }

    if (argc > 2) {
        peoples = atoi(argv[2]);
    } else {
        peoples = 10;                       // 发给10个人
    }

    if (argc > 3) {
        times = atoi(argv[3]);
    } else {
        times = 1000;                       // 发1000次
    }

    Red_Packet_nTimes((int)(amount * CENT_PER_YUAN), peoples, times);
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值