Skip to Content

No te olvides de visitar:

Reto PHP I: Obtener los divisores de un número

Hoy desde "El Rincón de Tolito" inauguramos una nueva sección: "RETOS PHP", la idea es la siguiente: yo(podéis enviarme ideas para nuevos retos) propongo desde aquí un reto con sus condiciones, y vosotros vais enviándome vuestras soluciones en forma de comentario del post (los comentarios están moderados, y por tanto no aparecerán publicados hasta el final del concurso), o por correo electrónico (tolito(arroba)gmail(dot)com). Una vez terminado el plazo publicaré los códigos ganadores, así como sus autores. Así que vamos al lío con el primer reto:

RETO PHP I

Enunciado: Desarrollar una función llamada "reto" a la cual la pasemos un número (integer) y nos devuelva un array (divisor[0]=x....divisor[n]=y) con todos los divisores de dicho número, incluyendo el 1 y el propio número.

Códigos Ganadores: Hay 2 premios:

  • Código veloz: Después de una batería de pruebas(serán las mismas para todos) se verá cual es el código más rápido de todos los participantes.
  • Código breve: Este premio se lo llevará el código que menos ocupe, así que a abreviar...

Condiciones:

  • Las pruebas se llevarán a cabo en un servidor Ubuntu con Apache y PHP 5.2.5.
  • Cada usuario puede enviar 2 programas, así puede optar a los 2 premios a la vez, ya que es complicado que el mismo código gane los 2 premios, aunque nunca se sabe...
  • El envío debe incluir el nick del autor, una dirección de correo electrónico (no se hará publica para evitar spameos), y el código de la función.
  • Fecha límite para el envío de respuestas: 30 de Septiembre del 2009
  • El código que no funcione, o que de errores no será considerado valido. Puede dar warnings o notice siempre que funcione correctamente

¡Animo a todos y a optimizar código!

Actualización [28 Agosto 2009 - 14:41]: Ya hemos recibido los primeros programas...

Actualización [01 Septiembre 2009 - 10:50]: Siguen llegando programas, pero todos son para el premio de brevedad...a ver si alguien se anima a optimizar el código para mejorar su rendimiento.

Actualización [02 Septiembre 2009 -19:21]: En el premio a la brevedad solo se tendrá en cuenta la función, no os preocupeis de etiquetas php y demás historias...

Velocidad (con datos precalculados)

<?php
precalc(123456789, 123456789);
$fInicio=microtime(true);
$asResult=reto(123456789);
$fFin=microtime(true);
echo ($fFin-$fInicio);
print_r($asResult);

/*
Tradicionalmente se ha podido balancear entre uso de memoria y velocidad. Es decir, generalmente se puede modificar un programa para que consuma menos memoria, aunque probablemente funcione más lentamente, y a la inversa, con más memoria, funcionaría más rápidamente.
Obviamente el enfoque más radical son las soluciones precalculadas. Nada es más rápido de calcular como algo que se sabe de antemano. Las normas no citan limitaciones de memoria ni espacio en disco, por lo que la implementación completa de esta solución, pasaría por disponer entorno a 500 GB en disco para guardar los valores precalculados, dónde a partir de ahí, el resultado es instantaneo.
Naturalemente la demostración está incluída con sólo el número 123456789, aunque el código del precalculador soportaría todo el espacio numérico de los int.
*/
function reto($piNumero)
{
require_once($piNumero . '.inc.php');
return($aRes);
}

/*
Genera los includes precalculados para numeros entre piMin y piMax
*/
function precalc($piMin, $piMax)
{
for ($iNumero=$piMin; $iNumero<=$piMax; $iNumero++)
{
if (!file_exists($iNumero . '.inc.php'))
{
$sRes='<?php $aRes=array(' . $iNumero;
for ($iDivisor=$iNumero>>1; $iDivisor>0; $iDivisor--)
{
if (!($iNumero % $iDivisor))
{
$sRes.=', ' . $iDivisor;
}
}
$sRes.='); ?>';
file_put_contents($iNumero . '.inc.php', $sRes);
}
}
}

?>

Velocidad

<?php

$fInicio=microtime(true);
$asResult=reto(2);
$fFin=microtime(true);
echo ($fFin-$fInicio);
print_r($asResult);

/*
Esta función de cálculo de los divisores de un número dado, explota determinadas características optimización genéricas aplicadas a PHP.
Los bucles son invertidos, que mejoran algo el rendimiento, lo que ocasiona que los divisores no se generen en orden ascendente, una casuistica que las normas no prohiben explicitamente.
El rendimiento se obtiene basicamente con el desenrollado de bucles. Lo he desplegado 4 veces para no complicarlo demasiado, teniendo en cuenta que con más, el aumento de rendimiento era marginal.
Evidentemente el desenrrollado sólo sale a cuenta con números grandes, ya que con pequeños, la lógica añadida, lo hará incluso más lento.
Según mis pruebas es entorno a un 250% más veloz que la versión optimizada para tamaño. Dudo que se pueda sacar mucho más, sin utilizar otro algoritmo de cálculo de divisores.
*/
function reto($piNumero)
{
$aRes=array($piNumero);
for ($iDivisor=$piNumero>>1; $iDivisor>0; $iDivisor--)
{
if (!($piNumero % $iDivisor))
{
array_push($aRes, $iDivisor);
}
}
return($aRes);
}
?>

