在C语言中高效地计算三个值的平均数

7
我们正在读取一些引脚的信号,并根据这些读数设置更多的事件。
为了保险起见,我想对引脚进行3次采样,比较这三个值并使用最常见的值(例如,如果样本A是1,B是3和C是1,则我想使用1;如果A、B和C都是2,则使用2,但如果A是1,B是2和C是3,则我想再次获取三个样本)。
目前我正在使用:
int getCAPValues (void)
{
//  Get three samples to check CAP signals are stable:  

        uint32_t x = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;           // First set of CAP values
        for (uint32_t i = 0; i < 7; i++) dummy = i;                                                     // Pause
        uint32_t y = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;           // second set
        for (uint32_t i = 0; i < 7; i++) dummy = i;                                                     // Pause
        uint32_t z = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;           // third set

        if (x == y) || (x == z)
        {
            //use the x value
        }
        else if (y == z)
        {
            // use the y value
            x = y;
        }
        else
        {           
            x = -1;
        }

    return x;
}

但是这种方法似乎不太高效,有更好的方法吗?

这是在C语言中使用SAMD21 Xplained Pro开发板时的情况。

编辑:

根据答案,我已经改变了代码,只有在需要读取“z”值时才会读取,并使用delay_us()代替虚假循环:

int getCAPValues (void)
{
//  Get three samples to check CAP signals are stable:  

        uint32_t x = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;           // First set of CAP values
        delay_us(1);
        //for (uint32_t i = 0; i < 7; i++) dummy = i;                                                   // Pause
        uint32_t y = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;           // second set
        // Using most common value, or error code of -1 if all different

        if (!(x == y))
        {
            delay_us(1);
            //for (uint32_t i = 0; i < 7; i++) dummy = i;                                               // Pause
            uint32_t z = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;       // third set
            if (x == z)
            {
                // use the x/z value
                return x;
            }
            else if (y == z)
            {
                // use the y/z value
                return y;
            }
            else
            {           
                return -1;
            }
        }
    return x;
}

7
编译器可能会将 for (uint32_t i = 0; i < 7; i++) dummy = i; 优化掉,因为它是一个无操作。建议使用 sleep 替代。 - Bathsheba
1
听起来更适合于codereview.stackexchange.com。 - AndersK
2
我认为价值选择的逻辑已经无法进一步改进了。你为什么认为它效率低下? - undur_gongor
2
除此之外,如果 x == y,测试 z 就没有意义了,但除此之外,它看起来对我来说相当优化。也许 OP 希望函数在每次迭代中大致花费相同的时间。不过,你需要修复那个“延迟”循环。 - Bathsheba
2
你正在计算“众数”,而不是“平均数”。 - aschepler
显示剩余3条评论
2个回答

3

如果 x==y,你将使用x的值。因此,在这种情况下,您可以避免第三次读取。

我不知道您的值有多么不稳定,但是如果争议的值确实很少,那么避免第二个延迟可能会有效地将性能提高近一倍。

确实,如果它们不罕见,整个理论基础可能是无效的。

int getCAPValues (void)
{
//  Get three samples to check CAP signals are stable:  

        uint32_t x = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;           // First set of CAP values
        for (uint32_t i = 0; i < 7; i++) dummy = i;                                                     // Pause
        uint32_t y = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;           // second set
        if(x!=y){
            //x & y are different. Get a tie-breaker...
            for (uint32_t i = 0; i < 7; i++) dummy = i;                                                     // Pause
            uint32_t z = (PORT->Group[IN_PORT_CAP].IN.reg & IN_PORT_CAP_MASK) >> IN_PORT_CAP_PIN;           // third set
            if (y == z) {
                // use the y value
                x = y;
            } else if(x!=z){
                //tie-breaking failed...
                x=-1;
            }
        }
        return x;
}

附注:我认为你应该使用 `usleep()` 而不是虚假循环。这取决于你的平台上可用的内容。


1
也许可以使用 usleep() 代替 sleep()。此外,当y==z时,可以直接返回y(而无需分配x)。对于* -1* 也一样。 - Déjà vu
@AndyHall,我错过了一个微小的调整,避免了最后一个if语句,当“y!= z”时。祝愉快。 - Persixty

0

如果你可以假设xyz都是01。那么你只需要使用x+y+z>1(如果它们中的大多数是1,则总和大于1,比较结果为1,否则为0)。

否则,你的(第二个)解决方案可能是最有效的。至少在你不知道结果概率的情况下是这样。此外,通过睡眠一微秒,这将是最耗时的操作。

但你应该考虑到,由于读取可能是非易失性的,如果你实际上不进行第三次读取,结果可能会有所不同。此外,你也应该考虑是否需要进行完整的重新读取。例如,如果你发现从端口的前三次读取都不同,并且第四次读取等于第二次或第三次,则可以将其用作优化。例如:

 x = read_port();
 y = read_port();

 if( x == y )
    return x;

 for(;;) {
    z = read_port();
    if( z == x || z == y )
        return z;
    x = y;
    y = z;
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接