Sean a, byc números enteros positivos no grandes. ¿A / b / c siempre es igual a a / (b * c) con aritmética de números enteros de C #? Para mí, en C # se ve así:
int a = 5126, b = 76, c = 14;
int x1 = a / b / c;
int x2 = a / (b * c);
Entonces mi pregunta es: ¿ x1 == x2
para todo a, by c?
c#
math
integer
integer-arithmetic
Jason Crease
fuente
fuente
Respuestas:
Dejar que
\
denotan división entera (C #/
operador entre dosint
s) y dejar que/
denotan división matemática habitual. Entonces, six,y,z
son enteros positivos y estamos ignorando el desbordamiento ,(x \ y) \ z = floor(floor(x / y) / z) [1] = floor((x / y) / z) [2] = floor(x / (y * z)) = x \ (y * z)
dónde
El salto de una línea
[1]
a otra[2]
se explica a continuación. Suponga que tiene dos números enterosa
yb
y un número fraccionariof
en el rango[0, 1)
. Es sencillo ver quefloor(a / b) = floor((a + f) / b) [3]
Si de acuerdo
[1]
a identificara = floor(x / y)
,f = (x / y) - floor(x / y)
yb = z
, a continuación,[3]
implica que[1]
y[2]
son iguales.Puede generalizar esta prueba a enteros negativos (aún ignorando el desbordamiento ), pero se lo dejo al lector para que sea sencillo.
Sobre el tema del desbordamiento , consulte la respuesta de Eric Lippert para obtener una buena explicación. También adopta un enfoque mucho más riguroso en la publicación y la respuesta de su blog , algo que deberías considerar si sientes que estoy siendo demasiado ondulante.
fuente
floor(x / y) - (x / y)
es pequeña,z >= 1
por lo que tomando el valorfloor
de eso es 0 y podemos ignorarlo. Eso en realidad no sigue, ya que en realidad es un sumando dentro de unfloor()
(es decir, considerarfloor(1/2)
vsfloor(1/2 + 1/2)
).Me gustó tanto esta pregunta que la convertí en el tema de mi blog el 4 de junio de 2013 . ¡Gracias por la gran pregunta!
Los estuches grandes son fáciles de conseguir. Por ejemplo:
a = 1073741823; b = 134217727; c = 134217727;
porque se
b * c
desborda a un número negativo.Yo agregaría a eso el hecho de que en aritmética comprobada , la diferencia entre
a / (b * c)
y(a / b) / c
puede ser la diferencia entre un programa que funciona y un programa que falla. Si el producto deb
yc
desborda los límites de un entero, el primero se bloqueará en un contexto verificado.Para pequeños enteros positivos, digamos, lo suficientemente pequeños como para caber en un corto, se debe mantener la identidad.
Timothy Shields acaba de publicar una prueba; Presento aquí una prueba alternativa. Suponga que todos los números aquí son enteros no negativos y que ninguna de las operaciones se desborda.
La división
x / y
entera de encuentra el valorq
tal queq * y + r == x
, donde0 <= r < y
.Entonces la división
a / (b * c)
encuentra el valorq1
tal quedónde
0 <= r1 < b * c
la división
( a / b ) / c
primero encuentra el valorqt
tal quey luego encuentra el valor
q2
tal queAsí que sustituye eso por
qt
y obtenemos:donde
0 <= r2 < c
y0 <= r3 < b
.Dos cosas iguales son iguales entre sí, así que tenemos
Supongamos
q1 == q2 + x
por algún número enterox
. Sustituye eso y resuelve parax
:dónde
0 <= r1 < b * c 0 <= r2 < c 0 <= r3 < b
¿Puede
x
ser mayor que cero? No. Tenemos las desigualdades:b * r2 + r3 - r1 <= b * r2 + r3 <= b * (c - 1) + r3 < b * (c - 1) + b == b * c
Entonces, el numerador de esa fracción es siempre menor que
b * c
, porx
lo que no puede ser mayor que cero.¿Puede
x
ser menor que cero? No, por argumento similar, se deja al lector.Por lo tanto, el entero
x
es cero y, por lo tantoq1 == q2
.fuente
x1
como lax2
operación fallarán de manera idéntica en ese casox1
yx2
fallarán sib
oc
son cero. Para otros valores, lax1
expresión es mejor, ya que evitará el posible desbordamiento de enteros( b * c)
quex2
tiene.Si los valores absolutos de
b
yc
están por debajo de aproximadamentesqrt(2^31)
(aprox. 46 300), por lo queb * c
nunca se desbordará, los valores siempre coincidirán. Si seb * c
desborda, se puede arrojar un error en unchecked
contexto o se puede obtener un valor incorrecto en ununchecked
contexto.fuente
Evitando los errores de desbordamiento notados por otros, siempre coinciden.
Supongamos eso
a/b=q1
, lo que significa esoa=b*q1+r1
, dónde0<=r1<b
.Ahora suponga eso
a/b/c=q2
, lo que significa esoq1=c*q2+r2
, dónde0<=r2<c
.Esto significa eso
a=b(c*q2+r2)+r1=b*c*q2+br2+r1
.Para
a/(b*c)=a/b/c=q2
, necesitamos tener0<=b*r2+r1<b*c
.Pero
b*r2+r1<b*r2+b=b*(r2+1)<=b*c
, según sea necesario, y las dos operaciones coinciden.Esto no funciona si
b
oc
son negativos, pero tampoco sé cómo funciona la división de enteros en ese caso.fuente
Ofreceré mi propia prueba por diversión. Esto también ignora el desbordamiento y, lamentablemente, solo maneja los positivos, pero creo que la prueba es clara y clara.
El objetivo es demostrar que
floor(floor(x/y)/z) = floor(x/y/z)
donde
/
es la división normal (a lo largo de esta prueba).Representamos el cociente y el resto de
a/b
únicamente comoa = kb + r
(con eso queremos decir quek,r
son únicos y también nota|r| < |b|
). Entonces tenemos:(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
Entonces, nuestro objetivo es simplemente mostrar eso
k1 == k2
. Bueno, tenemos: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
y por lo tanto:
(4) x/y = k1*z + r1 + r/y (from above) x/y = k2*z + r2 (from line 3)
Ahora observe de (2) que
r1
es un número entero (porquek1*z
es un número entero por definición) yr1 < z
(también por definición). Además de (1) sabemos esor < y => r/y < 1
. Ahora considere la sumar1 + r/y
de (4). La afirmación es quer1 + r/y < z
y esto se desprende de las afirmaciones anteriores (porque0 <= r1 < z
yr1
es un número entero, por lo que tenemos0 <= r1 <= z-1
. Por lo tanto0 <= r1 + r/y < z
). Así,r1 + r/y = r2
por definición der2
(de lo contrario, habría dos residuos de losx/y
que contradice la definición de residuo). Por tanto tenemos:y tenemos la conclusión deseada de eso
k1 = k2
.La prueba anterior debería funcionar con negativos, excepto por un par de pasos que necesitaría para verificar un caso (s) adicional (s) ... pero no lo verifiqué.
fuente
ejemplo de contador: INT_MIN / -1 / 2
fuente