Tamaño (Corregido)

<?php

$fInicio=microtime(true);
$asResult=reto(123456789);
$fFin=microtime(true);
echo ($fFin-$fInicio);
print_r($asResult);

/*
Esta función de cálculo de los divisores de un número dado, explota determinadas características de PHP no demasiado conocidas, para reducir al máximo el tamaño del fuente, quedando finalmente en 73 bytes.
Por un lado cuenta con que una variable sin inicializar, siempre puede ser convertida a número, y que cuando se convierte a rray, es un array vacío.
Además utiliza en dos ocasiones la evaluación booleana de expresiones no booleanas. Como por ejemplo que --$n es cierto mientras $n no sea cero. Además juega con pre/post incrementos para reducir el tamaño.
El único problema que tiene es que dependiendo de la configuración de PHP, se reportan warnings sobre variables no definidas, aunque ello á permitido por las normas.
Finalmente, se eliminan separadores innecesarios, viendo el código, veréis lo depurado que está PHP a la hora de determinar expresiones.
La anterior version tenia un fallo debido al predecremento de $--n que se soluciona con $n--
*/
function reto($n){$b=0;while($n--)if(!($n%(++$d)))$a[$b++]=$d;return$a;}
?>

Tamaño: 73 bytes

<?php

$asResult=reto(30);
print_r($asResult);

/*
Esta función de cálculo de los divisores de un número dado, explota determinadas características de PHP no demasiado conocidas, para reducir al máximo el tamaño del fuente, quedando finalmente en 73 bytes.
Por un lado cuenta con que una variable sin inicializar, siempre puede ser convertida a número, y que cuando se convierte a rray, es un array vacío.
Además utiliza en dos ocasiones la evaluación booleana de expresiones no booleanas. Como por ejemplo que --$n es cierto mientras $n no sea cero. Además juega con pre/post incrementos para reducir el tamaño.
El único problema que tiene es que dependiendo de la configuración de PHP, se reportan warnings sobre variables no definidas, aunque ello á permitido por las normas.
Finalmente, se eliminan separadores innecesarios, viendo el código, veréis lo depurado que está PHP a la hora de determinar expresiones.
*/
function reto($n){$b=0;while(--$n)if(!($n%(++$d)))$a[$b++]=$d;return$a;}
?>

function

function divisores($n)
{
return array_filter(range(1,$n), create_function('$v', 'return !(' . $n . '%$v);'));
}

Reto PHP I (versión corta)

Y la versión corta, que es básicamente la misma que la larga pero eliminando todo lo posible:
<?php
function f($n){for($i=1;$i<=$n/2;$i++)if($n%$i==0)$r[]=$i;$r[]=$n;return $r;}
print_r(f(4571));
?>

Un saludo desde bright-shadows tolito ;)

Reto PHP I (2)

Me olvidé de incluir el propio número en mi anterior código. Éste es el actualizado.
<?php
function f($n) {
for ($i = 1; $i <= ($n/2); $i++){
if ($n % $i == 0) $r []= $i;
}
$r[]=$n;
return $r;
}
print_r(f(12345));
?>

PHP Reto I

Espero no llegar tarde, aquí está mi código, sencillo y muy "n00b":
<?php
function f($n) {
for ($i = 1; $i <= ($n/2); $i++){
if ($n % $i == 0) $r []= $i;
}
return $r;
}
print_r(f(12345));
?>

Reto PHP (divisores)

Es el mísmo código que el primero que envié, pero este ocupa algo menos (127 bytes incluyendo etiquetas php)

<?php

function reto($n)
{
$res = array();

for($d = $n; $d>0; $d--)
if($n % $d === 0) $res[] = $d;

return $res;
}

?>

Reto

function reto($n){for($i=$n;$i;$i--)($n%$i)||$a[]=$i;return$a;}

reto corto

<?php
$numero=(int)$_GET['numero'];
$divisores=array(1,$numero);
for($i=2; $i<$numero; $i++){
if(!($numero%$i))$divisores[]=$i;
}
print_r($divisores);

Reto php

function reto($n){$d=array();$j=0;for($i=1;$i<=$n;$i++){if(0==$n%$i){$div[$j++]=$i;}}return$div;}

Divisor PHP

<?php

function reto($num)
{
$divisor = array();

for($div = $num; $div>0; $div--)
{
if($num % $div === 0) $divisor[] = $div;
}
return $divisor;
}

?>

Escribir un nuevo comentario

Tu dirección de email no se mostrará públicamente.
  • No se admite ninguna etiqueta HTML
  • Saltos automáticos de líneas y de párrafos.
  • Las direcciones de las páginas web y las de correo se convierten en enlaces automáticamente.

Más información sobre opciones de formato