揣测微信红包算法简洁版

本文介绍了两种模拟微信红包分配的算法实现。Version1中,通过设置每个红包的最大值为剩余金额的比例来分配红包,但存在首尾红包差异大的问题。Version2则引入了统计功能,增加了红包分配的公平性,并通过多次实验统计出运气最佳与最差的红包获得者概率及方差。

Version 1:

昨天写了一个模拟微信红包的算法,后来发现粗略的以红包里剩余的钱为每个红包的最大值会导致第一个红包很容易抢的很大,所以这次修改了每个红包的最大值,代码如下:

#include "stdio.h"
#include "stdlib.h"
#include "time.h"

void EntryPoint(double money,int count){
	double value = money;
	srand((unsigned)time(NULL)); //makesure the random every time is diffrent
	int max = money / count * 1.5 * 100; //max value of each redpacket
	double LuckMan = 0.1;
	int LuckManNum = 1;
	double UnLuckMan = money;
	int UnLuckManNum = 1;
	double MaxGap = 0;
	for (int index = 1; index <= count; index++)
	{
		if (index == count){ //the last people
			value = money;
		}
		else
		{
			value = rand() % max + 1;
			value = value / 100;
			for (; value > money - 0.1 * (count - index);)
			{
				value = rand() % max + 1;
				value = value / 100;
			}
		}
		
		LuckMan = value > LuckMan ? value : LuckMan;
		LuckManNum = LuckMan <= value ? index : LuckManNum;
		UnLuckMan = value < UnLuckMan ? value : UnLuckMan;
		UnLuckManNum = UnLuckMan >= value ? index : UnLuckManNum;
		MaxGap = LuckMan - UnLuckMan;
		money = money - value;
		printf("this is the %d red packet,value is =%.2f\n",index,value);
	}
	printf("the Luck Man's number is %d,value is %.2f; \n",LuckManNum,LuckMan);
	printf("the Unluck Man's number is %d,value is %.2f;\n",UnLuckManNum,UnLuckMan);
	printf("the max gap is %.2f \n",MaxGap);
}

int main() {
	double money = 4.88;
	int count = 5;
	EntryPoint(money,count);
	return 0;
}

运行效果如下,如此分析,前面抢的人红包范围都比较稳定,最后一个人属于风险比较大,取决于前面人抢完后,红包里钱的多少,即容易抢到最大的红包,也容易因为前面人都抢的很多,抢到剩下很少的红包。


但是数据多了之后还是很不合理!


所以还是需要改进。

Version 2:

#include "stdio.h"
#include "stdlib.h"
#include "time.h"
#include "math.h"

#define PeopleNum 10
#define RedPacetNum 500
struct RedPacketDate
{
	int LuckManNum;
	int UnLuckManNum;
	double Variance;
};
int PacketAllocate(int PacketNum, double Money, RedPacketDate &OneCase){
	int index;
	double CurrentValue = Money;
	double LuckManMoney = 0.1;//This value will increase when there is a bigger Redpackge
	int LuckManNum = 1;
	double UnLuckManMoney = Money;//This value will reduse when there is a less Redpackge
	int UnLuckManNum = 1;
	int max = Money / PacketNum * 2.2 * 100; //max value of each redpacket
	OneCase.Variance = 0;
	for (index = 1; index <= PacketNum; index++)
	{
		if (index == PacketNum){ //the last people
			CurrentValue = Money;
		}
		else
		{
			CurrentValue = rand() % max + 1;
			CurrentValue = CurrentValue / 100;
			for (; CurrentValue > Money - 0.1 * (PacketNum - index);)
			{
				CurrentValue = rand() % max + 1;
				CurrentValue = CurrentValue / 100;
			}
		}
		//update the correct value
		LuckManNum = LuckManMoney <= CurrentValue ? index : LuckManNum;
		LuckManMoney = CurrentValue > LuckManMoney ? CurrentValue : LuckManMoney;
		//printf("luckmannum = %d \n",LuckManNum);
		UnLuckManNum = UnLuckManMoney >= CurrentValue ? index : UnLuckManNum;
		UnLuckManMoney = CurrentValue < UnLuckManMoney ? CurrentValue : UnLuckManMoney;
		Money = Money - CurrentValue;
		OneCase.Variance = (OneCase.Variance + pow((CurrentValue - Money / PacketNum), 2)) / 2;
	}
	OneCase.LuckManNum = LuckManNum;
	OneCase.UnLuckManNum = UnLuckManNum;
	return 1;
}

