Home > Back-end >  Find closest value in a list over 360 degrees
Find closest value in a list over 360 degrees

Time:09-20

I have a long list of numbers that contains measured angles. The basic idea is that it looks something like this:

var list = new List<double>() {352.9, 354.9, 356.9, 359, 1, 3.1, 5.9};

I am looking for a way to obtain the nearest upper and lower value when I specify some value x. So for example if x = 354.6, I want the upper value to be x_up = 354.9 and the lower value to be x_low = 352.9. I though about using this method, but it does not taking into account that circle angles follow a modulo system.

When x = 0.2, I want x_up = 1 and x_low = 359.

Any ideas on how I can implement this?

CodePudding user response:

Please, note that floating point types (double, float) support remainder operation %; so you can put it as

  using System.Linq;

  ...

  List<double> list = new() {
    352.9, 354.9, 356.9, 359, 1, 3.1, 5.9
  };

  double x = 0.2;

  // 359
  var x_low = list.MaxBy(item => ((item - x) % 360   360) % 360);
  // 1
  var x_up = list.MinBy(item => ((item - x) % 360   360) % 360);

CodePudding user response:

I would highly recommend creating a Angle-type rather than using float/double to represent angles. This saves so much confusion when reading code since you do not have to worry about if it is in degrees or radians, and gives a nice way to gather various angle-related functionality. Make it a readonly struct reduce the overhead to nearly nothing.

This should allow you to make a function to normalize the angles:

private readonly double radians;
public Angle Normalized180_180{
  get{
     var Pi2 = PI * 2;
     var rad = radians;
     rad = (((rad % Pi2)   Pi2) % 360) - 180;
     return new Angle(rad);
  }
}

This should give you a angle guaranteed to be in the -180 to 180 interval, but you could adjust the code to use whatever interval you prefer.

Then you can take the difference, (overload operators to make this simple), normalize, and select the smallest negative and smallest positive value:

var nextSmallest = myAngles.MinBy(a => {
    var diff = (targetAngle - a).Normalized180_180.Radians;
    if(diff < 0)   return double.MaxValue;
    return diff;
};

Another approach would be to normalize and sort your values, and use BinarySearch to find the position of your target value in the list, but you need to fiddle around with the returned value to get the position, and probably also some separate checks to handle edge cases, like the beginning and end of the list.

  • Related