【问题标题】:how to find max value with less iterations如何用更少的迭代找到最大值
【发布时间】:2012-11-07 14:13:14
【问题描述】:

我将信号的相位从 0 度更改为 360 度以获得最大电压值。因为如果我改变信号的相位,电压也会改变。我有下面的代码来找到最大值。

void Maxphase(float *max, unsigned int *index) 
{
*max = 0.0;
float value;
unsigned int i, data;
for (i=0;i<=360;i++) 
{              
    phaseset(i); 
    delay_ms(100);
    data = readvalue(); 
    value = voltage(mux1);    
    if(value > *max)   //find max value 
    { 
        *max = value;    //max voltage 
        *index = i;   
    }  
}                           
}

从上面的代码中,我在 38 秒(360*100)后得到最大值(电压),因为对于每次读取操作,我的设备都需要 100 毫秒的延迟。这太大了,我无法更改硬件,因此我想通过优化软件在 2 到 3 秒内获得最大值。 然后我尝试了休闲代码。

void Maxphase(float *max1, unsigned int *index1) 
{
  max = 0.0;
  float value;
  unsigned int i,j,data;
  for (i=0;i<=360;i+=10) 
   {              
    phaseset(i); 
    delay_ms(100);
    data = readvalue(); 
    value = voltage(mux1);    
    if(value > max)   //find max value 
    { 
        max = value;    //max voltage 
        index = i;   
    }  
   }    
   *max1=max;
   *index1=index;
   for (i=*index1-9;i<=*index1+9;i+=1) 
     {       
     j=i;       
    phaseset(j); 
    delay_ms(100);
    data = readvalue(); 
    value = voltage(mux1);    
    if(value > *max1)   //find max value 
    { 
        *max1 = value;    //max voltage 
        *index1 = i;   
    }  
    }                         
}

我已将时间从 45 秒减少到 7 秒。我将迭代次数从 360 减少到 54(54*100)。我想将它减少 7 秒到 2 秒。

谁能帮助我更好的算法,我可以在 2 秒内获得 (0 到 360) 的最大值。

我已经通过改变相位使用示波器测量了电压值。我在下面写了它如何随相位改变电压。

Phase (degree)     voltage(max)
  0             0.9mv

 45             9.5mv

 90             9.0mv

135             0.9mv

180             292mv

225             601mv

270             555mv

315             230mv

360             0.9mv

我是 C 编程新手。谁能提供最佳算法的示例代码。

【问题讨论】:

  • 您可以从仅测量 {0,90,180,270) 开始,然后在前 2 个值 {180,270} -> 225 之间的象限内插值。等:二分搜索。
  • 你在做什么?可能有更好的方法来获得最大电压,需要的相位可能可以根据一些公式而不是使用简单的试验/错误来计算。
  • @wildplasser 我相信您的评论是正确的方法,您应该将其发布为答案。
  • 可能会有所不同吗?我的意思是你已经测量了它并打印了表明你认为它是稳定的值。那么为什么需要搜索整个域(0..360)?
  • 顺便说一下,for 循环中的 (i=0;i<=360;i+=10)。您应该只检查 0-360,您目前正在检查 0-370。

标签: c interpolation binary-search polar-coordinates


【解决方案1】:

Golden section search 可能是您所追求的。它很有效,但仍然很简单。

如果您想要更快、更复杂的东西,可以使用Brent's method

【讨论】:

  • 我不认为这些是正确的算法。 AC voltage 可以描述为正弦波,因此数据具有确定性。
  • @Lundin:OP没有提到交流电压,电压值也不会变成负数,但可能存在可以利用的数学关系。
  • 电压和相位是仅在交流电源中同时出现的术语。
  • 你从哪里得到这个? @Lundin
  • @alk 关于电子电源的基础知识?
【解决方案2】:

如果您可以确定 360 度上只有一个最高点,您就可以进行递归分治。

您首先查看例如在 0, 180, 270。假设您找到答案是 180 + 270 一起具有最高值。比你开始看 210.... 哪一边更高?等等……

