Mostrando entradas con la etiqueta C. Mostrar todas las entradas
Mostrando entradas con la etiqueta C. Mostrar todas las entradas

Leyendo de la entrada estándar con scanf y fgets

Hace tiempo, les contaba que me inicié en la programación competitiva, en la cual, la mayoría de problemas nos piden leer información de la entrada estándar o stdin seguramente me lo van a creer... yo no sabía cómo hacerlo de una manera "correcta" (._. ), en especial cuando de cadenas se trataba. En este post trataré de explicar cómo es que lo hago (y he aprendido que lo hacen) en C o C++.

La función scanf

Esta función mágica llega a nosotros gracias a la librería <stdio.h> en C o <cstdio> en C++, resulta la manera básica de recuperar valores. Nos sirve para todo tipo de entradas y si no tienes algo muy complicado de leer, esta es tu mejor opción, recuerda que esta función trata de leer el tipo de dato que le especifiques hasta que encuentra un salto de línea o un espacio en blanco. El uso de esta función depende de los especificadores de formato para recuperar los valores que deseas.

Por ejemplo, supongamos que tenemos la siguiente entrada
10 3161565164181
aab baa aba
y este código para leerlo
int a;
long long int b;
char cadena[15];
scanf("%d %lld", &a, &b); 
scanf("%s", cadena); // 'cadena' es ya una referencia a memoria
Una vez que se ejecute, en a tendremos 10, en b tendremos 3161565164181 y cadena será "aab" (¡porque solo lee hasta el espacio en blanco!).

La función fgets

Pero, qué pasa si la entrada es un poco más complicada, como la del problema Wetlands of Florida en la que vienen secuencias de caracteres seguidas inmediatamente por un par de números enteros:
// ...
LLLLLLLLL
3 2
// ...
Para ello se ocupa la función fgets (que es parecida a gets, solo que más segura), en combinación con sscanf que funciona de manera similar a scanf solo que esta "lee" de una secuencia de caracteres y no de stdin. En cuanto a fgets, sus parámetros son: la dirección de memoria, la cantidad de caracteres que esperamos leer como máximo, y el flujo del que los vamos a extraer, de tal manera que tenemos algo así:
int i, j;
char entrada[10];
fgets(entrada,10,stdin);
// Procesamos 'entrada', que contiene la siguiente secuencia de caracteres:
// 'L','W','W','W', 'L', 'L', 'L', 'L', 'W' y '\n' <= ¡Sí! el salto de línea
// Recuperamos los enteros:
fgets(entrada,10,stdin);
sscanf(entrada,"%d %d",&i,&j);
Ya para finalizar es necesario remarcar esto: fgets recupera hasta que encuentra el salto de línea. Repito: fgets recupera hasta que encuentra el salto de línea por lo que es necesario eliminarlo "a manita" con '\0' si es que no es lo que deseamos de él. 
Espero que les sirva como a mi me ha servido, ya que hay que recordar que una parte importante de resolver los problemas es saber interpretar de manera adecuada la entrada que nos dan, así como saber introducirla dentro de nuestra probable solución a este.


¡Saludos!
@fferegrino :)

Prácticas de Aplicaciones para comunicaciones de red

Pues ya estoy de vuelta en la escuela, y esta vez de nuevo hay una asignatura que me da la oportuindad de escribir código, me refiero a la Aplicaciones para comunicaciones de red (o Redes II, pa' los cuates). Al profe le gusta que hagamos prácticas y pues se me ocurrió otra vez abrir un repositorio de GitHub para publicarlas ahí, además de colaborar con mi compañera de equipo @FannyEvia.

Hasta ahora llevamos un servidor de FTP y un carrito de compras...  sobra decir que son muuuuy sencillas, pero funcionan para conocer el concepto, de ahí se pueden hacer tan complejos como uno desee. Las prácticas son solicitadas en Java o C, así que será lo principal que se pueden encontrar en el repositorio, sin embargo C# también se merece una mención, así que en la medida de lo posible también procuraré hacerlas en ese lenguaje.

Ya saben, pueden mirar el código y preguntar :)


