Niech a, b i c będą niedużymi dodatnimi liczbami całkowitymi. Czy a / b / c zawsze równa się a / (b * c) z arytmetyką całkowitoliczbową C #? U mnie w C # wygląda to tak:
int a = 5126, b = 76, c = 14;
int x1 = a / b / c;
int x2 = a / (b * c);
Więc moje pytanie brzmi: czy x1 == x2
dla wszystkich a, bi c?
c#
math
integer
integer-arithmetic
Jason Crease
źródło
źródło
Odpowiedzi:
Niech
\
oznaczają dzielenia całkowitego (C #/
operatora pomiędzy dwomaint
S) i pozwolić/
oznaczają zwykły podział matematycznej. Następnie, jeślix,y,z
są dodatnimi liczbami całkowitymi i ignorujemy przepełnienie ,(x \ y) \ z = floor(floor(x / y) / z) [1] = floor((x / y) / z) [2] = floor(x / (y * z)) = x \ (y * z)
gdzie
Skok od wiersza
[1]
do wiersza[2]
powyżej wyjaśniono w następujący sposób. Załóżmy, że masz dwie liczby całkowitea
ib
oraz liczbę ułamkowąf
w przedziale[0, 1)
. Łatwo to zobaczyćfloor(a / b) = floor((a + f) / b) [3]
Jeśli w linii
[1]
identyfikujesza = floor(x / y)
,f = (x / y) - floor(x / y)
ib = z
, to[3]
implikuje to[1]
i[2]
jesteś równy.Możesz uogólnić ten dowód na ujemne liczby całkowite (nadal ignorując przepełnienie ), ale zostawię to czytelnikowi, aby zachować prostotę.
Jeśli chodzi o kwestię przepełnienia - zobacz odpowiedź Erica Lipperta, aby uzyskać dobre wyjaśnienie! W swoim wpisie na blogu i odpowiedziach przyjmuje również znacznie bardziej rygorystyczne podejście , na co powinieneś zwrócić uwagę, jeśli uważasz, że jestem zbyt falujący.
źródło
floor(x / y) - (x / y)
mały,z >= 1
więc przyjmowaniefloor
tego wynosi 0 i możemy to zignorować. To tak naprawdę nie następuje, ponieważ w rzeczywistości jest to dodatek w ramachfloor()
(tj. Rozważfloor(1/2)
vsfloor(1/2 + 1/2)
).To pytanie tak mi się spodobało, że 4 czerwca 2013 r . Uczyniłem je tematem mojego bloga . Dzięki za świetne pytanie!
Duże skrzynie są łatwe do zdobycia. Na przykład:
a = 1073741823; b = 134217727; c = 134217727;
ponieważ
b * c
przepełnia do liczby ujemnej.Dodałbym do tego fakt, że w sprawdzonej arytmetyce różnica między
a / (b * c)
i(a / b) / c
może być różnicą między działającym programem a programem, który ulega awarii. Jeśli iloczyn liczby całkowitejb
ic
przekracza granice liczby całkowitej, poprzednia ulegnie awarii w sprawdzonym kontekście.W przypadku małych dodatnich liczb całkowitych, powiedzmy, dostatecznie małych, aby pasowały do skrótu, należy zachować tożsamość.
Timothy Shields właśnie przesłał dowód; Przedstawiam tutaj alternatywny dowód. Załóżmy, że wszystkie liczby są nieujemnymi liczbami całkowitymi i żadna z operacji nie przepełnia się.
Dzielenie liczb całkowitych
x / y
znajduje wartośćq
taką, żeq * y + r == x
, gdzie0 <= r < y
.Więc podział
a / (b * c)
znajduje taką wartośćq1
, żegdzie
0 <= r1 < b * c
dzielenie
( a / b ) / c
najpierw znajduje wartośćqt
taką, żea następnie stwierdza wartość
q2
w taki sposób,Więc zastąp to w for
qt
i otrzymamy:gdzie
0 <= r2 < c
i0 <= r3 < b
.Dwie równe sobie rzeczy są sobie równe, więc mamy
Załóżmy, że
q1 == q2 + x
dla jakiejś liczby całkowitejx
. Zastąp to w i rozwiążx
:gdzie
0 <= r1 < b * c 0 <= r2 < c 0 <= r3 < b
Czy może
x
być większe niż zero? Nie. Mamy nierówności:b * r2 + r3 - r1 <= b * r2 + r3 <= b * (c - 1) + r3 < b * (c - 1) + b == b * c
Zatem licznik tego ułamka jest zawsze mniejszy niż
b * c
, więcx
nie może być większy od zera.Czy może
x
być mniejsze niż zero? Nie, za pomocą podobnego argumentu pozostawiono czytelnikowi.Dlatego liczba całkowita
x
jest równa zero, a zatemq1 == q2
.źródło
x1
ix2
operacja padnie identycznie w tym przypadkux1
ix2
ulegną awarii, jeślib
lubc
są równe zero. Dla innych wartości,x1
ekspresja jest lepszy, ponieważ pozwoli uniknąć ewentualnego przepełnienia całkowitą( b * c)
żex2
ma.Jeśli wartości bezwzględne
b
ic
są poniżej okołosqrt(2^31)
(ok. 46 300), tak abyb * c
nigdy się nie przepełniły, wartości zawsze będą zgodne. W przypadkub * c
przepełnienia można zgłosić błąd wchecked
kontekście lub uzyskać niepoprawną wartość wunchecked
kontekście.źródło
Unikając błędów przepełnienia zauważonych przez innych, zawsze pasują.
Załóżmy, że to
a/b=q1
znaczy, żea=b*q1+r1
, gdzie0<=r1<b
.Teraz przypuśćmy, że to
a/b/c=q2
znaczy, żeq1=c*q2+r2
, gdzie0<=r2<c
.To znaczy, że
a=b(c*q2+r2)+r1=b*c*q2+br2+r1
.W tym celu
a/(b*c)=a/b/c=q2
musimy mieć0<=b*r2+r1<b*c
.Ale
b*r2+r1<b*r2+b=b*(r2+1)<=b*c
w razie potrzeby te dwie operacje są zgodne.To nie działa, jeśli są ujemne
b
lubc
są ujemne, ale nie wiem, jak działa dzielenie liczb całkowitych w tym przypadku.źródło
Dam własny dowód dla zabawy. To również ignoruje przepełnienie i niestety obsługuje tylko pozytywy, ale myślę, że dowód jest czysty i wyraźny.
Celem jest pokazanie tego
floor(floor(x/y)/z) = floor(x/y/z)
gdzie
/
jest normalny podział (w całym tym dowodzie).Przedstawiamy iloraz i resztę z
a/b
unikalnie asa = kb + r
(przez to rozumiemy, żek,r
są unikalne i również zauważalne|r| < |b|
). Potem będzie:(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
Więc naszym celem jest po prostu to pokazać
k1 == k2
. Cóż, mamy: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
a zatem:
(4) x/y = k1*z + r1 + r/y (from above) x/y = k2*z + r2 (from line 3)
Teraz obserwujemy z (2), że
r1
jest to liczba całkowita (fork1*z
jest liczbą całkowitą z definicji) ir1 < z
(również z definicji). Ponadto z (1) wiemy, żer < y => r/y < 1
. Rozważmy teraz sumęr1 + r/y
z (4). Twierdzenie jest takier1 + r/y < z
i jest to jasne z poprzednich twierdzeń (ponieważ0 <= r1 < z
ir1
jest liczbą całkowitą, więc mamy0 <= r1 <= z-1
. Dlatego0 <= r1 + r/y < z
). Zatemr1 + r/y = r2
z definicjir2
(w przeciwnym razie byłyby dwie reszty, zx/y
których jest sprzeczna definicja reszty). Stąd mamy:i mamy nasz upragniony wniosek
k1 = k2
.Powyższy dowód powinien działać z negatywami, z wyjątkiem kilku kroków, które musiałbyś sprawdzić w dodatkowych przypadkach ... ale nie sprawdziłem.
źródło
Przykład licznika: INT_MIN / -1 / 2
źródło