Raíces fonéticas: cuando un nombre encuentra a la persona correcta
Aplicación práctica de búsquedas fonéticas tipo Soundex: raíces, reservaciones, miembros, MSSQL y búsqueda de personas por sonido.

Lic. Carlos Enrique Loria Beeche.

Una aplicación práctica de la saga “Estructuras de datos: cuando la memoria se convierte en inteligencia”

Después de recorrer las estructuras de datos fundamentales, quería cerrar esta saga con un ejemplo real, nacido no solamente de la teoría, sino de muchos años de trabajo construyendo sistemas de reservaciones, recepción y administración hotelera.

Durante esta serie hablamos de arrays, linked lists, stacks, queues, hash tables, binary search trees, heaps, graphs y diccionarios. Cada estructura nos mostró una forma distinta de organizar la memoria y de resolver problemas.

Pero la verdadera prueba de una estructura de datos no está solo en el aula ni en el libro. Está en la vida real: cuando un sistema debe ayudar a una persona a encontrar una reservación, reconocer a un huésped, evitar duplicados o recordar correctamente a alguien que ya había estado antes.

La teoría de estructuras de datos se vuelve verdaderamente interesante cuando ayuda a resolver un problema humano: encontrar a la persona correcta.

De los años ochenta a los sistemas hoteleros

Mi relación con los sistemas de reservaciones viene desde los años ochenta, cuando empecé a trabajar en soluciones para hoteles como el Hotel Condovac, y más adelante para el Gran Hotel Costa Rica y el Hotel Europa, que luego llegaría a formar parte de la cadena Radisson.

En esos sistemas descubrí algo que parece sencillo, pero que en la práctica no lo es: encontrar correctamente a una persona.

En recepción, un huésped podía aparecer escrito de muchas formas. Una persona podía estar registrada como Carlos, Karlos, Karl, Carlo, con un apellido menos, con una tilde omitida, con una abreviatura, o con el nombre escrito según lo que alguien entendió por teléfono.

Para una computadora, esas cadenas pueden ser distintas. Para una persona en recepción, muchas veces son señales que apuntan al mismo huésped, o al menos a un candidato que conviene revisar antes de crear un registro nuevo.

En recepción, buscar bien no era un lujo técnico; era parte del servicio.

El problema: el nombre escrito no siempre identifica bien

Un sistema ingenuo podría intentar buscar exactamente el nombre escrito:

WHERE nombre = 'Carlos'

Pero esa búsqueda exacta dejaría por fuera variantes como:

Carlos
Karlos
Karl
Carlo
Carlos Loria
Carlos Loría
C. Loria
Carlos E. Loria

El problema se vuelve más delicado en hotelería porque el nombre puede llegar por teléfono, por fax, por correo, por una agencia, por un operador, por una empresa, por una lista de grupo o por digitación manual.

Además, la persona que hace la reservación no siempre escribe el nombre igual que quien recibe al huésped en recepción. Y, sin embargo, el sistema debe intentar reconocerlo.

Ahí nació una idea práctica: no buscar solamente el texto literal, sino construir una raíz fonética, una especie de huella simplificada del nombre, capaz de acercar nombres que suenan parecido aunque estén escritos de forma distinta.

Soundex y las búsquedas fonéticas

En aquellos tiempos investigué sobre técnicas de búsqueda fonética, especialmente sobre Soundex, un algoritmo clásico usado para indexar nombres según cómo suenan y no solamente según cómo se escriben.

Soundex nació precisamente para resolver un problema parecido: encontrar apellidos aunque hubieran sido escritos de diferentes maneras en registros, censos o documentos. La idea general es conservar parte de la estructura sonora del nombre, reducir diferencias menores y producir un código fonético que permita agrupar variantes probables.

Por ejemplo, en una lógica fonética, puede ser razonable acercar nombres como:

Carlos
Karlos
Karl
Carlo

El objetivo no era afirmar automáticamente que todos eran la misma persona. El objetivo era presentar una lista de posibles coincidencias para que el usuario del sistema pudiera escoger el perfil correcto.

La búsqueda fonética no reemplaza el criterio humano; lo ayuda a encontrar mejores candidatos.

¿Por qué se eliminan muchas vocales?

Muchos algoritmos fonéticos reducen o eliminan vocales porque las vocales suelen ser menos estables en la escritura de un nombre. Una vocal puede cambiar, omitirse, agregarse o escribirse de forma distinta sin que el nombre deje de sonar parecido.

La estructura consonántica, en cambio, suele conservar más información útil para reconocer la forma sonora básica.