¡Saludos!
@fferegrino :)

Algoritmos probabilistas

Las últimas prácticas de Análisis de algoritmos ( :'( ), esta vez es otro tipo de algoritmos, aquellos que emplean en azar para resolver el problema que se les asignó. A estos se les llama probabilistas y según la Wikipedia en español, hay de tres tipos; mismos tipos que nos tocó ejemplificar mediante algunos algoritmos conocidos empleando POO (lo cual es un poco difícil para ejemplos tan sencillos), son los siguientes:
Integración de Monte Carlo
Apelando al hecho de que una integral es la sumatoria de la infinidad de resultados de evaluar una función en un intervalo dado, se toman puntos de evaluación aleatorios dentro de ese intervalo dado, se evalúan y se van acumulando para luego realizarles un ajuste matemático, les diría que lo buscaran en Wikipedia, pero este documento me resultó especialmente revelador.

Para el caso de la implementación de POO lo que se consideró fue la creación de dos clases Random y Funcion, la primera para generar números aleatorios y la segunda para evaluar la función.

Aproximación a π
Para este caso se toma un cuarto de círculo de radio = 1, y se lanzan n dardos hacia él de esos n dardos supongamos que m aciertan en él entonces mediante matemágia, podemos encontrar que  π ≈ 4 * m / n

Para (mal) aplicar la POO, existe una clase que hace uso de Random, la clase definida en el ejericio anterior y de otra, llamada Aproximación en la cual se realiza la simulación de los dardos

Comprobación de primalidad (Algoritmo Monte Carlo)
Existe un test de primalidad, inventado por Fermat que nos dice que si tomamos la siguiente frómula a^(n-1) % n para cualquier número a ∈ [1,n-1] y si el resultado de eso es distinto de 1 entonces sabemos que n no es primo. Tomando una gran cantidad de números con valores aleatorios para a ∈ [1,n-1] podríamos asegurar con cierta probabilidad si un número n es primo. Si el test falla, es decir el resultado para cualquiera a es distinto de 1 podemos asegurar que n no es primo.

Resolución del problema de las N reinas (Algoritmo Las Vegas)
El ya conocidísimo problema delas N reinas también se puede resolver mediante probabilidad, es decir, tomamos la columna 1 y colocamos la primera reina, después para la segunda columna tratamos de colocar la siguiente reina en una posición elegida aleatoriamente, si se logró, continuamos con la siguiente columna, y así hasta colocar las N reinas o no poder colocar una, en ese caso falla.
Bastante simple.
Las primeras dos corresponden a la práctica 5, implementada en C++, y la segundas están implementadas en C#, con interfaz gráfica para Windows Phone 8 en XAML. El código, en analizando-algo.

¡Saludos!
@fferegrino :)

Codificación voraz de Huffman

La codificación Huffman es un algoritmo usado para la compresión de datos. Dicho algoritmo hace uso de una tabla de códigos para escribir un determinado símbolo, donde la tabla ha sido rellenada basándose en la probabilidad estimada de aparición de cada símbolo. A cada símbolo le corresponde un código (secuencia de 1 y 0) de longitud variable, puesto que los símbolos cuya aparición es menos frecuente reciben códigos más largos, mientras que los que aparecen más reciben códigos más cortos.

Para la ESCOM, el trabajo fue hacer la implementación voraz de dicho algoritmo, la cual tiene como variante que la comstrucción de la tabla de códigos es distinta para cada archivo, tomando solo en cuenta los símbolos contenidos en él y la cantidad de veces que aparecen.

He de decir que es de las prácticas más pesadas que he tenido en todo lo que llevo en la escuela, dentro del programa se hace uso de recursividad, operaciones a nivel de bits y estructuras de datos a más no poder:
  • Se usa una lista para para ir almacenando las frecuencias de cada caracter
  • Se usa un arreglo de árboles para generar el árbol único de códificación
  • Se usa una cola para obtener el código asignado a cada símbolo

El código, que se encuentra en el proyecto analizando-algo en GitHub, está muy comentado tratando de hacerlo lo más entendible posible. 

¡Saludos!
@fferegrino :)

