吉利数红包随机分配

第一次实验报告

程序语言: C
姓名: unicorn
学号: 12345678910
日期:2023/3/10


一、 问题重述

微信红包程序:给定一个钱数m,发红包人数n,其中10<=m, n<=200将钱数拆成几个指定的吉利数(如1.66,1.68, 16.8,1.78,17.8,1.88,18.8,1.99,5.20,0.66, 6.6, 6.66, 0.08, 0.88, 8.8, 8.88,0.99, 9.9, 9.99)并发出,要求要发出n个红包,分布比较均匀,并尽可能把钱发完。


二、 问题分析

首先观察到钱数和红包量都不大,所以不需要考虑算法的复杂度问题;接着就是如何定义吉利数,题目给出了19个吉利数,根据规律我们还想出了13.14,16.66,19.9,1.98,19.8 这5个吉利数
然后我们注意到“分布比较均匀“,为了使得分布比较均匀,我们考虑先计算红包金额的平均数,再根据各吉利数与红包金额的差距,确定该吉利数被选中的概率,通过随机数来进行红包的分配。我们先通过一层条件判断是否剩下的金额已经不够平均分配,不够的话就选取数值较小的随机数,如果够就进入第一层随机,第一层随机80%概率会在主随机吉利数数组中选取红包金额,主随机数数组由与平均数相差最近的几个数组成,20%的数由次随机吉利数数组组成并进入第二次随机,按照这些吉利数距离平均数的权重来获得他们被选中的概率,并且确保他们不会超过剩下金额,最后一个红包直接输出最后的剩下金额


三、 伪代码

伪代码如下:

