Sign in to follow this  
Followers 0
TheReveller

Modulus for continuous looping cycle - Maths for beginners

8 posts in this topic

#1 ·  Posted (edited)

Hello,

I always use Mod() to build a continuous looping cycle, but since I found the modulus returns negative numbers, I thought it would be nice to have another kind of modulus function.

I searched and I found this topic : http://www.autoitscript.com/forum/index.php?showtopic=13768

Maybe it is not a bug and yes the calculator returns negative numbers, but I don't know if in math it should. To me, I use modulus to be able to have for example a continuous cycle from 0 to 9 from the negative infinite to the positive infinite.

That means, if my loop is 0 to 9 for numbers 0 to 9, if the number is -1, it becomes 9 :

-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 becomes :

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0

But, with Mod(), it would become this :

-0, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0

I'm not saying that it's useless to have that kind of symetric response, but it would be nice to have another kind of modulus function.

So, if you have a table of 10 elements t[0] to t[9] and the variable n to scroll, well everytime you do n+1, you go to the next element and if n = 10, it will become 0, but if you scroll down with n-1, when n will be -1, it will become -1 instead of 9 which will make an error.

How to easily bypass that error with a little function :

Func _Mod($value1, $value2)
    Return Abs(Mod(Mod($value1,$value2)+$value2,$value2))
EndFunc

Be careful : With Mod(), if $value2 is 0, it will return -1.IND but with _Mod, if the $value2 is 0, it will return 1.QNAN

This will make it work how I wish and how maybe some of you wishes.

Example of values returned :

Opt("SendKeyDelay", 0)

Func _Mod($value1, $value2)
    Return Abs(Mod(Mod($value1,$value2)+$value2,$value2))
EndFunc

Run("notepad.exe")
Sleep("500")

 For $j = -10 to 10
    For $i = -10 to 10
        Send("Mod("&$i&","&$j&") = "&Mod($i,$j)&"  ==> _Mod("&$i&","&$j&") = "&_Mod($i,$j)&@CR)
    Next
    Send(@CR)
Next

How the basic modulus Mod($value1, $value2) works :

Success: Returns the remainder when value1 is divided by value2.

Failure: Returns -1.#IND if the divisor is zero.

Examples :

Mod(8,5) = 3 because 8/5 = 1 remains 3 (1*5 + 3)

Mod(5,8) = 5 because 5/8 = 0 remains 5 (0*8 + 5)

But

Mod(-8,5) = -3 because -8/5 = -1 remains -3 (-1*5 + -3)

Mod(-5,8) = -5 because -5/8 = 0 remains -5 (0*8 + -5)

Mod(-8,-5) = -3 because -8/-5 = 1 remains -3 (1*-5 + -3)

Mod(-5,-8) = -5 because -5/-8 = 0 remains -5 (0*-8 + -5)

Now, example of my _Mod($value1, $value2) :

Mod(8,5) = 3 because 8/5 = 1 remains +3 (1*5 + 3)

Mod(5,8) = 5 because 5/8 = 0 remains +5 (0*8 + 5)

And

Mod(-8,5) = 2 because -8/5 = -2 remains +2 (-2*5 + 2)

Mod(-5,8) = 3 because -5/8 = -1 remains +3 (-1*8 + 3)

Mod(-8,-5) = 2 because -8/-5 = 2 remains +2 (2*-5 + 2)

Mod(-5,-8) = 3 because -5/-8 = 1 remains +3 (0*-8 + 3)

That way, the remainder is always positive.

More visual examples of Mod vs _Mod :

Mod(-10,8) = -2 ==> _Mod(-10,8) = 6

Mod(-9,8) = -1 ==> _Mod(-9,8) = 7

Mod(-8,8) = -0 ==> _Mod(-8,8) = 0

Mod(-7,8) = -7 ==> _Mod(-7,8) = 1

Mod(-6,8) = -6 ==> _Mod(-6,8) = 2

Mod(-5,8) = -5 ==> _Mod(-5,8) = 3

Mod(-4,8) = -4 ==> _Mod(-4,8) = 4

Mod(-3,8) = -3 ==> _Mod(-3,8) = 5

Mod(-2,8) = -2 ==> _Mod(-2,8) = 6