Programación dinámica y la sucesión de Fibonacci

(Wikipediazo)En informática, la programación dinámica es un método para reducir el tiempo de ejecución de un algoritmo mediante la utilización de subproblemas superpuestos y subestructuras óptimas.

Decir que un problema tiene subproblemas superpuestos es decir que se usa un mismo subproblema para resolver diferentes problemas mayores. Por ejemplo, en la sucesión de Fibonacci (F3 = F1 + F2 y F4 = F2 + F3) calcular cada término supone calcular F2. Como para calcular F5 hacen falta tanto F3 como F4, una mala implementación para calcular F5 acabará calculando F2 dos o más veces.

Esto se puede evitar guardando las soluciones que ya hemos calculado. Entonces, si necesitamos resolver el mismo problema más tarde, podemos obtener la solución de la lista de soluciones calculadas y reutilizarla. Este acercamiento al problema se llama memoización (no confundir con memorización; en inglés es llamado memoization, véase en). Si estamos seguros de que no volveremos a necesitar una solución en concreto, la podemos descartar para ahorrar espacio.

Comenzando con el pseudocódigo:
FUNC Fibonacci (↓n: NATURAL): NATURAL
VARIABLES
tabla: ARRAY [0..n] DE NATURALES
i: NATURAL
INICIO
SI n = 0 ENTONCES
DEVOLVER 0
SINOSI n = 1 ENTONCES
DEVOLVER 1
SINO
tabla[0] := 0
tabla[1] := 1
PARA i = 2 HASTA n HACER
tabla[i] := tabla[i-1] + tabla[i-2]
FINPARA
DEVOLVER tabla[n]
FINSI
FIN

Una implementación en C de este algoritmo es la siguiente (he cambiado los tipos de dato para que se puedan calcular valores más grandes):
long long fibonacciArreglo(int n) {
long long * lista;
if(n == 0) return 0;
if(n == 1) return 1;
lista = (long long *) malloc(sizeof(long long) * n);
lista[0] = 0;
lista[1] = 1;
int i;
for (i = 2; i <= n; i++) {
lista[i] = lista[i-1] + lista[i-2];
}
return lista[n];
}
Como podemos ver, se reservan n espacios de memoria para almacenar todos los valores de la sucesión, aunque solamente se emplean dos de estos valores a la hora de cálcular el siguente número. Lo cual deriva en un "desperdicio" de memoria. 

Es por eso que basándome en el enunciado que está más arriba en negritas, pensé que implementar una cola sería una opción para ahorrar espacio, podemos agregar dinámicamente los nuevos valores y quitar los que ya no usaremos, de tal manera solo tendríamos en memoria los valores necesarios para calcular el siguiente número en la sucesión. El código queda como sigue:
long long fibonacciCola(int n) {
Cola cola;
if (n == 0) return 0;
if (n == 1) return 1;
creaLista(&cola);
formar(&cola, 0);
formar(&cola, 1);
int i;
for (i = 2; i <= n; i++) {
// Quitamos el elemento menos reciente
long long fi1 = atender(&cola);
// Obtenemos el valor del siguiente más reciente
long long fi2 = valorPrincipio(&cola);
// Almacenamos el resultado en la cola
formar(&cola, fi1 + fi2);
}
return valorFinal(&cola);
}
De esta forma aseguramos que para cualquier tamaño de n solo se ocupen a lo más 3 espacios de memoria.

El código está disponible en la sección de extras del proyecto analizando-algo

¡Saludos!
@fferegrino :)

Prácticas de análisis de algoritmos [ESCOM]

A lo largo del semestre, en la materia de Análisis de Algoritmos que llevo con el profesor Edgardo Franco tenemos que realizar varias prácticas. Las prácticas consisten en programar algoritmos conocidos para analizar su comportamiento, tanto en tiempo como en memoria consumida, mientras resuelven problemas de "gran" tamaño.

