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.