domingo, 25 de diciembre de 2016

Euler #30

https://projecteuler.net/problem=30 => "Digit fifth powers"

Un poco de culturilla matemática, se trata de "números narcisistas"

def checkSumDigits(number, power):
 _sum = 0
 for digit in str(number):
  _sum += (int(digit))**power
 return _sum == number

final_sum = 0
for a in range(2, 10**6):
 if checkSumDigits(a, 5):
  print a
  final_sum += a
print final_sum

Euler #29


numbers = []
for a in range(2, 101):
 for b in range(2, 101):
  n = a**b
  if not n in numbers:
   numbers.append(n)

numbers.sort() 
print len(numbers)

sábado, 17 de diciembre de 2016

Euler #28

https://projecteuler.net/problem=28 => Number spiral diagonals

Los problemas en "2D" son siempre entretenidos. El problema es bastante sencillo, pero hay que tener cuidado y llevar un control de en qué dirección vamos y a cuándo "hay que torcer".
import sys

if len(sys.argv) != 2:
 print "Usage: 028.py <SIZE>"
 sys.exit()

table = []
size = int(sys.argv[1])
if size % 2 == 0:
 print "Only odd numbers"
 sys.exit()

for _ in range(0, size):
 row = []
 for _ in range(0, size):
  row.append(0)
 table.append(row)

directions = ((0,1), (1,0),(0,-1),(-1,0))

def get_next_direction(current): 
 i = directions.index(current)
 if i == len(directions) - 1:
  i = 0
 else:
  i += 1
 return directions[i]

max_num = size**2
x = size/2
y = size/2
diagonal_sum = 0
current_direction = (-1,0)
for a in range(1, max_num+1):
 if x == y or x + y == size - 1:
  diagonal_sum += a
 table[x][y] = a
 new_direction = get_next_direction(current_direction)
 x1 = x + new_direction[0]
 y1 = y + new_direction[1]
 if table[x1][y1] == 0:
  x, y = x1, y1
  current_direction = new_direction
 else:
  x, y = x + current_direction[0], y + current_direction[1]

#for row in table:
# print row
print "diagonal_sum", diagonal_sum

sábado, 10 de diciembre de 2016

Euler #27

https://projecteuler.net/problem=27 => Cuadratic primes

Este problema es muy sencillo, simplemente hay que iterar entre todos los valores posibles para los dos coeficientes (-999 <= a <= 999 y -1000 <= b <= 1000) y comprobar cuántos números primos consecutivos permite calcular el polinomio n2 + an + b.

La primera implementación es sencilla y bastante rápida, apenas unos segundos. Pese a que es un problema con un orden de complejidad polinómico por definición (cuadrático para las posibles combinaciones de a y b más otros ciclo para comprobar los números primos), dado que los valores del programa son pequeños, se resuelve en un tiempo razonable.

Pero claro, siempre se quiere optimizar. Así que pensé "si al comprobar si un número es primo o no, puedo tenerlos ya guardados en una lista y así no tengo que calcular lo mismo una y otra vez".

Veamos la implementación, sólo cambia lo siguiente: una lista de números primos inicialmente vacía y una comprobación previa. El resto del código es igual. Sin embargo, al ejecutarlo, nos encontramos con que tarda bastante más tiempo.

¿Qué ha pasado? Fácil, que la supuesta optimización ha resultado ser peor. Con pensar 5 segundos se da uno cuenta por qué: cada vez que queremos saber si un número es primo debemos buscarlo en una lista que cada vez es más grande. Además, debemos asignar memoria y hacer diversas operaciones internas en la lista para redimensionarla al añadir un nuevo elemento. Desconozco la implementación exacta de la búsqueda en listas de Python, pero no puede ser mejor que del orden de O(log n).

captura-de-pantalla-2016-12-10-a-las-16-44-43

captura-de-pantalla-2016-12-10-a-las-16-45-47

Otra forma de crear esta caché de números primos es utilizar un diccionario Python, que requiere menos manejo de memoria, pero la verdad, la mejora, para las dimensiones de este problema, es mínima.

Conclusión: si optimizas, ten cuidado, no sea que lo estropees.

miércoles, 7 de diciembre de 2016

Euler #26

https://projecteuler.net/problem=26 => Reciprocal cycles

Este ha costado un poco más sacarlo, y ha sido gracias al número 1/17 cuando me he dado cuenta que un número sí que se podía repetir en el periodo (asumí que no). Sabiendo eso, la comprobación para ver si estamos empezando un nuevo periodo solo debe tener en cuenta si estamos repitiendo un resto. Veamos una implementación en Python:

from decimal import Decimal, getcontext

getcontext().prec = 40

def Div(D, d):
 return (D/d, D%d)

def GetPeriod(d):
 period = []
 remainders = []
 index = 0
 c, r = Div(1, d)
 if r == 0:
  return []
 if c != 0:
  period.append(c)
 while True:
  D = 10*r
  c, r = Div(D, d)
  if r == 0:
   return []
  period.append(c)
  if r in remainders:  
   return period[period.index(c):index]
  index += 1
  remainders.append(r)

 return period


longestList = []
longestInt = 1
for a in range(1, 1000):
 period = GetPeriod(a)
 if len(period) > len(longestList):
  longestList = period
  longestInt = a

print "Longest period for ", longestInt, " => ", longestList, " (len: ", len(longestList), ")"

Baúl de los recuerdos: "Euroconversor"

1 de enero de 2002... entra en vigor el Euro en España. La de código que se escribió en los meses previos para adaptar lo que no estaba preparado. Recuerdo que, con la tontería, me dio por escribir una pequeña utilidad, "Eurocalculadora", que se pusieron de moda en 2001. Todo el mundo tenía una en su web, yo hice una para "desktop" en Python+Glade.

Glade era un editor de interfaces de usuario en GTK que guardaba la descripción de la misma en un XML que posteriormente se podía parsear con el lenguaje que tuviera "bindings" a GTK y libglade.

euroconversor
En Windows (W2000, si no recuerdo mal)

euroconversor2
En Linux (gestor de ventanas, IceWM)

No es un código del que sentirse orgulloso, pero bueno, eran mis primeros pasos con Python :)

Captura de pantalla 2016-12-07 a las 1.51.07.png

euroconversor.py:
from funciones import interfaz
app=interfaz.EuroConversor()
  app.a_correr()

uno.glade
<?xml version="1.0"?>
<GTK-Interface>
<project>
  <name>Aplicacion</name>
  <program_name>aplicacion</program_name>
  <directory></directory>
  <source_directory>src</source_directory>
  <pixmaps_directory>pixmaps</pixmaps_directory>
  <language>C</language>
  <gnome_support>False</gnome_support>
  <gettext_support>False</gettext_support>
</project>
<widget>
  <class>GtkWindow</class>
  <name>window1</name>
  <signal>
    <name>destroy</name>
    <handler>on_window1_destroy</handler>
    <last_modification_time>Thu, 08 Aug 2002 16:22:11 GMT</last_modification_time>
  </signal>
  <title>Conversor</title>
  <type>GTK_WINDOW_TOPLEVEL</type>
  <position>GTK_WIN_POS_CENTER</position>
  <modal>False</modal>
  <allow_shrink>False</allow_shrink>
  <allow_grow>False</allow_grow>
  <auto_shrink>False</auto_shrink>
  <widget>
    <class>GtkVBox</class>
    <name>vbox2</name>
    <homogeneous>False</homogeneous>
    <spacing>0</spacing>
    <widget>
     <class>GtkLabel</class>
      <name>label1</name>
      <label>Euro Conversor:
Introduzca la cantidad a convertir</label>
      <justify>GTK_JUSTIFY_CENTER</justify>
      <wrap>False</wrap>
      <xalign>0.5</xalign>
      <yalign>0.5</yalign>
      <xpad>10</xpad>
      <ypad>10</ypad>
      <child>
        <padding>0</padding>
 <expand>True</expand>
 <fill>True</fill>
      </child>
    </widget>
    <widget>
      <class>GtkHBox</class>
      <name>hbox2</name>
      <homogeneous>False</homogeneous>
      <spacing>0</spacing>
      <child>
 <padding>0</padding>
 <expand>True</expand>
 <fill>True</fill>
      </child>
      <widget>
 <class>GtkLabel</class>
 <name>label2</name>
 <label>Pesetas</label>
 <justify>GTK_JUSTIFY_RIGHT</justify>
 <wrap>False</wrap>
 <xalign>0.5</xalign>
 <yalign>0.5</yalign>
 <xpad>5</xpad>
 <ypad>5</ypad>
 <child>
   <padding>0</padding>
   <expand>True</expand>
   <fill>True</fill>
 </child>
      </widget>
      <widget>
 <class>GtkEntry</class>
 <name>entry1</name>
 <can_focus>True</can_focus>
 <signal>
   <name>key_press_event</name>
   <handler>on_entry1_key_press_event</handler>
   <last_modification_time>Mon, 12 Aug 2002 13:18:49 GMT</last_modification_time>
 </signal>
 <editable>True</editable>
 <text_visible>True</text_visible>
 <text_max_length>0</text_max_length>
 <text></text>
 <child>
   <padding>5</padding>
   <expand>False</expand>
   <fill>True</fill>
 </child>
      </widget>
    </widget>
    <widget>
      <class>GtkHBox</class>
      <name>hbox3</name>
      <homogeneous>False</homogeneous>
      <spacing>0</spacing>
      <child>
 <padding>0</padding>
 <expand>True</expand>
 <fill>True</fill>
      </child>
      <widget>
 <class>GtkLabel</class>
 <name>label3</name>
 <label>Euros</label>
 <justify>GTK_JUSTIFY_RIGHT</justify>
 <wrap>False</wrap>
 <xalign>0.5</xalign>
 <yalign>0.5</yalign>
 <xpad>5</xpad>
        <ypad>5</ypad>
 <child>
   <padding>0</padding>
   <expand>True</expand>
   <fill>False</fill>
 </child>
      </widget>
      <widget>
 <class>GtkEntry</class>
 <name>entry2</name>
 <can_focus>True</can_focus>
 <signal>
   <name>key_press_event</name>
   <handler>on_entry2_key_press_event</handler>
   <last_modification_time>Mon, 12 Aug 2002 13:18:59 GMT</last_modification_time>
 </signal>
 <editable>True</editable>
 <text_visible>True</text_visible>
 <text_max_length>0</text_max_length>
 <text></text>
 <child>
   <padding>5</padding>
   <expand>False</expand>
   <fill>True</fill>
 </child>
      </widget>
    </widget>
  </widget>