Muchas (si no es que todas D:) estarán hechas para C, para Linux, esto debido a la librería empleada para medir los tiempos que consume la ejecución del algoritmo. Para facilitar la compilación cada práctica cuenta con un archivo Makefile, el cual contiene todas las instrucciones para compilar y ejecutar las prácticas. Dado que tenemos que analizar los tiempos de ejecución y a veces estos pueden ser muchos, para la ejecución de cada programa redireccionamos la salida estandar para que escribiera directamente a un archivo que podemos manejar sencillamente en una hoja de cálculo. Esto se puede ver en el archivo .sh que acompaña a cada práctica.

Todas las prácticas tienen comentarios, unas más, otras menos, pero los tienen. Aún así si queda alguna duda pues no dudes en preguntar. Otra cosa importante es que estas son solo las propuestas de solución que presentamos, no las copies directamente, basate en ellas o usa porciones del código, eso se vale. El link de descarga está al final del artículo ;).

Hasta el momento hemos realizado 3 prácticas:

Práctica 1: "Pruebas a posteriori (Algoritmos de ordenamiento)"

Con base en el archivo de entrada proporcionado que tiene 200,000 números diferentes. Ordenarlo bajo los siguientes métodos de ordenamiento y comparar experimentalmente las complejidades de estos.
  • Burbuja (Bubble Sort)
  • Burbuja Simple
  • Burbuja Mejorada
  • Inserción (Insertion Sort)
  • Selección (Selection Sort )
  • Shell (Shell Sort)
  • Ordenamiento con árbol binario de busqueda (Binary Search Tree)

Práctica 2: "Análisis temporal y notación de orden (Algoritmos de busqúeda)"

Con base en el ordenamiento obtenido a partir del archivo de entrada de la práctica 01 que tiene 200,000 números diferentes. Realizar la búsqueda de elementos bajo 3 métodos de búsqueda y variantes con procesos ligeros, realizar el análisis teórico y experimental de las complejidades; así como encontrar las cotas de los algoritmos.
  • Búsqueda lineal o secuencial (No recursiva)
  • Búsqueda binaria o dicotómica (No recursiva)
  • Búsqueda en un árbol binario de búsqueda (No recursiva)
  • Modificar los tres algoritmos para trabajar con procesos ligeros (Hilos).

Práctica 3: "Divide y vencerás (Rotación de una imagen)"

Implementar el algoritmo de rotación de imágenes que emplea la técnica divide y vencerás. La imágen a girar es cuadrada, y es un .bmp de 24 bits.

Descargas:
Como desde hace tiempo, puedes seguir el proyecto en GitHub analizando-algo (recomendado)
O descargar cada una de las tres prácticas de manera individual:
Práctica 3
Práctica 2
Práctica 3


¡Saludos!
@fferegrino :)

Proyectos de Compiladores, ESCOM

Esta vez los saludo con un megapack: los proyectos de la asignatura de Compiladores en la ESCOM, entre ellos un conversor expresión regular-AFN-AFD (Java) y un analizador léxico-sintáctico (C). Y es que por problemas técnicos (y un poco de flojera) no los subí en el tiempo en el que fueron realizados.

La asignatura la cursé con el profesor Juárez Gambino Omar, junto con mi equipo: Cruz Cruz Jorge y Gonzales Rodríguez  Javier (@newbie_geek). A lo largo del semestre aprendí demasiado, y parte de ello fue que había mucho tiempo para practicar lo aprendido, aplicando el conocimiento en varias practicas a lo largo del curso.

La primera, fue un programar un analizador léxico, código y un sencillo reporte el cual coloco a continuación:

La segunda, el conversor de expresión regular a autómata finito no determinista, y de autómata finito no determinista a uno determinista. Este está hecho en Java:

La tercera práctica consistió en utilizar el lenguaje Flex para generar un analizador léxico (muy básico) para C, si ya conocen el lenguaje sabrán que la extensión (por convención) es .l:

Para la cuarta práctica comenzamos a emplear Bison, para ayudarnos en la construcción de un analizador sintáctico, la práctica fue individual, porque en realidad era sencilla. Basarnos en un ejemplo y hacer una calculadora:

La quinta y última práctica fue un analizador léxico y sintáctico para C haciendo uso de Flex y Bison juntos, en teoría es la más complicada, y que requirió más trabajo pero no por eso faltó:

