¿Me estoy perdiendo de algo? y / x + 1 funciona muy bien (siempre que sepa que el operador / siempre se redondea).
rikkit
51
@rikkit: si y y x son iguales, y / x + 1 es demasiado alto.
Ian Nelson
1
Para cualquiera que acabe de encontrar esto, esta respuesta a una pregunta fraudulenta evita la conversión innecesaria al doble y evita las preocupaciones de desbordamiento, además de proporcionar una explicación clara.
ZX9
2
@IanNelson más generalmente si xes divisible por y, y/x + 1sería demasiado alto.
Ohad Schneider
1
@ ZX9 No, no evita problemas de desbordamiento. Es exactamente la misma solución que Ian Nelson publicó aquí.
user247702
Respuestas:
478
Encontró una solución elegante:
int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
-1 debido al error de desbordamiento señalado por Brandon DuRette
finnw
30
El Sr. Obvious dice: Recuerde asegurarse de que recordsPerPage no sea cero
Adam Gent
77
Buen trabajo, no puedo creer que C # no tenga un techo entero.
gosukiwi
2
Sí, aquí estoy a mediados de 2017 tropezando con esta gran respuesta después de probar varios enfoques mucho más complejos.
Mifo
1
Para lenguajes con un operador de división euclidiana adecuado como Python, sería un enfoque aún más simple pageCount = -((-records) // recordsPerPage).
supercat
194
La conversión a coma flotante y viceversa parece una gran pérdida de tiempo a nivel de CPU.
La solución de Ian Nelson:
int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
Se puede simplificar para:
int pageCount =(records -1)/ recordsPerPage +1;
AFAICS, esto no tiene el error de desbordamiento que Brandon DuRette señaló, y debido a que solo lo usa una vez, no necesita almacenar los registrosPerPage especialmente si proviene de una función costosa para obtener el valor de un archivo de configuración o alguna cosa.
Es decir, esto podría ser ineficiente, si config.fetch_value utiliza una búsqueda en la base de datos o algo así:
int pageCount =(records + config.fetch_value('records per page')-1)/ config.fetch_value('records per page');
Esto crea una variable que realmente no necesita, que probablemente tenga implicaciones (menores) de memoria y simplemente escriba demasiado:
int recordsPerPage = config.fetch_value('records per page')int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
Esta es toda una línea y solo obtiene los datos una vez:
int pageCount =(records -1)/ config.fetch_value('records per page')+1;
+1, el problema de cero registros que aún devuelve 1 pageCount es realmente útil, ya que todavía quisiera 1 página, mostrando el marcador de posición / fila falsa de "no hay registros que coincidan con sus criterios", ayuda a evitar cualquier problema de "conteo de 0 páginas" en lo que sea control de paginación que usa.
Timothy Walters
27
Tenga en cuenta que las dos soluciones no devuelven la misma pageCount para cero registros. Esta versión simplificada devolverá 1 pageCount para cero registros, mientras que la versión de Roland Backhouse devuelve 0 pageCount. Está bien si eso es lo que deseas, pero las dos ecuaciones no son equivalentes cuando las realiza C # / Java stylee integer division.
Ian Nelson
10
pequeña edición para mayor claridad para las personas que lo escanean y los bodmas faltantes al cambiar a la simplificación de la solución Nelson (¡como hice la primera vez!), la simplificación con corchetes es ... int pageCount = ((records - 1) / recordsPerPage) + 1;
Dave Heywood
1
Debe agregar paréntesis a la versión simplificada para que no dependa de un orden específico de operaciones. es decir, ((records - 1) / recordsPerPage) + 1.
Martin
1
@Ian, esta respuesta no siempre devuelve 1. Se puede devolver 0 si su recordsPerPage es "1" y hay 0 registros: -1 / 1 + 1 = 0. Si bien eso no es algo muy común, es importante tenerlo en cuenta si permite que los usuarios ajusten el tamaño de la página. Por lo tanto, no permita que los usuarios tengan un tamaño de página de 1, verifique el tamaño de la página o ambos (probablemente preferible para evitar un comportamiento inesperado).
Michael
81
Para C #, la solución es convertir los valores a un doble (como Math.Ceiling toma un doble):
int nPages =(int)Math.Ceiling((double)nItems /(double)nItemsPerPage);
¿Por qué esta respuesta está tan abajo cuando el operador solicita C # explícitamente?
felickz 01 de
2
También debe convertir la salida en intporque Math.Ceilingdevuelve a doubleo decimal, según los tipos de entrada.
DanM7
15
porque es extremadamente ineficiente
Zar Shardan
66
Puede ser ineficiente, pero es extremadamente fácil de entender. Dado que el cálculo de un recuento de páginas generalmente se realiza una vez por solicitud, cualquier pérdida de rendimiento no sería medible.
Jared Kells
1
Es apenas más legible que este "(dividendo + (divisor - 1)) / divisor;" También es lento y requiere la biblioteca de matemáticas.
rueda el
68
Esto debería darte lo que quieres. Definitivamente querrá x elementos divididos por y elementos por página, el problema es cuando aparecen números desiguales, por lo que si hay una página parcial también queremos agregar una página.
int x = number_of_items;int y = items_per_page;// with out libraryint pages = x/y +(x % y >0?1:0)// with libraryint pages =(int)Math.Ceiling((double)x /(double)y);
x / y + !! (x% y) evita la bifurcación de los lenguajes tipo C. Las probabilidades son buenas, sin embargo, su compilador lo está haciendo de todos modos.
Rhys Ulerich
2
+1 por no desbordarse como las respuestas anteriores ... aunque convertir ints en dobles solo para Math.ceiling y luego nuevamente es una mala idea en el código sensible al rendimiento.
Rueda dentada
3
@RhysUlerich que no funciona en C # (no puede convertir directamente un int en un bool). La solución de rjmunro es la única forma de evitar ramificaciones, creo.
mancha
18
La solución matemática entera que proporcionó Ian es buena, pero sufre un error de desbordamiento de enteros. Suponiendo que las variables son todas int, la solución podría reescribirse para usar las longmatemáticas y evitar el error:
int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;
Si recordses a long, el error permanece. La solución de módulo no tiene el error.
@finnw: AFAICS, no hay un ejemplo del mundo real en esa página, solo un informe de alguien más encontrando el error en un escenario teórico.
rjmunro
55
Sí, estaba siendo pedante al señalar el error. Muchos errores pueden existir a perpetuidad sin causar ningún problema. Un error de la misma forma existió en la implementación de binarySearch por parte del JDK durante unos nueve años, antes de que alguien lo informara ( googleresearch.blogspot.com/2006/06/… ). Supongo que la pregunta es, independientemente de lo poco probable que sea que encuentre este error, ¿por qué no solucionarlo por adelantado?
Brandon DuRette
44
Además, debe tenerse en cuenta que lo importante no es solo la cantidad de elementos paginados, sino también el tamaño de la página. Entonces, si está construyendo una biblioteca y alguien elige no paginar pasando 2 ^ 31-1 (Integer.MAX_VALUE) como el tamaño de la página, entonces se activa el error.
int q = records / recordsPerPage, r = records % recordsPerPage;int pageCount = q -(-r >>(Integer.SIZE -1));
Nota: (-r >> (Integer.SIZE - 1))consiste en el bit de signo de r, repetido 32 veces (gracias a la extensión de signo del >>operador). Esto se evalúa a 0 si res cero o negativo, -1 si res positivo. Entonces restarlo qtiene el efecto de sumar 1 si records % recordsPerPage > 0.
Para los registros == 0, la solución de rjmunro da 1. La solución correcta es 0. Dicho esto, si sabe que los registros> 0 (y estoy seguro de que todos hemos asumido recordsPerPage> 0), la solución de rjmunro da los resultados correctos y no tiene ninguno de los problemas de desbordamiento.
int pageCount =0;if(records >0){
pageCount =(((records -1)/ recordsPerPage)+1);}// no else required
Todas las soluciones matemáticas enteras serán más eficientes que cualquiera de las soluciones de coma flotante.
No hay controles aquí (desbordamiento, DivideByZeroetc.), siéntase libre de agregar si lo desea. Por cierto, para aquellos preocupados por la sobrecarga de invocación de métodos, el compilador puede incluir funciones simples como esta, de modo que no creo que sea de lo que se trate. Salud.
PD: también puede ser útil tener en cuenta esto (obtiene el resto):
int remainder;int result =Math.DivRem(dividend, divisor,out remainder);
Esto es incorrecto. Por ejemplo: DivideUp(4, -2)devuelve 0 (debería ser -2). Solo es correcto para enteros no negativos que no están claros en la respuesta o en la interfaz de la función.
Thash
66
Thash, ¿por qué no haces algo útil como agregar el pequeño cheque adicional y luego si el número es negativo, en lugar de rechazar mi respuesta y hacer incorrectamente la declaración general: "Esto es incorrecto", cuando en realidad es solo una ventaja caso. Ya dejé en claro que primero debe hacer otras comprobaciones: "No hay comprobaciones aquí (desbordamiento, DivideByZero, etc.), siéntase libre de agregar si lo desea ".
Nicholas Petersen
1
La pregunta mencionaba "Estoy pensando en particular en cómo mostrar los controles de paginación ", de modo que los números negativos hubieran estado fuera de límites de todos modos. Nuevamente, solo haga algo útil y sugiera una verificación adicional si lo desea, es un hombre de esfuerzo de equipo.
Nicholas Petersen
No quise ser grosero y lo siento si lo tomas de esa manera. La pregunta era "Cómo redondear el resultado de la división de enteros". El autor mencionó la paginación, pero otras personas pueden tener diferentes necesidades. Creo que sería mejor si su función reflejara de alguna manera que no funciona para enteros negativos, ya que no está claro desde la interfaz (por ejemplo, un nombre diferente o tipos de argumento). Para trabajar con enteros negativos, puede, por ejemplo, tomar un valor absoluto del dividendo y el divisor y multiplicar el resultado por su signo.
Thash
2
Otra alternativa es usar la función mod () (o '%'). Si hay un resto distinto de cero, incremente el resultado entero de la división.
Lo siguiente debería redondear mejor que las soluciones anteriores, pero a expensas del rendimiento (debido al cálculo de coma flotante de 0.5 * rctDenominator):
uint64_t integerDivide(constuint64_t& rctNumerator,constuint64_t& rctDenominator ){// Ensure .5 upwards is rounded up (otherwise integer division just truncates - ie gives no remainder)return(rctDenominator ==0)?0:(rctNumerator +(int)(0.5*rctDenominator))/ rctDenominator;}
x
es divisible pory
,y/x + 1
sería demasiado alto.Respuestas:
Encontró una solución elegante:
Fuente: Conversión de números, Roland Backhouse, 2001
fuente
pageCount = -((-records) // recordsPerPage)
.La conversión a coma flotante y viceversa parece una gran pérdida de tiempo a nivel de CPU.
La solución de Ian Nelson:
Se puede simplificar para:
AFAICS, esto no tiene el error de desbordamiento que Brandon DuRette señaló, y debido a que solo lo usa una vez, no necesita almacenar los registrosPerPage especialmente si proviene de una función costosa para obtener el valor de un archivo de configuración o alguna cosa.
Es decir, esto podría ser ineficiente, si config.fetch_value utiliza una búsqueda en la base de datos o algo así:
Esto crea una variable que realmente no necesita, que probablemente tenga implicaciones (menores) de memoria y simplemente escriba demasiado:
Esta es toda una línea y solo obtiene los datos una vez:
fuente
-1 / 1 + 1 = 0
. Si bien eso no es algo muy común, es importante tenerlo en cuenta si permite que los usuarios ajusten el tamaño de la página. Por lo tanto, no permita que los usuarios tengan un tamaño de página de 1, verifique el tamaño de la página o ambos (probablemente preferible para evitar un comportamiento inesperado).Para C #, la solución es convertir los valores a un doble (como Math.Ceiling toma un doble):
En java deberías hacer lo mismo con Math.ceil ().
fuente
int
porqueMath.Ceiling
devuelve adouble
odecimal
, según los tipos de entrada.Esto debería darte lo que quieres. Definitivamente querrá x elementos divididos por y elementos por página, el problema es cuando aparecen números desiguales, por lo que si hay una página parcial también queremos agregar una página.
fuente
La solución matemática entera que proporcionó Ian es buena, pero sufre un error de desbordamiento de enteros. Suponiendo que las variables son todas
int
, la solución podría reescribirse para usar laslong
matemáticas y evitar el error:int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;
Si
records
es along
, el error permanece. La solución de módulo no tiene el error.fuente
Una variante de la respuesta de Nick Berardi que evita una rama:
Nota:
(-r >> (Integer.SIZE - 1))
consiste en el bit de signo der
, repetido 32 veces (gracias a la extensión de signo del>>
operador). Esto se evalúa a 0 sir
es cero o negativo, -1 sir
es positivo. Entonces restarloq
tiene el efecto de sumar 1 sirecords % recordsPerPage > 0
.fuente
Para los registros == 0, la solución de rjmunro da 1. La solución correcta es 0. Dicho esto, si sabe que los registros> 0 (y estoy seguro de que todos hemos asumido recordsPerPage> 0), la solución de rjmunro da los resultados correctos y no tiene ninguno de los problemas de desbordamiento.
Todas las soluciones matemáticas enteras serán más eficientes que cualquiera de las soluciones de coma flotante.
fuente
Necesita un método de extensión:
No hay controles aquí (desbordamiento,
DivideByZero
etc.), siéntase libre de agregar si lo desea. Por cierto, para aquellos preocupados por la sobrecarga de invocación de métodos, el compilador puede incluir funciones simples como esta, de modo que no creo que sea de lo que se trate. Salud.PD: también puede ser útil tener en cuenta esto (obtiene el resto):
fuente
DivideUp(4, -2)
devuelve 0 (debería ser -2). Solo es correcto para enteros no negativos que no están claros en la respuesta o en la interfaz de la función.Otra alternativa es usar la función mod () (o '%'). Si hay un resto distinto de cero, incremente el resultado entero de la división.
fuente
Hago lo siguiente, maneja cualquier desbordamiento:
Y use esta extensión si hay 0 resultados:
Además, para el número de página actual (no se le preguntó pero podría ser útil):
fuente
Alternativa para eliminar la ramificación en la prueba de cero:
No estoy seguro de si esto funcionará en C #, debería hacerlo en C / C ++.
fuente
Un método genérico, cuyo resultado puede iterar puede ser de interés:
fuente
Lists.partition(List, int)
) e irónicamente, elsize()
método del resultadoList
(a partir der09
) sufre del error de desbordamiento mencionado en la respuesta de Brandon DuRette .Tenía una necesidad similar donde necesitaba convertir minutos a horas y minutos. Lo que usé fue:
fuente
Lo siguiente debería redondear mejor que las soluciones anteriores, pero a expensas del rendimiento (debido al cálculo de coma flotante de 0.5 * rctDenominator):
fuente
Querrá hacer una división de punto flotante y luego usar la función de techo para redondear el valor al siguiente entero.
fuente