2

I have an int that represents numbers in the range [0, 8[ that wraps around:

       2      
  1         3


0             4


  7         5
       6

Now I need to find the average of two numbers like this, so that for example the average of 7 and 0 would be 7.5, the average of 7 and 2 would be 0.5, the average of 0 and 4 would be 2 or 6, etc.

I found this ("How do you calculate the average of a set of angles?") related question, but it's about angles and I don't see how it could help here. There's also "How to subtract two unsigned ints with wrap around or overflow" but it's about subtracting, and not about finding an average. Any pointers?

I also have a wrap function, if that can be utilized here somehow:

template <class type>
inline type Wrap(type Value, type Minimum, type Maximum)
{
  Value = ((Value - Minimum) % (Maximum + 1 - Minimum));
  return (Value >= 0 ? Minimum : Maximum + 1) + Value;
}

Edit: Trying to define the rules more formally:

If abs(a - b) <= 4 then avg = (a + b) / 2..
Otherwise, avg = (a + b) / 2. + 4; if (avg >= 8) avg -= 8;.

Community
  • 1
  • 1
Emil Laine
  • 41,598
  • 9
  • 101
  • 157

6 Answers6

2

Another solution is to use the answer you cited by converting your numbers to angles first:

  1. Convert your numbers to angles.

    angle_a = a * pi / 4
    angle_b = b * pi / 4
    
  2. Compute unit vectors for each angle

    unit_a
    unit_b
    
  3. Compute unit vectors average

    unit_average = (unit_a + unit_b) / 2
    
  4. Compute angle of unit_average

    angle_average
    
  5. Convert angle_average to number

    number_average = angle_average * 4 / pi
    

then number_average is our answer

Community
  • 1
  • 1
dpmcmlxxvi
  • 1,292
  • 1
  • 9
  • 13
1

Crude but effective:

float foo(int a, int b)
{
  int c;
  if(a>b)
    {
      c=a;
      a=b;
      b=c;
    }

  if( b-a > 3)
    {
      c=a+8;
      a=b;
      b=c;
    }

  float f = 0.5*(a+b);
  if(f>7.6)
    f-=8.0;
  return(f);
}
phuclv
  • 37,963
  • 15
  • 156
  • 475
Beta
  • 96,650
  • 16
  • 149
  • 150
1

I think the first return expression is what you're after:

def av(a,b):
    mi = min(a,b)
    ma = max(a,b)
    if ma - mi > 4:
        return (((mi + 8) + ma) / 2.) % 8
    else:
        return (mi+ma)/2.

mi is the minimum of the two; ma is the max.

phuclv
  • 37,963
  • 15
  • 156
  • 475
dan
  • 982
  • 8
  • 24
1
float wAvg(int m, int n)
{
    int minimum = min(m, n);
    int maximum = max(m, n);

    int d1 = minimum + 8 - maximum; // difference between m and n
                                    // when wrapped around
    int d2 = max - min;             // normal difference

    float avg = 0.0f;

    if (d1 < d2)    // if wrapped around distance is shorter than normal distance
    {
        avg = d1 / 2.0f + maximum;

        if (avg >= 8.0f)
            avg -= 8.0f;
    }
    else
    {
        avg = (m + n) / 2.0f;
    }

    return avg;
}

I think this might work

phuclv
  • 37,963
  • 15
  • 156
  • 475
Alex
  • 3,111
  • 6
  • 27
  • 43
1

After seeing @Beta's "crude" answer, just for fun :) :

float wAvg(int m, int n)
{
    static float results[8][8] =
    {
        {0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 6.5f, 7.0f, 7.5f},
        {0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 7.5f, 0.0f},
        {1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 0.5f},
        {1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f},
        {2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 5.5f},
        {6.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 5.5f, 6.0f},
        {7.0f, 7.5f, 4.0f, 4.5f, 5.0f, 5.5f, 6.0f, 6.5f},
        {7.5f, 0.0f, 0.5f, 5.0f, 5.5f, 6.0f, 6.5f, 7.0f}
    };

    return results[m % 8][n % 8];
}
phuclv
  • 37,963
  • 15
  • 156
  • 475
Alex
  • 3,111
  • 6
  • 27
  • 43
  • since the table is symmetric about the diagonal axis, you can just store half the table to save memory – phuclv Oct 12 '20 at 16:03
0

Presumably the following would work (in the same way it does for angles):

function meanWrappingValue(values: Array<number>, range: number): number {
    return meanAngle(values.map(value => value * (Math.PI * 2) / range)) / (Math.PI * 2) * range;
}

function meanAngle(angles: Array<number>): number {
    let sinSum = angles.reduce((sum, cur) => sum + Math.sin(cur), 0);
    let cosSum = angles.reduce((sum, cur) => sum + Math.cos(cur), 0);
    return normalizeAngle(Math.atan2(sinSum / angles.length, cosSum / angles.length));
}

function normalizeAngle(angle: number): number {
    let range = Math.PI * 2;
    return ((angle % range) + range) % range;
}

In your case it would be:

let result = meanWrappingValue([7, 2], 8);
console.log(result); // => 0.5
phuclv
  • 37,963
  • 15
  • 156
  • 475
David Callanan
  • 5,601
  • 7
  • 63
  • 105