Mod(-1,8) = -1 ==> _Mod(-1,8) = 7

Mod(0,8) = 0 ==> _Mod(0,8) = 0

Mod(1,8) = 1 ==> _Mod(1,8) = 1

Mod(2,8) = 2 ==> _Mod(2,8) = 2

Mod(3,8) = 3 ==> _Mod(3,8) = 3

Mod(4,8) = 4 ==> _Mod(4,8) = 4

Mod(5,8) = 5 ==> _Mod(5,8) = 5

Mod(6,8) = 6 ==> _Mod(6,8) = 6

Mod(7,8) = 7 ==> _Mod(7,8) = 7

Mod(8,8) = 0 ==> _Mod(8,8) = 0

Mod(9,8) = 1 ==> _Mod(9,8) = 1

Mod(10,8) = 2 ==> _Mod(10,8) = 2

Mod(-10,0) = -1.IND ==> _Mod(-10,0) = 1.QNAN

Mod(-9,0) = -1.IND ==> _Mod(-9,0) = 1.QNAN

Mod(-8,0) = -1.IND ==> _Mod(-8,0) = 1.QNAN

Mod(-7,0) = -1.IND ==> _Mod(-7,0) = 1.QNAN

Mod(-6,0) = -1.IND ==> _Mod(-6,0) = 1.QNAN

Mod(-5,0) = -1.IND ==> _Mod(-5,0) = 1.QNAN

Mod(-4,0) = -1.IND ==> _Mod(-4,0) = 1.QNAN

Mod(-3,0) = -1.IND ==> _Mod(-3,0) = 1.QNAN

Mod(-2,0) = -1.IND ==> _Mod(-2,0) = 1.QNAN

Mod(-1,0) = -1.IND ==> _Mod(-1,0) = 1.QNAN

Mod(0,0) = -1.IND ==> _Mod(0,0) = 1.QNAN

Mod(1,0) = -1.IND ==> _Mod(1,0) = 1.QNAN

Mod(2,0) = -1.IND ==> _Mod(2,0) = 1.QNAN

Mod(3,0) = -1.IND ==> _Mod(3,0) = 1.QNAN

Mod(4,0) = -1.IND ==> _Mod(4,0) = 1.QNAN

Mod(5,0) = -1.IND ==> _Mod(5,0) = 1.QNAN

Mod(6,0) = -1.IND ==> _Mod(6,0) = 1.QNAN

Mod(7,0) = -1.IND ==> _Mod(7,0) = 1.QNAN

Mod(8,0) = -1.IND ==> _Mod(8,0) = 1.QNAN

Mod(9,0) = -1.IND ==> _Mod(9,0) = 1.QNAN

Mod(10,0) = -1.IND ==> _Mod(10,0) = 1.QNAN

Mod(-10,-5) = -0 ==> _Mod(-10,-5) = 0

Mod(-9,-5) = -4 ==> _Mod(-9,-5) = 4

Mod(-8,-5) = -3 ==> _Mod(-8,-5) = 3

Mod(-7,-5) = -2 ==> _Mod(-7,-5) = 2

Mod(-6,-5) = -1 ==> _Mod(-6,-5) = 1

Mod(-5,-5) = -0 ==> _Mod(-5,-5) = 0

Mod(-4,-5) = -4 ==> _Mod(-4,-5) = 4

Mod(-3,-5) = -3 ==> _Mod(-3,-5) = 3

Mod(-2,-5) = -2 ==> _Mod(-2,-5) = 2

Mod(-1,-5) = -1 ==> _Mod(-1,-5) = 1

Mod(0,-5) = 0 ==> _Mod(0,-5) = 0

Mod(1,-5) = 1 ==> _Mod(1,-5) = 4

Mod(2,-5) = 2 ==> _Mod(2,-5) = 3

Mod(3,-5) = 3 ==> _Mod(3,-5) = 2

Mod(4,-5) = 4 ==> _Mod(4,-5) = 1

Mod(5,-5) = 0 ==> _Mod(5,-5) = 0

Mod(6,-5) = 1 ==> _Mod(6,-5) = 4

Mod(7,-5) = 2 ==> _Mod(7,-5) = 3

Mod(8,-5) = 3 ==> _Mod(8,-5) = 2

