Random IRC quote :      <matalaz> wzzx, tu me la has metido 2 veces hoy

google code jam 2010

Programando a la luz de la luna
No hay nada como programar a la luz de la luna… O algo asi debieron pensar los organizadores del concurso de programacion Google Code Jam. La primera ronda ha tenido lugar este sabado, comenzando a la una de la madrugada y terminando a la una de la madrugada del domingo.
Y por si no tengo bastante con lo que programo en el curro, ni corto ni perezoso, me he puesto a solucionar los problemas de esta primera ronda.
De uno de ellos es del que os voy a hablar ahora…


Eliminando la narrativa del problema (joder, se tarda mas en leer alguno de los problemas de lo que tardas en resolverlo!!), basicamente el problema era encontrar un numero x que sumado a una coleccion de numeros nos permitiese maximizar su maximo comun divisor. Por ejemplo, dada la coleccion de numeros : (16, 6, 11) la solucion seria x=4, puesto que:

  (16+4, 6+4, 11+4) = (20,10,15)
  mcd(20,10,15) = 5
 

Pero, como solucionamos este problema? Me imagino que habra soluicones mejores, pero yo os voy a proponer la solucion que se me ocurrio a mi sobre la marcha, Y de paso me excusare comentando que el Code Jam es un concurso contra el crono con lo que la solucion mas optimas puede no ser la que mas nos interese.

Bueno, como lo que queremos hacer al final es maximizar un maximo comun divisor, vamos a ver la implementacion tipica del mcd, el algoritmo de euclides: Una posible implementacion es esta:

unsigned int mcd(unsigned int x,unsigned int y)
{
  unsigned int result;

  assert(x>y);

  if(y == 0)
  {
    result = x;
  }
  else
  {
    result = mcd(y, x%y);
  }
  return result;
}
 

Bien, si nos fijamos en esa descripción recursiva del maximo comun divisor vemos que tenemos un problema a la hora de llevar a cabo operaciones matematicas debido a la presencia de un modulo, pero en realidad tambien podemos expresar la recursividad con ayuda de la resta:

   mcd(a,b) = mcd(b, a-b)
 

Y este es el punto de partida para mi solucion. Nos estan pidiendo calcular el mcd(a+x,b+x), que podriamos desarrollar como:

mcd(a+x, b+x) =
mcd(b+x, a+x – b – x) =
mcd(b+x, a-b)
 

Y nuestro objetivo es maximzar el mcd. El maximo mcd(a,b) se da cuando a = b, por lo que tenemos:

b+x = a -b;
x = a -2b;
 

Con lo que finalmente tenemos :

mcd(a-b,a-b)  = a-b;
 

Ejemplo : a = 91, b = 81
a-b = 10;

Como resultado de haber cambiado el modulo por la resta, el valor de x puede salir negativo, en cuyo caso podremos calcular un x positivo alternativo:

          Si (a mod (a-b) == 0) entonces
               x = 0
         sino
               x = ((a-b)* ((a / (a-b)) + 1)) – a;
 

Que no es mas que calcular la diferencia entre a y el multiplo de (a-b) mas pequeño que sea mayor que a.

Bueno, Y ahora que sabemos como calcular x para dos numeros (a,b), como hacerlo para (a,b,c,…) Pues muy sencillo : Recolectando todas las diferencias y calculando el mcd comun a todas ellas

Y esto es to to todo amigos :o)

Espero que hayais «disfrutado» con la explicacion. Por cierto, google deja de disposicion de quien le interese el codigo fuente de todas las soluciones de todos los participantes…

riau!

5 Comentarios para “google code jam 2010”

  1. Comment por vierito5 | 05/09/10 at 5:29 am

    Un implementación no recursiva para Euclides sería:

    unsigned int gcd(unsigned int a, unsigned int b){

    unsigned int r = 1;
    while ( r != 0 ){
    r = a % b;
    a = b;
    b = r;
    }

    return a;

    }

  2. Comment por Miguel | 05/09/10 at 6:54 am

    /*++

    Vierito5. Correcto, esa seria una posible implementacion iterativa. Yo use un do-while para no tener que inicializar nada.

    Por otro lado, en el post hay un assert en el codigo de ka funcion, y esa restriccion no es necearia :oS

    salu2

    –*/

  3. Comment por erg0t | 05/10/10 at 2:56 am

    No entendi muy bien lo que pide el problema. Sera encontrar _el menor_ x que sumado maximize el gcd?

  4. Comment por Miguel | 05/10/10 at 3:28 am

    /*++

    Buenos dias erg0t!

    Se trataria de encontrar el menor x positivo que al sumar maximice el gcd. El maximo gcd tiene que ser necesariamente (a-b), o mas concretamente, |a-b|. Sin embago una vez calculado el gcd, existen infinitos valores x a sumar, puesto que gcd(a,a) == gcd(n*a,a)

    Por otro lado, he leido una solucion mucho mas sencilla para deducir el gcd |a-b|:
    Suponiendo que ese gcd existe entonces hay un numero T divisible por (a+x) y (b+x), por lo que
    (a+x) mod T = (b+x) mod T. De ahi se deduce que (a-b)mod T = 0 !! Que facil es verlo cuando te lo dicen verdad? xDDD (La madre que me pario!)

    riau
    –*/

  5. Comment por Cachuli | 05/11/10 at 9:28 am

    La foto es muy interesante. Ya era hora de que se normalizaran las relaciones con travestis tambien en el mundo de la informatica, que somos muy conservadores.

Se han cerrado los comentarios