Мод отрицательного числа тает мой мозг

Я пытаюсь преобразовать целое число, чтобы получить позицию в массиве, чтобы он зацикливался. Выполнение i % arrayLength отлично работает для положительных чисел, но для отрицательных чисел все идет не так.

 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

поэтому мне нужна реализация

int GetArrayIndex(int i, int arrayLength)

такое, что

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

Я делал это раньше, но по какой-то причине он таял мой мозг сегодня:(

Ответ 1

Я всегда использую собственную функцию mod, определенную как

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

Конечно, если вас беспокоит наличие двух вызовов операции модуля, вы можете записать его как

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

или его вариантов.

Причина, по которой он работает, заключается в том, что "x% m" всегда находится в диапазоне [-m + 1, m-1]. Так что, если это вообще отрицательно, добавление m к нему приведет его в положительный диапазон без изменения его значения по модулю m.

Ответ 2

Обратите внимание, что оператор С# и С++% фактически не является модулем, а остальным. Формула для модуля, которую вы хотите, в вашем случае:

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

Вы должны перекодировать это в С# (или С++), но так вы получаете модуль, а не остаток.

Ответ 3

Однострочная реализация с использованием % только один раз:

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

Ответ 4

Добавление некоторого понимания.

В Евклидовом определении результат мода должен быть всегда положительным.

Пример:

 int n = 5;
 int x = -3;

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

Вывод:

 -1

Ответ 5

Ответ ShreevatsaR не будет работать для всех случаев, даже если вы добавите "if (m < 0) m = -m;", если вы учитываете отрицательные дивиденды/делители.

Например, -12 mod -10 будет 8, и оно должно быть -2.

Следующая реализация будет работать как для положительных, так и для отрицательных дивидендов/делителей и соответствует другим реализациям (а именно Java, Python, Ruby, Scala, Scheme, Javascript и 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;
    }
}

Набор тестов с использованием 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 };
        }
    }

Ответ 6

Просто добавьте свой модуль (arrayLength) к отрицательному результату%, и все будет в порядке.

Ответ 7

Для более эффективной работы разработчиков

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

Небольшое сравнение производительности

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

Что касается производительности стоимость приведения к Uint, посмотрите здесь

Ответ 8

Сравнивая два преобладающих ответа

(x%m + m)%m;

а также

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

На самом деле никто не упомянул тот факт, что первый может вызвать исключение OverflowException а второй - нет. Хуже того, при непроверенном контексте по умолчанию первый ответ может возвращать неправильный ответ (см., mod(int.MaxValue - 1, int.MaxValue)). Таким образом, второй ответ не только кажется более быстрым, но и более правильным.

Ответ 9

Мне нравится трюк, представленный Питером Н Льюисом в этой теме: "Если n имеет ограниченный диапазон, то вы можете получить результат, который вы хотите, просто добавив известная постоянная кратность [делителя], которая больше, чем абсолютное значение минимума."

Итак, если у меня есть значение d, которое находится в градусах, и я хочу взять

d % 180f

и я хочу, чтобы избежать проблем, если d является отрицательным, вместо этого я просто делаю это:

(d + 720f) % 180f

Это предполагает, что хотя d может быть отрицательным, известно, что он никогда не будет более отрицательным, чем -720.

Ответ 10

Все ответы здесь отлично работают, если ваш делитель положительный, но не совсем полный. Вот моя реализация, которая всегда возвращает диапазон [0, b), так что знак выхода совпадает со знаком делителя, что позволяет использовать отрицательные делители в качестве конечной точки для выходного диапазона.

PosMod(5, 3) возвращает 2
PosMod(-5, 3) возвращает 1
PosMod(5, -3) возвращает -1
PosMod(-5, -3) возвращает -2

    /// <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;
    }

(где real_t может быть любого типа числа)

Ответ 11

Я заметил одну вещь. Это дает только положительные числа в питоне print 4%3 print 3%3 print 3%3 print 2%3 print 1%3 print 0%3 print -1%3 print -2%3 print -3%3 print -4%3 - это дало мне
1 0 0 2 1 0 2 1 0 2 Но в C это дает ответы, данные @gormenghastly

#include<stdio.h>

int main()
{
printf("\n %d",3 % 3);
printf("\n %d",4 % 3);
printf("\n %d",3 % 3);
printf("\n %d",2 % 3);
printf("\n %d",1 % 3); 
printf("\n %d", 0 % 3);
printf("\n %d",-1 % 3);
printf("\n %d",-2 % 3);
printf("\n %d",-3 % 3);
printf("\n %d",-4 % 3);

return 0;
}

Я определял% из того, что я мог видеть из вывода Python, но теперь я тоже запутался

Ответ 12

Самый простой способ вычислить a mod b без какого-либо нового метода -

double aMODb = Math.Sign(a) * a % b;