Turn while loop into math equation?

后端 未结 4 1021
无人及你
无人及你 2021-02-05 10:56

I have two simple while loops in my program that I feel ought to be math equations, but I\'m struggling to convert them:

float a = someValue;
int b = someOtherVa         


        
相关标签:
4条回答
  • 2021-02-05 11:35

    Assuming b is positive, abs(c) = floor((abs(a) - b/2) / b). Then, apply sign of a to c.

    0 讨论(0)
  • 2021-02-05 11:43

    It can be proved that the following is correct:

    c = floor((a+b/2)/b)
    a = a - c*b
    

    Note that floor means round down, towards negative infinity: not towards 0. (E.g. floor(-3.1)=-4. The floor() library functions will do this; just be sure not to just cast to int, which will usually round towards 0 instead.)

    Presumably b is strictly positive, because otherwise neither loop will never terminate: adding b will not make a larger and subtracting b will not make a smaller. With that assumption, we can prove that the above code works. (And paranoidgeek's code is also almost correct, except that it uses a cast to int instead of floor.)

    Clever way of proving it: The code adds or subtracts multiples of b from a until a is in [-b/2,b/2), which you can view as adding or subtracting integers from a/b until a/b is in [-1/2,1/2), i.e. until (a/b+1/2) (call it x) is in [0,1). As you are only changing it by integers, the value of x does not change mod 1, i.e. it goes to its remainder mod 1, which is x-floor(x). So the effective number of subtractions you make (which is c) is floor(x).

    Tedious way of proving it:

    At the end of the first loop, the value of c is the negative of the number of times the loop runs, i.e.:

    • 0 if: a > -b/2 <=> a+b/2 > 0
    • -1 if: -b/2 ≥ a > -3b/2 <=> 0 ≥ a+b/2 > -b <=> 0 ≥ x > -1
    • -2 if: -3b/2 ≥ a > -5b/2 <=> -b ≥ a+b/2 > -2b <=> -1 ≥ x > -2 etc.,

    where x = (a+b/2)/b, so c is: 0 if x>0 and "ceiling(x)-1" otherwise. If the first loop ran at all, then it was ≤ -b/2 just before the last time the loop was executed, so it is ≤ -b/2+b now, i.e. ≤ b/2. According as whether it is exactly b/2 or not (i.e., whether x when you started was exactly a non-positive integer or not), the second loop runs exactly 1 time or 0, and c is either ceiling(x) or ceiling(x)-1. So that solves it for the case when the first loop did run.

    If the first loop didn't run, then the value of c at the end of the second loop is:

    • 0 if: a < b/2 <=> a-b/2 < 0
    • 1 if: b/2 ≤ a < 3b/2 <=> 0 ≤ a-b/2 < b <=> 0 ≤ y < 1
    • 2 if: 3b/2 ≤ a < 5b/2 <=> b ≤ a-b/2 < 2b <=> 1 ≤ y < 2, etc.,

    where y = (a-b/2)/b, so c is: 0 if y<0 and 1+floor(y) otherwise. [And a now is certainly < b/2 and ≥ -b/2.]

    So you can write an expression for c as:

    x = (a+b/2)/b
    y = (a-b/2)/b
    c = (x≤0)*(ceiling(x) - 1 + (x is integer))
       +(y≥0)*(1 + floor(y))                
    

    Of course, next you notice that (ceiling(x)-1+(x is integer)) is same as floor(x+1)-1 which is floor(x), and that y is actually x-1, so (1+floor(y))=floor(x), and as for the conditionals:
    when x≤0, it cannot be that (y≥0), so c is just the first term which is floor(x),
    when 0 < x < 1, neither of the conditions holds, so c is 0,
    when 1 ≤ x, then only 0≤y, so c is just the second term which is floor(x) again. So c = floor(x) in all cases.

    0 讨论(0)
  • 2021-02-05 11:43
    c = (int)((a - (b / 2)) / b + 1);
    a -= c * b;
    

    Test case at http://pastebin.com/m1034e639

    0 讨论(0)
  • 2021-02-05 11:43

    I think you want something like this:

    c = ((int) a + b / 2 * sign(a)) / b
    

    That should match your loops except for certain cases where b is odd because the range from -b/2 to b/2 is smaller than b when b is odd.

    0 讨论(0)
提交回复
热议问题