Categorías
Austra

La constante de Kaprekar

Vamos a jugar un poco con Austra, para ver hasta dónde nos deja llegar. Comencemos por definir un número, que supondremos que tiene cuatro dígitos, y vamos a crear un vector de enteros con esos cuatro dígitos:

let x=4123, v = [int:x/1000%10, x/100%10, x/10%10, x%10] in
    v;

Ahora vamos a ordenar esos dígitos, primero de mayor a menor, y luego, en sentido contrario:

let x=4123, v = [int:x/1000%10, x/100%10, x/10%10, x%10];
v.sortDesc; v.sort;

Mi intención, no obstante, es recomponer las dos listas de dígitos y restar el número mayor del menor:

let x=4123, v = [int:x/1000%10, x/100%10, x/10%10, x%10] in
    polyeval(10, v.sortDesc) - polyeval(10, v.sort);

Cuando el número inicial es 4123, la respuesta es 3087. Para hacer las cosas fáciles, vamos a definir ahora una función que reciba un entero y lo transforme de acuerdo con este procedimiento:

let kt(x: int): int = 
  let v = [int:x/1000%10, x/100%10, x/10%10, x%10] in
    (polyeval(10, v.sortDesc) - polyeval(10, v.sort)).toInt in
      kt(4123);

He llamado kt a la función: Kaprekar‘s transform.
Ahora quiero aplicar esta función sucesivamente al resultado de la propia función. La forma más sencilla de hacerlo en Austra es usar el método de clase unfold de las secuencias de enteros:

let ks(n: int) =
  let kt(x: int): int = 
    let v = [int:x/1000%10, x/100%10, x/10%10, x%10] in
      (polyeval(10, v.sortDesc) - polyeval(10, v.sort)).toInt in
        iseq::unfold(100, n, n => kt(n)) in
          ks(4123)

Para evitar sustos, he limitado el número de elementos en la secuencia generada a 100. En realidad, nos sobran elementos, porque si ejecutamos lo anterior obtendremos:

4,123  3,087  8,352  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174
6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174
6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174
6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174
6,174  6,174  6,174  6,174  6,174  6,174  6,174  6,174  

Como se ve, a la tercera aplicación de la función de transformación, la secuencia produce el número 6174, que tiene la curiosa propiedad kt(6174)=6174. A este número, comprensiblemente, se le conoce como constante de Kaprekar. Este es un ejemplo de punto fijo de una función, que en este caso es la transformación de Kaprekar.
Para embellecer un poco el resultado, vamos a intentar parar la secuencia cuando se empiecen a repetir valores. A la secuencia que generamos antes, vamos a aplicarle la propiedad fixedPoint:

let ks(n: int) =
  let kt(x: int): int = 
    let v = [int:x/1000%10, x/100%10, x/10%10, x%10] in
      (polyeval(10, v.sortDesc) - polyeval(10, v.sort)).toInt in
        iseq::unfold(100, n, n => kt(n)).fixedPoint in
          ks(4123)

Con este pequeño cambio, la secuencia ahora es más clara:

4,123  3,087  8,352  6,174

Si, en vez de utilizar 4123 como semilla, usamos 7773, digamos, obtenemos una secuencia parecida, que también converge en 6174:

7,773  3,996  6,264  4,176  6,174

Esta vez hemos necesitado cuatro pasos para tropezar con la constante de Kaprekar. ¿Cuál es el número máximo de pasos para que una secuencia creada a partir de un número arbitrario de cuatro dígitos converja en la constante de Kaprekar? Para calcularlo, sólo necesitamos añadir un paso más: una comprensión de listas.

let ks(n: int) =
  let kt(x: int): int = 
    let v = [int:x/1000%10, x/100%10, x/10%10, x%10] in
      (polyeval(10, v.sortDesc) - polyeval(10, v.sort)).toInt in
        iseq::unfold(100, n, n => kt(n)).fixedPoint in
          [x <- 1000..9999 => ks(x).length].max

La respuesta es ocho. La cota inicial máxima de 100 pasos es más que suficiente, pero como las secuencias de Austra se calculan por demanda, no hace daño al tiempo de ejecución del algoritmo.
¿Para qué sirve todo esto? El punto fijo es un concepto muy importante en matemáticas. No voy a enumerar aquí las aplicaciones del concepto. Pero podemos quedar en que la constante de Kaprekar nos ha servido de excusa para probar si el lenguaje funcional de Austra es suficientemente potente. Para el ejemplo, he decidido crear una expresión enorme y relativamente complicada, pero también podríamos haberlo conseguido definiendo las funciones kt y ks por separado. Cuestión de gustos…

Categorías
Austra

Mejoras importantes en Austra

Después de un largo impasse, provocado por un proyecto complicado, volvemos a la carga con Austra.

Primero, las labores típicas de mantenimiento: migrar a .NET 10, comprobar que todo sigue bien, corregir algún error y simplificar el analizador sintáctico.

