a、b、cを大きくない正の整数とします。a / b / cは常にa /(b * c)とC#整数演算で等しいですか?私にとって、C#では次のようになります。
int a = 5126, b = 76, c = 14;
int x1 = a / b / c;
int x2 = a / (b * c);
だから私の質問は:x1 == x2
すべてのa、b、cについてですか?
a、b、cを大きくない正の整数とします。a / b / cは常にa /(b * c)とC#整数演算で等しいですか?私にとって、C#では次のようになります。
int a = 5126, b = 76, c = 14;
int x1 = a / b / c;
int x2 = a / (b * c);
だから私の質問は:x1 == x2
すべてのa、b、cについてですか?
回答:
ましょう\
表す整数除算(C#の/
両者オペレータint
単数または複数)およびlet/
示す通常の数学分割。次に、x,y,z
が正の整数であり、オーバーフロー無視し、
(x \ y) \ z
= floor(floor(x / y) / z) [1]
= floor((x / y) / z) [2]
= floor(x / (y * z))
= x \ (y * z)
どこ
a \ b = floor(a / b)
上記の行から行[1]
へのジャンプ[2]
は次のように説明されます。の範囲に2つの整数a
と1つb
の小数f
があるとします[0, 1)
。それを見るのは簡単です
floor(a / b) = floor((a + f) / b) [3]
行の場合[1]
、あなたは特定a = floor(x / y)
、f = (x / y) - floor(x / y)
およびb = z
、その後、[3]
そのことを意味[1]
し、[2]
同じです。
この証明を負の整数に一般化することはできますが(オーバーフローは無視します)、要点を単純にするために読者に任せます。
オーバーフローの問題について-良い説明については、Eric Lippertの回答を参照してください!彼はまた、彼のブログ投稿と回答ではるかに厳密なアプローチを取っています。私が手に負えすぎていると感じた場合は、これを調べる必要があります。
floor(x / y) - (x / y)
は小さいz >= 1
ので、それをとるfloor
と0であり、無視できます。それは実際にはa内の加数であるため、実際には続きませんfloor()
(つまり、floor(1/2)
vsを検討してくださいfloor(1/2 + 1/2)
)。
私はこの質問がとても好きで、2013年6月4日にブログの主題にしました。素晴らしい質問をありがとう!
大きなケースは簡単に手に入ります。例えば:
a = 1073741823;
b = 134217727;
c = 134217727;
b * c
負の数にオーバーフローするためです。
私はそれにその中に実際に追加するにチェック算術を、違いをa / (b * c)
し、(a / b) / c
作品やプログラムがクラッシュしていることをプログラム間の違いがあることができます。製品の場合b
とc
整数の範囲をオーバーフローし、その後前者は確認文脈でクラッシュします。
小さい正の整数、たとえば、shortに収まるほど小さい場合は、IDを維持する必要があります。
TimothyShieldsが証明を投稿しました。ここに別の証明を提示します。ここでのすべての数値は非負の整数であり、どの演算もオーバーフローしないと仮定します。
の整数除算x / y
は、次のq
ような値を見つけますq * y + r == x
。ここで0 <= r < y
。。
したがって、部門a / (b * c)
は次のq1
ような値を見つけます
q1 * b * c + r1 == a
どこ 0 <= r1 < b * c
部門は( a / b ) / c
最初に次のqt
ような値を見つけます
qt * b + r3 == a
そして、次のq2
ような値を見つけます
q2 * c + r2 == qt
代わりにそれを使用するqt
と、次のようになります。
q2 * b * c + b * r2 + r3 == a
どこ0 <= r2 < c
と0 <= r3 < b
。
同じものに等しい2つのものは互いに等しいので、
q1 * b * c + r1 == q2 * b * c + b * r2 + r3
あるq1 == q2 + x
整数について考えてみましょうx
。それを代入して解決しx
ます:
q2 * b * c + x * b * c + r1 = q2 * b * c + b * r2 + r3
x = (b * r2 + r3 - r1) / (b * c)
どこ
0 <= r1 < b * c
0 <= r2 < c
0 <= r3 < b
x
ゼロより大きくすることはできますか?いいえ。不平等があります。
b * r2 + r3 - r1 <= b * r2 + r3 <= b * (c - 1) + r3 < b * (c - 1) + b == b * c
したがって、その分数の分子は常に、よりも小さいためb * c
、x
ゼロより大きくすることはできません。
できる x
ゼロ未満にするますか?いいえ、同様の議論により、読者に任せました。
したがって、整数x
はゼロであり、したがってq1 == q2
。
x1
とx2
操作が、その場合には同様にクラッシュします
x1
、またはがゼロのx2
場合はクラッシュします。他の値の場合は、整数オーバーフローの可能性を回避するため、式の方が優れています。b
c
x1
( b * c)
x2
他の人が気づいたオーバーフローエラーを避けて、それらは常に一致します。
レッツはそれを仮定しa/b=q1
た手段は、a=b*q1+r1
どこ、0<=r1<b
。
今のことを仮定しa/b/c=q2
ていることを意味され、q1=c*q2+r2
ここで0<=r2<c
。
これは、を意味しa=b(c*q2+r2)+r1=b*c*q2+br2+r1
ます。
のa/(b*c)=a/b/c=q2
場合、が必要0<=b*r2+r1<b*c
です。
だがb*r2+r1<b*r2+b=b*(r2+1)<=b*c
、必要に応じて、2つの操作は一致します。
これは、b
またはc
が負の場合は機能しませんが、その場合も整数除算がどのように機能するかはわかりません。
私は楽しみのために私自身の証拠を提供します。これもオーバーフローを無視し、残念ながらポジティブのみを処理しますが、証拠は明確で明確だと思います。
目標はそれを示すことです
floor(floor(x/y)/z) = floor(x/y/z)
ここで、/
(この証明を通して)正常な分裂です。
私たちは、の商と剰余を表すa/b
一意のようにa = kb + r
(私たちはそれが意味することにより、k,r
ユニークでもノートですが|r| < |b|
)。次に、次のようになります。
(1) floor(x/y) = k => x = ky + r
(2) floor(floor(x/y)/r) = k1 => floor(x/y) = k1*z + r1
(3) floor(x/y/z) = k2 => x/y = k2*z + r2
したがって、私たちの目標はそれを示すことk1 == k2
です。さて、私たちは持っています:
k1*z + r1 = floor(x/y) = k = (x-r)/y (from lines 1 and 2)
=> x/y - r/y = k1*z + r1 => x/y = k1*z + r1 + r/y
したがって:
(4) x/y = k1*z + r1 + r/y (from above)
x/y = k2*z + r2 (from line 3)
ここで、(2)からr1
、整数(fork1*z
は定義による整数)およびr1 < z
(定義による)であることに注目してください。さらに、(1)から、次のことがわかりr < y => r/y < 1
ます。ここでr1 + r/y
、(4)の合計を考えます。主張はそれでr1 + r/y < z
あり、これは前の主張から明らかです(0 <= r1 < z
そしてr1
は整数であるため、私たちは持ってい0 <= r1 <= z-1
ます。したがって0 <= r1 + r/y < z
)。したがってr1 + r/y = r2
、の定義によるr2
(そうでない場合、剰余の定義と矛盾する2つの剰余がx/y
あります)。したがって、次のようになります。
x/y = k1*z + r2
x/y = k2*z + r2
そして、私たちはその望ましい結論を持っていますk1 = k2
。
上記の証明は、追加のケースをチェックする必要があるいくつかの手順を除いて、ネガで機能するはずです...しかし、私はチェックしませんでした。
反例:INT_MIN / -1 / 2