私は整数を変更して配列位置を取得しようとしているので、ループがループします。 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;
}
もちろん、twoモジュラス操作の呼び出しに悩まされている場合は、次のように書くことができます。
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++)で再コーディングする必要がありますが、これは剰余ではなくモジュロを取得する方法です。
%
を1回だけ使用する単一行の実装:
int mod(int k, int n) { return ((k %= n) < 0) ? k+n : k; }
いくつかの理解を追加します。
ユークリッド定義 によって、mod結果は常に正でなければなりません。
例:
int n = 5;
int x = -3;
int mod(int n, int x)
{
return ((n%x)+x)%x;
}
出力:
-1
ShreevatsaRの答えは、「if(m <0)m = -m;」を追加した場合でも、負の配当/除数を考慮しても、すべての場合に機能しません。
たとえば、-12 mod -10は8になり、-2になります。
次の実装は、正と負の両方の配当/除数で機能し、他の実装(つまり、Java、Python、Ruby、Scala、Scheme、Javascript、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 };
}
}
2つの主要な回答の比較
(x%m + m)%m;
そして
int r = x%m;
return r<0 ? r+m : r;
実際には、最初のものはOverflowException
をスローしても、2番目のものはスローしないという事実については誰も言及していません。さらに悪いことに、デフォルトの未チェックのコンテキストでは、最初の回答が間違った回答を返す場合があります(たとえば、mod(int.MaxValue - 1, int.MaxValue)
を参照)。したがって、2番目の答えはより高速であるように見えるだけでなく、より正確であるようにも見えます。
モジュラス(arrayLength)を%の負の結果に追加するだけで大丈夫です。
よりパフォーマンスを重視する開発者向け
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へのキャストのパフォーマンスコストについては、外観を確認してください here
このスレッド :でピーターNルイスによって提示されたトリックが好きです。「nの範囲が制限されている場合、既知の定数の[除数]を追加するだけで、望む結果を得ることができます最小値の絶対値よりも大きい。」
だから、もし度である値dがあり、私が取りたい
d % 180f
dが負の場合、問題を回避したいので、代わりにこれを行います:
(d + 720f) % 180f
これは、dが負であっても、-720よりも負になることはないことがわかっていると仮定しています。
ここでの答えはすべて、除数が正であればうまく機能しますが、完全ではありません。これは、出力の符号が除数の符号と同じになるように、常に[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
は任意の数値タイプにすることができます)