Luego, hemos implementado por completo los vectores y secuencias de fechas. Vamos a añadir más funcionalidad en las siguientes versiones, como el movimiento de fechas por días festivos, calendarios de días festivos. Mi idea es poder usar directamente Austra para generar cupones para productos financieros como bonos y swaps. Eso ya es posible usando directamente la librería, pero se puede simplificar su uso desde el lenguaje.

Finalmente, la novedad más importante en la versión que estoy preparando es la posibilidad de leer vectores, series y matrices desde un fichero CSV. Esto es importante para reducir la dependencia actual de la aplicación (no de la librería ni del lenguaje) de un fichero data.austra. En realidad, la idea siempre ha sido poder usar adaptadores configurables para leer series desde una fuente de datos financieros elegida por el usuario. El problema es que la principal fuente gratuita está en caída libre (no mencionaré nombres) y las restantes cobran una pasta considerable por la suscripción. Sin esto, el uso práctico de la aplicación se limita bastante. Al poder leer series, vectores y matrices desde ficheros CSV, hacemos posible inicializar Austra desde un script inicial, que se podría incluso ejecutar automáticamente, sin abandonar la posibilidad de cargar luego un data.austra.

Y si finalmente me compro un portátil con procesador ARM, miraré cuán simple o complicado sería ampliar la funcionalidad de procesamiento vectorial a este modelo. Sospecho que buena parte ya funcionaría casi automáticamente, al realizar buena parte de la optimización con las últimas clases de .NET. Pero hay que comprobarlo.

Categorías
C#

No tan bueno

Entusiasmado por las noticias, monté .NET 10 y Visual Studio 2026 Insiders, en paralelo con mi actual VS. Cargué entonces mi viejo y muy optimizado ray tracer, y me puse a enredar. Efectivamente, no tardé mucho en ver que, sin tocar mucho código, el runtime de .NET 10 hace que mi aplicación sea ligeramente más rápida. Un benchmark que se lleva unos 3 segundos, ahora termina en 2.9, aproximadamente. No es para tirar cohetes, pero, ¡eh!, no tuve que hacer nada, aparte de actualizar.

Pero lo que realmente quería probar. Ahora está de moda todo lo que se relacione con AI. Busqué un método que consumiese CPU, y elegí la generación de ruido de Perlin. Abrí el chat de Copilot GitHub (hasta ahora, yo pagaba mi suscripción) y le pedí que le echase un ojo al método. El amable señor se tiró unos quince minutos y reapareció con un par de recetas. ¿Quieres aplicarlas?, dijo. Sí, por supuesto. Y allá se lanzó a modificar código.

Resultados:

1- El método sin turbulencias es ahora dos nanosegundos más lento (de 23 ns a 25 ns).
2- El método con turbulencias es ahora dos nanosegundos (¡curioso!) más rápido. De 125 ns a 123 ns.

Entonces respondí: «Borra todos los cambios».

AI sigue significando Artificial Idiocy.

Quizás algún día cambien las cosas. Ese día no es hoy.

Categorías
Insights

No lo digo yo…

Sinceramente, he abandonado toda esperanza de dejar un mundo un poco menos estúpido cuando la diñe… La Verdad es fea. Las Mentiras son más bonitas. Pero sólo la Verdad es fértil.

Categorías
Música

The Mage

(The Mage: Spotify)

You, who try to understand
Symbols chiseled in the dark
Don’t give up, my friend,
Though you’re crying in the wilderness.

White shadows from the Moon
Trace a road back through the past
But your proudness breaks the spell
And the vision fades away in a thunderstorm.

Are we made of dusty matter and emotions?
Are our souls an empty web of silly notions?
It’s all the same…

Don’t ever think our past
Is deeply buried down the sands
When the whispers from the wind
Reveal some crazy secrets left behind.

It’s so easy to believe there is no meaning!
Are the bruises on your soul already healing?
Is there a turning back?

An old man looks into our darkness
While the shadows cry in silence
Cause they know he’s waiting for a light to spark.
It may seem he’s all alone
That his journey has no point
The sun sinks all the way down
And the shadows win another round.

Is it true that there’s a tree of life and knowledge?
Are our wishes and our love all going nowhere?
Did someone ever care?

An old man looks at the horizon
While the world is growing older
And the evil towers crumble to the ground.
Arms are raised up to the heavens
As he climbs his flaming vessel
The old man turns to say goodbye
And we wonder if he will come back.

Categorías
Música

Der Ravenmeister

En 1684, Jan Cornelius Maarten, organista de la catedral de Delft, visitó Londres en busca de piezas de repuesto para su instrumento. En una de las muchas tabernas de la ciudad, conoció a un ciego, que se presentó como Benedict Crowell, cuidador de los cuervos de la London Tower. Tras unas cuantas jarras de cerveza, Crowell invitó a Jan a conocer sus cuervos al día siguiente.

