Determinar si un número es múltiplo de diez o dentro de un conjunto particular de rangos


103

Tengo algunos bucles que necesito en mi programa. Puedo escribir el pseudocódigo, pero no estoy completamente seguro de cómo escribirlo de forma lógica.

Necesito -

if (num is a multiple of 10) { do this }

if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90

Esto es para un juego de mesa de serpientes y escaleras, si tiene más sentido para mi pregunta.

Me imagino que la primera declaración if necesitaré usar módulo, ¿ if (num == 100%10)sería correcta?

El segundo no tengo ni idea. Puedo escribirlo como, if (num > 10 && num is < 21 || etc)pero tiene que haber algo más inteligente que eso.


16
Generalmente, la longitud de un buen código es proporcional a la longitud del inglés que describe lo que hace. Entonces, cuando su "especificación" dice 11-20, 31-40, 51-60, 71-80, 91-100, puede esperar que su código mencione también esos números. Si esos números provienen de algún lugar o fueron generados por alguna razón, vea si puede codificar la razón en lugar de los números.
luqui

39
@ user3419168: Al compilador no le importa lo legible que sea su código; lo compilará en una fracción de segundo. Pero para los humanos que leen su código, las elecciones que haga pueden hacer que se comprenda en segundos, minutos, horas o nunca. Esto impone un costo; a las personas se les paga por leer y comprender el código, así que facilíteselo. Escriba siempre el código de producción para maximizar la legibilidad y recuerde que la concisión no necesariamente hace que el código funcione mejor.
Eric Lippert

2
@AmadeusDrZaius - MUY RARAMENTE he hecho lo mismo, pero solo para las secciones críticas de rendimiento. El bucle más estrecho que se llama 100 millones de veces califica; la declaración if en un juego de serpientes y escaleras no lo hace. Dónde trazar la línea entre ellos es una elección personal.
Floris

2
Odio decirlo, pero habiendo hecho suficiente trabajo corporativo, con principiantes escribiendo código real, tendría que recomendar la fuerza bruta. Porque entonces los nuevos lo entenderán y no lo romperán. triste pero cierto, en algunos casos es inteligente ser tonto.
Richard Le Mesurier

22
Esta es una pregunta decente, y no quiero quitarle nada al póster, pero esto no merece más de 500 puntos. Así es como terminamos con algunas de las tonterías que hacemos con personas con miles de puntos que parecen ser autoridades aquí. (Siéntase libre de mover este comentario si pertenece a otro lugar).
GaTechThomas

Respuestas:


86

Para el primero, para verificar si un número es un múltiplo de uso:

if (num % 10 == 0) // It's divisible by 10

Para el segundo:

if(((num - 1) / 10) % 2 == 1 && num <= 100)

Pero eso es bastante denso, y es mejor que enumere las opciones explícitamente.


Ahora que ha dado una mejor idea de lo que está haciendo, escribiría el segundo como:

   int getRow(int num) {
      return (num - 1) / 10;
   }

   if (getRow(num) % 2 == 0) {
   }

Es la misma lógica, pero al usar la función obtenemos una idea más clara de lo que significa.


79
if((num - 1) / 10) % 2 == 1 && num < 100)- Lloraría si lo viera.
Daniel Kamil Kozar

32
@DanielKamilKozar, como deberías.
Winston Ewert

2
@ user3419168, por sí solo deja a uno preguntarse qué significa en el mundo. No da pistas sobre lo que está tratando de hacer en el mundo. Es por eso que en la edición mostré una versión que divide la lógica en una función que aclara lo que realmente están haciendo los cálculos.
Winston Ewert

3
Puede ser prudente afirmar también num >= 11como (1) que el límite inferior está proscrito, y (2) %en un número negativo también devuelve un número negativo. (Debo admitir que usar & 1aquí es "más seguro", pero también asume conocimientos adicionales).
usr2564301

2
+1 para Editar, entra en el por qué de la lista de rangos y lo presenta de manera legible. En mi opinión, un paso adelante sería incluir también getRow(num) % 2 == 0una función para dejar en claro cuál es la intención. bool inEvenRow(int num){ return getRow(num) % 2 ==0;}
Mr.Mindor

40

if (num es un múltiplo de 10) {haz esto}

if (num % 10 == 0) {
  // Do something
}

si (el número está entre 11-20, 31-40, 51-60, 71-80, 91-100) {haz esto}