CARLOS  -> C R L S
KARLOS  -> K R L S
CARLO   -> C R L
KARL    -> K R L

Al reducir el nombre a una raíz más estable, diferencias como vocales omitidas, tildes, abreviaturas o variaciones de escritura dejan de impedir la búsqueda.

Equivalencias fonéticas

Soundex y otros enfoques fonéticos agrupan consonantes que pueden sonar parecido o confundirse según idioma, pronunciación o transcripción.

En una lógica fonética aplicada a nombres usados en recepción hotelera, algunas equivalencias prácticas pueden ser:

C ≈ K ≈ Q
B ≈ V
S ≈ Z ≈ C suave
Y ≈ LL
H puede ignorarse
Ñ puede normalizarse hacia N
Tildes pueden eliminarse

Estas equivalencias no significan que todos los nombres sean iguales. Significan que el sistema puede buscar de manera más inteligente que una simple comparación exacta de texto.

Por ejemplo:

Carlos / Karlos
Carla / Karla
Quirós / Kiros
Bolaños / Volaños
Jiménez / Giménez

En sistemas reales, estas aproximaciones podían marcar la diferencia entre encontrar un huésped conocido o crear otro perfil duplicado.

Una solución criolla para Aristóteles

Con ese conocimiento como referencia, desarrollé una solución propia, criolla, adaptada a la realidad de los sistemas hoteleros que estaba construyendo.

No se trataba de copiar ciegamente un algoritmo internacional. Se trataba de tomar la idea central de Soundex y de las búsquedas fonéticas —buscar por sonido aproximado, no solo por escritura exacta— y aplicarla a las necesidades concretas de recepción, reservaciones y perfiles de huéspedes.

Así, en mi sistema Aristóteles, una reservación no debía quedar amarrada únicamente al texto del nombre digitado en ese momento. La reservación podía crear o relacionarse con una entidad más estable: el miembro.

Ese detalle es importante: el perfil del miembro podía nacer en el mismo momento de la reservación. Si la persona no existía todavía, el sistema podía ayudar a crear ese perfil inicial. Si ya existía una coincidencia probable, el usuario podía elegir el perfil correcto para trabajar con él.

La reservación era el evento. El miembro era la memoria de la persona.

Miembros: el corazón de la búsqueda

Una de las decisiones importantes del diseño fue colocar la entidad Miembros como corazón de la identificación de personas.

En lugar de tratar al huésped como un simple texto dentro de una reservación, el sistema podía asociarlo con un perfil más estable. Ese perfil podía contener nombre, identificación, dirección, ciudad, nación, empresa, teléfonos, fax, correo electrónico y otros datos relevantes.

Conceptualmente, la diferencia era enorme.

Un enfoque simple diría:

Reservación -> Nombre escrito del huésped

El enfoque que me interesaba era más fuerte:

Reservación -> Código de miembro -> Perfil de persona

De esa forma, el sistema no dependía solamente de cómo alguien escribió el nombre en una reservación particular. Podía relacionar esa reservación con una persona reconocible dentro de la memoria del sistema.

Y cuando ese perfil no existía, la reservación podía ser precisamente el momento en que nacía el nuevo miembro.

La raíz fonética como llave de búsqueda

Para encontrar candidatos, el sistema calculaba una raíz fonética del nombre.

La idea general era:

Nombre escrito
     ↓
Normalización fonética
     ↓
Raíz
     ↓
Búsqueda de miembros similares

En el código, la búsqueda de miembros sinónimos seguía esa lógica:

public dsFront encuentraMiembrosSinonimos(string nombre) 
{
    dsFront dsFr1 = new dsFront();
    HashNombre hn = new HashNombre(nombre);

    return Miembros_TX_Raiz(hn.Raiz);
}

Primero se crea un objeto HashNombre a partir del nombre. Luego se toma su propiedad Raiz. Esa raíz se usa para buscar miembros que pertenezcan a la misma familia fonética.

El sistema no pregunta solamente:

¿Existe exactamente este nombre?

Pregunta algo más interesante:

¿Qué miembros tienen una raíz fonética compatible con este nombre?

La raíz fonética convertía un nombre escrito en una pista para encontrar a la persona correcta.

SQL Server como motor de búsqueda final

Aunque mis conocimientos de estructuras de datos me ayudaban a entender el problema, siempre tuve claro algo importante: el motor de base de datos debía hacer su trabajo.

Originalmente, muchas de estas soluciones nacieron en una época en la que era común trabajar con motores como Btrieve. Allí uno pensaba mucho en claves, índices, archivos, rendimiento y acceso eficiente.