Mod(9,-5) = 4 ==> _Mod(9,-5) = 1

Mod(10,-5) = 0 ==> _Mod(10,-5) = 0

By the way, about the modulus :

Computer language says :

Mod(8, 5) = 3

Mod(-8, 5) = -3

Mod(8, -5) = 3

Mod(-8, -5) = -3

My function says :

Mod(8, 5) = 3

Mod(-8, 5) = 2

Mod(8, -5) = 2

Mod(-8, -5) = 3

My 300$ calculator says :

Mod(8, 5) = 3

Mod(-8, 5) = 2

Mod(8, -5) = -2

Mod(-8, -5) = -3

Edited by TheReveller

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Yeh the mod function can be irritating to use in almost any language... it's well defined in Java, but in c++ it is compiler dependent if I recall correctly. As you have noticed, it just depends on exactly how you define the division algorithm. When you are only dealing with positive numbers it has an obvious definition, but when you start throwing negative divisors in then there is no longer an obvious way of defining it. As far as the Mod(0,n) goes... if you think about it for a second you will realize that Mod(n,0) is tantamount to dividing by zero. It might be just as easy to use a double loop for instances when you want to cycle from 1 to n more than once.

i.e.

For a fixed number of loops,

for(int i=0; i<LOOP_MAX; i++){
  for(int j=1; j<=MAX_IDX; j++){
    // Body of loop here
  }
}

or for an undetermined number of loops,

while(true){
  for(int j=1; j<=MAX_IDX; j++){
    // Body of loop here
  }
}

I don't know what performance hit these will incur, but I suspect they are faster than writing a custom Mod function. Also they seem simpler and more straightforward.

EDIT:

Fixed mistake in the codeblocks.

Edited by Wus

Share this post


Link to post
Share on other sites

i.e.

For a fixed number of loops,

for(int i=0; i<LOOP_MAX; i++){
  for(int j=1; j<=MAX_IDX; j++){
    // Body of loop here
  }
}

or for an undetermined number of loops,

while(true){
  for(int j=1; j<=MAX_IDX; j++){
    // Body of loop here
  }
}

I don't know what performance hit these will incur, but I suspect they are faster than writing a custom Mod function. Also they seem simpler and more straightforward.

EDIT:

Fixed mistake in the codeblocks.

Yes, but that's not that kind of looping I'm talking.

I mean, you have a table of n elements from t[0] to t[n-1] and you a a variable p that is your current position in the table. If p value is from 0 to n-1 everything is okay, but if you want to go to the next element, you do p += 1 and if you want to go to the previous element you do p -= 1 but at some time, p > n-1 so you want to go back to element 0 and also at some time p < 0 so you want to go back to element n-1 so for p value from the negative infinite to the positive infinite, you do _Mod(p,n) to have that loop from 0 to n-1.

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

I understand what you mean, but I made the assumption that you were doing it only to replicate an integral number of entire loops. I can't think of a reason that you'd need to do what you are asking for, but I'm a bit of an idiot. Sounds like what you are talking about is essentially a circularly doubly linked list implemented via an array which you may be able to find by googling.

If I was going to try to implement a Mod function for this purpose I wouldn't bother considering the case where the the divisor was negative, as it is superfluous. To avoid a nested Mod call I'd probably do something like

Func myMod($a,$n)
    Local $m = Mod($a, $n)
    If $m<0 Then
        $m += $n
    EndIf
    Return $m
EndFunc

EDIT:

I assumed that the lack of a nested Mod call would make mine more efficient... but apparently it is less efficient. Go figure.

Edited by Wus

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

I understand what you mean, but I made the assumption that you were doing it only to replicate an integral number of entire loops. I can't think of a reason that you'd need to do what you are asking for, but I'm a bit of an idiot. Sounds like what you are talking about is essentially a circularly doubly linked list implemented via an array which you may be able to find by googling.

If I was going to try to implement a Mod function for this purpose I wouldn't bother considering the case where the the divisor was negative, as it is superfluous. To avoid a nested Mod call I'd probably do something like

Func myMod($a,$n)
    Local $m = Mod($a, $n)
    If $m<0 Then
        $m += $n
    EndIf
    Return $m
EndFunc