【讨论】:

    【解决方案3】:

    利用此处的各种 cmets 和建议,我展示了这段未经测试的代码。我不知道这是否有效或者是对现有源的改进,但无论如何尝试都很有趣:

    extern void phaseset(int);
    extern void delay_ms(int);
    extern float readvalue();
    extern float voltage(int);
    extern int mux1;
    
    float probe(int phase)
    {
        float data;
        phaseset(phase);
        delay_ms(100);
        data = readvalue(); /* data is ignored? */
        return voltage(mux1); /* mux1? */
    }
    
    /* helper routine, find the max in a given range [phase1, phase2] */
    void maxphase_aux(int phase1, float vol1, int phase2, float vol2, int *phaseret, float *volret)
    {
        float xvol1 = 0, xvol2 = 0;
        int xphase1 = -1, xphase2 = -1;
    
        /* test the voltage in the middle */
        int phasem = abs(phase2 - phase1) / 2;
        float volm = probe(phasem);
    
        if (volm > vol1 && volm > vol2) {
            /* middle point is the highest so far,
             * search left and right for maximum */
            *volret = volm;
            *phaseret = phasem;
    
            maxphase_aux(phase1, vol1, phasem, volm, &xphase1, &xvol1);
            maxphase_aux(phase2, vol2, phasem, volm, &xphase2, &xvol2);
        } else if (volm < vol1 && volm > vol2) {
            /* vol1 is the highest so far,
             * search between volm and vol1 for maximum */
            maxphase_aux(phase1, vol1, phasem, volm, &xphase1, &xvol1);
        } else if (volm > vol1 && volm < vol2) {
            /* vol2 is the highest so far,
             * search between volm and vol2 for maximum */
            maxphase_aux(phase2, vol2, phasem, volm, &xphase2, &xvol2);
        } else {
            /* not possible? */
            return;
        }
    
        if (xvol1 > volm) {
            *volret = xvol1;
            *phaseret = xphase1;
        }
    
        if (xvol2 > volm) {
            *volret = xvol2;
            *phaseret = xphase2;
        }
    }
    
    void maxphase(int *phaseret, float *volret)
    {
        float v0 = probe(0);
        float v360 = probe(360);
        maxphase_aux(0, v0, 360, v360, phaseret, volret);
    }
    

    【讨论】:

    • 我试图理解但我对函数“void maxphase_aux(int phase1, float vol1, int phase2, float vol2, int *phaseret, float *volret)”感到困惑。你能稍微描述一下这段代码是如何工作的吗?
    • @user1759248 我添加了一些 cmets 来澄清工作。正如我已经说过的,我不知道这是否有效。其中可能存在一些错误。
    • 感谢您的代码。我会尝试理解并测试它。
    【解决方案4】:

    更新:2012-11-10。

    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    
    #define FAKE_TARGET 89
    unsigned fake_target = FAKE_TARGET;
    
    float probe_one(unsigned int phase);
    void Maxphase(float *max, unsigned int *index);
    
    void Maxphase(float *max, unsigned int *index)
    {
    
    unsigned int aim, idx, victim;
    
    struct best {
        unsigned pos;
        float val;
        } samples[4] = {{0, 0.0}, };
    
        for (aim = 0;aim < 360;aim += 90) {
            idx=aim/90;
            samples[idx].pos = aim;
            samples[idx].val = probe_one(samples[idx].pos);
            if (!idx || samples[idx].val < samples[victim].val ) victim = idx;
            }
    
            /* eliminate the weakist postion, and rotate the rest,
            ** such that:
            ** samples[0] := lower boundary.
            ** samples[1] := our best guess
            ** samples[2] := upper boundary
            ** samples[3] := scratch/probe element
            */
        fprintf(stderr, "Victim=%u\n", victim );
        switch(victim) {
        case 0: samples[0] = samples[1]; samples[1] = samples[2]; samples[2] = samples[3]; break;
        case 1: samples[1] = samples[3]; samples[3] = samples[0]; samples[0] = samples[2]; samples[2] = samples[3]; break;
        case 2: samples[2] = samples[1]; samples[1] = samples[0]; samples[0] = samples[3]; break;
        case 3: break;
        }
    
    
            /* Calculation is easier if the positions are increasing.
            ** (We can always perform the modulo 360 if needed)
            */
        if (samples[0].pos > samples[1].pos ) samples[1].pos  += 360;
        if (samples[1].pos > samples[2].pos ) samples[2].pos  += 360;
    
        while( 1) {
            int step;
    
            step = samples[2].pos - samples[0].pos;
            if (step < 3) break;
    
            do    {
                fprintf(stderr, "\n[%u %u %u] Diff=%d\n"
                , samples[0].pos , samples[1].pos , samples[2].pos , step);
                if (step > 0) step++; else step--;
                step /= 2;
                aim = (samples[0].pos + step ) ;
                    /* avoid hitting the middle cell twice */
                if (aim %360 != samples[1].pos %360) break;
                step += 1;
                aim = (samples[0].pos + step ) ;
                if (aim %360 != samples[1].pos %360) break;
                step -= 2;
                aim = (samples[0].pos + step ) ;
                break;
                } while(0);
    
            fprintf(stderr, "Step=%d Aim=%u, Idx=%u\n",step, aim,idx );
    
            samples[3].pos = aim;
            samples[3].val = probe_one( samples[3].pos );
    
            victim= (samples[3].pos > samples[1].pos ) ? 2 : 0;
            if (samples[3].val > samples[1].val) idx= 1; else idx = victim;
    
            fprintf(stderr, "Victim=%u, TargetIdx=%u\n", victim, idx );
                    /* This should not happen */
            if (samples[3].val < samples[victim].val) break;
            if (idx != victim) samples[2-victim] = samples[idx];
            samples[idx] = samples[3];
            }
    
        *max = samples[1].val;
        *index = samples[1].pos % 360;
    }
    float probe_one(unsigned int phase)
    {
        float value;
    
    #ifdef FAKE_TARGET
        int dif;
            dif = fake_target-phase;
        if (dif < -180) dif = 360+dif;
        else if (dif > 180) dif = 360-dif;
            /* value = 1.0 / (1 + pow(phase-231, 2)); */
            value = 1.0 / (1 + pow(dif, 2));
            fprintf(stderr, "Target = %d: Probe(%d:%d) := %f\n", fake_target, phase, dif, value );
            sleep (1);
    #else
        unsigned int data;
        phase %= 360;
        phaseset(phase);
        delay_ms(100);
        data = readvalue();  // what is this ?
        value = voltage(mux1);
    #endif
    
    return value;
    }
    
    int main(int argc, char **argv)
    {
    float value;
    unsigned int index;
    
    if (argv[1]) sscanf (argv[1], "%u", &fake_target);
    fake_target %= 360;
    
    Maxphase(&value, &index) ;
    printf("Phase=%u Max=%f\n", index, value );
    return 0;
    }
    

    【讨论】:

    • 我对您的代码感到困惑,您能否再给我一些解释,使用哪种方法。当我在 CodeVision AVR 中运行您的代码时,我收到类似“未定义符号 fprintf”的错误。如果我删除 fprintf,那么我会收到警告,例如“局部变量“frist”在其设置之前被使用。
    • 这只是普通的二进制搜索。如果您的平台没有标准输出:那是您的问题。顺便说一句:代码还不正确:如果两个最佳点位于 0/360/北极点不同侧的位置,则会失败。我正在做。顺便说一句:你的目标机器上有足够的内存吗?
    • 多少内存,我想我有足够的内存。 “如果两个最佳点位于 0/360/北极点不同侧的位置,则失败”是什么意思?
    • 新版本使用 360 个单元格的浮点数组。 (不是严格需要,但很方便。它将花费 360*sizeof(float) 字节,可能在堆栈上。(而不是当前版本中的 3*8,这是最小的)。BRB 在大约三个小时内。
    • 我没有看到代码有任何变化,你说我会在三个小时内更新。
    猜你喜欢
    • 2021-01-14
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 2019-05-23
    • 2022-08-17
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多