Más adelante, durante los últimos 25 años, Aristóteles evolucionó apoyándose fuertemente en Microsoft SQL Server. Y aunque yo diseñaba las raíces, las relaciones, los códigos y los procedimientos, dejaba al motor de base de datos resolver las búsquedas finales mediante sus mecanismos de indexación, consulta y optimización.

La idea era combinar dos inteligencias:

  • La inteligencia de diseño: cómo convertir un nombre en una raíz útil.
  • La inteligencia del motor: cómo buscar eficientemente dentro de los datos.

La inteligencia no estaba solo en el algoritmo fonético, sino en saber combinarlo con el motor de base de datos correcto.

Miembros_TX_Raiz: buscar por raíz

La búsqueda final se hacía mediante un procedimiento almacenado, pasando la raíz como parámetro.

private dsFront Miembros_TX_Raiz(string raiz) 
{
    dsFront dsFr1 = new dsFront();
    SqlDataAdapter da = new SqlDataAdapter("Miembros_TX_Raiz", conn);
    da.SelectCommand.CommandType = CommandType.StoredProcedure;

    da.SelectCommand.Parameters.Add(
        new System.Data.SqlClient.SqlParameter("@Raiz", System.Data.SqlDbType.Char, 8)
    );

    da.SelectCommand.Parameters["@Raiz"].Value = raiz;

    da.Fill(dsFr1.Tables[this.NombreTabla]);
    return dsFr1;
}

Esto muestra una decisión de arquitectura muy concreta: la aplicación calculaba la raíz fonética, pero la búsqueda de miembros se delegaba al motor de base de datos mediante SQL Server.

Así, la raíz se convertía en una llave de búsqueda. Y SQL Server podía hacer lo que mejor sabe hacer: encontrar registros usando índices, procedimientos y planes de ejecución.

RaicesHash: controlar familias de nombres

La otra pieza era la tabla RaicesHash.

Su propósito era controlar cuántos miembros existían dentro de una misma raíz fonética y generar el siguiente consecutivo disponible.

Conceptualmente, la tabla podía entenderse así:

Raíz     Valor
CRLS     0001
KRLS     0002
MRRN     0005

La raíz agrupaba nombres fonéticamente cercanos. El valor permitía asignar un consecutivo dentro de esa familia.

Así se podía construir un código de miembro como:

Raíz + consecutivo

Por ejemplo:

CRLS0001
CRLS0002
CRLS0003

Eso permitía reconocer que varias personas podían compartir una raíz parecida, sin confundirlas como si fueran automáticamente la misma persona.

Código preliminar y código definitivo

En el código se ve la diferencia entre generar un código preliminar y generar un código definitivo.

public string siguienteCodigoPreliminar(string nombrePasajero) 
{
    Aristoteles.DatosFront.RaicesHash tblRaices = new RaicesHash();
    HashNombre pax = new HashNombre(nombrePasajero);
    int x = tblRaices.SiguientePreliminar(pax.Raiz);
    return pax.Raiz + CodificaEnString(x);
}

public string siguienteCodigoDefinitivo(string nombrePasajero) 
{
    Aristoteles.DatosFront.RaicesHash tblRaices = new RaicesHash();
    HashNombre pax = new HashNombre(nombrePasajero);
    int x = tblRaices.SiguienteDefinitivo(pax.Raiz);
    return pax.Raiz + CodificaEnString(x);
}

La lógica era clara:

Nombre del pasajero
        ↓
HashNombre
        ↓
Raíz fonética
        ↓
RaicesHash
        ↓
Siguiente consecutivo
        ↓
Código de miembro

Ese código podía ser preliminar o definitivo, según el momento del proceso y la necesidad del sistema.

El código preliminar podía servir mientras se estaba trabajando una reservación o perfil en construcción. El código definitivo consolidaba la identidad dentro del sistema.

El pequeño secreto de diseño

El pequeño secreto de esta arquitectura era que el sistema no intentaba “adivinar mágicamente” quién era la persona. Lo que hacía era mucho más responsable: buscaba de manera inteligente posibles coincidencias.

Una búsqueda exacta podía fallar.

Una búsqueda fonética podía encontrar personas con nombres parecidos.

Un perfil de miembro podía consolidar identidad.

Y una reservación podía apuntar a ese perfil estable, o ayudar a crear uno nuevo cuando no existía todavía.