Al parecer, Jan Cornelius quedó impresionado con el trabajo de Crowell, porque escribió una larga carta a su hermano Felix, que todavía se conserva. El ciego había entrenado a los cuervos para que pidiesen comida dejando caer una piedra sobre una bandeja metálica. Así podía distinguir estas llamadas de los graznidos habituales, y podía pasar más tiempo bajo techo. Lo interesante es que los cuervos, por cuenta propia, habían elegido cada uno una piedra de tamaño diferente, y el ciego podía casi siempre distinguir de cuál cuervo se trataba por el sonido. Esta habilidad fascinó al músico.

Lo mejor de todo es que Jack, uno de los cuervos, a veces lanzaba la piedra de Eva para que la hembra comiese primero. Esto confundió durante un tiempo al bueno de Benedict, hasta que cayó en cuenta de lo que pasaba.

De regreso en Delft, Jan Cornelius escribió una fughetta, o pequeña fuga a tres voces, y la tituló «De Ravenmeester». Es decir, el Amo de los Cuervos, en holandés. Incluyó una copia manuscrita de la partitura en su carta a Felix Maarten, residente en Valladolid, y por este motivo conocemos la breve pieza.

Este es un modesto arreglo moderno, que cubre algunas de las partes que se han perdido del manuscrito original:

Categorías
Austra

Lambdas sobre operadores binarios

Esta es una pequeña mejora al lenguaje de Austra. Supongamos que queremos sumar los números del 1 al 100. Creamos una secuencia de enteros y le aplicamos el método reduce:

iseq(1, 100).reduce(0, (x, y) => x + y) 

En realidad, nos bastaría llamar a sum, pero el ejemplo me interesa por el uso de la función lambda.

El caso es que ahora podemos escribirlo así:

iseq(1, 100).reduce(0, int::+) 

El truco es sencillo, y Java lo usa a manos llenas al manejar streams. De momento, sólo he activado la equivalencia para operadores binarios, pero si encuentro más casos útiles, puedo ampliarla. Hay que tener presente que Austra ya permite declarar funciones con parámetros lambda arbitrarios. Que no haya un método en la librería que utilice determinado patrón de función, no quiere decir que el usuario no pueda usarlo por su cuenta.

Categorías
Música

Prayer

Soy consciente de que hace tiempo que no escribo sobre informática, que es de lo que, en definitiva, trata este blog. Es que me aburre lo que estoy haciendo y, lo peor, me está robando mucho tiempo. De cuando en cuando tengo la tentación de decir lo que opino, pero me la guardo con cuidado. Hay opiniones que explotan.

De momento, éste es mi último invento al piano:

Hay a quien le ha parecido un gospel, por el título y porque el tiempo es un vals tocado con eso que los músicos llaman, técnicamente hablando, «swing». Es decir, cada negra se divide irregularmente en casi tres partes. No era mi intención. Compuse una canción de enamoramientos y esas cosas, y esto es lo que ha salido. Disfrutadla. O detestadla. Estas son cosas que pasan cuando me aburro.

Categorías
Insights Música

Family Album

Mientras preparo el primer borrador de Nyx, le he dedicado un rato a la versión de Logic Pro para iPad. Normalmente, uso Sonar (el antiguo Cakewalk, que creo que vuelve a llamarse así), pero es mucho más cómodo usar un iPad mientras vas en tren a la oficina.

Esta es una pieza antigua, pero me gusta más como ha quedado esta vez:

Es una pieza sencilla, sin pretensiones, y es fácil de tocar. He mejorado un poco las dinámicas (cuánta fuerza usas con cada nota, y el volumen general de los pasajes) respecto a la subida original.

Mi rutina de grabación

Como sé que hay muchos programadores que tienen la música como afición, os cuento cómo hago estas cosas, ya sea por si a alguien le interesa o si alguien tiene consejos interesantes.

Ahora mismo, me he pasado, como decía, al Logic Pro para iPad. Me he comprado el iPad Pro de once pulgadas con un procesador M4. Elegí el de un terabyte de disco, porque los de menos capacidad tenían menos RAM. De todos modos, cuentan por ahí que la RAM adicional no se nota mucho en la mayoría de los benchmarks.

Logic Pro viene con un piano de estudio muy bueno. Ya había probado los pianos de Native Instruments, pero personalmente me gusta más éste. No me hagáis mucho caso.

Tengo desde hace unos años un Roland HP704. Buena acción de teclado y, lo que me resulta más cómodo ahora mismo, puedo conectarme al MIDI por Bluetooth.

Normalmente grabo MIDI, por si tengo que corregir algún pasaje complicado. Divido las notas en dos pistas, para ampliar la panorámica estéreo. He utilizado el asistente de masterización de Logic Pro, para mejorar los bajos y la banda de más de 10KHz.

Debería usar monitores de referencia profesionales, pero la idea era aprovechar el viaje en tren.

Categorías
Música

The Tiger

Me encanta este tema de Babelle:

Es más movidillo. Además, está en una tonalidad mayor. A mi edad, empieza uno a valorar cada día de sol.