C#整数演算では、a / b / cは常にa /(b * c)と等しくなりますか?


81

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についてですか?


3
これは数学の質問であり、プログラミングの質問ではありません。この質問のプログラミング固有の部分が何であるかを説明できますか?
2013年

38
確かに、有理数のスコープ内で@Odedですが、これは特に整数演算(C#)を指します。プログラミング関連にするIMO。たぶん、a / b / c == a /(b * c)が整数算術で成り立つという規則、あるいはそれは有理数算術でのみ成り立つでしょう。
ティムS.

43
これはC#に関する完全に合理的な質問であり、簡単に答えることができます。
Eric Lippert 2013年

12
@Odedこれは、コンピューターの算術と、それが純粋数学と同じように動作するかどうかについての質問です。閉じてはいけません。
ジェフリーサックス

4
オーバーフローを無視して、2つが実際には同等である理由(または実際にそうであるかどうか)の数学的証明に非常に興味がありますが、まだ1つをまとめることができていません。
ローリング2013年

回答:


71

ましょう\表す整数除算(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の回答を参照してください!彼はまた、彼のブログ投稿と回答ではるかに厳密なアプローチを取っています。私が手に負えすぎていると感じた場合は、これを調べる必要があります。


1
ああ、それは私が
求めていた

このために\と/を使用するのが好きです。物事をより明確にします。
ジャスティンモーガン

@JustinMorganこの表記は、実際には他のいくつかのプログラミング言語で使用されています(ただし、現時点ではどの言語かは覚えていません)。
ティモシーシールド

1
@ TimothyShieldsVB.netはそうします。
Arie Xiao

主張は本当だと思いますが、あなたの証拠には重要なステップが欠けているようです。2行目=> 3行目についてのあなたの正当性を誤解した可能性があります。私が解釈した方法floor(x / y) - (x / y)は小さいz >= 1ので、それをとるfloorと0であり、無視できます。それは実際にはa内の加数であるため、実際には続きませんfloor()(つまり、floor(1/2)vsを検討してくださいfloor(1/2 + 1/2))。
rliu 2013年

77

私はこの質問がとても好きで、2013年6月4日にブログの主題にしました。素晴らしい質問をありがとう!


大きなケースは簡単に手に入ります。例えば:

a = 1073741823; 
b = 134217727;
c = 134217727;

b * c負の数にオーバーフローするためです。

私はそれにその中に実際に追加するにチェック算術を、違いをa / (b * c)し、(a / b) / c作品やプログラムがクラッシュしていることをプログラム間の違いがあることができます。製品の場合bc整数の範囲をオーバーフローし、その後前者は確認文脈でクラッシュします。

小さい正の整数、たとえば、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 < c0 <= 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 * cxゼロより大きくすることはできません。

できる xゼロ未満にするますか?いいえ、同様の議論により、読者に任せました。

したがって、整数xはゼロであり、したがってq1 == q2


7
@JoseRuiSantosはい、しかし、両方x1 x2操作が、その場合には同様にクラッシュします
マルクGravell

@JoseRuiSantosは、どちらの場合にも当てはまりませんか?
ジョドレル2013年

vc 74の回答が削除されたため、ほとんどの人はあなたが参照している例を見ることができなくなりました。
Gabe 2013年

それは正しいです。両方ともx1、またはがゼロのx2場合はクラッシュします。他の値の場合は、整数オーバーフローの可能性を回避するため、式の方が優れています。bcx1( b * c)x2
Jose Rui Santos 2013年

オーバーフローとチェックされた算術についての興味深い点、ありがとう!
ジェイソンクリース2013年

4

絶対値場合bcについて、以下の通りですsqrt(2^31)(約46 300)、ようにすることはb * c、オーバーフロー、値が常に一致しませんありません。b * cオーバーフローした場合、checkedコンテキストでエラーがスローされるか、コンテキストで誤った値を取得する可能性がありuncheckedます。


2

他の人が気づいたオーバーフローエラーを避けて、それらは常に一致します。

レッツはそれを仮定し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が負の場合は機能しませんが、その場合も整数除算がどのように機能するかはわかりません。


0

私は楽しみのために私自身の証拠を提供します。これもオーバーフローを無視し、残念ながらポジティブのみを処理しますが、証拠は明確で明確だと思います。

目標はそれを示すことです

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

上記の証明は、追加のケースをチェックする必要があるいくつかの手順を除いて、ネガで機能するはずです...しかし、私はチェックしませんでした。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.