EDIT:

I assumed that the lack of a nested Mod call would make mine more efficient... but apparently it is less efficient. Go figure.

That function won't work with this : myMod(-8,-5) = -8

Here's an example of why would I use it from a part of a script I'm making :

If $Wheel == 1 Or _IsPressed($_Add) Then; Mouse wheel or +/- changes selected graph
            $graphnow = _Mod($graphnow+1, $graphnb+1)
            $Wheel = 0
            If _IsPressed($_Add) Then
                Sleep("200")
            EndIf
        ElseIf $Wheel == -1 Or _IsPressed($_Substract) Then
            $graphnow = _Mod($graphnow-1, $graphnb+1)
            $Wheel = 0
            If _IsPressed($_Substract) Then
                Sleep("200")
            EndIf
        EndIf
Edited by TheReveller

Share this post


Link to post
Share on other sites

That function won't work with this : myMod(-8,-5) = -8

Well I realize this since I stated that a precondition was that the divisor was positive.

I will think on the matter to see if I have anything useful to add.


Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Yes, but that's not that kind of looping I'm talking.

I mean, you have a table of n elements from t[0] to t[n-1] and you a a variable p that is your current position in the table. If p value is from 0 to n-1 everything is okay, but if you want to go to the next element, you do p += 1 and if you want to go to the previous element you do p -= 1 but at some time, p > n-1 so you want to go back to element 0 and also at some time p < 0 so you want to go back to element n-1 so for p value from the negative infinite to the positive infinite, you do _Mod(p,n) to have that loop from 0 to n-1.

Why do p -= 1? Why not p = Mod(p + n - 1, n)? You can then use the built-in function without worrying about negative results. I'm probably missing the point but that's how I'd do it. If, for example, I wanted to go backwards through an array of 10 elements numbered 0 to 9, I'd simple add 9 to my current position and take the modulus when divided by 10. p = Mod(p + 9, 10). Working forwards is still then p = Mod(p + 1, 10).

If $Wheel == 1 Or _IsPressed($_Add) Then; Mouse wheel or +/- changes selected graph
            $graphnow = Mod($graphnow+1, $graphnb+1)
            $Wheel = 0
            If _IsPressed($_Add) Then
                Sleep("200")
            EndIf
        ElseIf $Wheel == -1 Or _IsPressed($_Substract) Then
            $graphnow = Mod($graphnow + $graphnb, $graphnb+1)
            $Wheel = 0
            If _IsPressed($_Substract) Then
                Sleep("200")
            EndIf
        EndIf

WBD

Edited by WideBoyDixon

Share this post


Link to post
Share on other sites

Why do p -= 1? Why not p = Mod(p + n - 1, n)? You can then use the built-in function without worrying about negative results. I'm probably missing the point but that's how I'd do it. If, for example, I wanted to go backwards through an array of 10 elements numbered 0 to 9, I'd simple add 9 to my current position and take the modulus when divided by 10. p = Mod(p + 9, 10). Working forwards is still then p = Mod(p + 1, 10).

If $Wheel == 1 Or _IsPressed($_Add) Then; Mouse wheel or +/- changes selected graph
            $graphnow = Mod($graphnow+1, $graphnb+1)
            $Wheel = 0
            If _IsPressed($_Add) Then
                Sleep("200")
            EndIf
        ElseIf $Wheel == -1 Or _IsPressed($_Substract) Then
            $graphnow = Mod($graphnow + $graphnb, $graphnb+1)
            $Wheel = 0
            If _IsPressed($_Substract) Then
                Sleep("200")
            EndIf
        EndIf

WBD

Yes, that's brighter than me for my looping problem. I admit I haven't even thought about that solution.

Still, my function is good for all values of $graphnow and not yours, because if $graphnow < 0 and Abs($graphnow) > $graphnb, then Mod($graphnow + $graphnb, $graphnb+1) will be negative, but I know that in this case of use it shouldn't ever happen.

Anyway, if ever someone is looking for a short'n'easy way to have the same loop for any value, including all negatives, my code will have its purpose and is safer. Still, if someone ever need to do this in his script, I bet he'll be smart enough to think about what I thought.

But that's cool, you solved my looping problem with yet an easier way than what I did, thanks.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0