Foro de elhacker.net

Programación => .NET (C#, VB.NET, ASP) => Mensaje iniciado por: DarK_FirefoX en 19 Mayo 2015, 18:36 pm



Título: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 19 Mayo 2015, 18:36 pm
Librería de Códigos - C#

Me he decido a crear este tema con el objetivo de ayudar a las personas a las cuales les pueda ser útil estos códigos. Mi idea es ir posteando códigos que de una manera u otra son útiles para personas que estén aprendiendo a programar en C# y tengan alguna duda. No hay otra manera de explicarlo así que voy a comenzar:

1 - Hallar el máximo número entero de un array de enteros

Aquí introducimos como parámetro al método un array de enteros llamando "numeros", aquí estará la colección de numeros en la cual queremos buscar el máximo (Dígase máximo al número más grande). Para que entiendan:

int.MinValue es una constante dentro de la clase Int32 que representa el menor valor posible representable por un entero. Este valor es: -2,147,483,648. Por lo tanto, cualquier entero representable con el tipo de dato int, será mayor o igual que la constante MinValue.
Lo que hacemos es recorrer el array de int números y vamos indexando y comprobando cada posición del array con la variable maximo (int.MinValue en un principio), en caso de que el número sea mayor le asignamos a esa variable el valor en la posición i del array (o sea, la que se está comprobando) y seguimos recorriendo el array hasta que se hayan comprobado todos los elementos del array. Luego, la variable maximo tendrá almacenado el valor máximo del array. Lo que hacemos es devolverlo.

Código
  1. public static int Maximo(int[] numeros)
  2.        {
  3.            int maximo = int.MinValue;
  4.            for (int i = 0; i < numeros.Length; i++)
  5.            {
  6.                if (numeros[i] > maximo)
  7.                    maximo = numeros[i];
  8.            }
  9.            return maximo;
  10.        }



2 - Hallar el segundo mayor elemento en un array de enteros

La idea es parecida a como hallar el mayor elemento, de hecho, lo que se va a hacer es recorer el array e ir buscando el mayor elemento, pero cuando se encuentre uno mayor del que ya se encontró, ese que ya se había encontrado se va a guardar en otra variable. Entonces vamos a tener dos variables, una "mayor" y otra "sMayor" (segundo mayor). La idea es el que se encuentre se pone en la variable mayor y lo que estaba en la variable mayor se pone en la variable sMayor. De esta manera vamos llevando control de las dos variables. Hay que tener en cuenta que la comparación se realiza primero si el elemento indexado es mayor que "mayor", sino, hay que comprobar si es mayor que "sMayor" pues puede darse el caso que existan elementos entre las dos una vez después que se haya asignado los valores una vez y este elemento vendría a ser el segundo mayor.
Una vez recorrido todo el array solo resta devolver la variable "sMayor"

Código
  1. public static int SegundoMayor(int[] numeros)
  2.        {
  3.            int mayor = int.MinValue;
  4.            int sMayor = int.MinValue;
  5.            for (int i = 0; i < numeros.Length; i++)
  6.            {
  7.                if (numeros[i] > mayor)
  8.                {
  9.                    sMayor = mayor;
  10.                    mayor = numeros[i];
  11.                }
  12.                else if (numeros[i] > sMayor)
  13.                    sMayor = numeros[i];
  14.            }
  15.            return sMayor;
  16.        }



3 - Invertir los elementos de un array

Invertir los elementos viene siendo como hacer un Reverse (Invertir el orden de los elementos del array). Voy a exponer como hacerlo de dos maneras, que es prácticamente lo mismo con la diferencia que en un método modificamos el mismo array y en el otro devolvemos un array nuevo.

Modificando el mismo array

Aquí, recibimos como parámetro de entrada un array (en este caso es un array de int, esto puede servir para otros tipos de datos, o incluso se podría haber hecho genérico, lo expongo con int para simplificar). Básicamente vamos a recorrer el array hasta la mitad, utilizando la facilidad de indexar un array podemos cambiar los valores. Pues el primer elemento del array sería 0 y el último a.Length -1. Lo que utilizando la variable i del ciclo, podemos ir aumentando y disminuyendo a la vez de una forma desde afuera hacia adentro y haciendo lo que se conoce como Swap (intercambio) en esos elementos. Si nos damos cuenta el último elemento siempre nos vamos a referir como a[a.Length-1-i], puesto que i va aumentando ese valor va a ir reduciéndose. Por lo tanto el objetivo es: Cambiar el primero con el último, el segundo con el penúltimo, y así sucesivamente. Este método es void, puesto que no devolvemos nada, solamente trabajamos sobre el mismo array.

Nota: Para hacer el Swap lo que hacemos es crear una variable temporal donde asignamos uno de los valores, luego en esta variable que ya guardamos, ponemos el otro elemento a intercambiar, por último, el elemento que guardamos en la variable temporal se lo asignamos a la variable de donde sacamos el valor a intercambiar.

Código
  1. public static void InvierteV(int[] a)
  2.        {
  3.            for (int i = 0; i < a.Length / 2; i++)
  4.            {
  5.                int temp = a[i]; //Asignamos valor a la variable temporal
  6.                a[i] = a[a.Length - 1 - i]; //Copiamos el valor de la parte trasera del array a la frontal
  7.                a[a.Length - 1 - i] = temp; //Copiamos el valor guardado en la variable temporal a la parte trasera del array
  8.            }
  9.        }

Devolviendo un nuevo array

Código
  1. public static int[] InvierteN(int[] a)
  2.        {
  3.            int[] newInt = new int[a.Length];
  4.  
  5.            //Copiamos los elementos del array al nuevo array
  6.            for (int i = 0; i < a.Length; i++)
  7.                newInt[i] = a[i];
  8.  
  9.            for (int i = 0; i < newInt.Length/2; i++)
  10.            {
  11.                int temp = newInt[i]; //Asignamos valor a la variable temporal
  12.                newInt[i] = newInt[newInt.Length - 1-i]; //Copiamos el valor de la parte trasera del array a la frontal
  13.                newInt[newInt.Length - 1-i] = temp; //Copiamos el valor guardado en la variable temporal a la parte trasera del array
  14.            }
  15.            return newInt;
  16.        }

Esta forma funciona casi idéntico, solo que hacemos primero una copia del array en un array nuevo con la misma longitud y los mismos valores, para luego trabajar sobre este array y después devolverlo tras realizarle el proceso de invertir los elementos explicados en la forma anterior.

Nota: El proceso de crear la copia del array lo hago inicializando un array de la misma longitud que el que entra como parámetro y recorriendo este array y copiando los valores a este nuevo array. Quería destacar que para hacer la copia del array podíamos usar el método Array.Copy(), pasándole el array desde donde se quiere copiar, el array a donde se va a copiar y la cantidad de elementos a copiar, en este caso todos. Hubiera sido algo así:

Código
  1. Array.Copy(a, newInt, a.Length);

De esta forma nos hubiéramos ahorrado el primer ciclo del método (aunque en términos de eficiencia es prácticamente lo mismo)



4 - EsPrimo

Básicamente esto es algo bastante utilizado, aunque esta manera de implementarlo es "sencilla" a simple vista, puede ser un proceso extremadamente largo para números muy grandes.

Código
  1. public static bool EsPrimo(int a)
  2.        {
  3.           int raizDelNumero = (int)Math.Sqrt(a);
  4.            for (int i = 2; i <= raizDelNumero; i++)
  5.                if (a % i == 0)
  6.                    return false;
  7.            return true;
  8.        }

Lo que hacemos es calcular la raiz del numero y guardarla en una variable. Utilizamos el metodo .Sqrt dentro de la clase Math. El cuál calcula la raíz cuadrada de un número. Este método devuelve un double, por eso le hacemos un cast a int (cast es una conversión forzada a un tipo de datos). El objetivo del método es recorrer todos los números desde 2 hasta la raíz del número y ver si alguno de esos números divide al número (o sea, deja resto 0 con el número). En caso de que alguno de esos números divida al número, implica que el número no es primo. Si el ciclo termina sin devolver false, entonces devolvemos true pues el numero es primo.




Bueno, aquí les dejo los cuatro primeros, luego sigo añadiendo más de acuerdo al tiempo que tenga libre. Si alguien quiere aportar sean libres de hacerlo. Esto son cosas sencillas, pero que para personas que están aprendiendo pueden ser muy útiles. Recuerden, si añaden, comentar los códigos y/o explicarlos.

Salu2s



Título: Re: Libreria de códigos C# (Compartan sus códigos aquí)
Publicado por: Eleкtro en 19 Mayo 2015, 19:32 pm
Todos los códigos rehusables que tengo están desarrollados en VB.Net, pero éste lo convertí hace ya tiempo, así que lo comparto por aquí :P.

Es una función que sirve para darle un formato a un documento XML sin formatear (es decir, sin indentación), lo que comúnmente se denomina cómo "Beautifier" o embellecedor, el código es simple pero efectivo.

• Before/After:
(http://i.imgur.com/jbOCpmH.jpg)

• Código:
Código
  1. using ControlChars = Microsoft.VisualBasic.ControlChars;
  2. using Encoding = System.Text.Encoding;
  3. using StringBuilder = System.Text.StringBuilder;
  4. using System;
  5. using Xml = System.Xml;
  6.  
  7. namespace Tools
  8. {
  9.    class XmlUtil
  10.    {
  11.        /// <remarks>
  12.        /// *****************************************************************
  13.        /// Snippet Title: XML Beautifier
  14.        /// Code's Author: Elektro
  15.        /// Date Modified: 18-February-2015
  16.        /// *****************************************************************
  17.        /// </remarks>
  18.        /// <summary>Beautifies the contents of an XML document.</summary>
  19.        /// <param name="XMLText">
  20.        /// The XML text content.
  21.        /// It can be an entire document or a fragment.
  22.        /// </param>
  23.        /// <param name="IndentChars">
  24.        /// The string that is used to indent the XML.
  25.        /// Default value is: <see cref="Microsoft.VisualBasic.ControlChars.Tab"/>
  26.        /// </param>
  27.        /// <param name="IndentOnAttributes">
  28.        /// If set to <c>true</c>, attributes will be separated by newlines.
  29.        /// Default value is: <c>false</c>
  30.        /// </param>
  31.        /// <param name="TextEncoding">
  32.        /// The XML text encoding to use.
  33.        /// Default value is: <see cref="System.Text.Encoding.Default"/>.
  34.        /// </param>
  35.        /// <returns>The beautified XML text.</returns>
  36.        /// <exception cref="System.ArgumentNullException"></exception>
  37.        public static string XmlBeautifier(string xmlText,
  38.                                           string indentChars = null,
  39.                                           bool indentOnAttributes = false,
  40.                                           Encoding textEncoding = null)
  41.        {
  42.            if (string.IsNullOrEmpty(xmlText) || string.IsNullOrWhiteSpace(xmlText))
  43.            {
  44.                throw new ArgumentNullException(xmlText);
  45.            }
  46.  
  47.            StringBuilder sb = new StringBuilder();
  48.            Xml.XmlDocument doc = new Xml.XmlDocument();
  49.            Xml.XmlWriterSettings settings = new Xml.XmlWriterSettings();
  50.  
  51.            settings.Indent = true;
  52.            settings.CheckCharacters = true;
  53.            settings.OmitXmlDeclaration = false;
  54.            settings.ConformanceLevel = Xml.ConformanceLevel.Auto;
  55.            settings.NamespaceHandling = Xml.NamespaceHandling.Default;
  56.            settings.NewLineHandling = Xml.NewLineHandling.Replace;
  57.            settings.NewLineChars = ControlChars.NewLine;
  58.            settings.NewLineOnAttributes = indentOnAttributes;
  59.            settings.IndentChars = indentChars != null ? indentChars : Convert.ToString(ControlChars.Tab);
  60.            settings.Encoding = textEncoding != null ? textEncoding : Encoding.Default;
  61.  
  62.            using (Xml.XmlWriter writer = Xml.XmlWriter.Create(sb, settings))
  63.            {
  64.                doc.LoadXml(xmlText);
  65.                doc.WriteContentTo(writer);
  66.                writer.Flush();
  67.            }
  68.  
  69.            return sb.ToString();
  70.        }
  71.  
  72.    }
  73. }

• Características:

   · IndentChars
      This parameter determines the spacing character(s) used to indent the XML document.

   · IndentOnAttributes
      This parameter determines whether to indent the XML attributes.

   · TextEncoding
      This parameter determines the encoding used to read the XML document.

• Modo de empleo:
Código
  1. Encoding textEncoding = Encoding.Default;
  2.  
  3. string unformattedXmlDocument = File.ReadAllText("C:\\Unformatted Document.xml", textEncoding);
  4. string formattedXmlDocument = XmlBeautifier(xmlText: unformattedXmlDocument,
  5.                                            indentChars: new string(' ', 2),
  6.                                            indentOnAttributes: true,
  7.                                            textEncoding: textEncoding);
  8.  
  9. File.WriteAllText("C:\\Formatted Document.xml", formattedXmlDocument, textEncoding);

Saludos


Título: Re: Libreria de códigos C# (Compartan sus códigos aquí)
Publicado por: DarK_FirefoX en 19 Mayo 2015, 21:02 pm
5 - Hallar el número primo más cercano a un número

Esté código sirve para encontrar el número primo más cercano a un entero n. En éste código utilizamos el código para encontrar un número primo expuesto anteriormente (Lo añado aquí para que no tengan que ir a buscarlo al post anterior.

Código
  1. public static bool EsPrimo(int a)
  2.        {
  3.            int raizDelNumero = (int)Math.Sqrt(a);
  4.            for (int i = 2; i <= raizDelNumero; i++)
  5.                if (a % i == 0)
  6.                    return false;
  7.            return true;
  8.        }

La idea del método es comprobar si el número entrado es primo, si lo es, entonces el mismo es el primo más cercano, sino tendremos dos variables, numeroDer y numeroIzq, lo que podemos interpretarlas y usarlas para almacenar los números que hay a la izquierda y a la derecha del número n de la entrada. El objetivo es iterar por los números a la izquierda/derecha del número de la entrada y contar las distancia que tienen entre el y el número n de la entrada. Cuando encontremos un número primo a la derecha tendremos almacenado ese número y la distancia al número n, luego haremos lo mismo hacia la izquierda. Al final comprobaremos las distancias respectivas y devolveremos el número que tenga menor distancia.

Nota: Sabemos que un número primo es un número mayor que 1 tal que tiene únicamente dos divisores naturales distintos: él mismo y el 1. Por lo que si la entrada n viene dada por un número menor o igual que 1, tendrá como primo mas cercano al 2.

Código
  1. public static int PrimoMasCercano(int n)
  2.        {
  3.            int contadorDer = 0;
  4.            int contadorIzq = 0;
  5.            int numeroDer = n;
  6.            int numeroIzq = n;
  7.            if (n <= 1) return 2;
  8.            if (EsPrimo(n)) return n;
  9.            else
  10.            {
  11.                    while (!EsPrimo(numeroDer))
  12.                    {
  13.                        numeroDer++;
  14.                        contadorDer++;
  15.                    }  
  16.                    while (!EsPrimo(numeroIzq))
  17.                    {
  18.                        numeroIzq--;
  19.                        contadorIzq++;
  20.                    }              
  21.            }
  22.            if (contadorPos < contadorNeg) return numeroDer;
  23.            return numeroIzq;
  24.  
  25.        }



6 - Hallar el máximo común divisor de un número (MCD)

El siguiente código muestra como hallar el MCD de un número utilizando el Método de Euclides, que utiliza el algoritmo de la división. Voy a mostrarlo de manera iterativa y de manera recursiva. En la manera iterativa utilizo una función auxiliar Swap(int a, int b), para hacer un intercambio de valores de dos variables

Código
  1. static void Swap(int a, int b)
  2.        {
  3.            int temp = b;
  4.            b = a;
  5.            a = temp;
  6.        }

Forma iterativa

El método recibe las dos variables entre las cuales queremos el MCD. El MCD es el mayor número entero que los divide a ambos sin dejar resto.

(No voy a explicar cómo funciona el algoritmo de Euclides ni el algoritmo de la división. Asumo que lo conocen)

Lo primero que debemos hacer es comprobar si los números son iguales, en ese caso, el mismo es el MCD. Sino, comprobamos y determinamos cuál de los dos es el mayor, para comenzar con el mayor como dividendo. Luego planteamos nuestra división teniendo los términos definidos, dividendo, divisor y resto. El resto viene dado por:

Código
  1. int resto = dividendo % divisor

Luego vamos a ir "dividiendo" mientras el resto sea distinto de 0. Para cada iteración se van a ir cambiando los valores de los términos de la división. El dividendo pasa a ser el divisor, el divisor pasa a ser el resto, y el resto pasa a ser el nuevo resto (nuevo dividendo % nuevo divisor).
Por último, vamos a signarle a la variable answer el valor del resto si este es distinto de 0, para así garantizar que cuando termine el ciclo, esta variable (que es la que se va a devolver) tenga asignado el MCD, que el algoritmo de Euclides define como el último resto distinto de 0.

 
Código
  1. public static int MCD(int a, int b)
  2.        {
  3.            if(a==b) return a;
  4.            if(b>a)
  5.                Swap(a,b);
  6.            int dividendo = a;
  7.            int divisor = b;
  8.            int resto = dividendo % divisor;
  9.            int answer = a;
  10.            while (resto!=0)
  11.            {
  12.                dividendo = divisor;
  13.                divisor = resto;
  14.                resto = dividendo % divisor;
  15.                if (resto != 0) answer = resto;
  16.            }
  17.            return answer;
  18.        }

Forma recursiva

Gracias a la recursividad tenemos un método mucho más limpio y corto. Básicamente, el método realiza lo mismo. Comprobamos inicialmente si el resto entre ellos es 0, y si lo es retornamos b (utilizada como divisor). En caso contrario llamamos recursivo, esta vez utilizamos a b (divisor) como dividendo, y utilizamos al resto entre a y b (resto) como divisor, y así recursivamente.

Nota: Pueden preguntarse ¿Por qué no se comprueba cual es el mayor para hacer el Swap? El caso es que si a es menor que b, al realizarse la comprobación nos damos cuenta que el resto entre a y b va a ser a, entonces no va a ser 0, por lo tanto va a llamar recursivo, con b (la variable mayor) como a, y al resto entre a y b (que era a) como b

Código
  1. static int MCD(int a, int b)
  2.        {
  3.            if (a % b == 0) return b;
  4.            return MCD(b, a % b);
  5.        }



7 - Descomposición de un número en factores primos

Descomponer un número en factores primos consiste en descomponer un número compuesto en divisores no triviales primos que multiplicados den el número.

En este código muestro como descomponer un número n en factores primos.

Nota - 1: Utilizo el método EsPrimo(), explicado anteriormente.
Nota - 2: En este código muestro el resultado en consola utilizando:

Código
  1. Console.WriteLine()

Pueden modificarlo para que se almacenen en una lista.

Primero que todo comprobamos si el número es primo, en caso de que lo sea, no tiene otros divisores, por lo tanto, devolvemos el único divisor primo que tiene, él mismo.

Ahora, lo que vamos a hacer es ir iterando con una variable desde 2 hasta que encontremos un número que sea primo y que divida al número. En caso de que se cumplan esas dos condiciones, vamos a dividir el número por la variable i (con la cual se está iterando), vamos a devolver el valor de i pues este es el primer factor primo encontrado, y vamos a volver a hacer la variable i de nuevo 2. En caso de que el alguna de las condiciones no se cumpla, se incrementará la variable i y se realizaran las mismas comprobaciones con este nuevo valor. Estas comprobaciones se van a realizar mientras la variable numero sea distinta de 1. Como vemos, al estar dividiendo esta variable va a ir disminuyendo su valor hasta llegar a 1. En este caso terminará el ciclo.


Código
  1. public static void DescomposicionPrimos(int n)
  2.        {
  3.            if (EsPrimo(n)) Console.WriteLine(n);
  4.            else
  5.            {
  6.                int i = 2;
  7.                int numero = n;
  8.                while (numero!=1)
  9.                {
  10.                    if (EsPrimo(i))
  11.                    {
  12.                        if (numero % i == 0)
  13.                        {
  14.                            numero = numero / i;
  15.                            Console.WriteLine(i);
  16.                            i = 2;
  17.                        }
  18.                        else i++;
  19.                    }
  20.                    else i++;
  21.                }
  22.            }
  23.  
  24.        }

8 - Comprobar si un número es perfecto

Un número perfecto es un número natural que es igual a la suma de sus divisores positivos, sin incluirse a si mismo.

Nota: Aquí utilizamos también el método EsPrimo, puesto que si el número n de la entrada es primo entonces no es perfecto.

La variable suma va a ir controlando la suma de los divisores de n. Esta siempre va a comenzar ya con 1 pues el 1 divide a todos los números, por lo tanto siempre se cuenta. Vamos iterar desde 2 hasta la mitad del número. Puesto que ningún número mayor que la mitad va a dividir al número. Entonces lo que haremos es comprobar que la variable sobre la cual se está iterando deje resto 0 con n y se lo sumamos a la variable suma. una vez que termine el ciclo, devolvemos true en caso de que suma sea igual a n, o falso en caso contrario

Código
  1. public static bool EsPerfecto(int n)
  2.        {
  3.            if (EsPrimo(n)) return false;
  4.            int suma = 1;
  5.            for (int i = 2; i <= n / 2; i++)
  6.                if (n % i == 0)
  7.                    suma += i;
  8.            return suma == n;
  9.        }

Esto son los otros 4 que he ido terminando. Seguiré luego.

Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 20 Mayo 2015, 19:06 pm
9 - Hallar el n-ésimo término de la sucesión de Fibonacci (Recursivo)

La sucesión de Fibonacci es una sucesión infinita de números naturales:

1, 1, 2, 3, 5, 8, 13, 21...

la cual viene dada por la siguiente definición recursiva:

fn = fn-1 + fn-2

donde los dos primeros valores son predeterminados:

f0 = 0 y f1 = 1

Lo que voy a mostrar es un método para calcular el n-ésimo término de la sucesión:

Código
  1. static int Fibonacci(int n)
  2.        {
  3.            if (n == 0) return 0;
  4.            if (n == 1) return 1;
  5.            return Fibonacci(n - 2) + Fibonacci(n - 1);
  6.        }

Este código es muy sencillo de entender (si entendemos recursividad). Primero que todo planteamos los casos base. Si n=0 retornamos 0 (caso para f0 = 0) y si n=1 retornamos 1 (caso para f1 = 1). En caso contrario devolvemos el resultado de la suma de las llamadas recursivas de Fibonacci para n-2 y para n-1. Lo cual define la ecuación recursiva.

Esta definición nos lleva al siguiente código que quiero mostrar de acuerdo con la sucesión de Fibonacci. Si nos damos cuenta, esté código realiza operaciones duplicadas, puesto que si queremos calcular el 5 término de la sucesión, debido al llamado recursivo calculamos el 4 término y el 3 término, pero a su vez el 4 término debe calcular el 3 término y el 2 término, entonces nos damos cuenta que el 3 término se va a calcular 2 veces en el árbol de recursión. Esto se conoce como subproblemas superpuestos ¿Que podemos hacer? Utilizaremos programación dinámica.

Fibonacci Dinámico

Código
  1. static long[] fibonacci; //Para ir almacenando los valores de los términos calculados.
  2.  
  3.        static void Main(string[] args)
  4.        {
  5.            long n = long.Parse(Console.ReadLine());
  6.            fibonacci = new long[n+1];
  7.            Console.WriteLine(Fibonacci(n));
  8.  
  9.        }
  10.  
  11.        static long Fibonacci(long n)
  12.        {
  13.            if (fibonacci[n] != 0)
  14.                return fibonacci[n];
  15.            if (n == 1 || n==2)
  16.                return 1;
  17.            fibonacci[n - 2] = Fibonacci(n - 2);
  18.            fibonacci[n - 1] = Fibonacci(n - 1);
  19.            return fibonacci[n - 2] + fibonacci[n - 1];
  20.        }

Bueno, lo que vamos a hacer es ir almacenando los términos que se vayan calculando en un array cuya longitud va a ser el término de Fibonacci que se quiera calcular más 1. Y el método cambia un poco.

Primero que todo revisamos si ese término ya se calculó (comprobando si el array en esa posición es distinto de 0). Si se calculó lo devolvemos. Sino, vamos a analizar los casos base, en este caso añadimos una condición con un OR, para los 2 primeros términos de la sucesión. Retornamos 1 en caso de que n=1 o n=2. Luego de esto, en vez de devolver la suma de los llamados recursivos, llamamos recursivo asignándole los valores a las respectivas posiciones en el array. Una vez calculadas/devueltas los valores, entonces se devuelve la suma de los valores contenidos en el array.

Espero esto se haya entendido



10 - Devolver el Reverse de un string

El objetivo del siguiente código es devolver un string con sus char invertidos. El método recibe un string y devuelve un nuevo string. Por lo tanto lo primero que hacemos dentro del método es crear un string que será el que se devolverá. Recorreremos el string de entrada de atrás hacia adelante con un ciclo for. Luego solo nos queda dentro del ciclo concatenarle al string recién creado cada carácter que nos vayamos encontrando en el string original por medio de la posibilidad de indexar en el string.

Código
  1. public static string Reverse(string original)
  2.        {
  3.            string reverse = "";
  4.            for (int i = original.Length-1; i >= 0; i--)
  5.                reverse += original[i];
  6.            return reverse;
  7.        }

11 - Convertir de decimal a binario

Forma iterativa

Voy a mostrar como iterativamente convertir un número decimal (en este caso un int) a un número binario. La conversión de decimal a binario se realiza haciendo divisiones sucesivas por 2 al número y a sus cocientes. Luego, cuando el número a dividir sea 1 se finaliza la división y se ordena los restos en el orden inverso al que fueron apareciendo.

Lo primero que hacemos es comprobar si el número es 0, si lo es, devolvemos un string con el carácter 0. O sea, el número binario 0. Luego, lo que vamos hacer es ir almacenando los restos a medidas que aparezcan en un string que creamos dentro del método. Utilizamos un while para controlar el ciclo. Aquí utilizo un while(true), el cual va a estar corriendo siempre a no ser que se haga un break (el cual se hace). Lo primer que hacemos es establecer la condición para la cual se va a salir del ciclo, la cual es en el caso de que se divida y el número de 1. Por lo tanto concatenamos al principio el último resto, y luego el 1, que viene siendo el último número que apareció en la división. Entonces hacemos break. En caso de que no se cumpla la condición de n/2 == 1. Vamos a hacer el proceso de concatenar al principio el resto que encontremos y después dividir el número. Proceso que se ejecutará hasta que se cumpla la condición explicada anteriormente. Al final devolvemos el string.


Código
  1. public static string DecimalABinario(int n)
  2.        {
  3.            if (n == 0) return "0";
  4.            string a = "";
  5.            while (true)
  6.            {
  7.                if (n / 2 == 1)
  8.                {
  9.                    a = n % 2 + a;
  10.                    a = 1 + a;
  11.                    break;
  12.                }
  13.                a = n % 2 + a;
  14.                n = n / 2;
  15.            }
  16.            return a;
  17.        }

Quiero que esto podría hacerse de otra forma, en vez de concatenar al principio del string, podemos concatenar al final utilizando el operador += sobre el string a devolver, solo que la hora de devolver el string lo hacemos llamando al método Reverse, que explique anteriormente.

Forma recursiva

En esta forma iterativa voy a tener una cosa a la que yo llamo: "Método portal", el cuál es el método público (que se le muestra al usuario) el cual tiene una sobrecarga (método con otros parámetros) de tipo privado.

Código
  1. /// <summary>
  2.        /// Método portal
  3.        /// </summary>
  4.        public static string DecimalABinario(int dec)
  5.        {
  6.            return DecimalABinario(dec, ""); //Llamamos al método
  7.        }
  8.  
  9.        private static string DecimalABinario(int dec, string result)
  10.        {
  11.            if (dec == 0)
  12.                return "0" + result;
  13.            if (dec == 1)
  14.                return "1" + result;
  15.            return DecimalABinario(dec / 2, (dec % 2) + result);
  16.        }

El método portal recibe el número a convertir a través de la variable dec (int) (cuyo significado semántico es "decimal"l). Entonces a partir de ahi le pasamos al método recursivo esa variable y un string vacío. Los casos bases vienen dado por si el número es 0, devolvemos 0 concatenado al principio del string, si el número es 1 devolvemos 1 concatenado al principio del string. En caso contrario se hace la llamada recursiva, donde el número que se le pasa es la el número dividido por 2 y el string es el resto que deja el número con 2 concatenado al principio del string.

Espero esto se entienda. La verdad es necesario que tengan conocimientos de recursividad para entender esto, sino, miren la forma iterativa.

12 - Convertir de binario a decimal

Convertir de binario decimal es un proceso que viene dado por la suma de las multiplicaciones sucesivas de los dígitos de un número por 2 elevado al índice que ocupa este número (comenzando por 0). Por ejemplo el número binario 1011 seria igual a 1*23 + 0*22 + 1*21 + 1*20 = 8 + 0 + 2 + 1 = 11 (decimal)

Forma iterativa

Primero que todo voy a aclarar que recibimos el número binario a través de un string, los números binarios es mejor representarlos en un string pues si la representación binaria de un número es muy grande puede no ser representable por un int.

Código
  1. static int BinarioADecimal(string bin)
  2.        {
  3.            int dec = 0;
  4.            for (int i = 0; i < bin.Length; i++)
  5.                dec += (bin[i] - '0') * (int)Math.Pow(2, bin.Length - 1 - i);
  6.            return dec;
  7.        }

Primero voy a explicar ue cosa es:

Código
  1. bin[i]-'0'

Lo que estamos aquí es convirtiendo un char a int. ¿Cómo? Pues como sabemos un char es básicamente un entero con un puntero a la tabla ASCII. A todos los chars le corresponden un valor entero. Haciéndole la substracción por el char '0' lo que hace es buscar el valor de este caracter en la tabla, el cual es el valor base y se los substrae al valor del char
Código
  1. bin[i]
, lo cual nos devuelve el número original.

Una vez entendido esto, veamos que el método lo que hace es crear un int para devolver el valor, e ir sumándole a él la multiplicación del dígito por la potencia de dos. Recorremos el string (representación binaria del número) y vamos sumándole al int creado la multiplicación del digito
Código
  1. bin[i]-'0'
por la potencia.

La potencia la calculamos utilizando el método Pow() de la clase Math. El método recibe un double que  representa la base (en este caso 2) y un double que representa el exponente (en este caso bin.Length - 1 -i. Decimos -1 pues los índices de los dígitos van desde 0 hasta la longitud -1, y decimos -i pues queremos que este valor vaya disminuyendo a medida que avancemos en el string. Aclaro que al resultado de este método se le hace un cast a int pues el método devuelve un double, y no podemos multiplicar un int por un double sin hacer la conversión.

Cuando termine el ciclo devolvemos el número entero (representación decimal del número.

Forma recursiva

Código
  1.        /// <summary>
  2.        /// Método portal
  3.        /// </summary>
  4.        static int BinarioADecimal(string bin)
  5.        {
  6.            return BinarioADecimal(bin, 0);
  7.        }
  8.  
  9.        static int BinarioADecimal(string bin, int result)
  10.        {
  11.            if (bin.Length == 0)
  12.                return result;
  13.            return BinarioADecimal(bin.Remove(0, 1), result + (int)(Math.Pow(2, bin.Length - 1) * (bin[0] - '0')));
  14.        }

En este método es casi lo mismo, solo con un pequeño cambio, en vez de ir recorriendo los índices vamos a utilizar el método Remove() dentro de la clase String, el cual devuelve un substring de un string con los caracteres eliminados desde una posición especificada y la cantidad que se le especifiquen.

Lo que hacemos es comprobar si el string ya está vacío, en este caso devolvemos el resultado.

Sino, llamamos recursivo con el resultado del método Remove sobre el string bin eliminándole el primer carácter. Y también le pasamos la suma de la variable result por la multiplicación del dígito (primer) por la potencia de dos.

Espero se entienda.



En cuanto tenga tiempo hago más.
Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 20 Mayo 2015, 22:24 pm
13 - Saber si una cadena (string) es palíndromo

Primero que todo ¿Qué es palíndromo? Bueno, palíndromo es una palabra, número o frase que se lee igual hacia adelante que hacia atrás.

Ejemplo: anita lava la tina (anitalavalatina)

Este código es extremadamente sencillo. Supongamos que la entrada es un string y que no tiene espacios.

Forma iterativa

Código
  1. public static bool EsPalindromo(string cadena)
  2.        {
  3.            for (int i = 0; i < cadena.Length / 2; i++)
  4.            {
  5.                if (cadena[i] != cadena[cadena.Length - 1 - i])
  6.                    return false;
  7.            }
  8.            return true;
  9.        }

Simplemente recorremos el string hasta la mitad, pues gracias a los índices y a la propiedad Length de la clase string podemos acceder a los caracteres de la segunda mitad del string

El objetivo es comprobar el primer carácter con el último, el segundo con el penúltimo y así sucesivamente. Si se encuentra que alguno es diferente del otro, se devuelve false (no es palíndromo). Si se llega al final del ciclo sin devolver false, entonces se devuelve true (es palíndromo)

Forma recursiva:

Código
  1. static bool EsPalindromo(string s)
  2.        {
  3.            int lIzq = 0;
  4.            int lDer = s.Length - 1;
  5.            if (lIzq >= lDer) return true;
  6.            return s[lIzq] == s[lDer] &&
  7.                EsPalindromo(s.Substring(lIzq + 1, lDer - 1));
  8.        }

En la forma recursiva vamos a ir llevando un control de los índices manualmente. Utilizaremos también el método Substring() de la clase string. Este método devuelve un substring de un string dado desde una posición dada con una longitud dada.

Lo primero que hacemos es definir los índices del string. En este caso 0 y la s.Length-1. Luego comprobamos que no hayamos terminado de revisar el string completo. La comprobación viene dada por si los índices se superpusieron. Luego viene el llamado recursivo. Aqui viene dado por un AND entre si el string en las posiciones dadas son iguales y el llamado recursivo en sí. Al llamado recursivo en si se le pasa como parámetro el substring con la posición izquierda más 1 y la posición derecha menos 1. Desta forma garantizamos que seguir funcionando si en la posición actual los caracteres son iguales y solo si también es palíndromo lo que está entre esos caracteres.



14 - Saber el factorial de un número

El factorial de un número se define como el producto de todos los números enteros positivos desde 1 hasta el número.

Por ejemplo: 5! = 1*2*3*4*5 = 120

Forma iterativa

Código
  1. public static long Factorial(long numero)
  2.        {
  3.            long result = 1;
  4.            for (long i = 2; i <= numero; i++)
  5.                result *= i;
  6.            return result;
  7.        }

Este código es algo relativamente sencillo. Se basa en una productoria desde 1 hasta el número. Con un ciclo for resolvemos este problema sencillamente. Creamos una variable de tipo long, puesto que el factorial de un número puede llegar a ser un número grande y puede no caber en un int. La iniciamos en 1 pues el número 1 siempre se va a multiplicar. Luego el ciclo va desde 2 hasta el número y vamos multiplicando la variable result por la variable iteradora del ciclo y asignándosela a ella misma utilizando el operador *=

Nota: Quiero aclarar que el 0! es igual a 1. Y este código funcionara perfecto, pues si el número es 0, no se ejecutará nunca la instrucción dentro del ciclo pues nunca se cumplirá la condición de iteración y no se entrará al ciclo. Por lo tanto se devolverá result = 1

Forma recursiva

Código
  1. static long Factorial(long n)
  2.        {
  3.            if (n == 0) return 1;
  4.            return n * Factorial(n - 1);
  5.        }

Si entendemos recursividad esto sale straight-forward. El caso base es para n=0 devolvemos 1 sino, devolvemos n * la llamada recursiva del factorial de n-1. Si planteamos esto explícitamente quedaría algo así como:

n * Factorial(n - 1) * Factorial (n - 2) * ... * Factorial (n - i) * ... * Factorial(0)



15 - Saber si un año es bisiesto

Un año es bisiesto si es divisible entre 4 y no es divisible por 100, también es bisiesto si el año es divisible por 400.

Llevar esto a un código es algo muy sencillo. Aquí les dejo:

Código
  1. public static bool EsBisiesto(int anno)
  2.        {
  3.            return (anno % 4 == 0 && anno % 100 != 0 || anno % 400 == 0);
  4.        }

Realizamos la comprobación si es divisible entre 4 (deja resto 0 con 4) y no es divisible por 100 (no deja resto 0 con 100), también comprobamos si es divisible por 400 (deja resto 0 con 400). Si se cumple alguna de estas dos condiciones entonces se devuelve true, si no se cumple ninguna de las dos, se devuelve false

16 - Hallar la potencia de un número

La potencia es el resultado de multiplicar un número por sí mismo una cantidad de veces n. Un número elevado a la 0 es igual a 1.

Forma iterativa

Código
  1. static long PotenciaIterativo(int _base, int _potencia)
  2.        {
  3.            long result = 1;
  4.            for (int i = 1; i <= _potencia; i++)
  5.                result *= _base;
  6.            return result;
  7.        }

Esto se plantea de una forma sencilla. Recibimos como parámetro la base y la potencia. (Utilizo el underscore (_base) para que no entre en conflicto pues en C# existe la palabra reservada base). El objetivo es multiplicar la base un cantidad de veces igual a la variable _potencia. Creamos una variable, que será que se va a devolver (long result), con el valor 1. Luego vamos a multiplicar la variable por _base dentro del ciclo que va a hacer tantas iteraciones como el valor de la variable _potencia. Cabe destacar que si la variable _potencia es 0. Nunca se ejecutará el ciclo pues no se cumplirá la condición de iteración por lo tanto no entrará al ciclo y se devolverá la variable result con el valor 1.

Forma recursiva

Código
  1. static long Potencia(int _base, int _potencia)
  2.        {
  3.            if (_potencia == 0) return 1;
  4.            if (_potencia == 1) return _base;
  5.            return _base * Potencia(_base, _potencia - 1);
  6.        }

Básicamente es lo mismo, planteamos los casos bases. Si la potencia es 0 entonces se devuelve 1; Si la potencia es 1 se devuelve la base. En caso contrario se multiplica la base por el llamado recursivo. El llamado recursivo va a ir convergiendo a los casos bases, pues se le resta a la potencia 1.

Nota: También puedes hacer uso del método .Pow() dentro de la clase Math. Algo así como:

Código
  1. double potencia = Math.Pow(2, 4) //Esto eleva 2 a la 4



17 - Búsqueda binaria (colección ordenada)

Este código es algo más interesante. Este algoritmo reduce significativamente el número de comparaciones que se realiza para buscar en un array (previamente ordenado) un elemento que cumpla determinada propiedad. En este código voy a tratar con un array de int y voy a utilizar un método portal.

Código
  1. /// <summary>
  2.        /// Método portal
  3.        /// </summary>
  4.       public static bool BusquedaBinaria(int[] numerosOrdenado, int aBuscar)
  5.        {
  6.            return BusquedaBinaria(numerosOrdenado, aBuscar, 0, numerosOrdenado.Length - 1);
  7.        }
  8.  
  9.        private static bool BusquedaBinaria(int[] numerosOrdenado, int aBuscar, int inf, int sup)
  10.        {
  11.            if (inf > sup)
  12.                return false;
  13.            int medio = (inf + sup) / 2;
  14.            if(aBuscar==numerosOrdenado[medio])
  15.                return true;
  16.            if (aBuscar > numerosOrdenado[medio])
  17.                return BusquedaBinaria(numerosOrdenado, aBuscar, medio + 1, sup);
  18.            return BusquedaBinaria(numerosOrdenado, aBuscar, inf, medio - 1);
  19.        }
  20.  

En este algoritmo se compara el elemento a buscar con el elemento del medio del array, si el valor a buscar es menor que este, se repite el procedimiento en la parte del array que va desde el principio hasta el elemento del medio, sino se toma la parte del array que va desde el elemento central hasta el final del array. De esta manera se realiza sucesivamente con los pedazos hasta que cada vez van a ser más pequeños, hasta que se obtenga un intervalo que no se pueda dividir. Si el elemento no se encuentra dentro de este último entonces se deduce que el elemento buscado no se encuentra en el array.

Desde el método portal llamamos al método recursivo que tiene otros parámetros. Los parámetros son el array de los números (ordenado), el elemento a buscar, la posición inferior y la posición superior.
Lo primero que se hace es comprobar los extremos del intervalo. Si el inferior es mayor que el superior significa que el array se acabó y no se encontró el elemento, por lo tanto se retorna false. Sino se calcula la posición del medio. Luego se revisa si este elemento del medio es el que se está buscando, si lo es, se devuelve true. Entonces si esto no se cumple, vamos a revisar las mitades del array. Si el elemento a buscar es mayor que el elemento del medio se retorna el llamado recursivo pasándole como índice inferior al medio + 1 y manteniendo el superior (mitad de la derecha). En caso contrario se retorna el llamado recursivo pasándole como índice superior al medio - 1 y manteniendo el inferior.

Espero que este algoritmo se haya entendido.

Nota: Si quieres buscar en un array también puedes hacer uso de el método Contains() dentro de la clase array que devuelve un bool dependiendo si el elemento esta dentro del array. Seria algo así:

Código
  1. int valor = 2;
  2. bool estaElElemento = nombreArray.Contains(valor);

Mod: Agregado el método IndexOf(), utilizando el algoritmo de búsqueda binaria que a la vez devuelve el índice en el cual está el elemento buscado o devolviendo -1 si no se encuentra.

Código
  1. /// <summary>
  2.        /// Método portal
  3.        /// </summary>
  4.        static int IndexOf(int[] orderedArray, int toSearch)
  5.        {
  6.            return IndexOf(orderedArray, toSearch, 0, orderedArray.Length - 1);
  7.        }
  8.  
  9.        static int IndexOf(int[] orderedArray, int toSearch, int min, int max)
  10.        {
  11.            if (min > max)
  12.                return -1;
  13.            int medio = (min + max) / 2;
  14.            if (toSearch > orderedArray[medio])
  15.                return IndexOf(orderedArray, toSearch, medio + 1, max);
  16.            if (toSearch < orderedArray[medio])
  17.                return IndexOf(orderedArray, toSearch, min, medio - 1);
  18.            return medio;
  19.        }

Tenemos un método portal al igual que en el otro método. Lo único que cambia es lo que se devuelve, que en este caso se devuelve el valor del índice. En este método comprobamos si los límites del "sub-array" en el cual estamos comprobando se sobrepasan entre ellos. Luego calculamos la posición en el medio. Y comprobamos si el valor que se busca es mayor/menor que el elemento en la posición del medio calculada. En caso de que se cumpla una u otra, se llama recursivo, ajustando los límites del sub-array. Si no es mayor o menor, entonces se devuelve el valor "medio" o sea, es el elemento que está en el medio, por lo tanto se devuelve la posición.




Luego seguiré con más. Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 22 Mayo 2015, 17:51 pm
18 - Hallar la menor cantidad de inserciones necesarias para que una cadena sea palíndromo

Ya expliqué anteriormente que es un palíndromo.

Citar
Palíndromo es una palabra, número o frase que se lee igual hacia adelante que hacia atrás.

Este código es extremadamente sencillo. Supongamos que la entrada es un string y que no tiene espacios.

Este código lo hice de manera recursiva, tenemos también un método portal:

Código
  1. /// <summary>
  2.        /// Método portal
  3.        /// </summary>
  4.        static int PalindromInsertions(string line)
  5.        {
  6.            if (line.Length == null)
  7.                throw new NullReferenceException("La cadena no puede ser null");
  8.            if (line.Length == 1)
  9.                return 0;
  10.            return PalindromInsertions(line, 0, line.Length - 1);
  11.        }
  12.  
  13.        static int PalindromInsertions(string line, int inf, int sup)
  14.        {
  15.            if (inf > sup)
  16.                return 0;
  17.            if (line[inf] == line[sup])
  18.                return PalindromInsertions(line, inf + 1, sup - 1);
  19.            else
  20.                return Math.Min(1 + PalindromInsertions(line, inf + 1, sup),
  21.                    1 + PalindromInsertions(line, inf, sup - 1));
  22.        }

El método portal lo utilicé para validar la entrada de que no sea null y si tiene longitud 1 entonces no es necesario hacer inserciones para que sea palíndromo, pues ya es palíndromo.

Luego se llama al método PalindromInsertions() pasándole la cadena, el límite inferior y el superior (en principio, 0 y line.Length - 1 respectivamente).

El objetivo del método y de la recursividad en este caso es ver si la los caracteres de la cadena evaluada en los límites son iguales, si lo son, se llama recursivo aumentado y disminuyendo los limites inferior y superior respectivamente, en caso de que no lo sea, se va a devolver el Mínimo de hacer una inserción por la izquierda y haber una inserción por la derecha. Para esto utilizamos el método Min() de la clase Math. Este método devuelve el mínimo entre dos enteros (tiene otras sobrecargas para distintos tipos de datos). Representamos hacer 1 inserción sumándole 1 al llamado recursivo del método manteniendo un límite y aumentando o disminuyendo el límite inferior o superior respectivamente.

Al principio del método, comprobamos si los límites se superpusieron (ya revisamos la cadena completa). En este caso base, devolvemos 0. (No es necesario hacer inserciones)

Espero se entienda.



19 - Rotar un array n veces a la derecha

En este caso utilizaré nuevamente un array de int para simplificar. El método recibe un array de int y un int que representa la cantidad de posiciones a rotar.

Código
  1. public static void RotarDerecha(int[] a, int veces)
  2.        {
  3.            if (veces == a.Length || veces % a.Length == 0) return;
  4.            int aMover = a[0];
  5.            int posicion = 0;
  6.            int contador = 0;
  7.            while (contador < a.Length)
  8.            {
  9.                int proxPosicion = (posicion + veces) % a.Length; //Calculamos nueva posición
  10.                int proxValor = a[proxPosicion]; //Guardamos el elemento que esta en esa nueva posición
  11.                a[proxPosicion] = aMover; //Ponemos el elemento a mover en la nueva posición
  12.                aMover = proxValor; //Asignamos a la variable del elemento a mover el valor que estaba en la nueva posición
  13.                posicion = proxPosicion; //Guardamos la posición del nuevo elemento
  14.                contador++; //Hicimos un movimiento
  15.            }
  16.        }

Lo primero que tenemos que darnos cuenta es que rotar a la derecha significa mover cada elemento a la derecha n veces. En caso de que las rotaciones sobrepasen la longitud del array se deberá comenzar desde el inicio del array. ¿Cómo resolvemos esto? Bueno, cada posición nueva viene dada por el resto que deja la posición más la cantidad de rotaciones con la longitud del array. Esto nos garantiza que siempre nos va a quedar en una posición (y en la que es) dentro del array.

Lo primero que hacemos es comprobar si la cantidad de veces es igual a la longitud del array o si la cantidad de veces deja resto 0 con la longitud del array. En este caso no se hace ninguna rotación.

Luego, debemos tener una variable, para saber cuántas rotaciones estamos haciendo y saber cuándo vamos a parar. También una variable para llevar el elemento que estamos moviendo (en principio, el primero) y una variable para llevar su posición.

Ahora, utilizaremos el ciclo while para controlar la cantidad de movimientos que se han hecho. Vamos a ejecutar el ciclo while mientras la variable contador sea menor que la longitud del array.

Dentro del ciclo lo que haremos será calcular la posición nueva de ese elemento. Guardar en una variable el elemento en esa nueva posición y hacer un Swap entre el elemento que llevamos para mover y ese nuevo (que será el nuevo que queremos mover). También debemos guardar en la variable posicion la posición de ese nuevo elemento. Por último, incrementar la variable contador pues ya se hizo un movimiento.



He tenido poco tiempo, por eso solo dejo estos dos.
Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 26 Mayo 2015, 18:48 pm
20 - Saber si dos cadenas son anagramas

El objetivo del siguiente código es comprobar si dos cadenas son anagramas. Lo primero ¿Qué es un anagrama? Es una palabra o frase que resulta de la transposición de letras de la otra palabra o frase. En el siguiente código vamos solo a tener en cuenta "palabras".

Código
  1. public static bool SonAnagramas(string a, string b)
  2.        {
  3.            if (a.Length != b.Length) return false;
  4.            for (int i = 0; i < a.Length; i++)
  5.            {
  6.                bool igualdad = false;
  7.                for (int j = 0; j < b.Length; j++)
  8.                {
  9.                    if (a[i] == b[j])
  10.                    {
  11.                        b = b.Remove(j,1);
  12.                        igualdad = true;
  13.                        break;
  14.                    }
  15.                }
  16.                if (!igualdad) return false;
  17.            }
  18.            return b.Length == 0;
  19.        }

El objetivo del código es ir verificando los caracteres de una cadena con la otra. Primero verificamos si las longitudes son iguales. En caso de que no lo sean, se devuelve false pues una tiene caracteres que no tiene la otra. El objetivo es iterar por una cadena y fijar un carácter, y entonces iterar sobre la segunda y comprobar si se encuentra uno igual. En caso de que se encuentre uno igual se modifica la segunda cadena y se elimina el carácter. El objetivo de esto es que si al final de iterar la longitud de esta cadena es mayor que 0 entonces no es un anagrama. Si lo es, en caso contrario. También hay que tener en cuenta que si por un carácter fijado en la primera cadena no se elimina ningún carácter en la segunda cadena, entonces ya podemos decir que no es un anagrama.

Para eliminar un carácter de la cadena, utilizamos el método Remove() que pertenece a la clase string, el método recibe una posición a partir de la cual se va a eliminar caracteres y la cantidad de caracteres a eliminar. El método devuelve un nuevo string, por lo tanto hay que asignárselo a la variable pues este no modifica la instancia sobre la cual se llame.



21 - Contar ficheros de una extensión dada dentro de un directorio incluyendo subcarpetas (recursivamente)

El siguiente código lo que hará es contar la cantidad de ficheros de una extensión dada dentro de un directorio y sus subdirectorios.

Primero que todo, utilizaremos un método portal al cual le pasaremos como parámetro la ruta del directorio, y la extensión (Ej.: ".txt", ".jpg", etc.) En este método utilizaremos dos cosas que voy a explicar a continuación:

Primero, la clase DirectoryInfo dentro del namespace System.IO (recuerden añadirlo). Esta clase tiene métodos para copiar, mover, renombrar, eliminar y enumerar directorios y subdirectorios. Aquí creamos una instancia de esta clase pasándole como parámetro al constructor de la misma la ruta del directorio.

También utilizamos un delegate Func que encapsula un método y devuelve un valor, en este caso utilizamos:

Código
  1. Func<FileInfo, bool>

Que recibe un parámetro FileInfo como parámetro de entrada y devuelve bool. Este delegate lo vamos a utilizar como condición para comprobar si el fichero tiene una extensión dada. El delegate devuelve true si la extensión del archivo referido por la instancia de FileInfo es igual a la extensión definida por el usuario.

Nota: Asumo que tienen algún conocimiento sobre delegate y encapsulamiento de métodos. Sino, pueden investigar sobre esto.

Ahora, FileInfo es otra clase dentro del namespace System.IO. Tiene métodos para copiar, mover, renombrar, eliminar y abrir ficheros. La misma referencia a un fichero dentro de un directorio.

Código
  1. /// <summary>
  2.        /// Método portal
  3.        /// </summary>
  4.        public static int ContarFicheros(string path, string extension)
  5.        {
  6.            DirectoryInfo dir = new DirectoryInfo("Prueba"); //Creamos el DirectoryInfo pasándole la ruta del directorio
  7.  
  8.            Func<FileInfo, bool> extensionCondition = delegate(FileInfo fileInfo) { return fileInfo.Extension == extension; }; //Creamos el delegate
  9.  
  10.            return ContarFicheros(dir, extension, extensionCondition);
  11.        }

Ahora, el método que se llama para contar los ficheros:

Código
  1. public static int ContarFicheros(DirectoryInfo directory, string extension, Func<FileInfo, bool> extensionCondition)
  2.        {
  3.            int total = 0;
  4.            if (directory.GetDirectories().Count() == 0)
  5.            {
  6.                total = directory.GetFiles().Count(extensionCondition);
  7.                return total;
  8.            }
  9.            else
  10.            {
  11.                total += directory.GetFiles().Count(extensionCondition);
  12.                foreach (var directorio in directory.GetDirectories())
  13.                    total += ContarFicheros(directorio, extension, extensionCondition);
  14.                return total;
  15.            }
  16.        }

Recibe como parámetro el DirectoryInfo, la extensión y el delegate. El método tiene un enfoque recursivo, utilizando como caso de parada la condición de que un directorio no tenga más subdirectorios. En este caso solamente cuenta los ficheros en este directorio.
En caso contrario, cuenta los ficheros y llama recursivo por cada subdirectorio.

Para obtener los directorios utilizamos el método GetDirectories() dentro de la clase DirectoryInfo el cual devuelve un DirectoryInfo[] con todos los subdirectorios dentro del directorio actual. Y para contar los ficheros utilizamos el método de extensión Count() del método GetFiles() contenido en la clase DirectoryInfo. El método GetFiles() devuelve un FileInfo[] que representa todos los ficheros en el directorio actual. Y con el método Count() los contamos, utilizando una sobrecarga donde se le pasa un delegate

Código
  1. Func<FileInfo, bool>

El cual representa la condición que tienen que cumplir los ficheros para contarlos.

Nota: En vez de definir el delegate con un método anónimo como lo hice en el código, se puede también definir utilizando expresiones Lambda (aunque esto ya es otro tema)

Código
  1. Func<FileInfo, bool> extensionCondition = s => s.Extension == extension; ;

Espero que este código sea útil y se entienda.



22 - Saber si una cadena tiene paréntesis balanceados

El objetivo del siguiente código es saber si una cadena esta balanceada en cuanto a los paréntesis. Dígase paréntesis a (), [], {}. El objetivo es saber si están correctamente puestos.

Para esto utilizaremos una Pila de caracteres. (Stack<char>). Una pila es una estructura de datos en la que el modo en el que se accede a sus elementos es de tipo LIFO (Last In First Out - último en entrar, primero en salir). Utilizaremos el método Push() que se utiliza para empilar (meter) un elemento en la pila, el método Pop() para desempilar (quitar) el elemento en el tope de la pila y devolverlo, también el método Peek() que devuelve el último elemento en la pila, pero no lo desempila. Por último la propiedad Count que nos da que cantidad de elementos hay en la pila.

El objetivo es ir recorriendo la cadena y a medida que encontramos un paréntesis abierto meterlo en la  pila, en caso de que no sea un paréntesis abierto comprobar si es cerrado, y ver si la pila tiene elementos y en el tope de la pila está el paréntesis correspondiente abierto entonces sacarlo de la pila y seguir recorriendo la cadena. En caso de que el elemento no sea un paréntesis entonces ignorar y seguir recorriendo. Al final, el método devuelve true si la pila está vacía, false en caso contrario pues hubo algún paréntesis que no tenía su correspondiente paréntesis cerrado

Código
  1. static bool ParentesisBalanceados(string cadena)
  2.        {
  3.            Stack<char> pila = new Stack<char>();
  4.            for (int i = 0; i < cadena.Length; i++)
  5.            {
  6.                if (cadena[i] == '(')
  7.                    pila.Push('(');
  8.                else if (cadena[i] == '{')
  9.                    pila.Push('{');
  10.                else if (cadena[i] == '[')
  11.                    pila.Push('[');
  12.                else
  13.                {
  14.                    if (cadena[i] == ')')
  15.                        if (pila.Count != 0 && pila.Peek() == '(')
  16.                            pila.Pop();
  17.                        else return false;
  18.                    else if (cadena[i] == '}')
  19.                        if (pila.Count != 0 && pila.Peek() == '{')
  20.                            pila.Pop();
  21.                        else return false;
  22.                    else if (cadena[i] == ']')
  23.                        if (pila.Count != 0 && pila.Peek() == '[')
  24.                            pila.Pop();
  25.                        else return false;
  26.                }
  27.            }
  28.            return pila.Count == 0;
  29.        }

Ejemplo de uso:

Código
  1. Console.WriteLine(ParentesisBalanceados("ab(xa[y*c](..[.]+++){{}}cc)*”")); //Devuelve true
  2. Console.WriteLine(ParentesisBalanceados("ab(xa[y*c)bb]**")); //Devuelve false
  3. Console.WriteLine(ParentesisBalanceados("a(b))(")); //Devuelve false

Nota: También se podría haber hecho utilizando un Swtich

Código
  1. static bool ParentesisBalanceadosSwitch(string cadena)
  2.        {
  3.            Stack<char> pila = new Stack<char>();
  4.            for (int i = 0; i < cadena.Length; i++)
  5.            {
  6.                switch (cadena[i])
  7.                {
  8.                    case '(':
  9.                        pila.Push('(');
  10.                        break;
  11.                    case '{':
  12.                        pila.Push('{');
  13.                        break;
  14.                     case '[':
  15.                        pila.Push('[');
  16.                        break;
  17.  
  18.                     case ')':
  19.                        if (pila.Count != 0 && pila.Peek() == '(')
  20.                            pila.Pop();
  21.                        else return false;
  22.                        break;
  23.                    case '}':
  24.                         if (pila.Count != 0 && pila.Peek() == '{')
  25.                            pila.Pop();
  26.                        else return false;
  27.                        break;
  28.                     case ']':
  29.                        if (pila.Count != 0 && pila.Peek() == '[')
  30.                            pila.Pop();
  31.                        break;
  32.                    default:
  33.                        break;
  34.                }
  35.            }
  36.            return pila.Count == 0;
  37.        }



En cuanto pueda sigo haciendo!
Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 13 Junio 2015, 05:18 am
Voy a adentrarme ahora en lo que se conoce por combinatoria, exponiendo ejemplos sencillos y clásicos de esta rama de las matemáticas discretas.

23 - Variaciones con repetición

Lo primero que voy a hacer es poner un ejemplo:

Si quisiéramos hallar todas las variaciones con repetición en secuencias de 3 de este array:

Código
  1. string[] valores = {"a", "b", "c", "d", "e"};

Obtendríamos el siguiente resultado:

Citar
a a a
a a b
a a c
a a d
a a e
a b a
a b b
a b c
a b d
a b e
...
...
e e a
e e b
e e c
e e d
e e e

El orden de la complejidad temporal sería un equivalente a NM, siendo N la cantidad de elementos y M el tamaño de la secuencia. Esto es deducible de la posibilidad de por cada espacio de la secuencia (M), en este caso 3, se pueden poner (N), en este caso 5 posibles elementos. Por lo tanto sería 5 * 5 * 5 = 53.

Veamos el código:

Código
  1. //Variaciones con repetición de N elementos en M
  2.        /// <summary>
  3.        ///  Método portal
  4.        /// </summary>
  5.        static void VariacionesConRepeticion(string[] original, int size)
  6.        {
  7.            VariacionesConRepeticion(original, new string[size], 0);
  8.        }
  9.  
  10.        static void VariacionesConRepeticion(string[] original, string[] variacion, int pos)
  11.        {
  12.            if (pos == variacion.Length)
  13.            {
  14.                Print(variacion); //Llama al método Print(), el cual lo que hace es imprimir un array en pantalla.
  15.                Console.WriteLine();
  16.            }
  17.            else
  18.            {
  19.                for (int i = 0; i < original.Length; i++)
  20.                {
  21.                    variacion[pos] = original[i];
  22.                    VariacionesConRepeticion(original, variacion, pos + 1);
  23.                }
  24.            }
  25.        }

Primero que todo, veamos que utilizamos un método portal, el cual recibe el array (en este caso es de string para facilidad, pero se puede hacer de cualquier otro tipo incluso lo pueden implementar para que el método sea genérico). Lo otro que recibe es el tamaño de la variación (M) (M<=N) (N -> Cantidad de elementos).

En el método portal llamamos al método recursivo pasándole como parámetro el array original, un array "variacion" vacío con longitud M y una variable "pos" para iterar sobre este array "variacion"

Ahora, veamos, como todo método recursivo, tenemos una condición de parada que se cumplira cuando la variable "pos" (la cual itera sobre "variacion") sea igual al Length del array "variacion" (o lo que es lo mismo, que ya se ha haya rellenado ese array). En este caso, ya tenemos una variación completa, por lo tanto lo que haremos será Imprimirla en pantalla (lo que se haga puede variar de acuerdo al problema que estemos tratando de resolver.

Para imprimirlo en pantalla, utilizamos un método:

Código
  1. static void Print<T>(T[] array)
  2.        {
  3.            for (int i = 0; i < array.Length; i++)
  4.            {
  5.                if (i == array.Length - 1)
  6.                    Console.Write(array[i]); //Imprimimos el elemento del array es el último elemento
  7.                else
  8.                    Console.Write(array[i] + " "); //Imprimimos el elemento del array más un espacio si no es el último elemento
  9.            }
  10.        }

Ahora, en caso de que no se cumpla la condición de parada significa que todavía estamos "armando" la variación.

Para armar tenemos un ciclo for, que recorre el array "original", y lo que hacemos es asignarle al array "variacion" en la posición "pos" (que itera sobre "variacion") el valor del array original en la posición i (itera sobre el array "original" utilizando el ciclo for.

Luego lo que hacemos es llamar recursivo pasándole al método el array "original", "variacion" y la variable "pos" incrementada en 1 (pos + 1), ya que pusimos un elemento en el array y ahora vamos a poner otro en la siguiente posición.

Voy a poner un ejemplo:

Imaginen el array "original" como había dicho:

Código
  1. string[] valores = {"a", "b", "c", "d", "e"};

Y ahora, el array "variacion" estaría así en un principio

[ ] | [ ] | [ ]

O sea, vacío

[ a ] | [ ] | [ ]    - pos = 0

[ a ] | [ a ] | [ ]    - pos = 1

[ a ] | [ a ] | [ a ]    - pos = 2

Luego se retorna la llamada y entonces  pos=3, aquí se imprime la variación pues ya está completa. Luego el ciclo for camina y entonces la próxima asignación que se hace es:

[ a ] | [ a ] | [ b ]  - pos = 2

Espero esto se haya entendido.
Si tienen alguna duda pregunten, pues esto a veces es enredado para verlo si no se ha visto alguna vez y/o no entienden bien la recursividad. Sobre todo, también es algo difícil explicarlo así en plan texto.



Seguimos con combinatoria (espero hayan entendido el anterior, pues no explicaré tan a fondo debido a la similitud

24 - Variaciones sin repetición

Este algoritmo es algo parecido, simplemente que no vamos a repetir elementos en cada variación.

Lo primero que voy a hacer es poner un ejemplo:

Utilicemos el mismo array que teníamos en el código anterior:

Código
  1. string[] valores = {"a", "b", "c", "d", "e"};

Si quisiéramos hallar todas las variaciones sin repetición en secuencias de 3 de este array obtendríamos:

Citar
a a a
a b c
a b d
a b e
a c b
a c d
a c e
a d c
a d b
a d e
...
...
e a c
e a d
e a b

El orden de la complejidad temporal sería un equivalente a N!/(N-M)!, siendo N la cantidad de elementos y M el tamaño de la secuencia. Esto es deducible de la posibilidad de por cada espacio de la secuencia (M), en este caso 3, se pueden poner (N-1), este caso 4 elementos posibles en la próxima posición, y después en la próxima (N-2), y asi sucesivamente, por lo que quedaría algo como: 5*4*3 = 5*(5-1)*(5-2) = 5!/(5-3)! = N!/(N-M)!

Primero voy a explicar un método que vamos a utilizar aquí. Utilizaremos un método para hacer Swap entre dos elementos. Este método ya lo había explicado anteriormente en otro código, pero este tiene dos peculiaridades diferentes, primero que lo hice genérico para mostrar esto, y segundo que hace el intercambio pero de las referencias.

Este es el código:

Código
  1. static void Swap<T>(ref T a, ref T b)
  2.        {
  3.            T temp = a;
  4.            a = b;
  5.            b = temp;
  6.        }

Primero explico la palabra clave (keyword) ref. Esta palabra clave a un argumento causa que el argumento sea pasado al método por referencia y no por valor. El efecto de pasar un argumento por referencia implica que cualquier cambio que se realice al parámetro dentro del método es reflejado en el argumento en el método desde cual fue llamado.

Para llamar a un método pasándole un argumento por referencia se haría algo así:

Código
  1. static void Main(string[] args)
  2. {
  3.     int numberOne = 1;
  4.     Inc(ref numberOne);
  5.     Console.WriteLine(numberOne); //El valor que se imprimirá será 2
  6. }
  7.  
  8. public static void Inc(ref int number)
  9. {
  10.     number++;
  11. }
  12.  

Ahora, que el método sea genérico, implica que se utilizará un placeholder, dentro de todo el método para las operaciones con este tipo de datos dentro del método. Se podrá utilizar el método con cualquier tipo de dato siempre que no cause error en tiempo de ejecución de acuerdo a la función que se realice con él. Genéricas pueden ser clases, estructuras, interfaces y métodos.

Esto es una forma EXTREMADAMENTE lazy de explicarla, recomiendo ir a la MSDN para leer sobre esto.

Una vez visto, solo resta decir sobre este método que funciona intercambiando las referencias de las variables.

Ahora veamos el método como tal:

Código
  1. //Variaciones sin repetición de N elementos en M
  2.        /// <summary>
  3.        ///  Método portal
  4.        /// </summary>
  5.        static void VariacionesSinRepeticion(string[] original, int size)
  6.        {
  7.            VariacionesSinRepeticion(original, size, 0);
  8.        }
  9.  
  10.        static void VariacionesSinRepeticion(string[] original, int size, int pos)
  11.        {
  12.            if (pos == size)
  13.            {
  14.                Print(original, size);
  15.                Console.WriteLine();
  16.            }
  17.            else
  18.            {
  19.  
  20.                for (int i = pos; i < original.Length; i++)
  21.                {
  22.                    Swap(ref original[pos], ref original[i]);
  23.                    VariacionesSinRepeticion(original, size, pos+1);
  24.                    Swap(ref original[pos], ref original[i]);
  25.                }
  26.  
  27.            }
  28.  
  29.        }

Primero que todo démonos cuenta que es bastante parecido al otro método (VariacionesConRepeticion()). Tenemos el método portal y el método recursivo, pero vemos que solo trabajamos sobre un array, pues por eso utilizamos un método Swap para hacer intercambio de valores (que después lo volvemos a intercambiar para ponerlos en la posición original. Tenemos la condición de parada que se cumple cuando "pos" sea igual al tamaño (M) de la variación que viene dado por la variable "size"

Ahora, hay un pequeño cambio en el ciclo for, y es que el ciclo no empieza con la variable i en 0, sino que empieza con el valor de "pos". Y se hace un Swap antes del llamado recursivo y uno después, el cual pone los valores en la posición anterior a cambiarlo. Igual que en el método anterior se llama recursivo incrementando la variable "pos"

Veamos un ejemplo utilizando el siguiente array y calculando las variaciones con tamaño 3:

Código
  1. string[] letras = { "a", "b", "c" };

Como trabajamos sobre le array "original", el mismo estaría así al principio:

[ a ] | [ b ] | [ c ] - i= 0 y pos = 0
[ a ] | [ b ] | [ c ] - i= 0 y pos = 1
[ a ] | [ b ] | [ c ] - i= 0 y pos = 2

[ a ] | [ b ] | [ c ] - i= 0 y pos = 3 (Se cumplió la condición de parada y por lo tanto se tiene una variación, se imprime)

Luego, cuando se retorne el llamado tenemos a pos = 2 e i = 2 y cuando se incremente en el ciclo no se va a cumplir la condición del ciclo, va a terminar y va retornar la llamada, donde tendremos a pos = 1 e i = 1. Aquí ahora se va a incrementar i, pues va a cumplirse la condición del ciclo y tendremos a pos = 1 e i = 2, y por lo tanto se va a hacer el Swap y va a quedar así:

[ a ] | [ c ] | [ b ] - i= 2 y pos = 1

Y después de se cumplirá la condición de parada y tendremos la variación lista. Así es prácticamente el funcionamiento del algoritmo, les recomiendo que le hagan un Debug Step-By-Step para entenderlo mejor.

Nota: Como ven en ningón momento tenemos una variación en un array nuevo, si nuestro problema requiere devolver esta variación debemos hacer una copia del array original a uno nuevo copiando "size" elementos.

Código
  1. string[] destino = new string[size];
  2. Array.Copy(original, destino, size);



25 - Combinaciones

Primero que todo, veamos un ejemplo:

Si quisiéramos hallar todas las combinaciones posibles de secuencias de 5 de este array:

Código
  1. string[] valores = {"a", "b", "c", "d", "e"};

Obtendríamos:

Citar
a b c d e

En caso de que quisiéramos hallarlas en secuencias de 3, obtendríamos:

Citar
a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e

Consideremos cada secuencia un subconjunto del conjunto original (el array valores). Cada uno de estos subconjuntos son diferentes dos a dos.

Llamemos "combinaciones" a todas las formas posibles de tomar un subconjunto de elementos de un conjunto con un tamaño predefinido donde ninguno de estas formas son iguales y cada subconjunto no repite elemento.

Nota: Tengan en cuenta que vamos a utilizar el método Print() que declaramos y utilizamos arriba (Variaciones con repetición)

Como en casos anteriores vamos a tener un método portal, al cual le pasaremos como parámetro el array con los elementos. Ambos métodos los declaré genéricos. Repito, para entender mejor cómo funciona la genericidad de parámetros les recomiendo ir a la MSDN

Código
  1.        /// <summary>
  2.        /// Método portal
  3.        /// </summary>
  4.        static void Combinaciones<T>(T[] letras, int m)
  5.        {
  6.            Combinaciones(letras, new T[m], 0, 0);
  7.        }

Esté método recibe como parámetro el array con los elementos y un int que representa el tamaño de las combinaciones a buscar. Este int debe ser menor o igual que la cantidad de elementos del array. Por lo tanto les recomiendo que hagan una validación a este parámetro en el método portal.

El método recursivo recibe 4 parámetros, dos array y dos int. El primer array es el original, el segundo es el array para ir creando cada combinación, por lo tanto lo que se le pasa es un array nuevo con tamaño igual al int que recibió el método portal (new T[m]]. Ahora, los int que recibe son para iterar sobre los array original y combinacion respectivamente.

Luego, veamos el código del método recursivo:

Código
  1. static void Combinaciones<T>(T[] original, T[] combinacion,
  2.            int posOriginal, int posCombinacion )
  3.        {
  4.            if (posCombinacion == combinacion.Length)
  5.            {
  6.                Print(combinacion); //Imprimimos la combinación
  7.                Console.WriteLine(); //Hacemos un salto de linea en la consola
  8.            }
  9.            else if (posOriginal == original.Length)
  10.                return;
  11.            else
  12.            {
  13.                combinacion[posCombinacion] = original[posOriginal];
  14.                Combinaciones(original, combinacion, posOriginal + 1, posCombinacion + 1);
  15.                Combinaciones(original, combinacion, posOriginal + 1, posCombinacion);
  16.            }
  17.        }

En este método lo primero que hacemos es establecer las condiciones de parada (para la recursividad).

La primera es si se completó una combinación, por lo tanto vemos que la variable que itera sobre el array combinacion llego a la longitud del array. En tal caso, imprimimos la combinación.

La segunda condición es si ya se utilizaron todos los elementos originales en las combinaciones que antecedían con un elemento fijo.

Veamos un ejemplo de cómo funcionaría la otra parte del código:

Supongamos que el array original es:

Código
  1. string[] original = {"a", "b", "c", "d", "e"};

Antes de que se ejecute la línea:

Código
  1. combinacion[posCombinacion] = original[posOriginal];

Tendremos el array combinacion:

[ null ] | [ null ] | [ null ]

Luego:

[ a ] | [ null ] | [ null ]

Luego se ejecutará el llamado recursivo:

Código
  1. Combinaciones(original, combinacion, posOriginal + 1, posCombinacion + 1);

Y despues tendremos esto:

[ a ] | [ b ] | [ null ]

[ a ] | [ b ] | [ c ]

Y al llamar recursivo se cumplirá la primera condición de parada pues ya tenemos la primera combinación. Luego al salir de la recursividad, se llama recursivo de nuevo:

Código
  1. Combinaciones(original, combinacion, posOriginal + 1, posCombinacion);

Pero esta vez solo incrementamos la variable que itera sobre el array original. Por lo tanto esa variable estará apuntando al elemento (d) del array original

Y tras hacer:

Código
  1. combinacion[posCombinacion] = original[posOriginal];

Tendremos:

[ a ] | [ b ] | [ d ]

Luego:

[ a ] | [ b ] | [ e ]

Y Luego se cumplirá la segunda condición de parada, por lo tanto saldremos de la recursividad hasta que la variable que itere por el array combinacion este sobre el elemento 1.

Todo ira funcionando de esa manera.

Espero que se entienda, es un poco enredado, pero una vez que le hagas un step-by-step debug lo entenderán.



Luego continúo con los códigos.

Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: Eleкtro en 14 Julio 2015, 06:45 am
Este hilo ya ha llegado a las 900 visitas, es un poco frustrante que nadie se haya molestado en opinar sobre los códigos o el esfuerzo en desarrollarlos y publicarlos para compartirlos con ustedes... no sean tan descortés.

Genial aporte y gracias por tu colaboración desinteresada.

Hace falta más gente como tu en el foro ...en toda la WWW en general.

Saludos


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 18 Julio 2015, 01:20 am
26 - Evaluar una expresión en Notación Posfija (Notación Polaca Inversa)

Primero que todo, ¿Qué es la Notación Posfija (Notación Polaca Inversa)? Pues, es un método algebraico alternativo de introducción de datos, en está notación primero están los operandos y después viene el operador que se va a utilizar para realizar el cálculo sobre los operandos. En esta notación no se necesitan utilizar paréntesis para indicar el orden de las operaciones.

Veamos un ejemplo de notación posfija:

Código:
'4' '2' '10' '+' '*' '5' '-' = '43'

Para implementar esto, primero veamos el algoritmo que se utiliza:

Citar
-Si hay elementos en la bandeja de entrada
  -Leer el primer elemento de la bandeja de entrada.
    -Si el elemento es un operando.
      -Poner el operando en la pila.
    -Si no, el elemento es una función (los operadores, como "+", no son más que funciones que toman dos argumentos).
      -Se sabe que la función x toma n argumentos.
      -Si hay menos de n argumentos en la pila
        -(Error) El usuario no ha introducido suficientes argumentos en la expresión.
      -Si no, tomar los últimos n operandos de la pila.
      -Evaluar la función con respecto a los operandos.
      -Introducir el resultado (si lo hubiere) en la pila.
-Si hay un sólo elemento en la pila
  -El valor de ese elemento es el resultado del cálculo.
-Si hay más de un elemento en la pila
  -(Error) El usuario ha introducido demasiados elementos.

^^ Fuente: Wikipedia

En la implementación que hice, que estoy seguro, no es la mejor, solo tuve en cuenta los operadores +, -, *, /. Pueden añadirle más operadores para la raíz, potencia, etc.

Código
  1. static long EvalPosFixed(string[] exp)
  2.        {
  3.            Stack<long> values = new Stack<long>();
  4.            long first, second = 0;
  5.            for (int i = 0; i < exp.Length; i++)
  6.            {
  7.                switch (exp[i])
  8.                {
  9.                    case "+":
  10.                        if (values.Count < 2)
  11.                            throw new Exception("There are not enough arguments to operate");
  12.                        second = values.Pop();
  13.                        first = values.Pop();
  14.                        values.Push(first + second);
  15.                        break;
  16.                    case "-":
  17.                        if (values.Count < 2)
  18.                            throw new Exception("There are not enough arguments to operate");
  19.                        second = values.Pop();
  20.                        first = values.Pop();
  21.                        values.Push(first - second);
  22.                        break;
  23.                    case "*":
  24.                        if (values.Count < 2)
  25.                            throw new Exception("There are not enough arguments to operate");
  26.                        second = values.Pop();
  27.                        first = values.Pop();
  28.                        values.Push(first * second);
  29.                        break;
  30.                    case "/":
  31.                        if (values.Count < 2)
  32.                            throw new Exception("There are not enough arguments to operate");
  33.                        second = values.Pop();
  34.                        if (second == 0)
  35.                            throw new DivideByZeroException("Can't divide by 0");
  36.                        first = values.Pop();
  37.                        values.Push(first / second);
  38.                        break;
  39.                    default:
  40.                        values.Push(long.Parse(exp[i]));
  41.                        break;
  42.                }
  43.            }
  44.            if (values.Count > 1)
  45.                throw new Exception("The expression had more arguments than it should have");
  46.            return values.Peek();
  47.        }

El método no tiene nada complicado, recibe un array de string, el cual representa la expresión posfija, donde cada elemento del array representa un operando o un operador. En el método se recorre el array y se hace un switch para identificar los operadores. Lo demás se rige al algoritmo expuesto anteriormente.

Nota: No vuelvo a explicar el uso de una pila (Stack), pues ya lo expliqué en el código 22 - Saber si una cadena tiene paréntesis balanceados

Voy a ilustrarles, de cierta manera, el funcionamiento del algoritmo:

Supongamos que la entrada es:

Código
  1. string[] expresion = { "4", "2", "10", "+", "*", "5", "-"};

Luego:

EntradaOperaciónPilaComentario
4Introducir en la pila4
2Introducir en la pila4, 2
10Introducir en la pila4, 2, 10
+Suma4, 12Toma los dos últimos valores de la pila (2, 10) y los sustituye por el resultado (12)
*Multiplicación48Toma los dos últimos valores de la pila (4, 12) y los sustituye por el resultado (48)
5Introducir en la pila48 5
-Resta43Toma los dos últimos valores de la pila (48, 5) y los sustituye por el resultado (43)

Al finalizar, el resultado (en este caso 43) será el único elemento en la pila.

Espero que hayan entendido este código.

Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 18 Julio 2015, 01:25 am
27 - Calcular el determinante de una matriz cuadrada de orden n

Voy a mostrar un código recursivo para calcular el determinante de una matriz cuadrada de orden n. No voy a explicar tan en detalle que cosa es el determinante pues son conocimientos "básicos" de algebra que deben saber si necesitan utilizar esté código.

Veamos que:

Sea A una matriz cuadrada; asociada a esa matriz hay un número llamado determinante, que se simboliza por |A| o det(A) y que se calcula de la siguiente forma:

- Si el orden de A es 2 el determinante es el producto de los elementos de la diagonal principal menos el producto de los elementos de la diagonal secundaria:

Ej:

A = (a00, a01)
      (a10, a11)

Luego: det(A) = a00*a11 - a10*a01

- Si el orden de A es 3 el determinante se calcula utilizando la llamada Regla de Sarrus, según la cual si A es la matriz de orden 3:

A = (a00, a01, a02)
      (a10, a11, a12)
      (a20, a21, a22)

y: det(A) = a00*a11*a22 + a01*a12*a20 + a02*a10*a21 - a02*a11*a20 - a01*a10*a22 - a00*a12*a21

Ahora, si el orden de la matriz es superior a 3 el determinante se reduce al de una matriz de orden 3 o de orden 2 de la siguiente manera (que es la que vamos a implementar):

A partir de la matriz cuadrada arbitraria A, si removemos la fila i y la columna j obtenemos una nueva matriz de orden (una unidad) menor que la matriz A y que la vamos a denotar como Aij, cuyo determinante det(Aij) recibe el nombre de menor complementario del elemento aij. Si a este det(Aij) le acompañamos de su signo (-1)i+j tenemos el adjunto de aij

Resumiendo:

Menor complementario de aij = det(Aij)
Adjunto de aij = cij = (-1)i+j*det(Aij)

Entonces, si A es una matriz cuadrada de orden mayor que 3 el determinante de A se obtiene eligiendo una fila o columna arbitraria de A y multiplicando a cada elemento de esta por su adjunto. De esta forma, si elegimos la fila i, el determinante de A es:

det(A) = SUMATORIA(desde i, j=1 hasta n)(aij * (-1)i+j * det(Aij))

Espero esto se haya entendido, trate de explicarlo lo más sencillo posible.

Ahora vamos al código:

Vamos a tener varios métodos con las siguientes signaturas:

Código
  1. public static int Determinant(int[,] matrix)
  2. static int DeterminantOrder2(int[,] matrix)
  3. static int DeterminantRec(int[,] matrix)
  4. static void FillMatrix(int[,] matrix, int[,] toFill, int row, int column)

Como podemos observar el método Determinant() va a ser el método portal, luego tenemos para calcular el determinante de una matriz de orden 2, luego para calcular el determinante de una matriz de orden mayor que 2 y el último es un método auxiliar para rellenar la matriz adjunta de los menores.

Código
  1. public static int Determinant(int[,] matrix)
  2.        {
  3.            //Comprobamos que sea cuadrada
  4.            if (matrix.GetLength(0) != matrix.GetLength(1))
  5.                throw new ArgumentException("La matriz debe ser cuadrada");
  6.            //Vemos si es de orden 2
  7.            if (matrix.GetLength(0) == 2)
  8.                return DeterminantOrder2(matrix);
  9.            else
  10.                return DeterminantRec(matrix);
  11.  
  12.        }

En este ^^ método lo primero que hacemos es comprobar que la matriz sea cuadrada, para esto comprobamos el valor del método .GetLength(), el cual devuelve un int que representa la cantidad de elementos en cada dimensión, o sea el orden de la matriz horizontal y vertical. En este caso tenemos dos dimensiones, por lo tanto comprobamos que si se cumple que:

Código
  1. matrix.GetLength(0) != matrix.GetLength(1)

Entonces lanzamos una excepción pues la matriz no es cuadrada.

Luego, ya que sabemos que la matriz es cuadrada vamos a comprobar si es de orden 2 y llamamos al método correspondiente, sino es de orden mayor y llamamos al método recursivo.

Veamos primero el método DeterminantOrder2():

Código
  1. static int DeterminantOrder2(int[,] matrix)
  2.        {
  3.            return (matrix[0, 0] * matrix[1, 1]) - (matrix[0, 1] * matrix[1, 0]);
  4.        }

Este método es muy sencillo lo cual no es necesario explicar nada. (Revisen arriba como se calcula el determinante de orden 2)

Ahora, vamos con el método DeterminatRec() que viene a ser "el más complicado", lo pongo entre comilla, porque no es más que una representación en código de la ecuación que les presenté antes para calcular el determinante de orden mayor que 2:

Código
  1. static int DeterminantRec(int[,] matrix)
  2.        {
  3.            //Caso de parada que sea de orden 2 y lo calculamos con el método para
  4.            //el determinante de orden 2
  5.            if (matrix.GetLength(0) == 2)
  6.            {
  7.                return DeterminantOrder2(matrix);
  8.            }
  9.            else
  10.            {
  11.                int det = 0; //Inicializamos una variable para almacenar el valor del determinante
  12.  
  13.                //En este ciclo recorremos una fila para para obtener el adjunto de cada elemento
  14.                //de esta fila así calcular el determinante de este fijando cada elemento de la fila
  15.                //0
  16.                for (int i = 0; i < matrix.GetLength(1); i++)
  17.                {
  18.                    //Creamos una matriz para rellenarla con la matriz para luego calcular su
  19.                    //determinante llamando recursivo. Esta matriz tiene un orden menor (en una
  20.                    //unidad) que la matriz original
  21.                    int[,] minor = new int[matrix.GetLength(0) - 1, matrix.GetLength(1) - 1];
  22.  
  23.                    //Aquí llamamos al método auxiliar que se encarga de rellenar la matriz auxiliar
  24.                    FillMatrix(matrix, minor, 0, i);
  25.  
  26.                    //Aquí lo que vamos a definir si lo que se hace es sumar o restar. Esto es una
  27.                    //manera de representar el cambio de signo que trae consigo el (-1) elevado a
  28.                    //i+j. En este caso como utilizamos la fila 0 siempre, ese número va a ser
  29.                    //positivo solo cuando estemos en una columna (j) que sea par, negativo en
  30.                    //caso contrario
  31.                    if (i % 2 == 0)
  32.                        //Aquí lo que hacemos es multiplicar el valor del elemento en cuestión
  33.                        //por el  adjunto teniendo en cuenta el signo correspondiente y se lo
  34.                        //sumamos a la variable det (en el else es lo mismo lo que con el signo
  35.                        //negativo). Aquí es donde está la llamada recursiva pues hay que calcular el
  36.                        //determinante de la matriz "minor"
  37.                        det+= (matrix[0, i]) * (DeterminantRec(minor));
  38.                    else
  39.                        det+= (-matrix[0, i]) * (DeterminantRec(minor));
  40.                }
  41.  
  42.                //Una vez que ya recorrimos toda la fila, devolvemos el determinante
  43.                return det;
  44.            }
  45.        }

Ya explique todo en los comentarios del código.

Ahora veamos el método FillMatrix():

Este método recibe la matriz desde la cual se van a sacar los valores, la matriz donde se van a copiar (de un orden menor en una unidad), la fila y la columna que se va a eliminar:

Código
  1. static void FillMatrix(int[,] matrix, int[,] toFill, int row, int column)
  2.        {
  3.            //Ciclo anidado para recorrer la matriz desde la cual se van a sacar los valores
  4.            for (int i = 0; i < matrix.GetLength(0); i++)
  5.            {
  6.                for (int j = 0; j < matrix.GetLength(1); j++)
  7.                {
  8.                    //Si estamos en una fila menor a la que se va a eliminar
  9.                    if (i < row)
  10.                    {
  11.                        //Si estamos en una columna menor a la que se va a eliminar
  12.                        if (j < column)
  13.                            toFill[i, j] = matrix[i, j]; //Copiamos el valor normalmente
  14.                        else
  15.                            if (j > column)
  16.                              //En este momento estamos en una fila menor a la que se va a
  17.                              //eliminar pero por encima de la columna a eliminar, por lo tanto
  18.                              //se saca el valor de la matriz y se coloca en una columna con una
  19.                              //unidad menos (j-1)
  20.                              toFill[i, j - 1] = matrix[i, j];
  21.                    }
  22.                    else
  23.                    //Si estamos en una fila mayor a la que se va a eliminar
  24.                    if (i > row)
  25.                    {
  26.                        //Si estamos en una columna mayor a la que se va a eliminar
  27.                        if (j < column)
  28.                            //En este momento estamos por encima de la fila a eliminar pero por debajo
  29.                            //de la columna a eliminar, por lo tanto tenemos que quitarle una unidad a la fila
  30.                            //(i-1)
  31.                            toFill[i - 1, j] = matrix[i, j];
  32.                        else
  33.                            if (j > column)
  34.                              //Aquí estamos por encima tanto de la fila como de la columna a eliminar por lo
  35.                              //tanto le quitamos una unidad tanto a las filas como a la columna (i-1, j-1)
  36.                              toFill[i - 1, j - 1] = matrix[i, j];
  37.                    }
  38.                }
  39.            }
  40.        }

Espero que este código se haya entendido, sino, ya saben que pueden preguntar, opinar y/o criticar.

Quizás no sea la mejor manera (complejidad temporal) de hacerlo, si tienen alguna sugerencia será bienvenida.

Espero seguir subiendo códigos pronto.

Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: DarK_FirefoX en 20 Julio 2015, 00:35 am
28 - Drag&Drop sobre un TextBox (detectando la codificación de caracteres)

Primer que todo vamos a repasar los conceptos básicos que se van a mostrar.

¿Que es un Drag&Drop?

Drag significa Arrastrar
Drop significa Soltar

Por lo tanto => Drag&Drop => Arrastrar y Soltar. Vamos que es la simple función de arrastrar un documento y soltarlo en algún lado. En este caso vamos a ser capaces de seleccionar un archivo de texto y soltarlo sobre un TextBox dentro del formulario.

Luego tenemos como detectar la codificación de caracteres. La codificación de caracteres es el método que permite convertir un carácter de un lenguaje natural en un símbolo de otro sistema de representación aplicando normas o reglas de codificación

Existen varios tipos de codificación:

  • UTF-8
  • UTF-16 Big Endian
  • UTF-16 Little Endian
  • UTF-7
  • UTF-32

Entre otros.

Ahora, ¿Cómo vamos a detectar que codificación tiene un fichero?

Para esto vamos a analizar la "Marca de orden de bytes (BOM)". BOM es un carácter Unicode que se utiliza para indicar el orden de los bytes de un fichero de texto. Su código es U+FEFF.

Representaciones de las marcas de orden de bytes para cada codificación

Encoding Representación (Hex)
UTF-7 2B 2F 76
UTF-8 EF BB BF
UTF-16 Little Endian FF FE
UTF-16 Big Endian FE FF
UTF-32 Big Endian 00 00 FE FF

Veamos el método que vamos a utilizar para analizar la BOM:

Código
  1. /// <summary>
  2.        /// Determina la codificación de un fichero de texto analizando la marca de orden de bytes (BOM)
  3.        /// Devuelve la codificación por defecto de la página ANSI del sistema en caso de que el análisis falle
  4.        /// </summary>
  5.        /// <param name="filename">El fichero de texto a analizar</param>
  6.        /// <returns>La codificación detectada.</returns>
  7.        public static Encoding GetEncoding(string filename)
  8.        {
  9.            // Read the BOM
  10.            byte[] bom = new byte[4]; //Buffer para almacenar los datos leidos del fichero
  11.  
  12.            //Utilizamos un FileStream para abrir el fichero
  13.            using (FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read))
  14.            {
  15.                file.Read(bom, 0, 4); //Leemos los 4 primeros bytes del fichero
  16.            }
  17.  
  18.            // Analizamos la BOM y devolvemos la codificación correcta
  19.            if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7;
  20.            if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8;
  21.            if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
  22.            if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
  23.            if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32;
  24.  
  25.            //Devolvemos la codificacion por defecto de la página ANSI del sistema.
  26.            return Encoding.Default;
  27.        }

Nota: Para usar FileStream, debemos añadir a los using:

Código
  1. System.IO;

Nota: Ese ^^ nombre de espacio contiene tipos que permiten leer y escribir de archivos y flujos de datos, y tipos que brindan soporte básico para el trabajo con directorios.

Un FileStream, nos permite crear un flujo alrededor de un fichero, soportando operaciones de lectura y escritura tanto sincrónicas como asincrónicas.

Veamos que lo utilizamos con la declaración using que nos brinda una sintaxis conveniente para asegurar el uso correcto de objetos que implementen la interfaz IDisposable. Esta interfaz define un método que nos permite cerrar (de-allocate) los recursos de un objeto. Por lo tanto luego que se salga del ámbito de la declaración using los recursos del objeto sobre el cual se esta trabajando serán liberados. En este caso (FileStream) no será necesario hacerle Close()

El FileStream tiene un constructor con varias sobrecargas, en este caso vamos a utilizar la sobrecarga que tiene esta signatura:

Código
  1. FileStream(String, FileMode, FileAccess)

Donde el primer parámetro viene dado por la ruta del archivo, o sea, fileList[0]. El segundo es un enum que especifica como el sistema operativo debe abrir el archivo, en este caso FileMode.Open, lo cual indica que el sistema operativo debe abrir un fichero existente. La habilidad de abrir un fichero es dependiente del valor especificado por el enum  FileAccess en el próximo parámetro el cual define que permiso se tiene al acceder al archivo (Read, Write o Read/Write). En este caso vamos a utilizar FileAccess.Read, pues solo vamos a leer el archivo.

El método Read() que pertenece a la clase FileStream, nos permite leer un bloque de bytes desde el fichero apuntado por el flujo y guardarlo en un buffer dado. En este caso el método devuelve un int que representan la cantidad de bytes leídos. Pero modifica el primer parámetro, pues al ser un array, este es pasado al método por referencia, y por lo tanto es modificado. En él se guardan los bytes leídos. Este parámetro es un byte[]. El segundo parámetro es un int que representa el offset (digámosle la posición) en el array (primer parámetro) donde los bytes se comenzarán a guardar. Y el tercer parámetro es el máximo número de bytes a leer.

Ahora, como lo que queremos es mostrar el contenido de un fichero de texto en un TextBox vamos a insertar un TextBox en el formulario.

Nota: En este ejemplo>

Propiedad Name del Form -> mainForm
Propiedad Name del TextBox -> textBox


Ahora, para permitirle a textBox recibir información que haya sido arrastrada y soltada sobre él, le vamos a poner la propiedad AllowDrop = true

Ahora nos vamos subscribir a los eventos textBox_DragEnter y textBox_DragDrop. Luego vamos a necesitar un string[] para almacenar las rutas de el/los fichero/s que se soltaron sobre textBox. Para eso declaramos un campo privado dentro de la clase de mainForm:

Código
  1. string[] fileList;

Veamos que hacemos una vez que se capture el evento textBox_DragEnter

Código
  1. private void textBox_DragEnter(object sender, DragEventArgs e)
  2.        {
  3.            if (e.Data.GetDataPresent(DataFormats.FileDrop))
  4.            {
  5.                fileList = (string[])e.Data.GetData(DataFormats.FileDrop);
  6.                if (fileList.Length == 1 && Path.GetExtension(fileList[0]) == ".txt")
  7.                {
  8.                    e.Effect = DragDropEffects.Copy;
  9.                }
  10.                else
  11.                    e.Effect = DragDropEffects.None;
  12.            }
  13.        }

Lo primero que vemos es la propiedad Data del tipo DataEventArgs e que devuelve un objeto que implementa IDataObject, que contiene datos asociado con este evento. Entonces lo que hacemos es llamar al método GetDataPresent() de la interfaz IDataObject el cual nos permite determinar si la información almacenada en esta instancia está asociada a un formato especifico. En este caso le pasamos como parámetro el campo estático (solo-lectura) FileDrop de la clase DataFormats - DataFormats.FileDrop. El cual especifica el formato de Windows para arrastrar y soltar ficheros.

Una vez explicado esto, lo que comprobamos es que si lo que se está soltando encima de textBox es un archivo vamos a hacer una cosa:

Primero que todo:

Código
  1. fileList = (string[])e.Data.GetData(DataFormats.FileDrop);

Con el método GetData() sobre la propiedad Data del tipo DragEventArgs e obtenemos la información (con el formato establecido [DataFormats.FileDrop]) que estamos arrastrando sobre el textBox. Luego, vemos que le estamos haciendo un cast a string[] y se lo asignamos al campo que habíamos declarado. Aquí tendremos toda la información que viene siendo arrastrada y capturada por el evento. Como el formato es FileDrop lo que nos va a añadir en el array son las rutas a los documentos que están siendo arrastrados.

Ahora vamos a hacer 2 comprobaciones:

1 - Que solo estemos arrastrando 1 solo fichero, para esto solo tenemos que comprobar que la longitud del array sea 1

2 - Que la extensión del archivo sea ".txt".

Para comprobar esta segunda condición vamos a utilizar el método GetExtension() de la clase Path que pertenece al namespace System.IO. La clase estática Path realiza operaciones sobre strings que contienen información de rutas de ficheros o directorio. En este caso utilizamos el método estático GetExternsion(), el cual recibe un string que representa "la ruta" de la cual queremos sacar la extensión. En este caso: fileList[0], el cual es el único archivo que estamos arrastrando y comprobamos si es igual a ".txt". En ese caso vamos a definir la propiedad Effect del tipo DragEventArgs e a DragDropEffects.Copy, en caso contrario (que no se cumplan las dos condiciones) a DragDropEffects.None.

La propiedad Effect aplica el efecto determinado para el cursor cuando se hace una operación de Drag & Drop sobre el control.

Luego, vemos que le asignamos un valor utilizando el enum DragDropEffects, y su valor Copy, que crea el efecto de que lo que se está arrastrando es copiado a donde se va a soltar.

En el otro caso está DragDropEffects.None que implica que donde se va a soltar no acepta datos. Por lo tanto no aceptará los datos cuando se suelten los datos.

Ahora veamos el EventHandler para textbox_DragDrop:

Código
  1. private void textBox_DragDrop(object sender, DragEventArgs e)
  2.        {
  3.            Encoding fileEncoding = GetEncoding(fileList[0]);
  4.            using (StreamReader file = new StreamReader(fileList[0], fileEncoding))
  5.            {
  6.                textBox.Text = file.ReadToEnd();
  7.            }
  8.        }

Lo primero que hacemos es capturar lo que devuelve el método GetEncoding() (explicado anteriormente), el cual devuelve la codificación de caracteres correspondiente al fichero (fileList[0]) tras analizar la BOM.

Luego, utilizando un StreamReader, que también pertenece al namespace System.IO vamos a leer el fichero.

Un StreamReader nos permite leer caracteres de un flujo de bytes en una codificación específica. En este caso utilizamos la declaración using con el mismo objetivo que explique arriba con el FileStream. Utilizaremos una sobrecarga del constructor del StreamReader que recibe la ruta del fichero al cual le queremos crear el flujo (fileList[0]) y la codificación de caracteres con la cual queremos abrirla. En este caso le pasamos lo que nos devolvió el método GetEncoding().

Entonces dentro del ámbito de la declaración using vamos a asignarle a la propiedad Text de textBox, lo que nos devuelve el método ReadToEnd() perteneciente a la clase StreamReader. Este método lee todos los caracteres desde la posición actual (en el stream) hasta el final del stream. Se dice desde la posición actual, pues se pueden haber hecho lecturas desde el stream anteriormente y se queda guardada hasta que posición se leyó, en este caso no se había leído nada, por lo tanto se lee desde el principio hasta el final.

Bueno, esto es todo para este código, solo les queda probarlo. Espero que entiendan todo, quizás no sea la mejor manera de explicarlo, pero lo hice lo mejor y más sencillo que pude.

Cualquier duda/comentario/crítica será bienvenida.

Espero seguir subiendo códigos pronto.

Salu2s


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: Eleкtro en 13 Septiembre 2015, 13:04 pm
Comparar solamente 5 codificaciones y en caso de no coincidir ninguna devolver la codificación que representa a la página de códigos del sistema no es lo mejor que se puede hacer, lo que deberías hacer es iterar las codificaciones con la función System.Text.Encoding.GetEncodings y usar la función Encoding.GetEncoding para obtener la codificación y Encoding.GetPreamble para obtener el preámbulo (BOM) con el que comparar la secuencia de bytes.
De esta manera pasamos de comparar 5 codificaciones, a unas 150.

Un ejemplo (convertido de VB.Net a C#):
Código
  1. /// ----------------------------------------------------------------------------------------------------
  2. /// <remarks>
  3. /// Title : Get Encoding of textfile.
  4. /// Author: Elektro
  5. /// Date  : 17-January-2015
  6. /// </remarks>
  7. /// ----------------------------------------------------------------------------------------------------
  8. /// <example><code>
  9. /// Dim encoding As Encoding = TextfileUtil.GetEncoding("C:\file.txt")
  10. /// Dim encodingName As String = TextfileUtil.GetEncoding("C:\file.txt").WebName
  11. /// </code></example>
  12. /// ----------------------------------------------------------------------------------------------------
  13. /// <summary>
  14. /// Determines the <see cref="Encoding"/> of the source textfile.
  15. /// </summary>
  16. /// ----------------------------------------------------------------------------------------------------
  17. /// <param name="sourceFilepath">
  18. /// The source textfile path.
  19. /// </param>
  20. /// ----------------------------------------------------------------------------------------------------
  21. /// <returns>
  22. /// If the encoding can be detected, the return value is the detected <see cref="Encoding"/>,
  23. /// if the encoding can't be detected, the return value is <see cref="Encoding.Default"/>.
  24. /// </returns>
  25. /// ----------------------------------------------------------------------------------------------------
  26. [DebuggerStepThrough()]
  27. [DebuggerHidden()]
  28. public static Encoding GetEncoding(string sourceFilepath)
  29. {
  30.  
  31. Encoding encoding = null;
  32. byte[] bytes = File.ReadAllBytes(sourceFilepath);
  33.  
  34. foreach (EncodingInfo encodingInfo in encoding.GetEncodings()) {
  35. Encoding currentEncoding = encodingInfo.GetEncoding();
  36. byte[] preamble = currentEncoding.GetPreamble();
  37. bool match = true;
  38.  
  39. if ((preamble.Length > 0) && (preamble.Length <= bytes.Length)) {
  40.  
  41. for (int i = 0; i <= (preamble.Length - 1); i++) {
  42. if (preamble(i) != bytes(i)) {
  43. match = false;
  44. break; // TODO: might not be correct. Was : Exit For
  45. }
  46.  
  47. }
  48.  
  49. } else {
  50. match = false;
  51.  
  52. }
  53.  
  54. if (match) {
  55. encoding = currentEncoding;
  56. break; // TODO: might not be correct. Was : Exit For
  57. }
  58.  
  59. }
  60.  
  61. if (encoding == null) {
  62. return encoding.Default;
  63.  
  64. } else {
  65. return encoding;
  66.  
  67. }
  68.  
  69. }
  70.  
  71. //=======================================================
  72. //Service provided by Telerik (www.telerik.com)
  73. //=======================================================

Saludos


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: SnzCeb en 12 Diciembre 2015, 13:26 pm
Hola chicos, pues voy a hacer mi primera aportación de código a ver si os parece útil. Es una clase que está pensada como almacén de datos de juegos basado en tablero de celdas. Me ha servido para implementar el juego de la vida y seguro que os puede servir para juegos como el tres, el cuatro en raya, damas, que se yo xD.

Código
  1.  
  2.    /*************************************************************************************
  3.      * This class implements a bit board                                                 *
  4.      *                                                                                   *
  5.      * Foreach bit we have one cell. If we turn on a bit means occupied cell. By the     *
  6.      * opposite, if we turn off the bit, means empty cell                                *
  7.      * The main gain of a bitBoard is to decrease the amount of memory used.             *
  8.      * this fact is so important in algorithms like Minimax                              *
  9.      * foreach byte we can cover 8 positions                                             *
  10.      * For instance: If we have and 8x8 board, we can save 64 cells in a 8x1 board       *
  11.      * (8 bytes) because 1 byteCell can save 8 cells                                     *
  12.      *************************************************************************************/
  13.  
  14.  
  15.   public class Board
  16.    {
  17.  
  18.        protected readonly byte[,] board;
  19.        protected readonly int height;
  20.        protected readonly int byteWidth;
  21.        protected readonly int width;
  22.  
  23.        protected Board(int height, int width)
  24.        {
  25.            this.height = height;
  26.            this.byteWidth =(int) Math.Ceiling(width * 0.125); // 0.125 * width = width /8  
  27.            this.width = width;
  28.            board = new byte[height, byteWidth];
  29.        }
  30.  
  31.  
  32.        private byte IncreaseBitPosn(int n)
  33.        {
  34.  
  35.            return (byte)(0x1 << n);
  36.        }
  37.        private byte ReverseByte (byte b)
  38.        {
  39.            return  (byte) (b ^ 0xFF);
  40.        }
  41.  
  42.        private int ToByteColumn(int y)
  43.        {
  44.            return (int) 0.125 * y;
  45.        }
  46.  
  47.        private int ToBitColumn(int y)
  48.        {
  49.            return (y >= 8) ? y - (ToByteColumn(y) * 8) : y;
  50.        }
  51.  
  52.        /*
  53.         *  Checks if [x,y] is empty with an and bitwise
  54.         *  For instance:
  55.         *  x = 3
  56.         *  y = 7  -> Matchs to byteCol 0, bitCol 7
  57.         *  board (3,0)        = 1 0 0 1 0 1 0 0
  58.         *  IncreaseBitPosn(7) = 1 0 0 0 0 0 0 0
  59.         *  1 0 0 1 0 1 0 0 &  1 0 0 0 0 0 0 0 = 1 0 0 0 0 0 0 0 != 0 -> returns false              
  60.         */
  61.        protected bool PosIsEmpty(int x, int y)
  62.        {
  63.            return (board[x, ToByteColumn(y)] & IncreaseBitPosn(ToBitColumn(y))) == 0;
  64.    }
  65.        /*
  66.         *  Increases bit at position (x,y)
  67.         *  For instance:
  68.         *  x = 3
  69.         *  y = 7  -> Matchs to byteCol 0, bitCol 7
  70.         *  board (3,0)        = 1 0 0 1 0 1 0 0
  71.         *  IncreaseBitPosn(7) = 1 0 0 0 0 0 0 0
  72.         *  0 0 0 1 0 1 0 0  |  1 0 0 0 0 0 0 0 = 1 0 0 1 0 1 0 0  
  73.         */
  74.        protected void turnOnBit(int x, int y)
  75.        {
  76.            board[x, ToByteColumn(y)] |= IncreaseBitPosn(ToBitColumn(y));
  77.        }
  78.        /*
  79.          *  Decreases bit at position (x,y)
  80.          *  For instance:
  81.          *  x = 3
  82.          *  y = 7  -> Matchs to byteCol 0, bitCol 7
  83.          *  board (3,0)        = 1 0 0 1 0 1 0 0
  84.          *  ReverseByte(IncreaseBitPosn(7)) = 0 1 1 1 1 1 1 1
  85.          *  1 0 0 1 0 1 0 0  &  0 1 1 1 1 1 1 1 = 0 0 0 1 0 1 0 0  
  86.          */
  87.  
  88.        protected void turnOffBit(int x, int y)
  89.        {
  90.            board[x, ToByteColumn(y)] &=  ReverseByte(IncreaseBitPosn(ToBitColumn(y)));
  91.        }
  92.  
  93.    }
  94.  


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 8 Enero 2018, 16:30 pm
OBTENER EL PRIMER NUMERO O TODOS LOS NÚMEROS QUE FALTAN DE UNA SECUENCIA DEFINIDA, si los hay...

Pues trabajando en mi sistema de Sockets he inventado este sistema dinámico para obtener si un numero falta dentro de una secuencia. Digo dinámica porque funciona con tipos dinámicos, el último método podremos especificar más tipos si se requiere, como por ejemplo los que nos aporta System.Data.SqlTypes (gracias Elektro).

Código
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4.  
  5. namespace MissingNumbersOnSequence
  6. {
  7.    public class Program
  8.    {
  9.        public static void Main(string[] args)
  10.        {
  11.            List<ulong> longs = (new ulong[] { 1, 2, 3, 6, 7, 8, 12, 13, 14, 17 }).ToList();
  12.  
  13.            //Los dos metodos explicados
  14.            ulong numb = 0;
  15.            bool b = FindFirstMissingNumberFromSequence(longs, out numb); //Se devuelve una bool para saber si realmente había algún número que faltaba, puesto que por defecto se devolverá el 0, y el 0 puede formar parte de una secuencia a la cual no pertenece.
  16.  
  17.            Console.WriteLine("First missing number: {0}\nMissing numbers: {1}", numb, string.Join(", ", FindMissingNumbersFromSequence(longs).Select(x => x.ToString()))); //Para comprobar si la secuencia estaba incompleta solo habría que comprobar x.Count() > 0...
  18.            Console.Read();
  19.        }
  20.  
  21.        public static bool FindFirstMissingNumberFromSequence<T>(IEnumerable<T> arr, out T n)
  22.        {
  23.            if (!arr.Any(x => x.IsNumericType()))
  24.            {
  25.                Console.WriteLine("Type '{0}' can't be used as a numeric type!", typeof(T).Name);
  26.                n = default(T);
  27.                return false;
  28.            }
  29.  
  30.            IOrderedEnumerable<T> list = arr.OrderBy(x => x);
  31.            bool b = false;
  32.            n = default(T);
  33.  
  34.            foreach (T num in list)
  35.            {
  36.                b = (dynamic)num - n > 1;
  37.                if (b) break;
  38.                else n = (dynamic)num;
  39.            }
  40.  
  41.            n = (dynamic)n + 1;
  42.  
  43.            return b;
  44.        }
  45.  
  46.        public static IEnumerable<T> FindMissingNumbersFromSequence<T>(IEnumerable<T> arr) where T : struct
  47.        {
  48.            if (!arr.Any(x => x.IsNumericType()))
  49.            {
  50.                Console.WriteLine("Type '{0}' can't be used as a numeric type!", typeof(T).Name);
  51.                yield break;
  52.            }
  53.  
  54.            IOrderedEnumerable<T> list = arr.OrderBy(x => x);
  55.            T n = default(T);
  56.  
  57.            foreach (T num in list)
  58.            {
  59.                T op = (dynamic)num - n;
  60.                if ((dynamic)op > 1)
  61.                {
  62.                    int max = op.ConvertValue<int>();
  63.                    for (int l = 1; l < max; ++l)
  64.                        yield return (dynamic)n + l.ConvertValue<T>();
  65.                }
  66.                n = (dynamic)num;
  67.            }
  68.        }
  69.  
  70.    }
  71.  
  72.    public static class Extensions
  73.    {
  74.        public static T ConvertValue<T>(this object o) where T : struct
  75.        {
  76.            return (T)Convert.ChangeType(o, typeof(T));
  77.        }
  78.  
  79.        public static bool IsNumericType<T>(this T o)
  80.        {
  81.            return typeof(T).IsNumericType();
  82.        }
  83.  
  84.        public static bool IsNumericType(this Type type)
  85.        {
  86.            switch (Type.GetTypeCode(type))
  87.            {
  88.                case TypeCode.Byte:
  89.                case TypeCode.SByte:
  90.                case TypeCode.UInt16:
  91.                case TypeCode.UInt32:
  92.                case TypeCode.UInt64:
  93.                case TypeCode.Int16:
  94.                case TypeCode.Int32:
  95.                case TypeCode.Int64:
  96.                case TypeCode.Decimal:
  97.                case TypeCode.Double:
  98.                case TypeCode.Single:
  99.                    return true;
  100.                default:
  101.                    return false;
  102.            }
  103.        }
  104.    }
  105. }
  106.  

Un saludo.


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: Eleкtro en 14 Mayo 2018, 13:50 pm
Escanear un archivo o una url en VirusTotal.com

Se me ocurrió implementar la API 2.0 de VirusTotal en VB.NET, pero primero busqué para asegurarme de que nadie lo habia hecho todavía (de lo conrario sería absurdo reinventar la rueda), y descubrí que efectivamente ya alguien lo hizo, en C#. Os dejo el repositorio por aquí:

  • https://github.com/Genbox/VirusTotal.Net

PD: no lo he probado.

Saludos.


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 22 Octubre 2018, 18:48 pm
Thread Safe Bool: Implementación Thread-Safe de bools

El otro día me pasaba que al asignar una variable que estaba declarada en ambito de la clase desde otro thread, al leerla desde otro thread no me devolvía el resultado esperado, por eso os traigo esta utilidad.

Código
  1. using System.Threading;
  2.  
  3. namespace GTAMapper.Extensions.Threading
  4. {
  5.    /// <summary>
  6.    /// Thread safe enter once into a code block:
  7.    /// the first call to CheckAndSetFirstCall returns always true,
  8.    /// all subsequent call return false.
  9.    /// </summary>
  10.    public class ThreadSafeBool
  11.    {
  12.        private static int NOTCALLED = 0,
  13.                           CALLED = 1;
  14.  
  15.        private int _state = NOTCALLED;
  16.  
  17.        /// <summary>Explicit call to check and set if this is the first call</summary>
  18.        public bool Value
  19.        {
  20.            get
  21.            {
  22.                return Interlocked.Exchange(ref _state, CALLED) == NOTCALLED;
  23.            }
  24.        }
  25.  
  26.        /// <summary>usually init by false</summary>
  27.        public static implicit operator ThreadSafeBool(bool called)
  28.        {
  29.            return new ThreadSafeBool() { _state = called ? CALLED : NOTCALLED };
  30.        }
  31.  
  32.        public static implicit operator bool(ThreadSafeBool cast)
  33.        {
  34.            if (cast == null)
  35.                return false;
  36.  
  37.            return cast.Value;
  38.        }
  39.    }
  40. }

Extraído de: https://www.codeproject.com/Tips/375559/Implement-Thread-Safe-One-shot-Bool-Flag-with-Inte


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 22 Octubre 2018, 18:55 pm
ConcurrentQueuedCoroutines: Implementación Thread-Safe de ConcurrentQueues dentro de Coroutinas

La idea de esta utilidad es que cuando tu almacenas items desde otro thread, puedas acceder desde el thread principal.

Mezclando esta idea, con coroutinas, que básicamente, es un sistema del prehistorico que implementó Unity en su momento, que funciona de la siguiente forma, se crea un IEnumerator (con yields), el cual cada MoveNext se ejecuta en cada frame (yield return null) o cuando el programador especifique (yield return new WaitForSeconds(3) equivalente a Thread.Sleep(3000)) (por tal de no atorar el Main Thread, sí, Unity se ejecuta en un solo hilo).

Entonces, teniendo estas 2 cosas, porque no hacer Dequeue en cada MoveNext?

Código
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6.  
  7. namespace GTAMapper.Extensions.Threading
  8. {
  9.    public class ConcurrentQueuedCoroutines<T>
  10.    {
  11.        private List<Coroutine> coroutines;
  12.        private Coroutine coroutine;
  13.        private MonoBehaviour Mono;
  14.  
  15.        public ConcurrentQueue<object> Queue { get; private set; }
  16.  
  17.        public Action<T> Action { get; private set; }
  18.        public bool Debugging { get; set; }
  19.        public float SecondsToWait { get; private set; }
  20.  
  21.        private ConcurrentQueuedCoroutines()
  22.        {
  23.        }
  24.  
  25.        public ConcurrentQueuedCoroutines(float secondsToWait = -1)
  26.        {
  27.            Queue = new ConcurrentQueue<object>();
  28.            coroutines = new List<Coroutine>();
  29.            SecondsToWait = secondsToWait;
  30.        }
  31.  
  32.        public Coroutine StartCoroutine(MonoBehaviour monoBehaviour, Action<T> action)
  33.        {
  34.            coroutines.Add(monoBehaviour.StartCoroutine(InternalCoroutine()));
  35.            Mono = monoBehaviour;
  36.            Action = action;
  37.  
  38.            return coroutine;
  39.        }
  40.  
  41.        public Coroutine StartCoroutineOnce(MonoBehaviour monoBehaviour, Action<T> action)
  42.        {
  43.            if (Debugging)
  44.                Debug.Log("Starting dequeing!");
  45.  
  46.            if (coroutine == null)
  47.            {
  48.                coroutine = monoBehaviour.StartCoroutine(InternalCoroutine());
  49.                Mono = monoBehaviour;
  50.                Action = action;
  51.            }
  52.  
  53.            return coroutine;
  54.        }
  55.  
  56.        public void StopCoroutine()
  57.        {
  58.            if (coroutine != null && Mono != null)
  59.                Mono.StopCoroutine(coroutine);
  60.        }
  61.  
  62.        public void StopAllCoroutines()
  63.        {
  64.            if (Mono != null && coroutines != null && coroutines.Count > 0)
  65.                coroutines.ForEach((c) => Mono.StopCoroutine(c));
  66.        }
  67.  
  68.        public IEnumerator GetCoroutine(MonoBehaviour mono, Action<T> action)
  69.        {
  70.            Mono = mono;
  71.            Action = action;
  72.            return InternalCoroutine();
  73.        }
  74.  
  75.        private IEnumerator InternalCoroutine()
  76.        {
  77.            if (Debugging)
  78.                Debug.Log($"Starting dequeing {Queue.Count} values!");
  79.  
  80.            while (Queue.Count > 0)
  81.            {
  82.                object value = null;
  83.                bool dequeued = Queue.TryDequeue(out value);
  84.  
  85.                if (!dequeued)
  86.                {
  87.                    if (SecondsToWait == -1)
  88.                        yield return new WaitForEndOfFrame();
  89.                    else
  90.                        yield return new WaitForSeconds(SecondsToWait);
  91.  
  92.                    continue;
  93.                }
  94.  
  95.                Action?.Invoke((T)value);
  96.  
  97.                if (SecondsToWait == -1)
  98.                    yield return new WaitForEndOfFrame();
  99.                else
  100.                    yield return new WaitForSeconds(SecondsToWait);
  101.            }
  102.        }
  103.    }
  104. }

Y diréis, y para que sirve esta chorra, pues por ejemplo, lo que se puede conseguir, es visualizar como se recorre una textura.

(https://im.ezgif.com/tmp/ezgif-1-ad6d2d58b115.gif)

(https://im.ezgif.com/tmp/ezgif-1-515065e9c474.gif)

En el próximo post o enseñaré un caso de uso.


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 22 Octubre 2018, 18:59 pm
BatchedCoroutines: Iterar coroutinas una a una

Queréis que vuestras ConcurrentQueues se ejecuten una por una? No problemo, con esta implementación to hard-codeada lo conseguiréis:

Código
  1. using GTAMapper.Extensions.Threading;
  2. using System;
  3. using System.Collections;
  4. using UnityEngine;
  5.  
  6. namespace GTAMapper.Extensions
  7. {
  8.    public static class BatchedCoroutines
  9.    {
  10.        public static IEnumerator BatchCoroutines(
  11.            MonoBehaviour monoBehaviour,
  12.            Action finish,
  13.            Func<int, bool>[] waitUntil = null,
  14.            params Tuple<Action<object>, ConcurrentQueuedCoroutines<object>>[] tuple) // Tuple<Action<T>, ConcurrentQueuedCoroutines<T>> || dynamic
  15.                                                                                      // Fix for: https://stackoverflow.com/questions/15417174/using-the-params-keyword-for-generic-parameters-in-c-sharp
  16.        {
  17.            int i = 0;
  18.  
  19.            foreach (var val in tuple)
  20.            {
  21.                if (waitUntil != null && waitUntil[i] != null)
  22.                    yield return new WaitUntil(() => waitUntil[i](i));
  23.  
  24.                yield return val.Item2.GetCoroutine(monoBehaviour, val.Item1);
  25.  
  26.                ++i;
  27.            }
  28.  
  29.            finish?.Invoke();
  30.        }
  31.    }
  32. }

Un ejemplo de implementación:

Código
  1.        protected ConcurrentQueuedCoroutines<object> debuggingCoroutine = new ConcurrentQueuedCoroutines<object>(),
  2.                                                     colorCoroutine = new ConcurrentQueuedCoroutines<object>();
  3.  
  4. namespace GTAMapper.Core {
  5.    public class Program : MonoBehaviour {
  6.        public void Start() {
  7.        StartCoroutine(BatchedCoroutines.BatchCoroutines(
  8.                this,
  9.                () => areCoroutinesCollected = true,
  10.                F.GetFuncs(null, (_ii) => debuggingCoroutine.Queue.Count > 0),
  11.                new Tuple<Action<object>, ConcurrentQueuedCoroutines<object>>((obj) =>
  12.                {
  13.                    Tuple<int, Color> tuple = (Tuple<int, Color>)obj;
  14.  
  15.                    int i = tuple.Item1,
  16.                            _x = i % width,
  17.                            _y = i / width;
  18.  
  19.                    UnityEngine.Color actualColor = debugTexture.GetPixel(_x, _y),
  20.                                          mixedColor = UnityEngine.Color.Lerp(actualColor, tuple.Item2, .5f);
  21.  
  22.                    if (actualColor != mixedColor)
  23.                    {
  24.                        debugTexture.SetPixel(_x, _y, mixedColor);
  25.                        debugTexture.Apply();
  26.                    }
  27.                }, colorCoroutine),
  28.                new Tuple<Action<object>, ConcurrentQueuedCoroutines<object>>((obj) =>
  29.                {
  30.                    Color[] colors = (Color[])obj;
  31.  
  32.                    debugTexture.SetPixels32(colors.CastBack().ToArray());
  33.                    debugTexture.Apply();
  34.                }, debuggingCoroutine)));
  35.          }
  36.     }
  37. }

Básicamente, en las dos Queues vamos haciendo Enqueue donde sea necesario (en otro thread).

Cuando todo haya acabado, desde el primer thread, llamamos a que se ejecute lo que acabo de mostrar.

Y en mi caso por ejemplo, esto sirve para mostrar pixel a pixel donde se ha iterado una imagen.

Y lo siguiente que ocurre es que la imagen se rellena con el algoritmo de flood-fill que enseñe el otro día (https://foro.elhacker.net/net/libreria_de_snippets_para_vbnet_compartan_aqui_sus_snippets-t378770.0.html;msg2175819#msg2175819). (Básicamente, para saber si se ha hecho bien)

Nota: Si queréis el código de GetFuncs es este:

Código
  1. using System;
  2.  
  3. public static class F {
  4.        public static Func<int, bool>[] GetFuncs(params Func<int, bool>[] waitUntil)
  5.        {
  6.            return waitUntil;
  7.        }
  8. }


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 22 Octubre 2018, 19:07 pm
Named Thread & Thread Marked: Pon nombres a tus threads

Código
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4.  
  5. namespace GTAMapper.Extensions.Threading
  6. {
  7.    public class ThreadMarker : IDisposable
  8.    {
  9.        //[ThreadStatic]
  10.        //private static string __Name = $"Unity Thread #{Thread.CurrentThread.ManagedThreadId}";
  11.  
  12.        private static Dictionary<int, string> ThreadNames = new Dictionary<int, string>();
  13.  
  14.        public static string Name
  15.        {
  16.            get
  17.            {
  18.                lock (ThreadNames)
  19.                {
  20.                    try
  21.                    {
  22.                        return ThreadNames[Thread.CurrentThread.ManagedThreadId];
  23.                    }
  24.                    catch
  25.                    {
  26.                        return $"Unity Thread #{Thread.CurrentThread.ManagedThreadId}";
  27.                    }
  28.                }
  29.            }
  30.        }
  31.  
  32.        public ThreadMarker(string name)
  33.        {
  34.            lock (ThreadNames)
  35.            {
  36.                ThreadNames.AddOrSet(Thread.CurrentThread.ManagedThreadId, name);
  37.            }
  38.  
  39.            // __Name = name;
  40.        }
  41.  
  42.        public void Dispose()
  43.        {
  44.            ThreadNames.Remove(Thread.CurrentThread.ManagedThreadId);
  45.            // __Name = "Un-Owned";
  46.        }
  47.    }
  48. }

Código
  1. using System;
  2.  
  3. namespace GTAMapper.Extensions.Threading
  4. {
  5.    public class NamedHandler<TArg>
  6.    {
  7.        public readonly Func<string, TArg> Handler;
  8.  
  9.        public NamedHandler(Func<string, TArg> handler)
  10.        {
  11.            Handler = arg =>
  12.            {
  13.                using (new ThreadMarker(arg))
  14.                {
  15.                    return handler(arg);
  16.                }
  17.            };
  18.        }
  19.    }
  20. }

Caso de uso:

Código
  1. int TaskId = new Random().Next();
  2.  
  3. ThreadPool.QueueUserWorkItem(new NamedHandler<WaitCallback>(name => new WaitCallback(BackgroundRunner)).Handler($"Ninja #{TaskId}"));


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 22 Octubre 2018, 19:16 pm
ThreadedDebug: Una utilidad que funciona junto NamedThread & ThreadMarker mostrándote información del Thread actual al debuggear en Unity3D

Código
  1. using GTAMapper.Extensions.Threading;
  2. using System;
  3. using UnityEngine;
  4.  
  5. namespace GTAMapper.Utils.Debugging
  6. {
  7.    public static class ThreadedDebug
  8.    {
  9.        private static double TimeRunning
  10.        {
  11.            get
  12.            {
  13.                return (DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime).TotalSeconds;
  14.            }
  15.        }
  16.  
  17.        public static void Log(object obj, bool jumpBack = true)
  18.        {
  19.            Debug.Log($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + obj.ToString());
  20.        }
  21.  
  22.        public static void LogFormat(string str, params object[] objs)
  23.        {
  24.            LogFormat($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + str, true, objs);
  25.        }
  26.  
  27.        public static void LogFormat(string str, bool jumpBack = true, params object[] objs)
  28.        {
  29.            Debug.LogFormat($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + str, objs);
  30.        }
  31.  
  32.        public static void LogWarning(object obj, bool jumpBack = true)
  33.        {
  34.            Debug.LogWarning($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + obj.ToString());
  35.        }
  36.  
  37.        public static void LogWarningFormat(string str, params object[] objs)
  38.        {
  39.            LogWarningFormat($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + str, true, objs);
  40.        }
  41.  
  42.        public static void LogWarningFormat(string str, bool jumpBack = true, params object[] objs)
  43.        {
  44.            Debug.LogWarningFormat($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + str, objs);
  45.        }
  46.  
  47.        public static void LogError(object obj, bool jumpBack = true)
  48.        {
  49.            Debug.LogError($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + obj.ToString());
  50.        }
  51.  
  52.        public static void LogErrorFormat(string str, params object[] objs)
  53.        {
  54.            LogErrorFormat($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + str, true, objs);
  55.        }
  56.  
  57.        public static void LogErrorFormat(string str, bool jumpBack = true, params object[] objs)
  58.        {
  59.            Debug.LogErrorFormat($"[{ThreadMarker.Name} | {TimeRunning.ToString("F2")}] " + str, objs);
  60.        }
  61.  
  62.        public static void LogException(System.Exception ex, bool jumpBack = true)
  63.        {
  64.            Debug.LogException(ex);
  65.        }
  66.    }
  67. }

Básicamente, se usa igual que Debug.LogXX de Unity (https://docs.unity3d.com/ScriptReference/Debug.html) pero este a diferencia, te muesta el momento en el que se ha llamado, y desde el Thread que lo ha hecho.

Ejemplo:

(https://i.imgur.com/ITg6mH6.png)

PD: En el ejemplo, vemos dos Count, simplemente, estaba probando que el ConcurrentQueuedCoroutines funcionaba bien, viendo como ConcurrentQueued almacena los datos en todos los contextos usando la instrucción lock(...) internamente.


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 22 Octubre 2018, 19:33 pm
SendMessageContext: Una implementación Thread-Safe de SendMessage

Básicamente, el puñetero de Unity no te deja llamar la función SendMessage desde otro hilo, así que gracias a esta implementación que estuve probando, resolví el problema, para luego darme cuenta de que usaba (blocking) instrucciones en el main thread (dentro de una coroutina gracias a esta utilidad (https://assetstore.unity.com/packages/tools/thread-ninja-multithread-coroutine-15717) es facil/visual cambiar de contexto) :xD (Sí, así somos los programadores cuando tenemos sueño, implementamos cosas que luego ni usaremos)

La utilidad en cuestión: https://forum.unity.com/threads/sendmessage-argumentexception-error.73134/

Mi implementación:

Código
  1. using System.Collections.Concurrent;
  2. using UnityEngine;
  3.  
  4. namespace GTAMapper.Extensions.Threading
  5. {
  6.    public class SendMessageHelper : MonoBehaviour
  7.    {
  8.        private static ConcurrentQueue<SendMessageContext> QueuedMessages = new ConcurrentQueue<SendMessageContext>();
  9.  
  10.        public static void RegisterSendMessage(SendMessageContext context)
  11.        {
  12.            QueuedMessages.Enqueue(context);
  13.        }
  14.  
  15.        private void Update()
  16.        {
  17.            if (QueuedMessages.Count > 0)
  18.            {
  19.                SendMessageContext context = null;
  20.  
  21.                if (!QueuedMessages.TryDequeue(out context))
  22.                    return;
  23.  
  24.                context.Target.SendMessage(context.MethodName, context.Value, context.Options);
  25.            }
  26.        }
  27.    }
  28. }

Nota: No olvidéis añadir esto al inspector de cualquier gameobject donde se quiera usar (con añadir uno solo bastaría)

Código
  1. using UnityEngine;
  2.  
  3. namespace GTAMapper.Extensions.Threading
  4. {
  5.    public class SendMessageContext
  6.    {
  7.        public MonoBehaviour Target;
  8.        public string MethodName;
  9.        public object Value;
  10.        public SendMessageOptions Options = SendMessageOptions.RequireReceiver;
  11.  
  12.        public SendMessageContext(MonoBehaviour target, string methodName, object value, SendMessageOptions options)
  13.        {
  14.            this.Target = target;
  15.            this.MethodName = methodName;
  16.            this.Value = value;
  17.            this.Options = options;
  18.        }
  19.    }
  20. }

Algo que en el topic de arriba no está ;D

Código
  1. using UnityEngine;
  2.  
  3. namespace GTAMapper.Extensions.Threading
  4. {
  5.    public static class SendMessageExtensions
  6.    {
  7.        public static SendMessageContext SendSafeMessage(this MonoBehaviour monoBehaviour, string methodName, object value = null, SendMessageOptions sendMessageOptions = default(SendMessageOptions))
  8.        {
  9.            return new SendMessageContext(monoBehaviour, methodName, value, sendMessageOptions);
  10.        }
  11.    }
  12. }

Con esta extensión lo que se consigue es simplificar su uso, básicamente, desde un MonoBehaviour cualquiera dentro del metodo Start() (https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) haciendo esto:

Código
  1. this.SendSafeMessage("pepito")

Y luego (desde el mismo MonoBehaviour):

Código
  1. public void pepito() {
  2.    // Moar code que seguirá ejecutándose en el mismo hilo desde el que se llamo "SendSafeMessage"
  3. }

Un saludo.
PD: Aquí termina el flood de snippets, staff no preocuparse, llevo muchos días picando código, y he querido compartir post a post mis utilidades :xD
PD2: Ale ya puedo borrar todo lo que no uso. Que no es poco. ;-) ;-)


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 23 Octubre 2018, 18:31 pm
Simplificar una lista de puntos para obtener las esquinas (dicho de otra forma, obtener la minima cantidad de puntos que puedan definir dicha forma)

Esto ya lo respondí en Stackoverflow: https://stackoverflow.com/a/52952874/3286975 (Pero lo traduciré)

Citar
Después de horas de investigación he encontrado una librería Simplify.NET que internamente ejecuta el algoritmo de Ramer–Douglas–Peucker (https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm).

También, os puede interesar el algoritmo de Bresenham (https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm), con este algoritmo podéis dibujar una linea a partir de dos puntos (https://github.com/asilarslan/Computer-Graphic/blob/master/Computer%20Graphic/Assets/Scrit/BresenhamLines.cs).

Con este algoritmo, se podría comprobar si la tolerancia del primer algoritmo es muy alta (comprobando los puntos generados y los que ya tenías antes de simplificar, haciendo una función que te devuelva un porcentaje de similitud). Por suerte, la implementación de Simplify.NET es rápida (unos 25.000 ticks de Stopwatch), llamandose unas 10 veces solo llevaría unos ~30 ms.

Finalmente, es interesante mencionar el algoritmo Concave Hull (https://github.com/Liagson/ConcaveHullGenerator) y Convex Hull (https://gist.github.com/dLopreiato/7fd142d0b9728518552188794b8a750c) (y sus respectivas implementaciones en Unity3D).

Lo que he obtenido con esta implementación:

(https://i.stack.imgur.com/qqbNF.png)

(https://i.stack.imgur.com/SkE0Q.png)

Nota:
  • Los puntos azules son los puntos iterados.
  • Los puntos verdes son los puntos simplificados.

Y mi implementación: https://dotnetfiddle.net/aPOhPi

Es muy importante decir que los puntos deben de estar ordenados de tal forma que estén conectados. Si la forma es concava (como se puede ver en la segunda foto) quizás necesites una implementación que itere pixel a pixel dentro de la pared de la figura.

Aquí podéis ver una implementación (https://answers.unity.com/questions/1295599/order-points-from-array-based-on-8-connectivity-mo.html) gracias a Bunny83.

Un saludo.


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 14 Noviembre 2018, 22:03 pm
Dibujar curvas a partir de Perlin Noise (de forma acotada)

Por acotada me refiero, a que puedes establecer el centro del ruido y su amplitud.

Enlace: https://github.com/z3nth10n/Unity-Curve-Drawer

Imagen:

(https://imgur.com/DAL8Vz8.gif)

Un saludo.


Título: Re: Librería de códigos C# (Compartan aquí sus códigos)
Publicado por: z3nth10n en 12 Diciembre 2018, 02:47 am
Rasterizar un triangulo cualquiera

Hoy estoy felis, ya que he conseguido rasterizar un triangulo. Y me diréis pues muy bien no? Vale, os explico.

Rasterizar (https://es.wikipedia.org/wiki/Rasterizaci%C3%B3n) es esto. Es decir, pintar por pantalla los pixeles que conforman un poligono cualquiera. Bueno sí... Pero lo que no os he dicho que un poligono cualquiera se puede triangular, es decir, como en el post anterior (https://foro.elhacker.net/net/libreria_de_codigos_c_compartan_aqui_sus_codigos-t435475.0.html;msg2176252#msg2176252) al igual que con Ramer-Douglas-Pecker puedes conseguir el minimo de puntos que definen un poligono cualquiera, con triangulación consigues algo parecido, ya que si lo pensamos, un triangulo es la minima expresión de poligono, es decir, el poligono con menos vertices y que de sumas de ellos podemos definir los demas.

Si queréis conseguir una buena triangulación os recomiendo LibTessDotNet (https://github.com/speps/LibTessDotNet). Funciona muy bien.

Esto es muy potente ya que puedes mostrar por pantalla (aunque para esto ya está DirectX y OpenGL, internamente, esto que comento es un trabajo que la GPU hace muy bien ya que se trata de repetir), hacer procesos más complejos (que es por lo que fundamental lo necesito yo) o bien hacer librerías de pago de rasterización para Unity :xD.

Así que aquí os dejo mi buena *****:

Código
  1. using System;
  2. using UnityEngine;
  3.  
  4. namespace UnityEngine.Utilities.Drawing
  5. {
  6.    // Uncomment this if you don't a an own implementation
  7.    /*public struct Point
  8.     {
  9.         public int x;
  10.         public int y;
  11.  
  12.         public Point(int x, int y)
  13.         {
  14.             this.x = 0;
  15.             this.y = 0;
  16.         }
  17.     }*/
  18.  
  19.    public static class TriangleUtils
  20.    {
  21.        public static void Rasterize(Point pt1, Point pt2, Point pt3, Action<int, int> action, bool debug = false)
  22.        {
  23.            /*
  24.                  // https://www.geeksforgeeks.org/check-whether-triangle-valid-not-sides-given/
  25.                  a + b > c
  26.                  a + c > b
  27.                  b + c > a
  28.              */
  29.  
  30.            if (!CheckIfValidTriangle(pt1, pt2, pt3, debug))
  31.                return;
  32.  
  33.            if (TriangleArea(pt1, pt2, pt3) <= 1)
  34.            {
  35.                Point center = GetTriangleCenter(pt1, pt2, pt3);
  36.                action?.Invoke(center.x, center.y);
  37.  
  38.                return;
  39.            }
  40.  
  41.            Point tmp;
  42.  
  43.            if (pt2.x < pt1.x)
  44.            {
  45.                tmp = pt1;
  46.                pt1 = pt2;
  47.                pt2 = tmp;
  48.            }
  49.  
  50.            if (pt3.x < pt2.x)
  51.            {
  52.                tmp = pt2;
  53.                pt2 = pt3;
  54.                pt3 = tmp;
  55.  
  56.                if (pt2.x < pt1.x)
  57.                {
  58.                    tmp = pt1;
  59.                    pt1 = pt2;
  60.                    pt2 = tmp;
  61.                }
  62.            }
  63.  
  64.            var baseFunc = CreateFunc(pt1, pt3);
  65.            var line1Func = pt1.x == pt2.x ? (x => pt2.y) : CreateFunc(pt1, pt2);
  66.  
  67.            for (var x = pt1.x; x < pt2.x; ++x)
  68.            {
  69.                int maxY;
  70.                int minY = GetRange(line1Func(x), baseFunc(x), out maxY);
  71.  
  72.                for (int y = minY; y <= maxY; ++y)
  73.                    action?.Invoke(x, y);
  74.            }
  75.  
  76.            var line2Func = pt2.x == pt3.x ? (x => pt2.y) : CreateFunc(pt2, pt3);
  77.  
  78.            for (var x = pt2.x; x <= pt3.x; ++x)
  79.            {
  80.                int maxY;
  81.                int minY = GetRange(line2Func(x), baseFunc(x), out maxY);
  82.  
  83.                for (int y = minY; y <= maxY; ++y)
  84.                    action?.Invoke(x, y);
  85.            }
  86.        }
  87.  
  88.        private static int GetRange(float y1, float y2, out int maxY)
  89.        {
  90.            if (y1 < y2)
  91.            {
  92.                maxY = Mathf.FloorToInt(y2);
  93.                return Mathf.CeilToInt(y1);
  94.            }
  95.  
  96.            maxY = Mathf.FloorToInt(y1);
  97.            return Mathf.CeilToInt(y2);
  98.        }
  99.  
  100.        private static Func<int, float> CreateFunc(Point pt1, Point pt2)
  101.        {
  102.            var y0 = pt1.y;
  103.  
  104.            if (y0 == pt2.y)
  105.                return x => y0;
  106.  
  107.            float m = (float)(pt2.y - y0) / (pt2.x - pt1.x);
  108.  
  109.            return x => m * (x - pt1.x) + y0;
  110.        }
  111.  
  112.        public static void RasterizeStandard(Point p1, Point p2, Point p3, Action<int, int> action, bool debug = false)
  113.        {
  114.            // Thanks to: https://www.davrous.com/2013/06/21/tutorial-part-4-learning-how-to-write-a-3d-software-engine-in-c-ts-or-js-rasterization-z-buffering/
  115.  
  116.            if (!CheckIfValidTriangle(p1, p2, p3, debug))
  117.                return;
  118.  
  119.            // Sorting the points in order to always have this order on screen p1, p2 & p3
  120.            // with p1 always up (thus having the y the lowest possible to be near the top screen)
  121.            // then p2 between p1 & p3
  122.            if (p1.y > p2.y)
  123.            {
  124.                var temp = p2;
  125.                p2 = p1;
  126.                p1 = temp;
  127.            }
  128.  
  129.            if (p2.y > p3.y)
  130.            {
  131.                var temp = p2;
  132.                p2 = p3;
  133.                p3 = temp;
  134.            }
  135.  
  136.            if (p1.y > p2.y)
  137.            {
  138.                var temp = p2;
  139.                p2 = p1;
  140.                p1 = temp;
  141.            }
  142.  
  143.            // inverse slopes
  144.            float dP1P2, dP1P3;
  145.  
  146.            // http://en.wikipedia.org/wiki/Slope
  147.            // Computing inverse slopes
  148.            if (p2.y - p1.y > 0)
  149.                dP1P2 = (p2.x - p1.x) / (p2.y - p1.y);
  150.            else
  151.                dP1P2 = 0;
  152.  
  153.            if (p3.y - p1.y > 0)
  154.                dP1P3 = (p3.x - p1.x) / (p3.y - p1.y);
  155.            else
  156.                dP1P3 = 0;
  157.  
  158.            // First case where triangles are like that:
  159.            // P1
  160.            // -
  161.            // --
  162.            // - -
  163.            // -  -
  164.            // -   - P2
  165.            // -  -
  166.            // - -
  167.            // -
  168.            // P3
  169.            if (dP1P2 > dP1P3)
  170.            {
  171.                for (var y = p1.y; y <= p3.y; y++)
  172.                {
  173.                    if (y < p2.y)
  174.                        ProcessScanLine(y, p1, p3, p1, p2, action);
  175.                    else
  176.                        ProcessScanLine(y, p1, p3, p2, p3, action);
  177.                }
  178.            }
  179.            // First case where triangles are like that:
  180.            //       P1
  181.            //        -
  182.            //       --
  183.            //      - -
  184.            //     -  -
  185.            // P2 -   -
  186.            //     -  -
  187.            //      - -
  188.            //        -
  189.            //       P3
  190.            else
  191.            {
  192.                for (var y = p1.y; y <= p3.y; y++)
  193.                {
  194.                    if (y < p2.y)
  195.                        ProcessScanLine(y, p1, p2, p1, p3, action);
  196.                    else
  197.                        ProcessScanLine(y, p2, p3, p1, p3, action);
  198.                }
  199.            }
  200.        }
  201.  
  202.        // drawing line between 2 points from left to right
  203.        // papb -> pcpd
  204.        // pa, pb, pc, pd must then be sorted before
  205.        private static void ProcessScanLine(int y, Point pa, Point pb, Point pc, Point pd, Action<int, int> action)
  206.        {
  207.            // Thanks to current y, we can compute the gradient to compute others values like
  208.            // the starting x (sx) and ending x (ex) to draw between
  209.            // if pa.y == pb.y or pc.y == pd.y, gradient is forced to 1
  210.            var gradient1 = pa.y != pb.y ? (y - pa.y) / (pb.y - pa.y) : 1;
  211.            var gradient2 = pc.y != pd.y ? (y - pc.y) / (pd.y - pc.y) : 1;
  212.  
  213.            int sx = (int)Mathf.Lerp(pa.x, pb.x, gradient1);
  214.            int ex = (int)Mathf.Lerp(pc.x, pd.x, gradient2);
  215.  
  216.            // drawing a line from left (sx) to right (ex)
  217.            for (var x = sx; x < ex; x++)
  218.                action?.Invoke(x, y);
  219.        }
  220.  
  221.        public static void RasterizeBarycentric(Point v1, Point v2, Point v3, Action<int, int> action, bool debug = false)
  222.        {
  223.            // Thanks to: http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html#algo3 && https://www.cs.unm.edu/~joel/cs491_VR/src/DrawUtilities.cs
  224.  
  225.            /* checks for a valid triangle */
  226.  
  227.            if (!CheckIfValidTriangle(v1, v2, v3, debug))
  228.                return;
  229.  
  230.            /* get the bounding box of the triangle */
  231.            int maxX = Mathf.Max(v1.x, Mathf.Max(v2.x, v3.x));
  232.            int minX = Mathf.Min(v1.x, Mathf.Min(v2.x, v3.x));
  233.            int maxY = Mathf.Max(v1.y, Mathf.Max(v2.y, v3.y));
  234.            int minY = Mathf.Min(v1.y, Mathf.Min(v2.y, v3.y));
  235.  
  236.            //if (debug)
  237.            //    Debug.Log($"({minX}, {minY}, {maxX}, {maxY})");
  238.  
  239.            /* spanning vectors of edge (v1,v2) and (v1,v3) */
  240.            Point vs1 = new Point(v2.x - v1.x, v2.y - v1.y);
  241.            Point vs2 = new Point(v3.x - v1.x, v3.y - v1.y);
  242.  
  243.            for (int x = minX; x <= maxX; x++)
  244.            {
  245.                for (int y = minY; y <= maxY; y++)
  246.                {
  247.                    Point q = new Point(x - v1.x, y - v1.y);
  248.  
  249.                    float s = Vector2.Dot(q, vs2) / Vector2.Dot(vs1, vs2);
  250.                    float t = Vector2.Dot(vs1, q) / Vector2.Dot(vs1, vs2);
  251.  
  252.                    if ((s >= 0) && (t >= 0) && (s + t <= 1))
  253.                    { /* inside triangle */
  254.                        action?.Invoke(x, y);
  255.                    }
  256.                }
  257.            }
  258.        }
  259.  
  260.        public static void RasterizeBresenham(Point vt1, Point vt2, Point vt3, Action<int, int> action, bool debugException = false)
  261.        {
  262.            // Thanks to: http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html#algo3 && https://www.cs.unm.edu/~joel/cs491_VR/src/DrawUtilities.cs
  263.  
  264.            /* checks for a valid triangle */
  265.  
  266.            if (!CheckIfValidTriangle(vt1, vt2, vt3, debugException))
  267.                return;
  268.  
  269.            string invalidTriangle = $"The given points must form a triangle. {{{vt1}, {vt2}, {vt3}}}";
  270.  
  271.            /* at first sort the three vertices by y-coordinate ascending so v1 is the topmost vertice */
  272.            sortVerticesAscendingByY(ref vt1, ref vt2, ref vt3);
  273.  
  274.            /* here we know that v1.y <= v2.y <= v3.y */
  275.            /* check for trivial case of bottom-flat triangle */
  276.            if (vt2.y == vt3.y)
  277.            {
  278.                if (!fillBottomFlatTriangle(vt1, vt2, vt3, action, debugException))
  279.                {
  280.                    if (debugException)
  281.                        Debug.LogWarning(invalidTriangle);
  282.  
  283.                    return;
  284.                }
  285.            }
  286.            /* check for trivial case of top-flat triangle */
  287.            else if (vt1.y == vt2.y)
  288.            {
  289.                if (!fillTopFlatTriangle(vt1, vt2, vt3, action, debugException))
  290.                {
  291.                    if (debugException)
  292.                        Debug.LogWarning(invalidTriangle);
  293.  
  294.                    return;
  295.                }
  296.            }
  297.            else
  298.            {
  299.                /* general case - split the triangle in a topflat and bottom-flat one */
  300.                Point v4 = new Point((int)(vt1.x + (vt2.y - vt1.y) / (float)(vt3.y - vt1.y) * (vt3.x - vt1.x)), vt2.y);
  301.  
  302.                if (!fillBottomFlatTriangle(vt1, vt2, v4, action, debugException) || !fillTopFlatTriangle(vt2, v4, vt3, action, debugException))
  303.                {
  304.                    if (debugException)
  305.                        Debug.LogWarning(invalidTriangle);
  306.  
  307.                    return;
  308.                }
  309.            }
  310.        }
  311.  
  312.        public static bool CheckIfValidTriangle(Point v1, Point v2, Point v3, bool debug = false)
  313.        {
  314.            /*
  315.                 // https://www.geeksforgeeks.org/check-whether-triangle-valid-not-sides-given/
  316.                 a + b > c
  317.                 a + c > b
  318.                 b + c > a
  319.             */
  320.  
  321.            float a = Vector2.Distance(new Vector2(v1.x, v1.y), new Vector2(v2.x, v2.y)),
  322.                  b = Vector2.Distance(new Vector2(v2.x, v2.y), new Vector2(v3.x, v3.y)),
  323.                  c = Vector2.Distance(new Vector2(v3.x, v3.y), new Vector2(v1.x, v1.y));
  324.  
  325.            if (a + b <= c || a + c <= b || b + c <= a)
  326.            {
  327.                if (debug)
  328.                    Debug.LogWarning($"The given points must form a triangle. {{{v1}, {v2}, {v3}}}");
  329.  
  330.                return false;
  331.            }
  332.  
  333.            return true;
  334.        }
  335.  
  336.        public static bool CheckIfValidTriangle(Point v1, Point v2, Point v3, out float a, out float b, out float c)
  337.        {
  338.            a = Vector2.Distance(new Vector2(v1.x, v1.y), new Vector2(v2.x, v2.y));
  339.            b = Vector2.Distance(new Vector2(v2.x, v2.y), new Vector2(v3.x, v3.y));
  340.            c = Vector2.Distance(new Vector2(v3.x, v3.y), new Vector2(v1.x, v1.y));
  341.  
  342.            if (a + b <= c || a + c <= b || b + c <= a)
  343.            {
  344.                // Debug.LogWarning($"The given points must form a triangle. {{{v1}, {v2}, {v3}}}");
  345.                return false;
  346.            }
  347.  
  348.            return true;
  349.        }
  350.  
  351.        private static bool fillBottomFlatTriangle(Point v1, Point v2, Point v3, Action<int, int> action, bool debugException = false)
  352.        {
  353.            try
  354.            {
  355.                float invslope1 = (v2.x - v1.x) / (v2.y - v1.y);
  356.                float invslope2 = (v3.x - v1.x) / (v3.y - v1.y);
  357.  
  358.                float curx1 = v1.x;
  359.                float curx2 = v1.x;
  360.  
  361.                for (int scanlineY = v1.y; scanlineY <= v2.y; scanlineY++)
  362.                {
  363.                    DrawLine((int)curx1, scanlineY, (int)curx2, scanlineY, action);
  364.  
  365.                    curx1 += invslope1;
  366.                    curx2 += invslope2;
  367.                }
  368.            }
  369.            catch (Exception ex)
  370.            {
  371.                if (debugException)
  372.                    Debug.LogException(ex);
  373.  
  374.                return false;
  375.            }
  376.  
  377.            return true;
  378.        }
  379.  
  380.        private static bool fillTopFlatTriangle(Point v1, Point v2, Point v3, Action<int, int> action, bool debugException = false)
  381.        {
  382.            try
  383.            {
  384.                float invslope1 = (v3.x - v1.x) / (v3.y - v1.y);
  385.                float invslope2 = (v3.x - v2.x) / (v3.y - v2.y);
  386.  
  387.                float curx1 = v3.x;
  388.                float curx2 = v3.x;
  389.  
  390.                for (int scanlineY = v3.y; scanlineY > v1.y; scanlineY--)
  391.                {
  392.                    DrawLine((int)curx1, scanlineY, (int)curx2, scanlineY, action);
  393.                    curx1 -= invslope1;
  394.                    curx2 -= invslope2;
  395.                }
  396.            }
  397.            catch (Exception ex)
  398.            {
  399.                if (debugException)
  400.                    Debug.LogException(ex);
  401.  
  402.                return false;
  403.            }
  404.  
  405.            return true;
  406.        }
  407.  
  408.        private static void sortVerticesAscendingByY(ref Point v0, ref Point v1, ref Point v2)
  409.        {
  410.            if (v2.y < v1.y)
  411.                Swap(ref v1, ref v2);
  412.  
  413.            if (v1.y < v0.y)
  414.                Swap(ref v0, ref v1);
  415.  
  416.            if (v2.y < v1.y)
  417.                Swap(ref v1, ref v2);
  418.        }
  419.  
  420.        private static void Swap<T>(ref T lhs, ref T rhs)
  421.        {
  422.            (lhs, rhs) = (rhs, lhs);
  423.  
  424.            // Impl for versions lower than C# 7
  425.  
  426.            //T temp;
  427.            //temp = lhs;
  428.            //lhs = rhs;
  429.            //rhs = temp;
  430.        }
  431.  
  432.        public static float TriangleArea(Point p1, Point p2, Point p3)
  433.        {
  434.            float a, b, c;
  435.  
  436.            if (!CheckIfValidTriangle(p1, p2, p3, out a, out b, out c))
  437.                return 0;
  438.  
  439.            return TriangleArea(a, b, c);
  440.        }
  441.  
  442.        public static float TriangleArea(float a, float b, float c)
  443.        {
  444.            // Thanks to: http://james-ramsden.com/area-of-a-triangle-in-3d-c-code/
  445.  
  446.            float s = (a + b + c) / 2.0f;
  447.            return Mathf.Sqrt(s * (s - a) * (s - b) * (s - c));
  448.        }
  449.  
  450.        // Bresenham impl: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
  451.  
  452.        public static void DrawLine(Vector2 p1, Vector2 p2, Action<int, int> action)
  453.        {
  454.            DrawLine((int)p1.x, (int)p1.y, (int)p2.x, (int)p2.y, action);
  455.        }
  456.  
  457.        public static void DrawLine(int x0, int y0, int x1, int y1, Action<int, int> action)
  458.        {
  459.            int sx = 0,
  460.                sy = 0;
  461.  
  462.            int dx = Mathf.Abs(x1 - x0),
  463.                dy = Mathf.Abs(y1 - y0);
  464.  
  465.            if (x0 < x1) { sx = 1; } else { sx = -1; }
  466.            if (y0 < y1) { sy = 1; } else { sy = -1; }
  467.  
  468.            int err = dx - dy,
  469.                e2 = 0;
  470.  
  471.            while (true)
  472.            {
  473.                //colors[P(x0 % width, y0 % height, width, height)] = x0 / width >= 1 || y0 / height >= 1 ? UnityEngine.Color.yellow : c;
  474.                action?.Invoke(x0, y0);
  475.  
  476.                if ((x0 == x1) && (y0 == y1))
  477.                    break;
  478.  
  479.                e2 = 2 * err;
  480.  
  481.                if (e2 > -dy)
  482.                {
  483.                    err = err - dy;
  484.                    x0 = x0 + sx;
  485.                }
  486.                if (e2 < dx)
  487.                {
  488.                    err = err + dx;
  489.                    y0 = y0 + sy;
  490.                }
  491.            }
  492.        }
  493.  
  494.        public static Point GetTriangleCenter(Point p0, Point p1, Point p2)
  495.        {
  496.            // Thanks to: https://stackoverflow.com/questions/524755/finding-center-of-2d-triangle
  497.  
  498.            return new Point(p0.x + p1.x + p2.x / 3, p0.y + p1.y + p2.y / 3);
  499.        }
  500.    }
  501. }

Gist: https://gist.github.com/z3nth10n/7d60f22c7e906f645d53c9622507c23b (dadle una estrellica, no me seais gitanos :laugh: :laugh:)

Vídeo de ejemplo:

7yY3MIyRtPw

Un saludo.