Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Modificar un digito de un numero

Estas en el tema de Modificar un digito de un numero en el foro de C/C++ en Foros del Web. Hola amigos, he estado liado con un código en el cual tengo un unsigned long y al que le quiero modificar un digito. Lo explico ...
  #1 (permalink)  
Antiguo 08/11/2016, 17:01
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Modificar un digito de un numero

Hola amigos, he estado liado con un código en el cual tengo un unsigned long y al que le quiero modificar un digito. Lo explico con un ejemplo. Imaginen:
unsigned long valor = 12345678;
si quiero sustituir por ejemplo el digito de las decenas de millón a partir de algún cálculo con el digito de las unidades de millar tengo que hacer muchas operaciones de divisiones., modulos., sumas., y multiplicaciones para obtener ambos dígitos y volver a montar el número. Mi pregunta es si hay algún modo más rápido usando operadores binarios o algo así.
Ahora mismo hago algo asi:
Código PHP:
#include  <stdio.h>

int main  (){
    
unsigned long valor12345678;
    
int umillar= ((valor 10000) - (valor 1000)) / 1000;
    
int dmillon umillar 3;
    if(
dmillon 0)
        
dmillon += 10;
    
valor dmillon 10000000 valor 10000000;
    
printf ("%lu",valor);
    return 
0;

  #2 (permalink)  
Antiguo 08/11/2016, 17:26
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Modificar un digito de un numero

Tirando de C++11 puedes probar algo así (por favor, abandona la versión de Borland del siglo pasado ):

Función para calcular un exponente. El modificador constexpr permite que el compilador pueda conocer el resultado en tiempo de compilación (y usar dicho valor en vez de llamar a la función).
Código C++:
Ver original
  1. constexpr unsigned long Exponente(int e)
  2. {
  3.   return (e>0)? 10*Exponente(e-1) : 1;
  4. }

Template para reelmplazar el dígito:
Código C++:
Ver original
  1. template<int D> unsigned long Reemplazar(unsigned long valor, unsigned nuevoDigito)
  2. {
  3.   unsigned long superior = valor / Exponente(D+1);
  4.   unsigned long inferior = valor % Exponente(D);
  5.  
  6.   return (superior*10+nuevoDigito)*Exponente(D) + inferior;
  7. }

Y el uso:
Código C++:
Ver original
  1. int main()
  2. {
  3.   std::cout << Reemplazar<2>(123456,8) << '\n'; //123856
  4.   std::cout << Reemplazar<5>(111111,5) << '\n'; //511111
  5. }

Aunque también se puede hacer sin template:

Código C++:
Ver original
  1. unsigned long Reemplazar(unsigned long valor, unsigned indice, unsigned nuevoDigito)
  2. {
  3.   unsigned long superior = valor / Exponente(indice+1);
  4.   unsigned long inferior = valor % Exponente(indice);
  5.  
  6.   return (superior*10+nuevoDigito)*Exponente(indice) + inferior;
  7. }

La diferencia básica es que al usar el template hay cálculos que se realizarán en tiempo de compilación... reduciendo el tiempo necesario para ejecutar la función.

Por otro lado, la lógica binaria no te sirve porque los números se almacenan en base 2 y cada dígito de la numeración base10 afecta a varios dígitos de la base 2... no hay operación binaria posible.

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #3 (permalink)  
Antiguo 08/11/2016, 18:23
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Modificar un digito de un numero

Ok mil gracias. Una cosa, me dices que la ventaja de usar templates aquí es que parte del los cálculos se realizan en tiempo de compilación pero si el valor es solicitado en tiempo de ejecución ¿que cálculos va a hacer en tiempo de compilación?
Ahora mismo lo tengo así y funciona perfecto:
Código C:
Ver original
  1. #include  <stdio.h>
  2.  
  3. unsigned long Exponente(int e)
  4. {
  5.     return (e>0)? 10*Exponente(e-1) : 1;
  6. }
  7.  
  8. int ObtenerDigito(unsigned long valor, unsigned indice)
  9. {
  10.     valor %= Exponente(indice+1);
  11.     valor  /= Exponente(indice);
  12.  
  13.     return valor;
  14. }
  15.  
  16. unsigned long ReemplazarDigito(unsigned long valor, unsigned indice, unsigned nuevoDigito)
  17. {
  18.   unsigned long superior = valor / Exponente(indice+1);
  19.   unsigned long inferior = valor % Exponente(indice);
  20.  
  21.   return (superior*10+nuevoDigito)*Exponente(indice) + inferior;
  22. }
  23.  
  24. int CalcularValorDigito(int digito,int a){
  25.     digito -= a;
  26.     if(digito < 0)
  27.         digito += 10;
  28.     return digito;
  29. }
  30.  
  31. int main(){
  32.     unsigned long valor=12345678;
  33.     int digito=ObtenerDigito(valor, 3);
  34.     digito=CalcularValorDigito(digito,3);
  35.     valor=ReemplazarDigito(valor,7,digito);
  36.     printf("%lu",valor);
  37.     return 0;
  38. }
¿Podrías explicarme el comportamiento de la función recursiva Exponente? No entiendo su funcionamiento.

Última edición por aguml; 08/11/2016 a las 19:27
  #4 (permalink)  
Antiguo 09/11/2016, 04:00
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Modificar un digito de un numero

Cita:
Iniciado por aguml Ver Mensaje
¿que cálculos va a hacer en tiempo de compilación?
Código C++:
Ver original
  1. constexpr unsigned long Exponente(int e)
  2. {
  3.   return (e>0)? 10*Exponente(e-1) : 1;
  4. }
  5.  
  6. template<int D> unsigned long Reemplazar(unsigned long valor, unsigned nuevoDigito)
  7. {
  8.   unsigned long superior = valor / Exponente(D+1);
  9.   unsigned long inferior = valor % Exponente(D);
  10.  
  11.   return (superior*10+nuevoDigito)*Exponente(D) + inferior;
  12. }

El dígito a convertir tiene que ser conocido en tiempo de compilación y da la casualidad que Exponente() está etiquetada como constexpr. En consecuencia, las llamadas Exponente(D) y Exponente(D+1) se sustituirán por su correspondiente solución en tiempo de compilación.

Cita:
Iniciado por aguml Ver Mensaje
¿Podrías explicarme el comportamiento de la función recursiva Exponente? No entiendo su funcionamiento.
Código C++:
Ver original
  1. constexpr unsigned long Exponente(int e)
  2. {
  3.   return (e>0)? 10*Exponente(e-1) : 1;
  4. }

Imaginemos que llamamos a Exponente(3). Esto es lo que sucede:
  • Exponente(3): e=3, luego e>0. Calculamos 10*Exponente(2)
  • Exponente(2): e=2, luego e>0. Calculamos 10*Exponente(1)
  • Exponente(1): e=1, luego e>0. Calculamos 10*Exponente(0)
  • Exponente(0): e=0, return 1.
  • Exponente(1): return 10*Exponente(0) = 10*1 = 10
  • Exponente(2): return 10*Exponente(1) = 10*10 = 100
  • Exponente(3): return 10*Exponente(2) = 10*100 = 1000

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #5 (permalink)  
Antiguo 09/11/2016, 06:03
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Modificar un digito de un numero

Muchísimas gracias por todo. Una última pregunta ¿que es más eficiente en este caso? ¿usar recursividad o usar un bucle para obtener el exponente?
  #6 (permalink)  
Antiguo 09/11/2016, 08:10
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Modificar un digito de un numero

Para lo que estás haciendo te va a dar prácticamente igual porque el número de iteraciones es ridículo y para ejemplo el siguiente código:

Código C++:
Ver original
  1. template<int E> struct Exponente
  2. {
  3.     enum { value = 10 * Exponente<E-1>::value };
  4. };
  5.  
  6. template<> struct Exponente<0>
  7. {
  8.     enum{ value = 1 };
  9. };
  10.  
  11. template<int E> unsigned long ExponenteVTemplate(int)
  12. { return Exponente<E>::value; }
  13.  
  14. constexpr unsigned long ExponenteVConstExpr(int e)
  15. {
  16.   return (e>0)? 10*ExponenteVConstExpr(e-1) : 1;
  17. }
  18.  
  19. unsigned long ExponenteVRecursivo(int e)
  20. {
  21.   return (e>0)? 10*ExponenteVRecursivo(e-1) : 1;
  22. }
  23.  
  24. unsigned long ExponenteVIterativo(int e)
  25. {
  26.   unsigned long toReturn = 1;
  27.  
  28.   while( e )
  29.   {
  30.     toReturn *= 10;
  31.     e--;
  32.   }
  33.  
  34.   return toReturn;
  35. }
  36.  
  37. template<class T>
  38. void Test(T func, const std::string& msg)
  39. {
  40.   auto begin = std::clock();
  41.  
  42.   unsigned long accum = 0;
  43.   for( unsigned long i=0; i<100000000;i++ )
  44.   {
  45.     accum += func(7);
  46.   }
  47.  
  48.   std::cout << "Tiempo de " << msg << ": " << static_cast<float>(std::clock()-begin)/CLOCKS_PER_SEC << '\n';
  49. }
  50.  
  51. int main()
  52. {
  53.   Test(ExponenteVTemplate<7>,"version template ");
  54.   Test(ExponenteVConstExpr,  "version constexpr");
  55.   Test(ExponenteVRecursivo,  "version recursiva");
  56.   Test(ExponenteVIterativo,  "version iterativa");
  57. }

El template Test lo he creado únicamente para no tener que duplicar el bucle y el contador de tiempo.

En mi caso aparecen los siguientes resultados:

Código resultado:
Ver original
  1. Tiempo de version template : 0.145
  2. Tiempo de version constexpr: 0.483
  3. Tiempo de version recursiva: 0.543
  4. Tiempo de version iterativa: 0.521

Verás que salvo en el caso de la versión template los tiempos son prácticamente similares y esto es debido a que el coste de las multiplicaciones es muy superior al coste del resto de las operaciones. Eso sí, la versión de templates tiene un par de desventajas:
  • La profundidad de la recursividad está limitada en función del compilador, luego no te sirve para todas las situaciones.
  • Usar el template te exige saber con antelación el exponente a utilizar, ya que ese valor hay que proporcionarlo en tiempo de compilación. Este detalle impide que puedas, por ejemplo, usar una variable para determinar el exponente que quieres calcular.

nota: La versión template usa un parámetro int al que no da uso simplemente para ser compatible con la función de Test que he diseñado. Fuera de este contexto la función no necesitaría recibir ningún argumento.

Si las funciones tuviesen un número de iteraciones significativo lo normal es que la versión iterativa fuese mejorando los tiempos respecto a la versión recursiva, ya que una función recursiva implica realizar múltiples llamadas a la función, lo que lleva a realizar muchos saltos en el código, las variables de la función estarían duplicadas en la pila, ...

En cuanto a la versión constexpr. Un detalle que se me pasó comentar es que el compilador no está obligado a calcular el valor en tiempo de compilación... sin embargo constexpr tiene otra serie de usos que no creo que entren en el alcance de la pregunta inicial... google tiene mucha información al respecto :)

