it-swarm.com.de

Mod der negativen Zahl schmilzt mein Gehirn

Ich versuche, eine Ganzzahl zu modifizieren, um eine Array-Position zu erhalten, damit sie eine Schleife bildet. i % arrayLength funktioniert gut für positive Zahlen, aber für negative Zahlen geht alles schief.

 4 % 3 == 1
 3 % 3 == 0
 2 % 3 == 2
 1 % 3 == 1
 0 % 3 == 0
-1 % 3 == -1
-2 % 3 == -2
-3 % 3 == 0
-4 % 3 == -1

also brauche ich eine implementierung von

int GetArrayIndex(int i, int arrayLength)

so dass

GetArrayIndex( 4, 3) == 1
GetArrayIndex( 3, 3) == 0
GetArrayIndex( 2, 3) == 2
GetArrayIndex( 1, 3) == 1
GetArrayIndex( 0, 3) == 0
GetArrayIndex(-1, 3) == 2
GetArrayIndex(-2, 3) == 1
GetArrayIndex(-3, 3) == 0
GetArrayIndex(-4, 3) == 2

Ich habe das schon mal gemacht, aber aus irgendeinem Grund schmilzt es heute mein Gehirn :( 

157
gormenghastly

Ich verwende immer meine eigene mod-Funktion, definiert als

int mod(int x, int m) {
    return (x%m + m)%m;
}

Wenn Sie sich jedoch bemüht haben, zwei Aufrufe der Moduloperation zu erhalten, können Sie es als schreiben

int mod(int x, int m) {
    int r = x%m;
    return r<0 ? r+m : r;
}

oder Varianten davon.

Der Grund dafür ist, dass "x% m" immer im Bereich [-m + 1, m-1] liegt. Wenn es also überhaupt negativ ist, wird m durch Addieren in den positiven Bereich gesetzt, ohne dass der Wert modulo m geändert wird.

243
ShreevatsaR

Bitte beachten Sie, dass der Operator% von C # und C++ KEIN Modulo ist, es ist der Rest. Die Formel für Modulo, die Sie in Ihrem Fall wünschen, lautet:

float nfmod(float a,float b)
{
    return a - b * floor(a / b);
}

Sie müssen dies in C # (oder C++) umkodieren, aber auf diese Weise erhalten Sie Modulo und keinen Rest.

Single-Line-Implementierung mit % nur einmal:

int mod(int k, int n) {  return ((k %= n) < 0) ? k+n : k;  }
13
Evgeni Sergeev

Die Antwort von ShreevatsaR funktioniert nicht in allen Fällen, auch wenn Sie "if (m <0) m = -m;" hinzufügen, wenn Sie negative Dividenden/Divisoren berücksichtigen.

Beispielsweise ist -12 mod -10 8 und sollte -2 sein.

Die folgende Implementierung funktioniert sowohl für positive als auch für negative Dividenden/Divisoren und entspricht anderen Implementierungen (nämlich Java, Python, Ruby, Scala, Scheme, Javascript und Google Calculator):

internal static class IntExtensions
{
    internal static int Mod(this int a, int n)
    {
        if (n == 0)
            throw new ArgumentOutOfRangeException("n", "(a mod 0) is undefined.");

        //puts a in the [-n+1, n-1] range using the remainder operator
        int remainder = a%n;

        //if the remainder is less than zero, add n to put it in the [0, n-1] range if n is positive
        //if the remainder is greater than zero, add n to put it in the [n-1, 0] range if n is negative
        if ((n > 0 && remainder < 0) ||
            (n < 0 && remainder > 0))
            return remainder + n;
        return remainder;
    }
}

Testsuite mit xUnit:

    [Theory]
    [PropertyData("GetTestData")]
    public void Mod_ReturnsCorrectModulo(int dividend, int divisor, int expectedMod)
    {
        Assert.Equal(expectedMod, dividend.Mod(divisor));
    }

    [Fact]
    public void Mod_ThrowsException_IfDivisorIsZero()
    {
        Assert.Throws<ArgumentOutOfRangeException>(() => 1.Mod(0));
    }

    public static IEnumerable<object[]> GetTestData
    {
        get
        {
            yield return new object[] {1, 1, 0};
            yield return new object[] {0, 1, 0};
            yield return new object[] {2, 10, 2};
            yield return new object[] {12, 10, 2};
            yield return new object[] {22, 10, 2};
            yield return new object[] {-2, 10, 8};
            yield return new object[] {-12, 10, 8};
            yield return new object[] {-22, 10, 8};
            yield return new object[] { 2, -10, -8 };
            yield return new object[] { 12, -10, -8 };
            yield return new object[] { 22, -10, -8 };
            yield return new object[] { -2, -10, -2 };
            yield return new object[] { -12, -10, -2 };
            yield return new object[] { -22, -10, -2 };
        }
    }
5
dcastro

Für die leistungsstärkeren Entwickler

uint wrap(int k, int n) ((uint)k)%n

Ein kleiner Leistungsvergleich

Modulo: 00:00:07.2661827 ((n%x)+x)%x)
Cast:   00:00:03.2202334 ((uint)k)%n
If:     00:00:13.5378989 ((k %= n) < 0) ? k+n : k

