Я пытаюсь модифицировать целое число для получения позиции в массиве, чтобы оно обходилось по кругу. Выполнение 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
Я'делал это раньше, но по какой-то причине это'плавит мой мозг сегодня :(
Я всегда использую свою собственную функцию 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.
Обратите внимание, что оператор % в C# и C++'на самом деле НЕ является модулем, это остаток. Формула для модуля, которая вам нужна, в вашем случае такова:
float nfmod(float a,float b)
{
return a - b * floor(a / b);
}
Вам придется перекодировать это в C# (или C++), но именно так вы получите модулор, а не остаток.
Однострочный реализации с помощью %
только один раз:
int mod(int k, int n) { return ((k %= n) < 0) ? k+n : k; }
При добавлении какого-то понимания.
По Евклидово определение Результатом мод должен быть всегда положительным.
Экс:
int n = 5;
int x = -3;
int mod(int n, int x)
{
return ((n%x)+x)%x;
}
Выход:
-1
ShreevatsaR'ы ответьте выиграл't работа для всех случаев, даже если добавить "если(м в<0) м=-м; - то", Если вам приходится за негативное дивидендов/делителей.
Например, мод -12 -10 будет 8, и он должен быть -2.
Следующая реализация будет работать для обоих положительных и отрицательных дивидендов / делители и согласуется с другими реализациями (а именно, Ява, питон, Руби, Скала, схемы, скрипты и Google'калькулятор ы):
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 };
}
}
Для большей производительности курсе девс
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 посмотреть здесь
Сравнивая две главные ответы
(x%m + m)%m;
и
int r = x%m;
return r<0 ? r+m : r;
На самом деле никто не упомянул о том, что первый может бросить для себя исключение overflowexception при второй выиграл'т. Что еще хуже, с непроверенными контекста по умолчанию, первый ответ может возвращать неправильный ответ (см. мод(инт.Максвеллову - 1, инт.Максвеллову)` например). Так что второй ответ не только кажется быстрее, но и правильнее.
Мне нравится трюк представлен Петр н Льюис на этой теме: "Если N имеет ограниченный спектр, то вы можете получить желаемый результат, просто добавив известная постоянным кратным [делитель], что превышает абсолютное значение минимума.&и"
Так что если у меня есть значение д То есть в градусах и я хочу взять
d % 180f
и я хочу избежать проблем, если д отрицательное, то вместо того, чтобы просто сделать это:
(d + 720f) % 180f
Это предполагает, что хотя Д может быть отрицательным, известно, что он никогда не будет больше негатива, чем -720.
Я заметил одну вещь. Это дает только положительные числа в Python ` печать 4%3 печать 3%3 печать 3%3 печать 2%3 печати 1%3 печать 0%3 выведите -1%3 печать -2%3 печать -3%3 печать -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, но теперь я тоже в замешательстве
Все ответы здесь отлично работают если делитель положителен, но это's не совсем полным. Вот моя реализация, которая всегда возвращает на интервале `[0, в), такую, что знак на выходе совпадает со знаком делителя, позволяющих отрицательные делители в качестве конечной точки для выходной диапазон.
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
может быть любой тип номера)