void EntryPoint(int packetcount,double money,int count){
	int status;
	double value = money;
	double temp = money;
	srand((unsigned)time(NULL)); //makesure the random every time is diffrent
	int max = money / count * 2.2 * 100; //max value of each redpacket
	RedPacketDate OnePacketCase[RedPacetNum];
	int LuckManMoney[PeopleNum] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	double Variance[500];
	double AveVariance = 0;
	int index = 0;
	for (index = 0; index < packetcount; index++){
		status = PacketAllocate(count, money, OnePacketCase[index]);
		LuckManMoney[OnePacketCase[index].LuckManNum-1] += 1;
		money = temp;
	}
	for (index = 0; index < sizeof(LuckManMoney)/sizeof(int); index++){
		printf("the chence of the Man's number is %d got the LuckManMoney is %0.2f%%\n", index + 1, ((double)LuckManMoney[index] / packetcount*100));
	}
	for (index = 0; index < packetcount;index++){
		AveVariance += OnePacketCase[index].Variance;
	}
	printf("variance is %f \n", AveVariance/packetcount);
	
}

int main() {
	int packcount = 500;
	double money = 100;
	int count = 10;
	EntryPoint(packcount,money,count);
	return 0;
}

      这版本的代码就是增加了一个统计的功能,初步设定了500个红包,每个红包里面都是100元,分别给十个人抢,最后统计运气王是第几个人抢到的,然后算这些每个红包的方差然后把所有方差平均一下;这样方便我们看到这个红包分配算法之下,抢红包的顺序不同所造成的影响;

    当然腾讯是不会用这么low的红包分配算法的啦,我们只是写着瞎玩娱乐嘛!

<think> 我们分析`updateFlashMenu`方法的结构,从最外层的if-else开始,逐步深入内层条件。由于代码较长,我们按代码块拆分,并基于方法名和上下文推测其意图。 ### 外层结构分析 1. **最外层条件**:`if (isDestroy()) { return; }` - 如果当前对象已销毁,则直接返回,避免后续操作。 2. **获取EIS状态**: ```java IFeatureDistributor.DistributorPipeline pipeline = ...; boolean isVideoSuperEisWideOpen = false; if (null != pipeline) { isVideoSuperEisWideOpen = pipeline.getMethodResult(IFeatureDistributor.MethodType.EIS_IS_VIDEO_SUPER_EIS_WIDE_OPEN); } ``` - 通过`IFeatureDistributor`获取视频超级EIS(电子防抖)是否在广角开启的状态。 3. **主要分支1:卫星模式开启(`isSatOpen()`)** 这是代码的第一个大分支,当卫星模式(多摄协同)开启时执行。内部包含一个`ThreadManager.runOnUiThread`(确保在UI线程执行)。 4. **主要分支2:卫星模式关闭且`resetForSatOff`为true** 当卫星模式关闭,并且传入参数`resetForSatOff`为true时,执行此分支。同样在UI线程中执行。 5. **主要分支3:普通拍照模式且满足特定条件** 当当前模式是普通拍照模式(`CAPTURE_MODE_COMMON`)并且(运动抓拍开启或微距模式开启)时执行。 6. **主要分支4:人像模式(`CAPTURE_MODE_PORTRAIT`)** 仅单地重新加载闪光灯菜单。 ### 分支1(卫星模式开启)内部详细分析 - 在UI线程中执行,首先检查`isPaused()`,如果暂停则返回。 - 确定`menuKey`:根据是否是拍照模式(`mFeatureListener.isCaptureModeType()`)决定使用拍照闪光灯菜单键(`FlashFeatureKeys.OPTION_KEY_FLASH_MODE`)还是视频闪光灯菜单键(`FlashFeatureKeys.OPTION_KEY_VIDEO_FLASH_MODE`)。 - 如果是视频模式且配置`KEY_SUPPORT_CAPTURE_FLASH_ONLY_MAIN_ZOOM`为true,则直接返回(即视频模式下,如果闪光灯仅支持主摄变焦,则不做更新?)。 - 记录之前的闪光模式(`previousFlashMode`)。 - **核心条件A**:`if (isFlashMenuEnableByZoom() && !isFlashMenuDisableByMonoFilter() && ...)` 这个条件非常重要,它判断闪光灯菜单是否可用(根据变焦、滤镜、预设冲突等)。如果可用,则进入A分支(保持或更新闪光灯状态);否则进入B分支(关闭闪光灯并更新UI)。 - **A分支(闪光灯可用)**: - 检查是否请求禁用闪光灯(`DATA_KEY_REQUEST_DISABLE_FLASH`),如果是则返回。 - 处理连拍模式下的闪光灯:如果当前是连拍且闪光灯为ON或AUTO,则不更新并返回。 - 检查电池电量是否导致闪光灯关闭(`checkFlashOffByBattery`),如果是则关闭闪光灯并更新UI。 - 根据配置`KEY_SUPPORT_FLASH_FULL_ZOOM`(闪光灯是否支持全变焦范围)和`isFlashOnlyNotSupportUltraWide()`(闪光灯是否不支持超广角)来更新菜单选项: - 如果支持全变焦,则添加ON和AUTO选项。 - 如果闪光灯不支持超广角,但在主摄变焦范围内,则添加ON和AUTO;否则移除。 - 其他逻辑:处理切换相机时的默认闪光模式、设置高亮清除、启用菜单选项等。 - 处理外接闪光灯的逻辑(`isSupportOutFlashFunction`)。 - **B分支(闪光灯不可用)**: - 如果支持全变焦(`KEY_SUPPORT_FLASH_FULL_ZOOM`),则: - 处理外接闪光灯的关闭(当变焦导致冲突时)。 - 如果之前的闪光模式是ON或AUTO,则临时禁用闪光灯(`setFlashTemporaryDisabled(true)`),并更新闪光状态为OFF。 - 如果之前是TORCH(手电筒)模式且是拍照模式,则恢复。 - 移除ON和AUTO选项(如果配置不支持卫星模式下的自动闪光)。 - 否则(不支持全变焦): - 如果之前闪光灯不是OFF,则关闭并临时禁用。 - 在UI上禁用闪光灯菜单,并设置为OFF。 - 隐藏提示视图。 最后重新加载菜单(`reloadSingleCameraSettingMenu`)。 ### 分支2(卫星模式关闭且resetForSatOff为true)内部分析 - 同样在UI线程执行,检查暂停状态。 - 确定`menuKey`(拍照或视频)。 - 重置临时禁用标志(`setFlashTemporaryDisabled(false)`)。 - 如果是拍照模式,并且(支持全变焦或闪光灯不支持超广角),且不支持长曝光,则: - 如果是长焦高分辨率模式且配置仅支持手电筒(`KEY_SUPPORT_TELE_HIGH_PICTURE_ONLY_TORCH`),则移除ON和AUTO选项。 - 否则添加ON和AUTO选项。 - 启用菜单选项,更新闪光状态,重新加载菜单。 ### 分支3(普通拍照模式下的特定条件)分析 - 获取当前闪光模式。 - 如果配置`KEY_ALL_FLASH_TYPE_ALL_ZOOM_SUPPORT`为false(即不是所有变焦都支持闪光灯)或者运动抓拍开启,并且开启了非卫星长焦或超长焦,则: - 如果当前闪光模式是ON或AUTO,则临时禁用闪光灯,更新状态为OFF,并在UI上设置为OFF。 - 移除ON和AUTO选项。 - 否则,恢复菜单选项(添加ON和AUTO),并更新闪光状态。 ### 分支4(人像模式)分析 - 仅重新加载闪光灯菜单。 ### 推测方法名意图 - `isSatOpen()`:卫星模式(多摄协同)是否开启。 - `isPaused()`:当前界面是否暂停(如Activity暂停)。 - `isCaptureModeType()`:当前是否是拍照模式(非视频)。 - `isFlashMenuEnableByZoom()`:根据当前变焦倍数,判断闪光灯菜单是否可用(核心方法,与问题直接相关)。 - `isFlashMenuDisableByMonoFilter()`:是否因单色滤镜(黑白滤镜)而禁用闪光灯菜单。 - `checkPresetConflictFlash()`:检查预设场景(如HDR)是否与闪光灯冲突。 - `isVideoRecording()`:是否正在录像。 - `isBurstCapturing()`:是否连拍中。 - `checkFlashOffByBattery()`:检查是否因电量低而关闭闪光灯。 - `withinSatMainRange()`:是否在卫星模式的主摄变焦范围内(如1x-3x)。 - `isZoomChangedBetweenMainAndTele()`:是否在主摄和长焦之间切换变焦。 - `isNoneSatUltraWideAngleOpen()`:非卫星模式的超广角是否开启(可能指直接使用超广角镜头)。 - `isMotionCaptureEnable()`:运动抓拍是否开启。 ### 问题原因聚焦 根据您的描述,视频模式开启闪光灯后,变焦到3x以上闪光灯自动关闭。核心原因在分支1的**核心条件A**:当变焦超过3x时,`isFlashMenuEnableByZoom()`返回`false`,进入B分支关闭闪光灯。因此,需要检查`isFlashMenuEnableByZoom()`的实现,它决定了变焦对闪光灯可用性的控制。 §§相关问题§§ 1. `isFlashMenuEnableByZoom()`方法的具体实现逻辑是什么?它如何根据变焦倍数判断是否启用闪光灯菜单? 2. 在视频模式下,变焦事件如何触发`updateFlashMenu`方法的调用?调用时参数`resetForSatOff`的值如何确定? 3. 配置项`KEY_SUPPORT_FLASH_FULL_ZOOM`在不同设备上的默认值是什么?它如何影响分支1中B分支的处理逻辑? 4. 除了变焦,还有哪些条件(如`isFlashMenuDisableByMonoFilter`、`checkPresetConflictFlash`)会导致进入B分支关闭闪光灯? 5. 如何修改代码以实现视频模式下高倍变焦(如3x以上)仍保持闪光灯开启?需要调整哪些条件判断? </think> 好的,我将从外到内分析`updateFlashMenu`方法中的主要条件分支结构,结合方法名称推测其功能逻辑: ### 外层条件结构分析(按执行顺序) 1. **`if (isDestroy())`** - **作用**:检查当前组件/Activity是否已被销毁,避免在销毁状态执行UI更新 - **推测**:`isDestroy()`可能来自Activity/Fragment生命周期状态检查 2. **`if (null != pipeline)`** - **作用**:获取视频超级EIS(电子防抖)在广角端的开启状态 - **关键方法**:`pipeline.getMethodResult(IFeatureDistributor.MethodType.EIS_IS_VIDEO_SUPER_EIS_WIDE_OPEN)` - **推测**:判断当前是否开启了广角端的视频超级防抖模式 --- ### 第一主分支:`if (isSatOpen())` **卫星模式开启时的处理逻辑**(推测`isSatOpen`指多摄协同工作模式) #### 子条件1:`if (isFlashMenuEnableByZoom() && ...)` **闪光灯可用时的处理**: ```java // 检查变焦是否允许闪光灯 if (isFlashMenuEnableByZoom() && !isFlashMenuDisableByMonoFilter() // 单色滤镜禁用检查 && !checkPresetConflictFlash() // 预设冲突检查 && !isVideoRecording()) // 录像状态检查 { // ... [更新闪光灯选项逻辑] } ``` - **核心方法解析**: - `isFlashMenuEnableByZoom()`:**关键方法**,判断当前变焦倍数是否支持闪光灯(您的问题核心) - `isFlashMenuDisableByMonoFilter()`:推测检查是否启用单色滤镜(通常禁用闪光灯) - `checkPresetConflictFlash()`:检查拍摄预设(如HDR)是否与闪光灯冲突 #### 子条件2:`else`分支 **闪光灯不可用时的处理**: ```java } else { if (CameraConfig.getConfigBooleanValue(ConfigKey.KEY_SUPPORT_FLASH_FULL_ZOOM)) { // ... [支持全变焦闪光时的禁用逻辑] } else { // ... [常规禁用逻辑] if (!FlashValue.OFF.equals(previousFlashMode)) { setFlashTemporaryDisabled(true); // 临时禁用标志 updateFlashState(FlashValue.OFF, true); // 实际关闭闪光灯 } mFlashUIManager.disableCameraSettingMenuOption(...); // UI禁用 } } ``` - **关键操作**:当条件不满足时强制关闭闪光灯并更新UI --- ### 第二主分支:`else if (resetForSatOff && ...)` **卫星模式关闭时的重置逻辑**(参数`resetForSatOff`为true时触发) ```java } else if (resetForSatOff && (getSupportFunction(FunctionKey.SAT_CAMERA) || getSupportFunction(FunctionKey.KEY_SUPPORT_NONE_SAT_ULTRA_WIDE_ANGLE)) && !isVideoSuperEisWideOpen && ...) { // ... [重置闪光灯菜单选项] setFlashTemporaryDisabled(false); // 解除临时禁用 mFlashUIManager.addCameraSettingMenuOptionItems(...); // 重新添加选项 } ``` - **推测意图**:退出多摄协同模式后恢复闪光灯可用状态 --- ### 第三主分支:`else if (CommonConstant.CapMode.CAPTURE_MODE_COMMON.equals(...)` **普通拍照模式下的特殊处理** ```java } else if (...) { if ((!KEY_ALL_FLASH_TYPE_ALL_ZOOM_SUPPORT || isMotionCaptureEnable()) && (isNoneSatTeleAngleOpen() || isNoneSatUltraTeleAngleOpen())) { // 长焦端强制关闭闪光灯 updateFlashState(FlashValue.OFF, true); mFlashUIManager.removeCameraSettingMenuOptionItems(...); } else { // 恢复闪光灯选项 mFlashUIManager.addCameraSettingMenuOptionItems(...); } } ``` - **关键方法**: - `isNoneSatTeleAngleOpen()`:推测检查是否启用非协同长焦镜头 - `isMotionCaptureEnable()`:运动捕捉模式检查 --- ### 第四主分支:`else if (CommonConstant.CapMode.CAPTURE_MODE_PORTRAIT.equals(...))` **人像模式处理** ```java } else if (...) { ThreadManager.runOnUiThread(() -> { mFlashUIManager.reloadSingleCameraSettingMenu(...); // 仅重载菜单 }); } ``` - **特点**:最单的分支,仅刷新UI不修改状态 --- ### 关键配置项解析 1. **`KEY_SUPPORT_FLASH_FULL_ZOOM`** 控制闪光灯是否支持全变焦范围,为`false`时会根据变焦禁用 2. **`KEY_SUPPORT_CAPTURE_FLASH_ONLY_MAIN_ZOOM`** 视频模式下主摄变焦专属配置(触发return跳过更新) 3. **`KEY_SUPPORT_TELE_HIGH_PICTURE_ONLY_TORCH`** 长焦高分辨率模式下手电筒专属支持 --- ### 您的问题定位 视频模式变焦到3x以上闪光灯关闭的直接原因是: 1. 变焦状态改变触发`updateFlashMenu`调用 2. 在卫星模式开启时,`isFlashMenuEnableByZoom()`返回`false` 3. 进入`else`分支执行`updateFlashState(FlashValue.OFF, true)` 4. 同时更新UI为禁用状态
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值