</widget>
</GTK-Interface>

__init__.py
__all__ = ["interfaz", "conversiones"]

conversiones.py
def validar_entrada1( texto ):
    try:
         return str(long(texto))
    except ValueError:
         return ""

def validar_entrada2( texto ):
    try:
         return str(float(texto))
    except ValueError:
         return ""

def pelas_a_euros( entrada ): #Aquí viene lo que han tecleado
    return "%.2f" % (float(entrada)/166.386)

def euros_a_pelas( entrada ): #Aquí viene lo que han tecleado
    return "%d" % (float(entrada)*166.386)

interfaz.py
import libglade, gtk
from funciones import conversiones

class EuroConversor:
    def __init__(self):
        self.arbol = libglade.GladeXML( "aplicacion.glade", "window1" )
        self.caja1 = self.arbol.get_widget( "entry1" )
        self.caja2 = self.arbol.get_widget( "entry2" )

        manejadores = { "on_entry1_key_press_event" : self.pasar,
                        "on_entry2_key_press_event" : self.pasar,
                       }
        self.arbol.signal_autoconnect( manejadores )
        self.arbol.signal_connect( "on_window1_destroy", self.salir )

    def a_correr(self):
        gtk.mainloop()

    def salir(self, obj):
        print "Saliendo..."
        gtk.mainquit()

    def pasar(self, obj, ev):
        """  En 'obj' viene el objeto que llama (la caja de texto),
             En 'ev' viene el evento, dir(ev) = keyval, send_event, state, string, time, type, window
        """
        nombre = libglade.get_widget_name( obj )
        if nombre == "entry1":
           entrada_validada1 = conversiones.validar_entrada1(obj.get_text() + ev.string)
           if entrada_validada1:
                self.caja2.set_text( conversiones.pelas_a_euros( entrada_validada1 ) )
           else:
                self.caja1.set_text( "" )
                self.caja2.set_text( "" )
        elif nombre == "entry2":
           entrada_validada2 = conversiones.validar_entrada2(obj.get_text() + ev.string)
           if entrada_validada2:
                self.caja1.set_text( conversiones.euros_a_pelas( entrada_validada2 ) )
           else:
                self.caja1.set_text( "" )
                self.caja2.set_text( "" )

Baúl de los recuerdos: "Power Helper"

De vez en cuando toca alguna intervención nocturna y uno aprovecha para revisar cosas pendientes... tenía un directorio con backups antiguos y rebuscando, rebuscando... he encontrado algunos programillas que hice hace tiempo.

Ahí va uno, que en su día llamé "Power Helper", era un script en Tcl/Tk que servía para controlar los perfiles de rendimiento de la CPU, administración de energía. Lo usé mucho tiempo en un portátil que tenía.

powerhelper

No recuerdo cuándo escribí este programa, pero sería entre 2001 y 2003, que fue una época en la que estuve experimentando con muchos lenguajes de script.

Me llamó mucho la atención en su día Tcl. Su estilo muy similar a una shell y sus tipos de datos eran bastante curiosos.

#!/usr/bin/wish

set governors [exec cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors]

proc show_message {tit mes} {
    tk_messageBox -icon info -title $tit -message $mes
}

proc show_governor {} {
    show_message "Rendimiento"  "Perfil de rendimiento: [exec cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor]"
}

proc show_Batería {} {
    show_message "Batería"  "Estado de la batería:\n[string trim [exec acpi]]\n[exec cat /proc/acpi/battery/BAT1/state]"
}

proc show_cpu {} {
    show_message "CPU"  "Información CPU:\n[exec cat /proc/cpuinfo]"
}

proc Hibernar {} {
    exec /bin/echo 4 > /proc/acpi/sleep
}

proc set_governor {g} {
    exec cpufreq-set {-g} "$g"
    show_governor
}

button .show -text {Perfil de rendimiento} -width 30 -command show_governor
pack .show -padx 5 -pady 3

foreach {g} $governors {
    button .g$g -text "Cambiar a perfil $g" -width 30 -command "set_governor $g"
    pack .g$g -padx 5 -pady 3
}

button .batstate -text "Estado de la batería" -width 30 -command show_Batería
pack .batstate -padx 5 -pady 3

button .cpu -text "Información CPU" -width 30 -command show_cpu
pack .cpu -padx 5 -pady 3

button .hibernar -text "Hibernar" -width 30 -command Hibernar
pack .hibernar -padx 5 -pady 3

button .end -text {Salir} -width 30 -command exit
pack .end -padx 5 -pady 3

wm title . "Power helper"