Мод отрицательного числа тает мой мозг
Я пытаюсь преобразовать целое число, чтобы получить позицию в массиве, чтобы он зацикливался. Выполнение 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;