Trong số học số nguyên C #, a / b / c có luôn bằng a / (b * c) không?


81

Cho a, b và c là các số nguyên dương không lớn. A / b / c có luôn luôn bằng a / (b * c) với số nguyên C # không? Đối với tôi, trong C # có vẻ như:

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

Vì vậy, câu hỏi của tôi là: không x1 == x2cho tất cả a, b và c?


3
Đây là một câu hỏi toán học, không phải lập trình. Bạn có thể giải thích phần lập trình cụ thể của câu hỏi này là gì không?
Oded

38
@ Được gắn trong phạm vi của bất kỳ số hữu tỉ nào, nhưng điều này đặc biệt đề cập đến số học số nguyên (trong C #). IMO làm cho nó liên quan đến lập trình. Có thể quy tắc mà a / b / c == a / (b * c) nắm giữ trong số học nguyên, có thể nó chỉ nắm giữ trong số học hữu tỉ.
Tim S.

43
Đây là một câu hỏi hoàn toàn hợp lý về C #, và dễ trả lời.
Eric Lippert

12
@Oded Đây là một câu hỏi về số học máy tính và liệu nó có hoạt động giống như toán học thuần túy hay không. Nó không nên được đóng lại.
Jeffrey Sax

4
Tôi khá quan tâm đến một bằng chứng toán học về lý do tại sao (hoặc thực sự là có), bỏ qua phần tràn, trên thực tế cả hai tương đương nhau, nhưng tôi vẫn chưa thể ghép chúng lại với nhau.
Rawling

Câu trả lời:


71

Đặt \biểu thị phép chia số nguyên ( /toán tử C # giữa hai ints) và /biểu thị phép chia toán học thông thường. Sau đó, nếu x,y,zsố nguyên dương và chúng tôi đang bỏ qua tràn ,

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

Ở đâu

a \ b = floor(a / b)

Bước nhảy từ dòng này [1]sang dòng [2]ở trên được giải thích như sau. Giả sử bạn có hai số nguyên abvà một số phân số ftrong phạm vi [0, 1). Thật đơn giản để thấy rằng

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

Nếu trong dòng [1]bạn xác định a = floor(x / y), f = (x / y) - floor(x / y)b = z, thì [3]ngụ ý rằng [1][2]bằng nhau.

Bạn có thể khái quát bằng chứng này thành các số nguyên âm (vẫn bỏ qua phần tràn ), nhưng tôi sẽ để người đọc giữ cho vấn đề đơn giản.


Về vấn đề tràn - hãy xem câu trả lời của Eric Lippert để có lời giải thích tốt! Anh ấy cũng có cách tiếp cận nghiêm ngặt hơn nhiều trong bài đăng trên blog và câu trả lời của mình, điều mà bạn nên xem xét nếu bạn cảm thấy tôi đang quá tay.


1
Hah, đó là những gì tôi đã theo đuổi :)
Rawling

Tôi thích việc bạn sử dụng \ và / cho việc này. Làm cho mọi thứ rõ ràng hơn nhiều.
Justin Morgan,

@JustinMorgan Kí hiệu thực sự được sử dụng trong một số ngôn ngữ lập trình khác (mặc dù tôi không nhớ là ngôn ngữ nào vào lúc này).
Timothy Shields

1
@TimothyShields VB.net thì có.
Arie Xiao

Tôi nghĩ tuyên bố là đúng, nhưng bằng chứng của bạn dường như thiếu một bước quan trọng. Có thể tôi đã hiểu sai lời biện minh của bạn cho dòng 2 => dòng 3. Cách tôi giải thích nó floor(x / y) - (x / y)là nhỏ và z >= 1vì vậy lấy floorsố đó là 0 và chúng tôi có thể bỏ qua nó. Điều đó không thực sự tuân theo vì nó thực sự là một quảng cáo trong floor()(tức là xem xét floor(1/2)so với floor(1/2 + 1/2)).
rliu,

77

Tôi thích câu hỏi này đến nỗi tôi đã đặt nó làm chủ đề cho blog của mình vào ngày 4 tháng 6 năm 2013 . Cảm ơn vì câu hỏi tuyệt vời của bạn!


Trường hợp lớn rất dễ đi qua. Ví dụ:

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

b * ctràn thành một số âm.

Tôi muốn nói thêm rằng thực tế là trong số học đã kiểm tra , sự khác biệt giữa a / (b * c)(a / b) / ccó thể là sự khác biệt giữa một chương trình hoạt động và một chương trình bị treo. Nếu tích của bcvượt quá giới hạn của một số nguyên thì tích trước đó sẽ bị lỗi trong ngữ cảnh được kiểm tra.

Ví dụ, đối với các số nguyên dương nhỏ, đủ nhỏ để vừa với một số ngắn, danh tính nên được duy trì.


Timothy Shields vừa đăng một bằng chứng; Tôi trình bày ở đây một bằng chứng thay thế. Giả sử tất cả các số ở đây là số nguyên không âm và không có thao tác nào bị tràn.

Phép chia số nguyên của x / ytìm giá trị qsao cho q * y + r == x, ở đâu 0 <= r < y.

Vậy phép chia a / (b * c)tìm giá trị q1sao cho

q1 * b * c + r1 == a

Ở đâu 0 <= r1 < b * c

( a / b ) / cđầu tiên phép chia tìm giá trị qtsao cho

qt * b + r3 == a

và sau đó tìm giá trị q2sao cho

q2 * c + r2 == qt

Vì vậy, thay thế nó vào qtvà chúng tôi nhận được:

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

ở đâu 0 <= r2 < c0 <= r3 < b.

Hai vật bằng nhau thì bằng nhau nên ta có

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

Giả sử q1 == q2 + xcho một số nguyên x. Thay thế nó trong và giải quyết cho x:

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

Ở đâu

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

xthể lớn hơn không? Không. Chúng ta có sự bất bình đẳng:

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

Vì vậy, tử số của phân số đó luôn nhỏ hơn b * c, vì vậy xkhông thể lớn hơn không.

xthể nhỏ hơn không? Không, bằng lập luận tương tự, để lại cho người đọc.

Do đó số nguyên xbằng 0, và do đó q1 == q2.


7
@JoseRuiSantos có, nhưng cả hai x1 các x2hoạt động sẽ sụp đổ hệt trong trường hợp đó
Marc Gravell

@JoseRuiSantos điều đó không đúng với cả hai trường hợp?
Jodrell

Câu trả lời của vc 74 đã bị xóa, vì vậy hầu hết mọi người không còn có thể nhìn thấy ví dụ mà bạn đang tham khảo.
Gabe

Điều đó đúng, cả hai x1x2sẽ sụp đổ nếu bhoặc cbằng không. Đối với các giá trị khác, x1biểu hiện là tốt hơn, vì sẽ tránh tràn số nguyên có thể xảy ra ( b * c)x2có.
Jose Rui Santos

Điểm thú vị về tràn và kiểm tra số học, cảm ơn!
Jason Crease

4

Nếu các giá trị tuyệt đối của bcnhỏ hơn khoảng sqrt(2^31)(xấp xỉ 46 300) b * c, thì giá trị đó sẽ không bao giờ tràn, các giá trị sẽ luôn khớp. Nếu b * ctràn, thì lỗi có thể được tạo ra trong checkedngữ cảnh hoặc bạn có thể nhận được giá trị không chính xác trong uncheckedngữ cảnh.


2

Tránh lỗi tràn do người khác nhận thấy, chúng luôn khớp.

Hãy giả sử rằng a/b=q1, có nghĩa là a=b*q1+r1, ở đâu 0<=r1<b.
Bây giờ giả sử rằng a/b/c=q2, có nghĩa là q1=c*q2+r2, ở đâu 0<=r2<c.
Điều này có nghĩa là a=b(c*q2+r2)+r1=b*c*q2+br2+r1.
Để có a/(b*c)=a/b/c=q2, chúng ta cần phải có 0<=b*r2+r1<b*c.
Nhưng b*r2+r1<b*r2+b=b*(r2+1)<=b*c, theo yêu cầu, và hai hoạt động khớp với nhau.

Điều này không hoạt động nếu bhoặc clà số âm, nhưng tôi cũng không biết phép chia số nguyên hoạt động như thế nào trong trường hợp đó.


0

Tôi sẽ cung cấp bằng chứng của riêng tôi cho vui. Điều này cũng bỏ qua tràn và chỉ xử lý các mặt tích cực, nhưng tôi nghĩ rằng bằng chứng là sạch sẽ và rõ ràng.

Mục đích là thể hiện rằng

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

đâu /là phép chia bình thường (trong suốt bằng chứng này).

Chúng tôi đại diện cho thương số và phần còn lại của a/b duy nhấta = kb + r(do đó chúng tôi có nghĩa k,rlà duy nhất và cũng cần lưu ý |r| < |b|). Sau đó chúng tôi có:

(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

Vì vậy, mục tiêu của chúng tôi chỉ là thể hiện điều đó k1 == k2. Chúng tôi có:

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

và như vậy:

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

Bây giờ quan sát từ (2) đó r1là một số nguyên (đối với k1*zlà một số nguyên theo định nghĩa) và r1 < z(cũng theo định nghĩa). Hơn nữa từ (1) chúng ta biết rằng r < y => r/y < 1. Bây giờ hãy xem xét tổng r1 + r/ytừ (4). Yêu cầu là vậy r1 + r/y < zvà điều này rõ ràng so với các yêu cầu trước đó (bởi vì 0 <= r1 < zr1là một số nguyên nên chúng ta có 0 <= r1 <= z-1. Do đó 0 <= r1 + r/y < z). Do đó r1 + r/y = r2theo định nghĩa của r2(nếu không sẽ có hai phần dư x/ymà từ đó mâu thuẫn với định nghĩa phần dư). Do đó chúng tôi có:

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

và chúng tôi có kết luận mong muốn của chúng tôi rằng k1 = k2.

Bằng chứng trên sẽ hoạt động với các trường hợp phủ định ngoại trừ một vài bước mà bạn cần kiểm tra (các) trường hợp bổ sung ... nhưng tôi đã không kiểm tra.


0

ví dụ bộ đếm: INT_MIN / -1 / 2


"Cho a, b và c là không lớn tích cực số nguyên."
Pang

Đó là một trường hợp thú vị (tức là -INT_MIN là một phần tràn). Cảm ơn!
Jason Crease
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.