4

I am looking for a modulus operator Mod(a,b,m) such that:

  • Mod(2,6,7)=-3
  • Mod(6,2,7)=3

That is, the operator avoids the 4-hop path between 2 and 6 and, instead, circles around via a path of length 3. The answer preserves the path direction if you leave from a and go to b.

The following code calculates this, but there is almost surely a better way.

#include <iostream>
#include <cmath>

double absmin(double a, double b){
  if(std::abs(a)<std::abs(b))
    return a;
  else
    return b;
}

double ModDist(double src, double dest, double m){
  if(dest<src)
    return absmin(dest+m-src, dest-src);
  else
    return absmin(dest-src, dest-m-src);
}

int main(){
  std::cout<<ModDist(2,6,7)<<std::endl;
  std::cout<<ModDist(6,2,7)<<std::endl;
}
Richard
  • 56,349
  • 34
  • 180
  • 251
  • 2
    Suggest dropping C tag. – chux - Reinstate Monica May 12 '16 at 02:11
  • 2
    Possibly better on [Code Review](http://codereview.stackexchange.com/) – Steve May 12 '16 at 02:20
  • 1
    Maybe you should show what you expect for other examples with modulus 7; also, what do you expect from `ModDist(2,5,6)` and `ModDist(5,2,6)` and why? – Jonathan Leffler May 12 '16 at 02:31
  • 1
    @JonathanLeffler, that's a nice edge case about which I have no particular expectations since it is the same distance to go either way. – Richard May 12 '16 at 03:13
  • @Steve: The code here fulfills the "What have you tried?" portion of a good SO question. Like all my code, it is perfect and glorious; however, it may be that there's a better way, mathematically to achieve this. I shall re-tag the question. – Richard May 12 '16 at 03:14
  • 1
    @chux: I've changed up a few tags, but the C/C++ are both fine here since the operations of interest translate with very little modification between the two languages. – Richard May 12 '16 at 03:15

3 Answers3

1

If your direct distance is less than half the distance you wont do better by going the other way. Conversely if it is greater than half the distance you do better by changing direction.

This assumes 0<=src,dest<=m

double ModDist(double src, double dest, double m){
  double directDistance = dest - src;
  if(abs(directDistance)<m/2)
    return directDistance;
  return -(m - abs(directDistance))*sgn(directDistance);
}
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
1

The math library function remainder, as its name suggests, returns the remainder on dividing its first argument by it's second. Unlike fmod it returns a signed result, and indeed remainder(a,d) is the smallest number r, by absolute value, such that a-r is an integral multiple of d. What you want is this applied to b-a, ie:

double  ModDist( double a, double b, double m)
{ return remainder( b-a, m);
}
dmuir
  • 4,211
  • 2
  • 14
  • 12
  • Rather than just pasting a random bunch of code, explain what you did and why. That way, the OP and any future readers with the same problem can actually learn something from your answer, rather than just copy/pasting it and asking the same question again tomorrow. – Oldskool May 12 '16 at 09:54
  • While looking up fmod() I was presented with remainder() as an alternative, but I was so sure I'd found the easiest way that I didn't look into it. :-) – m69's been on strike for years May 12 '16 at 13:07
0

You can use the way in which the standard modulo function treats negative numbers to make it do the work of deciding which direction to go in:

#include <cmath>
double ModDist(double s, double d, double m) {
    return std::fmod((d - s) * 2, m) + s - d;
}