La finalidad de poner estos códigos y reportes como descargables es que tú los puedas checar, leer, tratar de entender y posteriormente implementar a tu modo. No simplemente los descargues y entregues, o lo que es peor, no los bajes y cambies los nombres a las variables (nos gustan los nombres de nuestras variables, no los cambies). Como dicen "allá afuera no vas a encontrar las cosas hechas", así que lo ideal sería que estudies y trates de comprender el código.

Si hay alguna parte que no te queda clara puedes comentarla debajo o enviarme un correo, ya de menos un tuít. Pero hagas lo que hagas, NO COPIES.

¡Saludos!
@fferegrino :)

De C a HTML con Flex (como Norman manda)

Ya hace unos días les había hablado de flex y la práctica que teníamos que hacer. Bien, resulta que apenas se entrega. Y no es como inicialmente la había hecho:

Lo que había hecho fue de manera individual y esta que subo hoy es en equipo (Mi equipo: Byron, Gaby y Robert).
Las funciones que escriben el archivo html están por separado. Para mejor explicación visita el post anterior.

Descargas
PracticaC2HTML.tar.gz
PracticaC2HTML.zip


¡Saludos!
@fferegrino :)

De C a HTML con Flex

De nuevo por acá, esta vez gracias a otra de mis asignaturas en la ESCOM. Toca el turno a Teoría computacional, fue el turno de usar Flex, herramienta que en lo particular no conocía. Lo que teníamos que hacer era usar Flex para escribir un programa que de un archivo con código en lenguaje C nos lo "transfiriera" a un archivo con formato html estilizado con CSS.

La idea era encontrar palabras reservadas, tipos de datos, identificadores, directivas, operadores, y cualquier otro tipo de cadenas permitidas con el analizador léxico y asignarles cierto estilo en un documento html. 

Trato de explicar gráficamente: la imagen de la izquierda es código en C, después de introducir este archivo en el programita nos debe entregar algo como la captura derecha, que es un na representación estilizada con CSS del archivo que introdujimos.



¿Qué es Flex?
Resulta que Flex, en términos prácticos (Wikipediazo), es un programa para generar analizadores léxicos. Uno mete la especificación de un analizador léxico y Flex regresa como salida la implementación en C de lo que nosotros introdujimos. En realidad es muy sencillo utilizarlo, solo hay que tener en cuenta la estructura del archivo que vamos a usar como entrada al programa, esta es sencilla y muy fácil de recordar, un archivo "lex", se divida en tres secciones (separadas por líneas que solo contienen '%%' como texto en ellas). Las líneas son:
1.- Sección de declaraciones
Lugar para incluir cabeceras, macros, variables globales... también es posible definir patrones a usar en la sección de reglas
%%
2.- Sección de reglas
Aquí es donde se asocian los patrones(expresiones regulares) a buscar con sentencias(código) de C. Cada vez que el analizador encuentra un a cadena que coincide con el patrón, se ejecuta el código en C
%%
3.- Sección de código en C
Contiene sentencias en C y funciones que serán copiadas íntegramente en el archivo que Felx genere, comúnmente se colocan aquí las funciones que se llamarán en la sección de reglas.

Descargas
Al compilar el programa en Flex nos devuelve un código compilable pero ahora en lenguaje C, el cual ya debería de hacer lo que nosotros le indicamos. En el descargable básico viene el archivo cToHtml.l que es el archivo en lenguaje Lex, el archivo de estilos estilos.css que es la hoja de estilos que se usa para formatear el documento de salida y el archivo Makefile que nos facilita la compilación y el archivo Leeme.txt PARA QUE LO LEAN, en donde viene como se usa el programa final. En el descargable completo, viene, además de lo indicado arriba, el archivo lex.yy.c que es el código que se genera al compilar en Flex y el archivo salida.html que es un ejemplo de lo que entrega nuestro programa si hacemos pasar por el a lex.yy.c.

Recuerda que para cualquier duda, comentario o invitación a una cerveza, está la sección de comentarios, mi cuenta en Twitter o mi correo. 

¡Saludos! @fferegrino :)