You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌入式板LED随机点亮问题:数组复制后出现重复值

解决嵌入式LED随机点亮时数组重复值的问题

嘿,我来帮你搞定这个重复值的问题!你现在遇到的核心问题是随机选取/洗牌逻辑没做到真正的无重复,尤其是在循环重复执行时,可能随机数生成或者数组处理的环节出了问题。下面我给你拆解原因,再提供两种可靠的实现方案。

为什么会出现重复值?

大概率是这两个原因之一:

  • 随机数生成器没正确初始化:如果每次循环的随机种子一样,生成的随机序列会完全重复,导致选出来的LED组合也重复。
  • 洗牌/选取逻辑有漏洞:比如简单随机选元素时没跳过已选的,或者洗牌算法本身有问题,导致元素重复出现。

可靠的实现方案

假设你的原始LED位值数组是类似这样的(对应8个LED的位掩码):

const uint8_t original_leds[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

方案一:Fisher-Yates洗牌算法(推荐)

这是业界公认的无偏洗牌算法,能保证每个排列的概率相等,而且绝对不会出现重复值。思路是先复制原始数组到临时数组,打乱整个数组后取前4个元素。

#include <stdint.h>
#include <stdlib.h>

// 定义常量
#define NUM_LEDS 8
#define SELECT_NUM 4
// 原始LED位掩码数组
const uint8_t original_leds[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

// 初始化随机数种子(程序开头调用一次)
void init_rand(void) {
    // 嵌入式系统如果没有time(),用硬件定时器值或随机数发生器做种子
    // 示例:srand((unsigned int)TIMER1->CNT);
    srand((unsigned int)time(NULL)); 
}

// 获取随机且无重复的4个LED位值,存入result数组
void get_random_leds(uint8_t result[]) {
    uint8_t temp_leds[NUM_LEDS];
    // 复制原始数组到临时数组,避免修改原数组
    for (int i = 0; i < NUM_LEDS; i++) {
        temp_leds[i] = original_leds[i];
    }

    // Fisher-Yates洗牌
    for (int i = NUM_LEDS - 1; i > 0; i--) {
        int rand_idx = rand() % (i + 1); // 生成0到i的随机索引
        // 交换当前元素和随机选中的元素
        uint8_t temp = temp_leds[i];
        temp_leds[i] = temp_leds[rand_idx];
        temp_leds[rand_idx] = temp;
    }

    // 取前4个打乱后的元素到结果数组
    for (int i = 0; i < SELECT_NUM; i++) {
        result[i] = temp_leds[i];
    }
}

int main(void) {
    init_rand();
    uint8_t selected_leds[SELECT_NUM];
    int repeat_times = 10; // 重复x次,这里设为10示例

    while (repeat_times-- > 0) {
        get_random_leds(selected_leds);
        // 这里执行点亮LED的操作,比如把选中的位值合并输出到GPIO
        // GPIO->OUT = selected_leds[0] | selected_leds[1] | selected_leds[2] | selected_leds[3];
        // 加个延时让效果可见,比如delay_ms(500);
    }
    return 0;
}

方案二:标记已选元素法

如果不需要打乱整个8元素数组,只是随机选4个唯一的LED,可以用标记数组记录已选元素,避免重复选取。

void get_random_leds(uint8_t result[]) {
    uint8_t is_used[NUM_LEDS] = {0}; // 标记是否已选中,初始为0(未选)
    int selected_count = 0;

    while (selected_count < SELECT_NUM) {
        int rand_idx = rand() % NUM_LEDS;
        if (!is_used[rand_idx]) { // 只选未被标记的LED
            result[selected_count++] = original_leds[rand_idx];
            is_used[rand_idx] = 1; // 标记为已选
        }
    }

    // 可选:如果需要打乱这4个LED的顺序,再做一次小洗牌
    for (int i = SELECT_NUM - 1; i > 0; i--) {
        int j = rand() % (i + 1);
        uint8_t temp = result[i];
        result[i] = result[j];
        result[j] = temp;
    }
}

关键注意事项

  • 随机种子初始化很重要:嵌入式系统如果没有实时时钟,别用time()做种子,改用硬件定时器的当前计数值、ADC采样的噪声值或者板载硬件随机数发生器,确保每次程序运行的随机序列都不一样。
  • 别修改原始数组:每次操作都用临时数组或者标记法,保证原始的LED位值数组始终可用,不会被循环修改破坏。

内容的提问来源于stack exchange,提问作者Peter Fred

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:20:36