Was die Aufführungskosten des Casts anbelangt, so haben Sie einen Blick auf hier

5
Markus Cozowicz

Verständnis hinzufügen.

Nach euklidischer Definition Das Mod-Ergebnis muss immer positiv sein.

Ex:

 int n = 5;
 int x = -3;

 int mod(int n, int x)
 {
     return ((n%x)+x)%x;
 }

Ausgabe:

 -1
5
Abin Mathew

Addieren Sie einfach Ihren Modulus (arrayLength) zum negativen Ergebnis von% und Sie werden in Ordnung sein.

4
starblue

Vergleich zweier vorherrschender Antworten

(x%m + m)%m;

und

int r = x%m;
return r<0 ? r+m : r;

Tatsächlich hat niemand die Tatsache erwähnt, dass die erste eine OverflowException werfen darf, die zweite nicht. Noch schlimmer ist, dass bei einem ungeprüften Standardkontext die erste Antwort möglicherweise die falsche Antwort zurückgibt (siehe beispielsweise mod(int.MaxValue - 1, int.MaxValue)). Die zweite Antwort scheint also nicht nur schneller, sondern auch korrekter zu sein.

2
lilo0

Ich mag den von Peter N Lewis vorgestellten Trick in diesem Thread : "Wenn n einen begrenzten Bereich hat, können Sie das gewünschte Ergebnis erhalten, indem Sie einfach ein bekanntes Vielfaches von [Divisor] hinzufügen, das größer ist als der absoluter Wert des Minimums. "

Wenn ich also einen Wert d habe, ist das in Grad und ich möchte es nehmen 

d % 180f

und ich möchte die Probleme vermeiden, wenn d negativ ist, dann mache ich stattdessen Folgendes:

(d + 720f) % 180f

Dies setzt voraus, dass, obwohl d negativ sein kann, bekannt ist, dass es niemals negativer sein wird als -720.

1
RenniePet

Alle Antworten hier funktionieren gut, wenn Ihr Divisor positiv ist, aber nicht ganz abgeschlossen ist. Hier ist meine Implementierung, die immer für einen Bereich von [0, b) zurückgegeben wird, so dass das Vorzeichen der Ausgabe dem Vorzeichen des Divisors entspricht, wobei negative Divisoren als Endpunkt für den Ausgabebereich verwendet werden.

PosMod(5, 3) gibt 2 zurück
PosMod(-5, 3) gibt 1 zurück
PosMod(5, -3) gibt -1 zurück
PosMod(-5, -3) gibt -2 zurück

    /// <summary>
    /// Performs a canonical Modulus operation, where the output is on the range [0, b).
    /// </summary>
    public static real_t PosMod(real_t a, real_t b)
    {
        real_t c = a % b;
        if ((c < 0 && b > 0) || (c > 0 && b < 0)) 
        {
            c += b;
        }
        return c;
    }

(wobei real_t ein beliebiger Zahlentyp sein kann)

0
Aaron Franke