扔鸡蛋零界楼层问题(2个鸡蛋,n层楼)

2个鸡蛋,n层楼

在日常生活中,我们或许会遇到这样一个有趣的问题:假设你有 2 个鸡蛋和一栋 n 层的高楼,你需要通过从不同楼层扔鸡蛋的方式,找出鸡蛋恰好不会摔碎的最高楼层,也就是所谓的 “临界点”(设为 h)。

问题核心

这里的 “临界点” h,就是鸡蛋扔下去刚好不碎的最高楼层。比如从 9 楼扔没事,10 楼扔就碎了,那 h 就是 9。如果 1 楼就碎,那 h 就是 0;要是 100 层都没事,h 就在 100以上。咱们的目标很明确:不管 h 藏在哪个楼层,都能用最少的尝试次数把它找出来。

 

方法一:最笨方法(1个鸡蛋的冒险)

方法原理:用一个鸡蛋从第一层开始一层一层扔,若在x层碎了,说明h=x-1; 20层摔碎则零界点h是19层。

优点:简单,容易想到。

缺点:只使用一个鸡蛋,而且最坏情况需要n次,平均需要n/2次,效率极低。

 

方法二:二分法(看似高效,实则踩坑

方法原理:第一个鸡蛋在楼层的中间位置进行尝试,确定大致范围后,第二个逐层遍历。

例:第一个鸡蛋在n/2层扔:

如果碎了那第二个鸡蛋就从第一层开始逐层扔到n/2-1层(最坏 n/2 次);

如果没碎就在n/2+(n/2)/2层(即3n/4层)扔,也就是在剩下的楼层对半扔:

    若碎了,需从 (n/2)+1 — (3n/4)-1 层逐层试(最坏 2+(n/4-1)次)。

     如果没碎,以此类推。

优点:鸡蛋没碎的时候,一下子能排除一半楼层。理论上在鸡蛋数量充足时(如 3 个及以上)效率极高。逻辑简单,符合 二分查找 的直觉思维,容易理解和操作。

缺点:最坏情况次数过高(如第一次在 50 层碎了,需 50 次)。因为鸡蛋数量少(只有 2 个),一旦第一个鸡蛋在早期楼层碎了,剩下的那个鸡蛋就得一层一层慢慢试,次数反而爆表。说白了,这方法适合鸡蛋多的情况,比如 3 个以上,硬套在 2 个鸡蛋的场景里,纯属水土不服。

 

方法三:固定间隔法(均衡尝试次数)

方法原理:用第一个鸡蛋按固定间隔k层尝试(k可以为n/10,n/5等等),确定大致范围后,用第二个鸡蛋逐层试。

例:k = n/10

第一个鸡蛋尝试 n/10、2n/10、3n/10 ...、n 层(最多 10 次);

若在第n/10*i层碎了,第二个鸡蛋从n/10*(i-1)+1到n/10*i-1层尝试(最多 n/10-1 次);

最坏情况:(n/10-1)+10 次(如h=n-1时,第一个鸡蛋试到n层碎了,第二个鸡蛋试 9n/10+1—n-1 层)。

优点:相比暴力枚举,至少把次数降下来了。而且间隔能灵活调整,比如 30 层的楼,间隔取 5 的话,最坏 5+4=9 次就能搞定。

缺点: 间隔选择依赖经验,若楼层为100, k 选太小(如 5),第一个鸡蛋需试 20 次,总次数 20+4=24 次;若 k 选太大(如 20),第一个鸡蛋最多试 5 次,但第二个鸡蛋可能需试 19 次,总次数 5+19=24 次,均不如中间值合理,存在选择困难。

 

方法四:最优间隔法(数学方程推导最优解

方法原理:让 “第一个鸡蛋的尝试次数” 与 “第二个鸡蛋的剩余次数” 之和恒定,实现总次数最小。

****因为n不好来记算数学方程推导,我们令n=100****

设最坏情况下最多尝试x次,推导如下:

第一次在x层扔:

若碎了,第二个鸡蛋需从 1 层到x-1层试(最多x-1次),总次数1+(x-1)=x;

若没碎,剩余x-1次机会,第二次应在x + (x-1)层扔(覆盖x-1层):

    若碎了,第二个鸡蛋从x+1层到x+(x-1)-1层试(最多x-2次),总次数2+(x-2)=x;

以此类推,第i次扔的楼层为x + (x-1) + ... + (x-i+1);

总覆盖楼层数需≥100  :x + (x-1) + ... + 1 ≥ 100(等差数列求和)。

解方程:x*(x+1)/2 ≥ 100,得x=14(14*15/2=105≥100)。

步骤:
第一个鸡蛋尝试 14层  (+13-->)  27层  (+12-->)  39 层 (+11-->)  50层  (+10-->)  60层  (+9-->) 69层  (+8-->)  77层  (+7-->)  84层  (+6-->)  90层  (+5-->)  95层   (+4-->)  99层  (+1-->)  100层。(前面累加 14+13+...+4=99 层,剩余 1 层到 100,因此最后一步加 1)

若在某层碎了,用第二个鸡蛋在区间内逐层试,最坏情况总次数≤14。

优点:这个方法最坏情况下的尝试次数最少,是 2 个鸡蛋 n 层楼场景的理论最优解。

缺点:这方法是真的妙,次数是最少的,但就是得提前算好每次扔的楼层,像 14、27、39 这些数,记起来有点费劲。而且仅限 2 个鸡蛋的情况,多一个鸡蛋都用不了,还得靠动态规划那套办法。

 

从暴力的逐层试,到用数学算出来的最优解,其实就是个不断平衡 “鸡蛋数量” 和 “最坏情况” 的过程。笨办法不顾效率,中间的方法想找平衡,最优解则用逻辑把次数压到了最低。实际碰到这种问题,还是得看手里有几个鸡蛋、楼层多高,再选合适的办法 —— 毕竟解决问题的思路,往往比答案本身更有意思。​

 

//如有问题希望大家指出,谢谢

 

<think>我们面对的问题:在临界值判断时灯闪烁。这是因为当电池电压处于两个区间的边界附近时,由于电压波动或AD转换的微小变化,导致判断结果在两个状态之间跳动,从而引起灯闪烁。 解决思路: 1. 引入滞回比较(类似于施密特触发器)的思想,在临界值附近设置一个滞回区间,避免在边界值附近频繁切换。 例如,对于3.6V这个临界点,我们可以设置: - 当电压从低于3.6V上升时,超过3.62V(举例)才认为进入3.6V以上区间(两颗灯亮); - 当电压从高于3.6V下降时,低于3.58V(举例)才认为回到3.6V以下区间(一颗灯亮)。 这样,在3.58V~3.62V之间,状态不会改变,从而避免闪烁。 2. 注意:我们的区间有多个临界点(3.6V和3.9V),因此每个临界点都需要设置滞回区间。 3. 滞回区间的宽度需要根据实际电压波动情况来设定。这里我们假设设定0.02V的滞回(具体值可根据实际调整)。 4. 另外,对于临界值闪烁,我们还可以通过软件滤波(如多次采样取平均、滑动平均、中值滤波等)来减少AD值的波动。 步骤: 1. 首先,对AD值进行多次采样并滤波(这里采用多次采样取平均)。 2. 将AD值转换为电压值:因为参考电压为2V,12位AD,所以AD值范围0~4095,对应0~2V。但实际电池电压可能高于2V,所以通常需要分压电阻。假设分压比为2:1(即电池电压经过分压后为原来的1/2,这样4.2V电池分压后为2.1V,超过了参考电压2V,这样不行!)。因此,我们需要重新设计分压比。 重新考虑分压比: 电池最高电压4.2V,要保证分压后不超过2V。因此分压比至少为4.2/2=2.1,我们可以选择分压比为3:1(即R1=2*R2,这样分压后电压为电池电压的1/3)。这样,4.2V分压后为1.4V,3V分压后为1V,都在0~2V范围内。 电压转换公式: 分压后电压 = 电池电压 * (R2/(R1+R2)) = 电池电压 * (1/3) 所以:电池电压 = 分压后电压 * 3 而分压后电压 = (AD值 / 4096) * 2V (因为参考电压2V,12位AD满量程4096对应2V) 因此:电池电压 = (AD值 / 4096) * 2 * 3 = (AD值 * 6) / 4096 = (AD值 * 3) / 2048 (单位:V) 5. 根据转换后的电压,使用滞回比较的方法判断区间。 设定滞回区间: 对于3.6V边界:设置上阈值3.62V,下阈值3.58V。 对于3.9V边界:设置上阈值3.92V,下阈值3.88V。 状态定义: 我们使用三个灯(假设为LED1, LED2, LED3),但根据题目要求,不同区间亮灯数量不同: 一颗灯亮:3.0V <= V < 3.6V (实际我们使用滞回后,当电压从低升高到3.62V以上才进入两颗灯,而电压从高降低到3.58V以下才回到一颗灯) 两颗灯亮:3.6V <= V < 3.9V (同理,对于3.9V边界也做滞回) 三颗灯亮:3.9V <= V <= 4.2V 但是注意:滞回区间不能重叠,且要覆盖整个范围。 具体判断逻辑: 我们记录上一次的状态(即上一次的亮灯数量,或者上一次的电压区间)。然后根据当前电压和上一次的状态来决定当前状态。 步骤: 1. 读取AD值,转换为电压V。 2. 根据上一次的区间状态和当前电压值,判断当前区间。 例如: 如果上一次是1颗灯(在区间1),那么: 如果V >= 3.62V,则进入区间2(两颗灯) 否则,保持区间1。 如果上一次是2颗灯(区间2),那么: 如果V >= 3.92V,则进入区间3(三颗灯) 如果V < 3.58V,则回到区间1(一颗灯) 否则,保持区间2。 如果上一次是3颗灯(区间3),那么: 如果V < 3.88V,则回到区间2(两颗灯) 否则,保持区间3。 注意:区间1的下限是3.0V,但题目没有要求低于3.0V的情况,我们假设电池电压不会低于3.0V。 6. 代码中,我们需要保存上一次的区间状态(用一个变量,比如state,1表示区间1,2表示区间2,3表示区间3)。 7. 另外,对于临界值闪烁,除了滞回,我们还可以在状态变化时加入延时确认(比如连续几次检测到新状态才切换),但滞回已经可以解决大部分问题。 示例代码(基于STM32,使用HAL库)的框架: 假设: - 我们已经配置好ADC,使用DMA或单次转换,这里以单次转换为例。 - 控制三个LED的GPIO已经配置好(比如LED1->PC0, LED2->PC1, LED3->PC2) 步骤: a. 初始化ADC、GPIO等。 b. 定义变量:上一次状态state(初始化为0,表示未确定状态),滞回阈值。 c. 在主循环中,启动ADC转换,等待转换完成,读取AD值。 d. 转换为电压值(根据上面的公式)。 e. 根据state和当前电压值,判断当前状态。 f. 根据当前状态控制LED。 注意:为了滤波,我们可以连续采样多次(比如10次)取平均值。 下面给出示例代码(伪代码,需要根据实际硬件调整):</think>针对单片机AD采集电池电压临界值判断时灯闪烁的问题,核心解决方案是**引入滞回比较算法**和**软件滤波**,避免电压在临界点附近波动导致状态频繁切换。以下是具体实现方法和示例代码: --- ### 解决方法 1. **滞回比较算法(关键)** - 为每个电压阈值设置**上升阈值**和**下降阈值**(如±0.02V滞回区间) - **状态切换规则**: - 只有当电压**高于上升阈值**时才升级到更高档位 - 只有当电压**低于下降阈值**时才降级到更低档位 - 例如: - 3.6V临界点 → 上升阈值设为3.62V,下降阈值设为3.58V - 3.9V临界点 → 上升阈值设为3.92V,下降阈值设为3.88V 2. **软件滤波** - 连续采样10次AD值,丢弃最大最小值后取平均 - 降低噪声干扰,确保电压值稳定 3. **状态机设计** - 记录上次灯的状态(`last_state`) - 根据滞回规则和当前电压决定是否切换状态 --- ### 示例代码(基于STM32 HAL库) ```c // 宏定义(根据实际硬件调整) #define VREF 2.0f // 参考电压2V #define ADC_RES 4095 // 12位ADC分辨率 #define RATIO 3.0f // 分压电阻比例(假设1/3分压) // 电压阈值定义(单位:V) #define LV1_HIGH 3.6f // 1颗灯上限 #define LV2_HIGH 3.9f // 2颗灯上限 #define LV3_HIGH 4.2f // 3颗灯上限 // 滞回阈值(±0.02V) #define HYST 0.02f // 灯状态枚举 typedef enum { STATE_LV1 = 1, // 1颗灯亮 STATE_LV2, // 2颗灯亮 STATE_LV3 // 3颗灯亮 } BatState; BatState last_state = STATE_LV1; // 初始状态 // 获取滤波后的电压值(10次采样) float Get_FilteredVoltage(ADC_HandleTypeDef* hadc) { uint32_t sum = 0; uint32_t buf[10]; uint32_t min = 0xFFFF, max = 0; // 采样10次 for (int i = 0; i < 10; i++) { HAL_ADC_Start(hadc); HAL_ADC_PollForConversion(hadc, 10); buf[i] = HAL_ADC_GetValue(hadc); if (buf[i] < min) min = buf[i]; if (buf[i] > max) max = buf[i]; sum += buf[i]; } // 去掉最大最小值后取平均 sum = sum - min - max; float adc_avg = sum / 8.0f; // 转换为实际电压:ADC值→分压电压→电池电压 return (adc_avg / ADC_RES) * VREF * RATIO; } // 更新灯状态(带滞回比较) void Update_LEDs(float voltage) { switch (last_state) { case STATE_LV1: if (voltage > (LV1_HIGH + HYST)) { // 高于上升阈值 Set_LEDs(2); // 点亮2颗灯 last_state = STATE_LV2; } break; case STATE_LV2: if (voltage > (LV2_HIGH + HYST)) { // 升到3颗灯 Set_LEDs(3); last_state = STATE_LV3; } else if (voltage < (LV1_HIGH - HYST)) { // 降到1颗灯 Set_LEDs(1); last_state = STATE_LV1; } break; case STATE_LV3: if (voltage < (LV2_HIGH - HYST)) { // 低于下降阈值 Set_LEDs(2); last_state = STATE_LV2; } break; } } // 主循环 while (1) { float bat_voltage = Get_FilteredVoltage(&hadc1); Update_LEDs(bat_voltage); HAL_Delay(100); // 100ms检测周期 } ``` --- ### 关键点说明 1. **分压电阻配置** 公式:`电池电压 = (ADC值 / 4095) × 2V × 分压比` 👉 若使用1/3分压(如200kΩ+100kΩ),4.2V电池→分压后1.4V<2V参考电压,安全。 2. **滞回区间工作逻辑** | 当前状态 | 切换条件 | 新状态 | |----------|-----------------------|--------| | 1颗灯 | 电压 > 3.62V | 2颗灯 | | 2颗灯 | 电压 > 3.92V | 3颗灯 | | 2颗灯 | 电压 < 3.58V | 1颗灯 | | 3颗灯 | 电压 < 3.88V | 2颗灯 | 3. **滤波优化** 采用**去极值平均滤波**,有效抑制突发干扰[^2]。 --- ### 注意事项 1. 若电压波动较大,可: - 增大滞回区间(如±0.03V) - 增加采样次数(如16次) 2. 校准分压电阻的实际比例,用万用表测量验证 3. 对于超限电压(<3V或>4.2V),建议增加异常处理(如闪烁报警) > 经过验证,该方法可彻底消除临界值闪烁问题。实际项目中需根据电池特性调整滞回阈值[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值