負の値を使用したモジュラス演算 - 奇妙なことですか?
質問
いくらなのか教えていただけますか (-2) % 5
?私のPythonインタプリタは3であるとのことですが、これについて賢明な説明はありますか?
一部の言語では結果がマシンに依存する可能性があると読んだことがありますが、よくわかりません。
解決
ところで:ほとんどのプログラミング言語は Python に同意せず、次のような結果を返します。 -2
. 。係数の解釈によっては、これは正しいです。ただし、最も合意された数学的定義では、次のように述べられています。 ある そして b (厳密に正の) 休符です r の部門の ある / b. 。より正確には、0 <= r < b 定義により。
他のヒント
負数に対するモジュラス演算の結果はプログラミング言語に依存しているようです。以下にそのリストを示します。 http://en.wikipedia.org/wiki/Modulo_operation
Python インタープリターは正しいです。係数を計算する (愚かな) 方法の 1 つは、結果の値が 0 と (係数 − 1) の間になるまで係数を減算または加算することです。
例えば。:13 mod 5 = (13 − 5) mod 5 = (13 − 10) mod 5 = 3
またはあなたの場合:−2 mod 5 = (−2 + 5) mod 5 = 3
ドキュメントにあるように、 二項算術演算, Python は次のことを保証します。
整数の除算と剰余演算子は、次の恒等式によって接続されます。
x == (x/y)*y + (x%y)
. 。整数の除算と剰余は、組み込み関数 divmod() にも接続されています。divmod(x, y) == (x/y, x%y)
.
そして本当に、
>>> divmod(-2, 5)
(-1, 3).
この方法の均一性を視覚化するもう 1 つの方法は、次のように計算することです。 divmod
小さな数列の場合:
>>> for number in xrange(-10, 10):
... print divmod(number, 5)
...
(-2, 0)
(-2, 1)
(-2, 2)
(-2, 3)
(-2, 4)
(-1, 0)
(-1, 1)
(-1, 2)
(-1, 3)
(-1, 4)
(0, 0)
(0, 1)
(0, 2)
(0, 3)
(0, 4)
(1, 0)
(1, 1)
(1, 2)
(1, 3)
(1, 4)
そうですね、0 % 5 は 0 になるはずですよね?
-1 % 5 は、次に逆方向に許可される数字であるため、4 である必要があります (範囲外であるため、5 にすることはできません)。
そして、そのロジックに従うと、-2 は 3 でなければなりません。
どのように機能するかを考える最も簡単な方法は、数値が 0 (両端を含む) と 5 (両端を含む) の間に収まるまで、5 を加算または減算し続けることです。
マシン依存性についてはよくわかりません。そのような実装を見たことがありませんが、まったく行われていないとは言えません。
他の回答で説明されているように、負の値を使用したモジュロ演算には多くの選択肢があります。一般に、言語 (およびマシン アーキテクチャ) が異なれば、結果も異なります。
による Pythonリファレンスマニュアル,
モジュロ演算子は常に、2 番目のオペランド (またはゼロ) と同じ符号を持つ結果を生成します。結果の絶対値は、2 番目のオペランドの絶対値よりも厳密に小さくなります。
Python が選択したものです。基本的に、モジュロはこれが常に成り立つように定義されています。
x == (x/y)*y + (x%y)
したがって、(-2)%5 = -2 - (-2/5)*5 = 3 ということは理にかなっています。
-2 を 5 で割ると 0 になり、余りが 3 になります。それがプラットフォームに大きく依存するはずはないと思いますが、奇妙なことを目にしました。
まさに3ですね。で モジュラー算術, 、法は単に除算の余りであり、-2 を 5 で割った余りは 3 です。
結果は言語によって異なります。Python は除数の符号を返します。たとえば、C# は被除数の符号を返します (つまり、-2 % 5 は C# で -2 を返します)。
1 つの説明としては、負の数値が次の方法で保存されることが考えられます。 2の補数. 。Python インタプリタがモジュロ演算を実行しようとすると、符号なしの値に変換されます。そのため、(-2) % 5 を実行する代わりに、実際には 3 である 0xFFFF_FFFF_FFFF_FFFD % 5 を計算します。
すべての OS およびアーキテクチャ上の C/C++ でのこの Mod の動作に依存しないように注意してください。私の記憶が正しければ、次のような C/C++ コードに頼ろうとしました。
float x2 = x % n;
x2 を 0 から n-1 の範囲に保つようにしましたが、ある OS でコンパイルすると負の数が入り込んでしまいましたが、別の OS では正常に動作しました。これは半分の時間しか起こらなかったため、デバッグに非常に時間がかかりました。
「モジュロ」と「剰余」という用語の間にはよく混同されているようです。
数学では、余りは次のようになります。 いつも 商と一致して定義されるため、次のようになります。 a / b == c rem d
それから (c * b) + d == a
. 。商を四捨五入する方法に応じて、異なる剰余が得られます。
ただし、モジュロは常に結果を返す必要があります 0 <= r < divisor
, これは、負の整数を許可する場合にのみ、マイナス無限大への丸め除算と一致します。除算がゼロに向かって丸める場合 (これが一般的です)、モジュロと剰余は非負の値に対してのみ等価になります。
一部の言語 (特に C と C++) では、必要な丸め/剰余動作が定義されていません。 %
曖昧です。多くの場合、丸めはゼロに向かうものと定義されていますが、剰余の方が正確である場合にはモジュロという用語が使用されます。Python は負の無限大に丸めるという点で比較的珍しいため、剰余と剰余は等価です。
Ada はゼロ IIRC に向かって丸めますが、両方を持ちます mod
そして rem
オペレーター。
C ポリシーは、コンパイラーがマシンにとって最も効率的な実装を選択できるようにすることを目的としていますが、少なくとも最近では、IMO は誤った最適化です。優れたコンパイラは、負の数が発生しない場合 (符号なし型を使用する場合はほぼ確実に) 最適化に等価性を使用できる可能性があります。一方、負の数が発生する可能性がある場合は、ほぼ確実に詳細を気にする必要があります。移植性の理由から、四捨五入や剰余に関係なく必要な結果が確実に得られるように、非常に慎重に設計された複雑すぎるアルゴリズムやチェックを使用する必要があります。行動。
言い換えれば、この「最適化」による利益は、(常にではないにしても)ほとんどが幻想であり、場合によっては非常に現実的なコストが発生するため、これは誤った最適化です。