C # tamsayı aritmetiğinde, a / b / c her zaman a / (b * c) 'ye eşit midir?


81

A, b ve c büyük olmayan pozitif tamsayılar olsun. A / b / c her zaman a / (b * c) ile C # tamsayı aritmetiğiyle eşit midir? Benim için C # dilinde şöyle görünüyor:

int a = 5126, b = 76, c = 14;
int x1 = a / b / c;
int x2 = a / (b * c);

Yani sorum şu: x1 == x2a, b ve c'nin tümü için mi?


3
Bu bir matematik sorusu, programlama değil. Bu sorunun programlamaya özgü kısmının ne olduğunu açıklayabilir misiniz?
Oded

38
@ Herhangi bir rasyonel sayı kapsamına girilir, elbette, ancak bu özellikle tamsayı aritmetiğine (C # 'da) atıfta bulunuyor. Bunu programlama ile ilgili yapan IMO. Belki a / b / c == a / (b * c) kuralı tamsayı aritmetiğinde tutulur, belki sadece rasyonel sayı aritmetiğinde geçerlidir.
Tim S.

43
Bu, C # hakkında son derece makul ve yanıtlaması kolay bir sorudur.
Eric Lippert

12
@Oded Bu, bilgisayar aritmetiği ve saf matematik ile aynı şekilde davranıp davranmadığıyla ilgili bir sorudur. Kapatılmamalıdır.
Jeffrey Sax

4
Taşmaları görmezden gelerek, neden (veya gerçekten olup olmadığına) dair matematiksel bir kanıtla oldukça ilgilenirim, ikisi aslında eşdeğerdir, ancak henüz birini bir araya getirmeyi başaramadım.
Rawling

Yanıtlar:


71

Let \anlamında olabildikleri tamsayı bölme (C # /iki arasındaki operatör ints) ve let /göstermektedirler zamanki matematik bölümü. Sonra, eğer x,y,zolan pozitif tamsayılar ve biz edilir taşma görmezden ,

(x \ y) \ z
    = floor(floor(x / y) / z)      [1]
    = floor((x / y) / z)           [2]
    = floor(x / (y * z))
    = x \ (y * z)

nerede

a \ b = floor(a / b)

Hattından atlama [1]hattına [2]aşağıdaki gibi yukarıda açıklanmıştır. Aralıkta iki tam sayınız ave bbir kesirli sayınız olduğunu varsayalım . Bunu görmek çok basitf[0, 1)

floor(a / b) = floor((a + f) / b)  [3]

Satırda , [1]tanımlarsanız a = floor(x / y), f = (x / y) - floor(x / y)ve b = zsonra bunu [3]ima eder [1]ve [2]eşittir.

Bu ispatı negatif tam sayılara genelleyebilirsiniz (yine de taşmayı göz ardı ederek ), ancak konuyu basit tutmak için bunu okuyucuya bırakacağım.


Konusunda taşma - İyi bir açıklama için Eric Lippert cevabını bakın! Ayrıca blog gönderisinde çok daha titiz bir yaklaşım benimsiyor ve cevap veriyor, eğer çok dalgalı olduğumu hissediyorsan bakman gereken bir şey.


1
Hah, peşinde olduğum buydu :)
Rawling

Bunun için \ ve / kullanımınızı beğendim. İşleri çok daha net hale getirir.
Justin Morgan

@JustinMorgan Bu notasyon aslında bazı diğer programlama dillerinde de kullanılıyor (şu anda hangilerinin olduğunu hatırlamıyorum).
Timothy Shields

1
@TimothyShields VB.net yapar.
Arie Xiao

Sanırım iddia doğru, ancak kanıtınız önemli bir adımı kaçırıyor gibi görünüyor. 2 => 3. satır için gerekçenizi yanlış anlamış olabilirim. Bunu yorumlama şeklim floor(x / y) - (x / y)küçük ve z >= 1bu yüzden bunu almak floor0'dır ve onu görmezden gelebiliriz. Bir dahilinde bir birDeğer aslında beri Aslında takip etmez floor()(yani düşünün floor(1/2)vs floor(1/2 + 1/2)).
rliu

77

Bu soruyu o kadar beğendim ki , 4 Haziran 2013 tarihinde blogumun konusu yaptım . Bu mükemmel soru için teşekkürler!


Büyük vakaların gelmesi kolaydır. Örneğin:

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

çünkü b * cnegatif bir sayıya taşar.

Ben aslında o kadar eklersiniz aritmetik kontrol arasındaki fark a / (b * c)ve (a / b) / cbir programı arasındaki fark olabilir eserler ve bir program çöker söyledi. Bir tamsayının çarpımı bve cbir tamsayının sınırlarını aşarsa, o zaman birincisi kontrol edilen bir bağlamda çökecektir.

Küçük pozitif tamsayılar için, diyelim ki, kısaca sığacak kadar küçük, kimlik korunmalıdır.


Timothy Shields az önce bir kanıt yayınladı; Burada alternatif bir kanıt sunuyorum. Buradaki tüm sayıların negatif olmayan tam sayılar olduğunu ve işlemlerin hiçbirinin taşmadığını varsayın.

Tamsayı bölümü x / y, değeri qöyle bulur ki q * y + r == x, nerede 0 <= r < y.

Böylelikle bölüm a / (b * c), değeri q1şu şekilde bulur:

q1 * b * c + r1 == a

nerede 0 <= r1 < b * c

bölünme ( a / b ) / cilk önce değeri qtşu şekilde bulur:

qt * b + r3 == a

ve sonra değeri q2öyle bulur ki

q2 * c + r2 == qt

Öyleyse yerine koyun qtve şunu elde ederiz:

q2 * b * c + b * r2 + r3 == a

nerede 0 <= r2 < cve 0 <= r3 < b.

Aynı olan iki şey birbirine eşittir.

q1 * b * c + r1 == q2 * b * c + b * r2 + r3

q1 == q2 + xBir tam sayı için varsayalım x. Bunu yerine koyun ve şunu çözün x:

q2 * b * c + x * b * c + r1 = q2 * b * c + b * r2 + r3
x  = (b * r2 + r3 - r1) / (b * c)

nerede

 0 <= r1 < b * c
 0 <= r2 < c
 0 <= r3 < b

xSıfırdan büyük olabilir mi? Hayır. Eşitsizliklerimiz var:

 b * r2 + r3 - r1 <= b * r2 + r3 <= b * (c - 1) + r3 < b * (c - 1) + b == b * c

Yani bu kesrin payı her zaman daha küçüktür b * c, dolayısıyla xsıfırdan büyük olamaz.

Can xsıfırdan küçük olması? Hayır, benzer bir argümanla okuyucuya bırakılmıştır.

Bu nedenle tamsayı xsıfırdır ve bu nedenle q1 == q2.


7
Evet @JoseRuiSantos, ancak her iki x1 vex2 operasyonun bu durumda aynı kilitlenmesine
Marc Gravell

@JoseRuiSantos bu her iki durum için de doğru değil mi?
Jodrell

vc 74'ün yanıtı silindi, bu nedenle çoğu insan artık referans verdiğiniz örneği göremiyor.
Gabe

Bu doğru, hem x1ve x2eğer kilitlenmesine bveya csıfırdır. Diğer değerler için x1olası tamsayı taşması önlemek olacağından ifadesi, daha iyi ( b * c)olduğunu x2vardır.
Jose Rui Santos

Taşmalar ve kontrol edilen aritmetik hakkında ilginç bir nokta, teşekkürler!
Jason Crease

4

Mutlak değerleri bve cyaklaşık sqrt(2^31)(yaklaşık 46 300) altında ise, bu b * casla taşmaz, değerler her zaman eşleşecektir. b * cTaşma olursa , bir checkedbağlamda bir hata atılabilir veya bir bağlamda yanlış bir değer elde edebilirsiniz unchecked.


2

Başkalarının fark ettiği taşma hatalarından kaçınarak, her zaman eşleşirler.

Diyelim ki a/b=q1, bu şu anlama geliyor a=b*q1+r1, nerede 0<=r1<b.
Şimdi varsayalım ki a/b/c=q2, bu demek oluyor ki q1=c*q2+r2, nerede 0<=r2<c.
Bu şu demektir a=b(c*q2+r2)+r1=b*c*q2+br2+r1.
Bunun a/(b*c)=a/b/c=q2için sahip olmamız gerekiyor 0<=b*r2+r1<b*c.
Ancak b*r2+r1<b*r2+b=b*(r2+1)<=b*c, gerektiği gibi ve iki işlem eşleşiyor.

Negatif bveya cnegatif ise bu işe yaramaz , ancak bu durumda da tamsayı bölmesinin nasıl çalıştığını bilmiyorum.


0

Eğlenmek için kendi kanıtımı sunacağım. Bu aynı zamanda taşmayı da yok sayıyor ve maalesef sadece pozitifleri ele alıyor, ancak kanıtın temiz ve açık olduğunu düşünüyorum.

Amaç bunu göstermek

floor(floor(x/y)/z) = floor(x/y/z)

/normal bölünme nerede (bu kanıt boyunca).

Bölümü ve kalanını a/b benzersiz olarak temsil ediyoruz a = kb + r(bununla k,rbunun benzersiz olduğunu ve aynı zamanda not olduğunu kastediyoruz |r| < |b|). O zaman bizde:

(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

Yani amacımız sadece bunu göstermek k1 == k2. Bizde:

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

ve böylece:

(4) x/y = k1*z + r1 + r/y (from above)
x/y = k2*z + r2 (from line 3)

Şimdi (2) den r1bir tamsayı ( k1*ztanım gereği bir tamsayıdır) ve r1 < z(ayrıca tanım gereği) gözlemleyin . Ayrıca (1) 'den bunu biliyoruz r < y => r/y < 1. Şimdi r1 + r/y(4) 'ten gelen toplamı düşünün . Talebi olduğunu r1 + r/y < zve bu, daha önceki istemlerden açıktır (nedeniyle 0 <= r1 < zve r1biz bu nedenle bir tamsayıdır 0 <= r1 <= z-1. Bu nedenle 0 <= r1 + r/y < z). Böylece r1 + r/y = r2tanımı gereği r2(aksi takdirde , kalan tanımıyla çelişen iki kalıntı olur x/y). Dolayısıyla bizde:

x/y = k1*z + r2
x/y = k2*z + r2

ve istediğimiz sonuca sahibiz k1 = k2.

Yukarıdaki kanıt, fazladan bir durumu kontrol etmeniz gereken birkaç adım dışında negatiflerle çalışmalıdır ... ama kontrol etmedim.


0

sayaç örneği: INT_MIN / -1 / 2


"A, b ve c büyük olmayan pozitif tamsayılar olsun."
Pang

Bu ilginç bir durum (yani -INT_MIN bir taşmadır). Teşekkürler!
Jason Crease
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.