El truco aquí es buscar algún tipo de concordancia entre los rangos. Por supuesto, siempre puede utilizar el método de "fuerza bruta":

if ((num > 10 && num <= 20) ||
    (num > 30 && num <= 40) ||
    (num > 50 && num <= 60) ||
    (num > 70 && num <= 80) ||
    (num > 90 && num <= 100)) {
  // Do something
}

Pero puede notar que, si resta 1de num, tendrá los rangos:

10-19, 30-39, 50-59, 70-79, 90-99

En otras palabras, todos los números de dos dígitos cuyo primer dígito es impar. A continuación, debe crear una fórmula que exprese esto. Puede obtener el primer dígito dividiendo por 10, y puede probar que es impar al verificar el resto de 1 cuando divide por 2. Poniendo todo eso junto:

if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
  // Do something
}

Dada la compensación entre un código más largo pero que se puede mantener y un código "inteligente" más corto, elegiría más y más claro cada vez. Por lo menos, si intenta ser inteligente, incluya un comentario que explique exactamente lo que está tratando de lograr.

Es útil asumir que el próximo desarrollador que trabajará en el código está armado y sabe dónde vive. :-)


7
Todavía optaría por el código inteligente, pero lo convertiría en código mantenible extrayendo funciones. Sería igual de legible si se dijera ese último bit && isTensDigitOdd(num), tal vez con un comentario antes de la definición de la función que explique lo que hace. Si tal patrón existe, un comentario que explique el razonamiento del patrón es esclarecedor para la mantenibilidad imo.
chris

3
Chris, esa es una gran estrategia cuando la "inteligencia" tiene una clara ventaja: un código mucho más corto (lo que significa menos posibilidades de un error tipográfico, especialmente si cambia) o una gran mejora en la eficiencia. Casi siempre existe una compensación entre brevedad, claridad y eficiencia, y encontrar un buen compromiso es una gran habilidad para desarrollar. (Vea stackoverflow.com/a/2151844/29157 para una risa.)
Adam Liss

1
Este es un enfoque mucho mejor. Mucho más fácil de entender que el 'código inteligente' y la diferencia de rendimiento probablemente sea insignificante.
user1477388

@AdamLiss, Sí, mi opinión tiene poco valor ya que no he tenido suficiente experiencia para ver las repercusiones de estas decisiones. Estoy seguro de que lo haré pronto, y me aseguraré de obtener una segunda opinión si es necesario.
chris

1
No se quede corto. Tus instintos son muy sensibles y pareces ansioso por seguir aprendiendo. Cada opinión es valiosa si hay una buena razón detrás de ella ... y a veces incluso si no la hay. Apostaría dinero a que llegarás lejos.
Adam Liss

30

Si está utilizando GCC o cualquier compilador que admita rangos de casos , puede hacer esto, pero su código no será portátil .

switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
    // Do something
    break;
default:
    // Do something else
    break;
}

1
¿Puede decirme por qué este código no es portátil?
M Sharath Hegde

8
@MSharathHegde porque requiere la extensión GCC, que no es parte del estándar y algunos compiladores no la admiten
Bryan Chen

5
Esta es la respuesta correcta, porque es inmediatamente evidente cuál es la intención. Todas esas respuestas 'inteligentes' con módulo son una pesadilla de mantenimiento, incluso con comentarios.
smirkingman

@smirkingman De hecho, eso es lo que dije en mi comentario a la pregunta principal. Solo se necesita algo de experiencia de los nuevos codificadores en un trabajo corporativo para darse cuenta de que la forma obvia es a menudo mucho mejor que la forma del ninja inteligente.
Richard Le Mesurier

15

Esto es para futuros visitantes más que para principiantes. Para una solución más general, similar a un algoritmo, puede tomar una lista de valores iniciales y finales y verificar si un valor pasado está dentro de uno de ellos:

template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
    return std::any_of(first, last, [&val](const auto &p) {
        return p.first <= val && val <= p.second;
    });
}

Para simplificar, utilicé una lambda polimórfica (C ++ 14) en lugar de un pairargumento explícito . Esto probablemente también debería ceñirse al uso <y ==ser coherente con los algoritmos estándar, pero funciona así siempre que se Elemhaya <=definido para ello. De todos modos, se puede usar así:

std::pair<int, int> intervals[]{
    {11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};

const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);

Hay un ejemplo vivo aquí .