Un saludo
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.

Última edición por eferion; 09/11/2016 a las 08:17
  #7 (permalink)  
Antiguo 09/11/2016, 10:32
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Modificar un digito de un numero

El exponente lo se con anterioridad pero eso de que la profundidad de recursividad dependa del compilador no me hace mucha gracia. En mi caso el valor máximo seria 10000000 ¿podría tener problemas?
  #8 (permalink)  
Antiguo 09/11/2016, 13:12
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Modificar un digito de un numero

Si te refieres a tener 10.000.000 de dígitos si, podrías tener un problema. Pero imagino que te refieres a que el rango de números va de 0 a 10.000.000, luego la profundidad de de la recursividad sería de máximo 8 (igual que el número de dígitos).

Un saludo
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #9 (permalink)  
Antiguo 09/11/2016, 19:54
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Modificar un digito de un numero

Es lo segundo que comentas ¿no habría problema no?
  #10 (permalink)  
Antiguo 10/11/2016, 00:00
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Modificar un digito de un numero

Yo te diría que no, pero vas a tardar menos haciendo la prueba tratando de sustituir el 8 dígito de un número.

Para que te hagas una idea mi compilador se revela si le meto una profundidad superior a 500 (creo recordar, hace tiempo que no lo exprimo a ese nivel en temas de recursividad)

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #11 (permalink)  
Antiguo 10/11/2016, 01:54
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Modificar un digito de un numero

Ok muchísimas gracias. La verdad es que de templates aun no vi nada pero se ve muy interesante y para mi propósito la ganancia en tiempo de ejecución es tan insignificante que a simple vista no notariamos diferencia pero es casi medio segundo jejeje.

Etiquetas: int, modificar, numero, operadores
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 01:11.