En mi otro blog, hace poco estuve hablando de un sistema de posicionamiento mixto como era el sistema de numeración en base primorial, el cual varía en cada posición la base, bueno pues he creado una estructura en la cual se incluye además de la conversión, los operadores de suma, resta, multiplicación y división y los de comparación. Es una estructura sencilla en la que no me he esmerado mucho, pero espero que quede claro el uso de estructuras, interfaces y sobrescritura de operadores. Lo primero de todo, ¿por que una estructura y no una clase?. Antes de nada vamos a enumerar las características de las estructuras:
- Una estructura hereda de la clase abstracta
System.ValueType
y esta a su vez deobject
- Una estructura no permite la herencia, solo pueden implementar interfaces
- Una estructura no puede ser abstracta
- No permite un constructor sin parámetros y no puede tener destructor.
- Si contiene campos o propiedades, tienen que ser asignados en el constructor
- Las variables no pueden ser inicializadas fuera del constructor
- Es posible crear una estructura sin usar el operador new, en tal caso, no es posible usar la estructura hasta que todos los campos se inicialicen.
¿Para que se utilizan? Para representar objetos de poca monta que no tengan grandes cálculos (también podríamos hacerlo con una clase), pero lo primero, nos aseguramos que se han inicializado todas las variables y campos y en el caso de tener un constructor, contiene un parámetro de entrada como mínimo, además no es necesario instanciar la estructura mediante new. El típico ejemplo de una estructura sería un sistema de coordenadas, como podemos ver en el ejemplo, se asignan los valores en el constructor y podemos tener
public struct Cooordenadas3D { public int x, y, z; public Cooordenadas3D(int _x, int _y, int _z) { x = _x; y = _y; z = _z; } public override string ToString() { return "(" + x + ", " + y + ", " + z + ")"; } public double GetModulo() { //.... return ... } public double GetAngulo() { //.... return ... }
Y yo con mi sistema de numeración en base primorial lo he hecho, he implementado el sistema mediante una estructura y que vamos a detallar con detenimiento.
Lo primero, hemos dicho que no puede heredar de ninguna clase, pero si puede implementar interfaces. Yo implemento las interfaces IComparable, IComparable<PrimorialBaseNumber>, IEquatable<PrimorialBaseNumber>
. La primera y la segunda implementan el método CompareTo
que nos hará falta para los operadores y la tercera, que implementa el método Equals
que nos hará falta también para los operadores.
He acotado el código en regiones y dentro de estas, explico que hay:
- Constructor (contructor). En el constructor, le pasamos como parámetro el número decimal que queremos convertir a nuestro sistema y dentro de este inicializamos el array de los primero 45 números primos y ¿por qué 45? esta lista se usará para generar el primorial y el primorial 45 o lo que es lo mismo 199# equivale a 7.7999220416834615532491991063298e+81 que para este ejemplo creo que va bien, al mismo tiempo, este número tan grande, lo he usado como valor máximo del sistema, (por acotar)
- Constants (constantes). He declarado cinco constantes, de la cuales realmente solo usamos dos, el valor máximo y mínimo (el resto las tengo de otras estructuras y clases y las he dejado). como he comentado, el valor máximo y mínimo, es el valor del primorial de 199 (199#)
- Properties and Fields (Campos y propiedades). Un array estática con los 45 primeros números primos, un propiedad para almacenar el valor en decimal (
DecimalValue
) y otra el valor en base primorial (Value
). Dentro de la propiedadDecimalValue
, se calcula el valor deValue
, de modo que si cambiamos el valor del primero, se calcula el del segundo. - Public Methods (Métodos públicos).
- Comparisons (comparaciones). En esta región entran los métodos
CompareTo
de objeto y del tipoPrimorialBaseNumber
, obligatoriamente implementados ya que la interfaceIComparable, IComparable
así lo marcan. Estos devuelven 1 si es mayor, 0 si es igual y -1 si es menor y el métodoEquals
por la interfazIEquatable<PrimorialBaseNumber>
; esta devuelve true si los dos objetos son iguales. - Conversions (conversiones). En esta región, tres métodos y uno de ellos sobrecargado.
ToString()
que devuelve el valor en base primorial,ToDecimal()
que devuelve el valor en base decimal yToPrimorialBase()
oToPrimorialBase(decimal _number)
, el primero hace lo mismo queToString()
pero devuelve un valor tipo decimal y el segundo asigna el valor en base decimal aDecimalValue
y en consecuencia lo convierte y devuelve el número convertido. - Operators (operadores). Un ejemplo de sobrecarga de los operadores lo expuse en el post de calculadora de números complejos, en el que si yo sumo el objeto complex con otro complex, el compilador no sabe como ejecutar el cálculo, mientras que si sobrecargamos el método, podemos decirle como efectuar esa operación y cada vez que se use, así lo hará. He sobrecargado todos los operadores posibles, para hacerlo lo más genérico posible. Para realizar las operaciones podría haber usado un método más elemental matemáticamente hablando, sumando dígito a dígito, acarreos, etc., pero lo he hecho con las operaciones en base decimal, es más fácil, ¿no?.
- Static (Estáticos). En esta región incluyo la obtención del primorial mediante un método estático, el cual puede ser accesible desde fuera y dentro de la estructura.
- Comparisons (comparaciones). En esta región entran los métodos
- Private methods (métodos privados). aquí iría el motor de la estructura, que para el usuario debe estar oculto (tu no ves en tu coche los pistones moverse o las explosiones en los cilindros, ¿verdad?), donde se calculan las conversiones a base decimal, a base primorial.
y ahora el código
/* ************************************************************************************ * © JOAQUIN MARTINEZ RUS 2016 * PROYECTO: PrimorialBaseNumber * Nombre: Mersenne * Archivo: PrimorialBaseNumber.cs * Descripción: Estructura de un sistema de numeración mixto primorial * Historial: * 1. Joaquin Martínez Rus - 19 ago 2016. Creación * * Comentarios: Contempla la conversión de números en base decimal a base primorial y viceversa, * así como los operadores de suma, resta, multiplicación, división, resto, igualdad, * distinto, complemento y comparativos * * ************************************************************************************/ using System.Collections.Generic; using System.Linq; using System.Text; namespace System { public struct PrimorialBaseNumber : IComparable, IComparable, IEquatable { #region Constructor public PrimorialBaseNumber(decimal _DecimalNumber) { // Inicializando todas las propiedades y variables PrimorialBaseNumber.primeNumber = new decimal[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199 }; Value = 0; _decimalValue = 0; DecimalValue = _DecimalNumber; } #endregion #region Constants public const double MaxValue = 7.7999220416834615532491991063298e+81; // 199# public const double MinValue = -7.7999220416834615532491991063298e+81; public const double NaN = 0D / 0D; public const double NegativeInfinity = -1D / 0D; public const double PositiveInfinity = 1D / 0D; #endregion #region Properties and Fields private decimal _decimalValue; public decimal DecimalValue { get { return _decimalValue; } set { _decimalValue = value; Value= ConvertToPrimorialBaseNumber(_decimalValue); } } public decimal Value { get; set; } public static decimal[] primeNumber { get; set; } #endregion #region Public Methods #region Comparisons /// <summary> /// Compara esta instancia con un objeto específico y retorna un entero que indica /// si el valor es menor que, igual a o mayor que el valor de objeto específico /// </summary> /// Objeto a comparar a null /// Retorna un entero con signo. Negativo si es menor, un 0 si es igual y un entero positivo si es mayor. /// Si el valor es null, retorna error public int CompareTo(object obj) { try { if (obj == null) throw new ArgumentNullException(); return CompareTo((PrimorialBaseNumber)obj); } catch (Exception) { throw new FormatException("Error en la conversión del tipo Object al tipo PrimorialBaseNumber"); } } /// <summary> /// Compara esta instancia con un objeto específico y retorna un entero que indica /// si el valor es menor que, igual a o mayor que el valor de objeto específico /// </summary> /// Objeto a comparar a null /// Retorna un entero con signo. Negativo si es menor, un 0 si es igual y un entero positivo si es mayor. /// Si el valor es null, se devuelve NaN public int CompareTo(PrimorialBaseNumber obj) { int value = 0; if (DecimalValue > obj.DecimalValue) { value = 1; } else if (DecimalValue < obj.DecimalValue) { value = -1; } return value; } /// <summary> /// Retorna un valor que indica si esta instancia es igual a un PrimorialBaseNumber específico /// </summary> /// Un objeto PrimorialBaseNumber a comparar con esta instancia /// True si tiene el mismo valor que esta instancia public bool Equals(PrimorialBaseNumber other) { try { return this == other; } catch (Exception) { throw new FormatException(); } } /// <summary> /// Retorna un valor que indica que esta instancia es igual a un objeto específico /// </summary> /// Un objeto a comparar con esta instancia /// True si es una instancia del tipo PrimorialBaseNumber y tiene el mismo valor que esta instancia public override bool Equals(Object other) { try { return Equals((PrimorialBaseNumber)other); } catch (Exception) { throw new FormatException("Error en la conversión del tipo Object al tipo PrimorialBaseNumber"); } } #endregion #region Conversions /// <summary> /// Convierte el valor numérico de esta instancia a su equivalente string /// </summary> /// El equivalente a string de esta instancia public override string ToString() { return Value.ToString(); } /// <summary> /// Obtiene el valor numérico en base decimal /// </summary> /// El equivalente al número en base decimal public decimal ToDecimal() { return DecimalValue; } /// <summary> /// Obtiene el valor numérico en base primorial /// </summary> /// El equivalente al número en base primorial public decimal ToPrimorialBase() { return Value; } /// <summary> /// Convierte el número del parámetro en base decimal a base primorial y /// retorna el número convertido asignándolo al valor de esta instancia /// </summary> /// Número en base decimal que se va a convertir a base primorial /// Número en base primorial public decimal ToPrimorialBase(decimal _number) { DecimalValue = _number; return Value; } #endregion #region Operators /// <summary> /// Retorna un valor que indica la suma de ambos términos /// </summary> /// Primer valor de la suma /// Segundo valor de la suma /// Valor de la suma de ambos términos en base Primorial public static PrimorialBaseNumber operator + (PrimorialBaseNumber left, PrimorialBaseNumber right) { decimal value = left.DecimalValue + right.DecimalValue; if ((double)value > MaxValue || (double)value < MinValue) { throw new OverflowException("El valor de la suma es superior o inferior al valor máximo o mínimo respectivamente de la instancia"); } return new PrimorialBaseNumber(value); } /// <summary> /// Retorna un valor que indica la resta de ambos términos /// </summary> /// Primer valor de la resta /// Segundo valor de la resta /// Valor de la resta de ambos términos en base Primorial public static PrimorialBaseNumber operator -(PrimorialBaseNumber left, PrimorialBaseNumber right) { decimal value = left.ConvertToDecimal() - right.ConvertToDecimal(); if ((double)value > MaxValue || (double)value < MinValue) { throw new OverflowException("El valor de la resta es superior o inferior al valor máximo o mínimo respectivamente de la instancia"); } return new PrimorialBaseNumber(value); } /// <summary> /// Retorna un valor que indica el producto de ambos términos /// </summary> /// Primer valor del producto /// Segundo valor del producto /// Valor del producto de ambos términos en base Primorial public static PrimorialBaseNumber operator * (PrimorialBaseNumber left, PrimorialBaseNumber right) { decimal value = left.ConvertToDecimal() * right.ConvertToDecimal(); if ((double)value > MaxValue || (double)value < MinValue) { throw new OverflowException("El valor del producto es superior o inferior al valor máximo o mínimo respectivamente de la instancia"); } return new PrimorialBaseNumber(value); } /// <summary> /// Retorna un valor que indica la división de ambos términos /// </summary> /// Primer valor de la división /// Segundo valor de la división /// Valor de la división de ambos términos en base Primorial public static PrimorialBaseNumber operator / (PrimorialBaseNumber left, PrimorialBaseNumber right) { if (right.ConvertToDecimal()==0) { throw new DivideByZeroException(); } decimal value = left.ConvertToDecimal() / right.ConvertToDecimal(); if ((double)value > MaxValue || (double)value < MinValue) { throw new OverflowException("El valor de la división es superior o inferior al valor máximo o mínimo respectivamente de la instancia"); } return new PrimorialBaseNumber(value); } /// <summary> /// Retorna un valor que indica el resto de la división de ambos términos /// </summary> /// Primer valor de la operación resto /// Segundo valor de la operación resto /// Valor del resto de la división de ambos términos en base Primorial public static PrimorialBaseNumber operator % (PrimorialBaseNumber left, PrimorialBaseNumber right) { decimal value = left.DecimalValue % right.DecimalValue; return new PrimorialBaseNumber(value); } /// <summary> /// Retorna un valor que indica el complemento de esta instancia /// </summary> /// Valor del número al que se le calcula el complemento /// Valor del complemento del número public static PrimorialBaseNumber operator !(PrimorialBaseNumber _number) { int index = 0; decimal primorial = 1; decimal complemento = 0; // Buscar el primer primorial que sea mayor que el número do { primorial = GetPrimorial(index); index++; } while (primorial < _number.DecimalValue); complemento = primorial - _number.DecimalValue; return new PrimorialBaseNumber(complemento); } /// <summary> /// Retorna un valor que indica que ambos valores son iguales /// </summary> /// Primer valor de la comparación /// Segundo valor de la comparación< /// Verdadero si el valor de la izquierda y el de la derecha son iguales; en otro caso, retorna false public static bool operator ==(PrimorialBaseNumber left, PrimorialBaseNumber right) { bool isEqual = true; if (left.ToString().Length!= right.ToString().Length) { isEqual = false; } for (int i = 0; i < left.ToString().Length; i++) { if (left.ToString()[i]!=right.ToString()[i]) { isEqual = false; break; } } return isEqual; } /// <summary> /// Retorna un valor que indica que ambos valores no son iguales /// </summary> /// Primer valor de la comparación /// Segundo valor de la comparación /// Verdadero si el valor de la izquierda y el de la derecha no son iguales; en otro caso, retorna false public static bool operator !=(PrimorialBaseNumber left, PrimorialBaseNumber right) { return !(left==right); } /// <summary> /// Retorna un valor que indica que un valor específico es menor que otro /// </summary> /// Primer valor de la comparación /// Segundo valor de la comparación /// Verdadero si el valor de la izquierda es menor que el de la derecha; en otro caso, retorna false public static bool operator <(PrimorialBaseNumber left, PrimorialBaseNumber right) { if (left.CompareTo(right) ==-1) { return true; } else { return false; } } /// <summary> /// Retorna un valor que indica que un valor específico es mayor que otro /// </summary> /// Primer valor de la comparación /// Segundo valor de la comparación /// Verdadero si el valor de la izquierda es mayor que el de la derecha; en otro caso, retorna false public static bool operator >(PrimorialBaseNumber left, PrimorialBaseNumber right) { if (left.CompareTo(right) == 1) { return true; } else { return false; } } /// <summary> /// Retorna un valor que indica que un valor específico es menor o igual que otro /// </summary> /// Primer valor de la comparación /// Segundo valor de la comparación /// Verdadero si el valor de la izquierda es menor o igual que el de la derecha; en otro caso, retorna false public static bool operator <=(PrimorialBaseNumber left, PrimorialBaseNumber right) { if (left.CompareTo(right) == -1 || left.CompareTo(right)==0) { return true; } else { return false; } } /// <summary> /// Retorna un valor que indica que un valor específico es mayor o igual que otro /// </summary> /// Primer valor de la comparación /// Segundo valor de la comparación /// Verdadero si el valor de la izquierda es mayor o igual que el de la derecha; en otro caso, retorna false public static bool operator >=(PrimorialBaseNumber left, PrimorialBaseNumber right) { if (left.CompareTo(right) == 1 || left.CompareTo(right) == 0) { return true; } else { return false; } } #endregion #region Static /// <summary> /// Retorna el valor del primorial de n /// </summary> /// Valor de n en el conjunto de los números primos /// Retorna el valor correspondiente al primorial de n public static decimal GetPrimorial(int n) { if (n > primeNumber.Length) { throw new OverflowException(); } decimal result = 1; for (int i = 0; i < n; i++) { Decimal prime = primeNumber[i]; result *= prime; } return result; } #endregion #endregion #region Private Methods /// <summary> /// Convierte un número en base decimal a base primorial /// </summary> /// Número que se va a convertir /// Valor decimal del número en base primorial private decimal ConvertToDecimal(decimal _number) { IEnumerable arrayToString = _number.ToString().ToCharArray().Reverse(); Decimal result = 0; int contador = 0; foreach (var item in arrayToString) { Decimal primorial = GetPrimorial(contador++); int digit = Convert.ToInt32(item.ToString()); result += digit * primorial; } return result; } /// <summary> /// Convierte el valor en base decimal de esta instancia a base primorial /// y lo asigna al valor en base primorial de esta instancia /// </summary> /// Valor decimal del número en base primorial private decimal ConvertToDecimal() { return ConvertToDecimal(Value); } /// <summary> /// Convierte un número decimal en base 10 a base primorial /// </summary> /// Número a convertir /// Número en base primorial private decimal ConvertToPrimorialBaseNumber(decimal _number) { StringBuilder result = new StringBuilder(); decimal dividendo = _number; decimal resto = 0; int index = 0; do { resto = dividendo % primeNumber[index]; dividendo = Math.Truncate(dividendo / primeNumber[index]); result.Insert(0, resto); index++; } while (dividendo > 0); return Convert.ToDecimal(result.ToString()); } #endregion } }
y para ver como funciona, o creáis un formulario y le añadis la estructura o creáis una aplicación de consola y hacéis lo mismo. Yo he creado un formulario, le he añadido un textbox y le he ido añadiendo los calculos para ver que funciona
// Iniciando estructuras PrimorialBaseNumber pbnn1 = new PrimorialBaseNumber(907); PrimorialBaseNumber pbnn2 = new PrimorialBaseNumber(487); textBox.AppendText("487=> " + pbnn1.ToPrimorialBase(487) + "\n"); textBox.AppendText("907=> " + pbnn1.ToPrimorialBase(907) + "\n"); textBox.AppendText("suma 487+907 => " + (pbnn1 + pbnn2) + "\n"); textBox.AppendText("resta 907-487 => " + (pbnn1 - pbnn2) + "\n"); textBox.AppendText("multi 487*907 =>" + (pbnn1 * pbnn2) + "\n"); textBox.AppendText("!1544=> " + (2310-1544) + "\n"); pbnn1.ToPrimorialBase(1544); textBox.AppendText("!1544=> " + !pbnn1 + "\n"); textBox.AppendText("766=> " + !pbnn1 + "\n");
y este el resultado
487=> 22101 907=> 42101 suma 487+907 => 64210 resta 907-487 => 20000 multi 487*907 => 14922301 !1544=> 766 !1544=> 34220 766=> 34220
como siempre, comentad el código y mantenedlo organizado y si tenéis alguna pregunta, no dudéis en dejar un comentario
saludos
Pd: Repito, esta estructura está realizada en una tarde, por lo que entiendo que es muy mejorable y debe abarcar todos las cotas y aspectos, pero para aprender , a mi parecer, está bien, además como mejor se aprende, es picando código!
Un comentario en “Sistema de numeración en base primorial”