Solución ordenada. Probablemente habría usado una sola matriz, ya que puede formatearla con 2 números por línea para representar pares.
Kevin Lam

@ HunterGuy2, Muy buen punto. De hecho, voy a cambiarlo para que funcione en pares porque solo estaba pensando en iteradores zip por alguna razón.
chris

¡Enfoque stl realmente agradable! ¡Quiéralo!
higuaro

5

El primero es facil. Solo necesita aplicar el operador de módulo a su valor num:

if ( ( num % 10 ) == 0)

Dado que C ++ está evaluando cada número que no es 0 como verdadero, también podría escribir:

if ( ! ( num % 10 ) )  // Does not have a residue when divided by 10

Para el segundo, creo que esto es más claro de entender:

El patrón se repite cada 20, por lo que puede calcular el módulo 20. Todos los elementos que desee estarán en una fila excepto los que se pueden dividir entre 20.

Para obtenerlos también, simplemente use num-1 o mejor num + 19 para evitar lidiar con números negativos.

if ( ( ( num + 19 ) % 20 ) > 9 )

Esto supone que el patrón se repite para siempre, por lo que para 111-120 se aplicaría nuevamente, y así sucesivamente. De lo contrario, debe limitar los números a 100:

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )

5

Con un par de buenos comentarios en el código, se puede escribir de forma bastante concisa y legible.

// Check if it's a multiple of 10
if (num % 10 == 0) { ... }

// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }

2
El primer comentario es innecesario. Cualquier programador con un poco de experiencia sabrá que num % 10 == 0es lo mismo que numun múltiplo de 10.
Justin

7
sí, pero los principiantes también leen este sitio. Normalmente no usaría ese comentario en mi propio código, pero hace que la respuesta sea más clara para los principiantes que se beneficiarían de esta pregunta para principiantes.
La-comadreja

2
Por favor, nunca hagas esto. En realidad, reduce la legibilidad al ralentizar al lector y obligarlo a leer todo dos veces. Cualquier programador que no entienda eso if (num % 10 == 0)significa lo mismo que // Check if it's a multiple of 10no debería mantener su código. Este es un anti-patrón bien conocido.
Dawood ibn Kareem

1
@DavidWallace ver comentario arriba. No podemos garantizar que los lectores de esta publicación conozcan este anti-patrón.
La-comadreja

1
No, quiero decir que comentar cada línea para decir lo que hace es un anti-patrón. No quiero decir que usar %sea ​​un anti-patrón; obviamente no lo es. Realmente, asumiendo que muchos de los lectores de este post serán principiantes, enseñarles este estilo de escribir comentarios está haciendo una contribución negativa a su desarrollo como programadores.
Dawood ibn Kareem

4

Básicamente, usted mismo explicó la respuesta, pero aquí está el código por si acaso.

if((x % 10) == 0) {
  // Do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
  // Do this
}

2
Corrija x < 41 x > 50y ponga paréntesis.
101010

1
@ 40two, técnicamente, operator&&tiene una precedencia más alta que operator||, por lo que está bien, pero estoy bastante seguro de que GCC advierte de todos modos.
chris

18
Considere representar la desigualdad 10 < x < 21como en 10 < x && x < 21lugar de x > 10 && x < 21. Es más fácil leer la desigualdad cuando está en el mismo orden en que la escribirías matemáticamente.
Eric Lippert

5
Este código es bastante ilegible y dice poco sobre la lógica real. No me gusta esta respuesta.
Dariusz

3
Estoy rechazando esto porque respondiste exactamente lo que hizo el OP.
Bruno Ferreira

3

Puede que estés pensando demasiado en esto.

if (x % 10)
{
   .. code for 1..9 ..
} else
{
   .. code for 0, 10, 20 etc.
}

La primera línea if (x % 10)funciona porque (a) un valor que es un múltiplo de 10 calcula como '0', otros números dan como resultado su remainer, (b) un valor de 0 en una ifse considera false, cualquier otro valor es true.

Editar:

Para alternar hacia adelante y hacia atrás en veinte, use el mismo truco. Esta vez, el número fundamental es 10:

if (((x-1)/10) & 1)
{
  .. code for 10, 30, ..
} else
{
   .. code for 20, 40, etc.
}

x/10devuelve cualquier número de 0 a 9 como 0, 10 a 19 como 1y así sucesivamente. Probar en pares o impares (el & 1) te dice si es par o impar. Dado que sus rangos son en realidad "11 a 20", reste 1 antes de realizar la prueba.


1

Una petición de legibilidad

Si bien ya tiene algunas buenas respuestas, me gustaría recomendar una técnica de programación que hará que su código sea más legible para algún lector futuro, que puede ser usted en seis meses, un colega pidió realizar una revisión del código, su sucesor, ... .

Esto es para envolver cualquier declaración "inteligente" en una función que muestre exactamente (con su nombre) lo que está haciendo. Si bien hay un impacto minúsculo en el rendimiento (debido a la "sobrecarga de llamadas a funciones"), esto es realmente insignificante en una situación de juego como esta.

A lo largo del camino, puede desinfectar sus entradas, por ejemplo, probar valores "ilegales". Por lo tanto, podría terminar con un código como este: ¿ve cuánto más legible es? Las "funciones auxiliares" se pueden ocultar en algún lugar (no es necesario que estén en el módulo principal: se desprende de su nombre lo que hacen):

#include <stdio.h>

enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};

int notInRange(int square) {
  return(square < 1 || square > 100)?YES:NO;
}

int isEndOfRow(int square) {
  if (notInRange(square)) return OUT_OF_RANGE;
  if (square == 100) return WINNER; // I am making this up...
  return (square % 10 == 0)? YES:NO;
}

int rowType(unsigned int square) {
  // return 1 if square is in odd row (going to the right)
  // and 0 if square is in even row (going to the left)
  if (notInRange(square)) return OUT_OF_RANGE; // trap this error
  int rowNum = (square - 1) / 10;
  return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
                                       // and 1 (EVEN) for 11-20, 31-40, ...
}

int main(void) {
  int a = 12;
  int rt;
  rt = rowType(a); // this replaces your obscure if statement

  // and here is how you handle the possible return values:
  switch(rt) {
  case ODD:
    printf("It is an odd row\n");
    break;
  case EVEN:
    printf("It is an even row\n");
    break;
  case OUT_OF_RANGE:
    printf("It is out of range\n");
    break;
  default:
    printf("Unexpected return value from rowType!\n");
  }

  if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
  if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}

3
¿No está tratando de llevarlo demasiado lejos con YESy NO?
rmobis

@Raphael_ - bien podría ser: solo estaba mostrando un "por ejemplo". Mucha gente usa verdadero / falso, obviamente. Pero nunca puedo recordar (ya que los diferentes lenguajes que utilizan diferentes convenciones): es es TRUE, Trueo true? ¿Y qué archivos de encabezado, si los hubiera, debería incluir en C ordinario? Así que rodé el mío. Me pregunto si eso es lo que recibió un voto negativo ...
Floris

1

Para el primero:

if (x % 10 == 0)

se aplicará a:

10, 20, 30, .. 100 .. 1000 ...

Para el segundo:

if (((x-1) / 10) % 2 == 1)

solicitará:

11-20, 31-40, 51-60, ..

Básicamente, primero lo hacemos x-1para obtener:

10-19, 30-39, 50-59, ..

Luego los dividimos entre 10para obtener:

1, 3, 5, ..

Entonces comprobamos si este resultado es extraño.


1

Puedes probar lo siguiente:

        // multiple of 10
        if ((num % 10) == 0)
        {
           // Do something
        }
        else if (((num / 10) % 2) != 0)
        {
            //11-20, 31-40, 51-60, 71-80, 91-100
        }
         else
        {
            //other case
        }

En la pregunta OP, la verificación del múltiplo de 10 no está relacionada con la verificación del rango, y en la verificación del rango 20 debe estar en el mismo rango de 11, con su código ((20/10)% 2) -> ( 2% 2) -> 0
Serpiton

0

Sé que esta pregunta tiene tantas respuestas, pero de todos modos arrojaré la mía aquí ...

Tomado de Código completo de Steve McConnell , 2da edición: "Tablas de acceso de escalones:

Otro tipo de acceso a la mesa es el método escalonado. Este método de acceso no es tan directo como una estructura de índice, pero no desperdicia tanto espacio de datos. La idea general de las estructuras escalonadas, ilustrada en la Figura 18-5, es que las entradas en una tabla son válidas para rangos de datos más que para puntos de datos distintos.

Ingrese la descripción de la imagen aquí

Figura 18-5 El enfoque escalonado clasifica cada entrada determinando el nivel en el que llega a una "escalera". El "paso" que da determina su categoría.

Por ejemplo, si está escribiendo un programa de calificación, el rango de entrada "B" puede ser del 75 al 90 por ciento. Aquí hay una variedad de calificaciones que quizás tenga que programar algún día:

Ingrese la descripción de la imagen aquí

Para usar el método escalonado, coloque el extremo superior de cada rango en una tabla y luego escriba un bucle para comparar una puntuación con el extremo superior de cada rango. Cuando encuentre el punto en el que la puntuación excede por primera vez el límite superior de un rango, sabrá cuál es la calificación. Con la técnica de escalones, debe tener cuidado de manipular correctamente los puntos finales de los rangos. Aquí está el código en Visual Basic que asigna calificaciones a un grupo de estudiantes según este ejemplo:

Ingrese la descripción de la imagen aquí

Aunque este es un ejemplo simple, puede generalizarlo fácilmente para manejar varios estudiantes, múltiples esquemas de calificación (por ejemplo, diferentes calificaciones para diferentes niveles de puntos en diferentes asignaciones) y cambios en el esquema de calificación ".

Code Complete , 2da edición, páginas 426 - 428 (Capítulo 18).


¿Por qué crees que esta es la pregunta incorrecta? solo porque no proporcioné un ejemplo sobre el caso OP no significa que respondí la pregunta incorrecta ...
lauCosma

0

Como han señalado otros, hacer que las condiciones sean más concisas no acelerará la compilación o la ejecución, y tampoco ayuda necesariamente con la legibilidad.

Puede ayudar a hacer su programa más flexible, en caso de que más tarde decida que quiere una versión del juego para niños pequeños en un tablero de 6 x 6, o una versión avanzada (que puede jugar toda la noche) en un tablero de 40 x 50 .

Entonces lo codificaría de la siguiente manera:

// What is the size of the game board?
#define ROWS            10
#define COLUMNS         10

// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare     1
#define lastSquare      (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num)   (num == lastSquare)
#define overShot(num)   (num > lastSquare)

// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
//  have simplified these expressions)
#define getRow(num)   (((num - 1) / COLUMNS) + 1)
#define getCol(num)   (((num - 1) % COLUMNS) + 1)

// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num)    ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num)    ((getRow(num) % 2) == 0)

// Are we on the last square in the row?
#define isLastInRow(num)    (getCol(num) == COLUMNS)

// And finally we can get onto the code

if (notStarted(mySquare))
{
  // Some code for when we haven't got our piece on the board yet
}
else
{
  if (isLastInRow(mySquare))
  {
    // Some code for when we're on the last square in a row
  }


  if (isRightToLeftRow(mySquare))
  {
    // Some code for when we're travelling from right to left
  }
  else
  {
    // Some code for when we're travelling from left to right
  }
}

Sí, es detallado, pero deja claro exactamente lo que está sucediendo en el tablero de juego.

Si estuviera desarrollando este juego para mostrarlo en un teléfono o tableta, haría variables FILAS y COLUMNAS en lugar de constantes, para que puedan configurarse dinámicamente (al comienzo de un juego) para que coincidan con el tamaño y la orientación de la pantalla.

También permitiría que la orientación de la pantalla se cambiara en cualquier momento, a mitad del juego; todo lo que necesitas hacer es cambiar los valores de FILAS y COLUMNAS, dejando todo lo demás (el número de casilla actual en el que está cada jugador y el cuadrados de inicio / final de todas las serpientes y escaleras) sin cambios. Luego, 'solo' tiene que dibujar bien el tablero y escribir código para sus animaciones (supongo que ese era el propósito de sus ifdeclaraciones) ...


También voté a favor de la respuesta de Floris, un estilo diferente para lograr un resultado similar, que no vi antes de escribir mi respuesta
Laurence Renshaw

2
debe usar la función en línea en lugar de#define
Bryan Chen

Es una buena práctica cuando usa #defineinstrucciones similares a funciones , poner paréntesis alrededor de los argumentos, donde aparecen en la expansión. Entonces, en lugar de hacerlo #define finished(num) (num == lastSquare), debería escribir #define finished(num) ((num) == lastSquare). La razón es que si usa una instrucción de este tipo con una expresión que contiene un operador con una precedencia lo suficientemente baja, no obtendrá la respuesta que espera. En este caso, si no usa los paréntesis adicionales, entonces se finished(a & b)expande en lo (a & b == lastSquare)que es casi seguro que no es lo que desea.
Dawood ibn Kareem
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.