void send(float money, int count)
	float aver <- money / count; //平均数
	srand((unsigned)time(NULL)); // 用系统时间初始化随机数生成器
	/* 生成平均数距离  */
	for i<-0 to TOTAL do
		lucky[i].distance <- fabs( lucky[i].num - aver) ;
	//对吉利数按吉利数本身进行排序
	for i<-0 to TOTAL do
		for j<-i to TOTAL do
			if lucky[i].num>lucky[j].num then
				t<-lucky[i];
				lucky[i]<-lucky[j];
				lucky[j]<-t;
	//计算权重值和累计权重值 
	max<-lucky[TOTAL-1].num;
	sum=max * TOTAL - sum;
	for i <- Main to TOTAL do
		weight[i] <- (max - lucky[i].distance) / sum *1000.0 ;
		if i > 0 then
		addwei[i] <- weight[i] + addwei[i-1];
	addwei[TOTAL]<-1000;
	//对吉利数按吉利数与平均数距离进行排序
	for i<-0 to TOTAL do
		for j<-i to TOTAL do
			if lucky[i].distance>lucky[j].distance then
				t<-lucky[i];
				lucky[i]<-lucky[j];
				lucky[j]<-t;
	Main=0;
	//计算主随机函数数量(与平均数距离小于2)和小于1的吉利数数组
	for i<-0 to TOTAL do
		if lucky[i].distance<2 then
			Main++;
		if lucky[i].num<1 then
			small[point++]<-lucky[i].num;
	/* 循环发n-1个红包 */
	for  i <- 1 to count do
	     packet_random<-0; //辅助计算随机数
		if remain<((count-i+1)*aver) then 
		 //先判断是否剩余金额不够,不够的话先使用比较小的数补充
			if rand()%2 then
			    packet_money <- 0.01;
			else  packet_money <- small[rand()%point] ;
		}
		else {
			/* 随机生成一个指定吉利数的红包金额:
			分为两层随机,第一层判断金额数是落入主金额数组还是次金额数组
			80%落入主金额,20%落入次金额 */
			if rand()%5!=0  then
				packet_money <- lucky[rand()%Main].num;
			 else 
				ok<-1;
				while ok do
					middle <- rand();
					packet_random <- middle % 1000;
					/* 在累加权重中查看随机数落点并生成对应金额红包 */
					for j = Main to TOTAL-1 do
						if packet_random < addwei[j+1] 
						&& packet_random >= addwei[j] then
							packet_money <- luck[j];
							if packet_money<remain then
							    ok<-0;
		printf("第%d个红包:%.2f元\n", 
		i, packet_money,remain,(count-i+1)*aver);
		/* 红包金额从总金额中扣除 */
		remain -= packet_money;
	//最后一个红包直接把剩余的钱发出去
	printf("第%d个红包:%.2f元\n", count, remain);


四、代码

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#define TOTAL 24
#define MAIN 3
#define MIN 3
int Main=0;
struct Luckynum {
	float num;
	float distance;
} lucky[TOTAL];
float luck[TOTAL] = {1.66,1.68,16.8,1.78,17.8,1.88,18.8,1.99,
5.20,0.66,6.6,6.66,0.08,0.88,
8.8,8.88,0.99,9.9,9.99,13.14,16.66,19.9,1.98,19.8};
// 吉利数的列表
float small[TOTAL] = {0};
//吉利数中比较小的数的列表
int point=0; //small数组指针
void print(float aa[])//测试使用函数
{
	for (int i = 0; i < TOTAL; i++) {
		printf("%f\t",aa[i]);
	}
}
void send(float money, int count)
{
	float remain = money;
	float packet_money;
	float aver = money / count; //平均数
	float dis[TOTAL]= {0}; //与平均数距离 ;即将吉利数放在平均数一侧
	float weight[TOTAL]= {0}; //权重:不科学的权重计算方法以及可能总和不为100%
	float addwei[TOTAL+1]= {0}; //累计权重
	srand((unsigned)time(NULL)); // 用系统时间初始化随机数生成器
	/* 生成平均数距离  */
	for (int i = 0; i < TOTAL; i++) {
		lucky[i].distance = fabs( lucky[i].num - aver) ;
	}
	//对吉利数按吉利数本身进行排序,采用冒泡排序,若数量较大则应该换更高效的排序方法
	for(int i = 0 ; i < TOTAL ; i++) {
		for(int j = i ; j < TOTAL  ; j++) {
			if(lucky[i].num>lucky[j].num) {
				struct Luckynum t;
				t=lucky[i];
				lucky[i]=lucky[j];
				lucky[j]=t;
			}
		}
	}
	//计算权重值和累计权重值 
	float sum=0,max=lucky[TOTAL-1].num;
	sum=max * TOTAL - sum;
	for (int i = Main; i < TOTAL; i++) {
		weight[i] = (max - lucky[i].distance) / sum *1000.0 ;
		if(i > 0) addwei[i] = weight[i] + addwei[i-1];
		//printf("%f\t",weight[i]);
	}
	addwei[TOTAL]=1000;
	//对吉利数按吉利数与平均数距离进行排序,采用冒泡排序,若数量较大则应该换更高效的排序方法
	for(int i = 0 ; i < TOTAL ; i++) {
		for(int j = i ; j < TOTAL  ; j++) {
			if(lucky[i].distance>lucky[j].distance) {
				struct Luckynum t;
				t=lucky[i];
				lucky[i]=lucky[j];
				lucky[j]=t;
			}
		}
	}
	Main=0;
	for(int i = 0 ; i < TOTAL ; i++) {
		if(lucky[i].distance<2)
			Main++;
		if(lucky[i].num<1)
			small[point++]=lucky[i].num;
	}
	/* 循环发n-1个红包 */
	for (int i = 1; i < count; i++) {
		int packet_random=0; //辅助计算随机数
		if(remain<((count-i+1)*aver)) { //先判断是否剩余金额不够,不够的话先使用比较小的数补充
			packet_random =999999;
			if(rand()%2)packet_money = 0.01;
			else  packet_money = small[rand()%point] ;
		}
		else {
			/* 随机生成一个指定吉利数的红包金额:
			分为两层随机,第一层判断金额数是落入主金额数组还是次金额数组
			80%落入主金额,20%落入次金额 */
			if(rand()%5!=0) {
				packet_money = lucky[rand()%Main].num;
			} else {
				int ok=1;
				while(ok) {
					int middle = rand();
					packet_random = middle % 1000;
					/* 在累加权重中查看随机数落点并生成对应金额红包 */
					for(int j = Main ; j < TOTAL-1 ; j ++) {
						if(packet_random < addwei[j+1] 
						&& packet_random >= addwei[j]) {
							packet_money = luck[j];
							if(packet_money<remain)ok=0;
						}
					}
				}

			}
		}
		printf("第%d个红包:%.2f元\n", i, packet_money,remain,(count-i+1)*aver);
		/* 红包金额从总金额中扣除 */
		remain -= packet_money;
	}
	//最后一个红包直接把剩余的钱发出去
	printf("第%d个红包:%.2f元\n", count, remain);
}

int main()
{
	float money;
	int count;
	//先对吉利数结构体数组进行初始化
	for(int i = 0 ; i < TOTAL ; i++) {
		lucky[i].num=luck[i];
	}
	printf("请输入红包金额(10元至200元):");
	scanf("%f", &money);
	printf("请输入红包数量(10个至200个):");
	scanf("%d", &count);
	//for(int i = 0 ; i < TOTAL ; i++)
	//printf("%.2f\n",luck[i]);
	if ( (money < 10 || money >200) 
	  || (count < 10 || count >200)) {
		printf("红包金额或数量不不合法\n");
		return 0;
	}

	send(money, count);

	return 0;
}





五、 实验结果

下列分别列出了总金额为200元,红包个数分别为20,50,80个时的实验结果,结果较均匀,较符合实际。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


六、 总结

该方案存在两点主要问题:第一是吉利数不够完善,由于吉利数定义不全,导致吉利数列举不全,无法很好的展示。第二是分布概率算法不完善,使用平均数附近的正态分布进行计算可能会更科学。这里只采用两层随机,第二层随机选择与平均数的距离来设置权重,可能导致最后一个红包的金额过大。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值