El sistema calculaba la raíz fonética del nombre, buscaba miembros compatibles con esa raíz y presentaba al usuario una lista de posibles coincidencias. Luego era la persona que usaba el sistema —por ejemplo, en recepción o reservaciones— quien decidía cuál perfil correspondía trabajar.

Eso era muy importante: el sistema ayudaba a encontrar mejor, pero no reemplazaba el criterio humano.

La inteligencia no estaba en decir “esta es la persona correcta” de forma automática, sino en presentar mejores candidatos para que el usuario pudiera decidir.

Así se evitaba que el sistema multiplicara innecesariamente personas que en realidad podían ser la misma, o que dejara de reconocer a un huésped frecuente por una diferencia menor de escritura.

En otras palabras, Aristóteles no pretendía sustituir al recepcionista ni al usuario experto. Pretendía darle mejores pistas. La decisión final seguía siendo humana, pero el sistema reducía el ruido, acercaba coincidencias y hacía visible lo que una búsqueda exacta habría dejado escondido.

El sistema no solo guardaba reservaciones; ayudaba a recordar personas.

Hotel Punta Leona y la prueba del tiempo

Durante aproximadamente 25 años, el Hotel Punta Leona utilizó un sistema de recepción basado en estas ideas. Para mí, ese uso prolongado no es un detalle menor: es una prueba práctica de que la combinación de diseño, estructuras de datos, motor de base de datos y conocimiento del negocio podía producir valor real.

Muchas veces el éxito profesional de un sistema no depende de una sola gran característica visible, sino de muchas decisiones pequeñas que trabajan silenciosamente: cómo se busca, cómo se evita duplicar, cómo se reconoce a una persona, cómo se recupera el historial, cómo se aprovecha la base de datos y cómo se le facilita el trabajo al usuario.

En este caso, la búsqueda fonética, la creación o selección del perfil de miembro, y la relación de la reservación con esa entidad estable formaban parte de esa inteligencia silenciosa del sistema.

A veces la mejor tecnología es la que trabaja en silencio para que el usuario encuentre lo que necesita.

Cómo se conecta con la saga de estructuras de datos

Este ejemplo cierra muy bien la saga porque no se trata de una estructura aislada. En un sistema real, varias ideas se combinan.

Concepto de la sagaAplicación en este caso
Array / índiceLa base de datos y sus estructuras internas organizan datos para acceso eficiente.
Hash TableLa idea de transformar una clave en una ruta de búsqueda inspira el uso de raíces.
DiccionarioUna clave significativa permite llegar a un valor o perfil asociado.
GraphUna reservación se relaciona con una persona, empresa, historial, habitación y servicios.
Base de datosEl motor ejecuta búsquedas, índices y procedimientos almacenados.
Búsqueda fonéticaEl nombre escrito se convierte en una raíz para encontrar candidatos probables.

La memoria no se volvió inteligente por casualidad. Se volvió útil porque se diseñaron relaciones, claves, raíces, perfiles y búsquedas.

La lección final

Este post no pretende decir que una búsqueda fonética sea perfecta. No lo es. Tampoco pretende decir que un algoritmo pueda sustituir por completo el criterio humano. No debe hacerlo.

Lo que sí demuestra es que un sistema puede ayudar mucho cuando organiza bien la información.

Cuando un nombre se convierte en raíz, cuando una raíz lleva a candidatos, cuando los candidatos apuntan a miembros, y cuando las reservaciones se relacionan con perfiles estables, el software deja de ser un simple archivo de datos.

Empieza a comportarse como una memoria organizada.

Cuando la memoria se organiza bien, no solo guarda datos: ayuda a reconocer personas.

Cierre de la saga

La saga comenzó con una idea: la memoria se convierte en inteligencia cuando se organiza bien.

Recorrimos estructuras clásicas y luego vimos aplicaciones más concretas. Pero quise cerrar con este ejemplo porque representa lo que más me interesa de la computación: convertir conocimiento técnico en soluciones reales.

En una recepción hotelera, encontrar a Carlos, Karlos o Karl no era un ejercicio académico. Era parte de atender bien, recordar bien y servir mejor.

Y tal vez por eso sigo creyendo que las estructuras de datos no son solamente un tema de estudiantes de ciencias de la computación. Son una forma de pensar.


Referencias para profundizar



Complemento técnico

Como continuación de este artículo, preparé una entrada técnica dedicada a la clase Fonetica, una fuente histórica de mi biblioteca de controles. Allí explico cómo esa clase transforma un nombre en un código fonético, muestro su diseño interno y comparto el enlace al código fuente original.

Leer el análisis técnico de la clase Fonetica

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *