jueves, 30 de agosto de 2012

2nd Assignment - One Time Pad Analysis

For the second assignment, I was tasked to do some sort of analysis for my one-time-pad program that I did for the previous assignment, and with that hack it. I don't think I can actually hack the program, because even if the random numbers that python's randint function uses aren't totally random(of course they aren't) I can't seem to think of a way to predict them or figure a pattern.

So I will focus on the analysis of the python random generator, doing some tests.

Character Frequency Test

First, I did a simple frequency test, to see which letters were generated the most for the key. I have to say that my keys were generated using randint in a reduced range for all the letters of the alphabet(in lowercase) and also the space character. So it would be something like randint(0, 28).

With that said, I just wanted to get a frequency of the letters, which would also be the frequency of the numbers, to see how many times some characters were repeated. Of course this doesn't tell much, because as we can see on the graphic, the frequency of each number, even though it is different, it is not a huge difference so we can't tell if there's a pattern at all.

Frequency Monobit Test

To test the proportion of ones and zeros in order to find if there are many zeros or many ones, the monobit test is useful. The puropose is to find if the number of zeros and ones is approximately the same, as expected in a random sequence. I already had done this in a previous class, so I just had to adapt a little the code to my current one-time-pad key generator, but a better explanation to the monobit test can be found here(in spanish).

def frequency_test(lista):
  suma = 0
  lista = list(lista)
  n = len(lista)
  for i in range(len(lista)):
    if lista[i] == "0":
      suma += -1
      suma += 1
  suma_abs = abs(suma)/math.sqrt(n)
  p_value = math.erfc(suma_abs/math.sqrt(2))
  if p_value < 0.01:
    print "Frequency Test: Not passed\n"
    print "Frequency Test: Passed\n"

The test produced the next results:

The p value, which is used to determine if the test is passed or not, was bellow 0.01, which means the test didn't passed, so our random generator  doesn't generate a good enough proportion of random zeros and ones. I think generating the bits of the key one by one, using an uniform distribution would help in improving this, because the number of ones and zeros would be more proportioal.

Searching for a Graphical Pattern in a Bitmap

An easy way to tell if a random generator is just plain dumb, is to look at the patterns it can generate in a graphical way. For example, this image taken from RANDOM.ORG is a bitmap of the PHP rand function on Windows:

PHP rand in Windows

As we can see, there is a clear graphical pattern, so we can tell that is not really random. Now i tried to do the same with the python random generator, using Tkinter to draw small dots using the line widget. I move between every coordinate in an area, and I draw a dot if a random generated number is 1, or I don't if it is 0. The simple python script I used:

from Tkinter import *
from random import randint

master = Tk()

w = Canvas(master, width=700, height=700, background="white")
for i in range(700):
  for j in range(700):
    if randint(0, 1) == 1:
      w.create_line(i, j, i+1, j+1)


 The result was this:

Python's random in Linux

I personally can't see a visual pattern here, so I assume this is a pretty good random generator, so I call this test passed.

Chi2 test

Using the chisquare function of the scipy library, it is possible to do the chisquare test to an array of frequencies. For this, I used the frequencies of the first test, and the simple code is the following:

from random import randint
import numpy
import scipy.stats
alphabet = "abcdefghijklmnopqrstuvwxyz "
def generate_key(size):
  key = ""
  for i in range(size):
     key += alphabet[randint(0, len(alphabet)-1)]
  return key

def zeros(size):
  array = []
  for i in range(size):
  return array

def frequency_test(key):
  freq = zeros(len(key))  
  for i in range(len(key)):
    for j in range(len(key)):
      if key[i] == key[j]:
  return freq

def expected(size):
  box_size = size/len(alphabet)
  freq_exp = zeros(size)
  for i in range(size):
    freq_exp[i] = box_size
  return freq_exp

freq_obs = frequency_test(generate_key(100))
freq_exp = expected(len(freq_obs))
array_obs = numpy.array(freq_obs)
array_exp = numpy.array(freq_exp)
print "Observable Frequencies(Random Numbers frequencies): %s\n"%array_obs
print "Expected Frequencies: %s"%array_exp

chi_stat, p_value = scipy.stats.chisquare(array_obs, array_exp)
print "Chi2 Statistic: %s"%chi_stat
print "P Value Statistic: %s"%p_value

The function scipy.stats.chisquare(f_obsf_exp=Noneddof=0) generates two values, the chisquare test statistic, and the p-value, used to check if the test was passed or not.

The parameters are:
  • f_obs : array
    • observed frequencies in each category
  • f_exp : array, optional
    • expected frequencies in each category. By default the categories are assumed to be equally likely.
  • ddof : int, optional
    • adjustment to the degrees of freedom for the p-value

To get the observed frequencies, like I said I use the first frequency test to get which letters, or random numbers are repeated and how much time, and I fill a list of the respective frequencies. For the expected frequencies, I generated a list of frequencies, but these frequencies are as we would expect to be, so I divided the size of the random samples (the size of the alphabet used to generate keys) between the size of the key, so I could fill boxes with the same frequencies, as expected from a uniform random generator. 

Then giving these two parameters to the function, I got a p_value, and a chisquare statistic. The p value just like the monobit test, help us to determine if the test was passed or not. In this case the test never passed:

Things to get my One-Time-Pad better

First of all, I fixed some minor things that my one time pad:
  1. My one-time-pad generated one key per word of the message, which at the end wouldn't be much different from the regular results, but it wasn't the best approach.
  2. My one-time-pad used to skip spaces, not encrypting them. So, now I added the spaces to the alphabet array, so it can be encrypted along with the message.
  3. My one-time-pad wasn't modular. It used to be sequential, doing a lot of things in the same function, so I separated every important part into its own function, like encrypting, deciphering, generating the key, etc.
Now an important thing that I think would make it better is a file, were all the used keys are stored, this is important, because even though python didn't reuse a single key running the program 10000 times, it is still likely that it will, sometime. So using this file, the program can make sure the generated key is totally new. Of course there is the problem with the reading/wrinting to files, but security comes first.

The code, updated:

from sys import argv, stdout
from random import randint
import math

alphabet = "abcdefghijklmnopqrstuvwxyz "
def letter_pos(letter):
  for i in range(len(alphabet)):
    if alphabet[i] == letter:
      return i
def wiki_cipher(msg, key):
  cipher_msg = ""
  for i in range(len(msg)):
    cipher_msg += alphabet[(letter_pos(msg[i]) + letter_pos(key[i])) % len(alphabet)]
  del key
  print "Cipher: %s"%cipher_msg
  return cipher_msg
def wiki_decipher(cipher_msg, key):
  msg = ""
  for i in range(len(cipher_msg)):
    msg += alphabet[(letter_pos(cipher_msg[i]) - letter_pos(key[i])) % len(alphabet)]
  del key
  print "Decipher %s"%msg
  return msg

def generate_key(size):
  key = ""
  for i in range(size):
     key += alphabet[randint(0, len(alphabet))]
  key = key[0:size]
  return key

def main():
   filename = argv[1]
   f = open(filename, "r")
   msg = f.readlines()[0]
   msg.replace("\n", "")
   print "Message:"
   print msg
 except IndexError as err:
   print "File not specified, using default message."
          msg = "testing the one time pad encryption"
 print "Message: %s"%msg.lower()
 key = generate_key(len(msg))
 cipher = wiki_cipher(msg, key)
 wiki_decipher(cipher, key)


martes, 28 de agosto de 2012

Tarea 3: Lógica Proposicional - Aplicaciones

Para la tercera tarea de Verificación y Validación de Software, tenemos que investigar las aplicaciones de la lógica proposicional.

Para esto encontré algunas aplicaciones como: Inteligencia Artificial, Circuitos Lógicos, y Acertijos Lógicos, pero solo explicaré estos dos últimos.

Aplicación - Acertijos Lógicos:

Una aplicación de la lógica proposicional que me pareció interesante, fue la de la creación de acertijos en base a proposiciones lógicas. El uso de la lógica para crear desafios mentales complicados, si bien no es una aplicación profeional, es un enfoque diferente a lo usual que estoy acostumbrado(circuitos, computación lógica, y demás).

Los acertijos que están basados en la lógica proposicional retan a las personas a obtener la respuesta correcta a partir de un conjunto de oraciones, las cuales pueden ser verdaderas o falsas según los condicionantes del propio acertijo.


[2]Guardian mentiroso

En una prisión de la que debemos salir, existen dos puertas. Una lleva a la salida. La otra, a la muerte segura. Cada puerta está custodiada por un guardián. Sabemos que uno de ellos dice siempre la verdad y que el otro miente siempre, pero no sabemos cuál es cada uno. La cuestión es: si pudieras hacer sólo una pregunta a uno de los dos, ¿qué pregunta le harías para saber qué puerta es la buena?

Solución: La pregunta que le haría es: "¿Cuál es la puerta que diría tu compañero que es la correcta?". En todo caso, la respuesta será la falsa.

[1]Knights and Knaves

Un visitante encuentra a tres habitantes de la isla de los caballeros y escuderos. Se acerca al primero y le pregunta: "¿Tú eres caballero o escudero?". Éste responde, pero el visitante no le entiende bien. Por su parte, el segundo dice: "Ha dicho que es escudero". Y el tercero apostilla: "Eso es mentira". ¿Qué son los habitantes segundo y tercero? (Tomando en cuenta que los caballeros siempre dicen la verdad, y los escuderos siempre mienten)

Solución: Nadie puede decir de sí mismo que es escudero, puesto que, si es caballero, debe decir la verdad y, si es escudero, dirá igualmente que es caballero porque miente siempre. Por lo tanto, el segundo habitante miente: es escudero. Y, el tercero, dice la verdad, por lo tanto, es caballero.

[2]Acertijo de los Cofres

Tenemos 4 cofres y dentro de uno hay un tesoro. Cada cofre contiene una inscripción y sabemos que 2 dicen la verdad y 2 mienten. ¿Dónde está el tesoro?

Cofre 1: El tesoro no está aquí.
Cofre 2: El cofre 1 dice la verdad.
Cofre 3: El tesoro no está en el cofre 2.
Cofre 4: El cofre 3 está vacío.

Solución: Para que se cumpla que dos digan la verdad y dos mientan, el tesoro debe estar en el cofre 1

Aplicación - Circuitos Lógicos

La aplicación quizás más conocida de la lógica proposicional son los circuitos lógicos.

[3]Un circuito lógico es un dispositivo que tienen una o más entradas y exactamente una salida.  En cada instante cada entrada tiene un valor, 0 o 1; estos datos son procesados por el circuito para dar un valor en su salida, 0 o 1.
Los valores 0 y 1 pueden representar ciertas situaciones físicas como, por ejemplo, un voltaje nulo y no nulo en un conductor.

Los circuitos lógicos se construyen a partir de ciertos circuitos elementales denominados compuertas lógicas, entre las cuales diferenciaremos:
  • Compuertas lógicas básicas:  OR, AND, NOT. 
  • Compuertas lógicas derivadas:  NOR, NAND
Un ejemplo de un circuito lógico, es el siguiente:

Compuerta OR 

En una compuerta OR con entradas A y B, la salida Y resulta: Y = A + B, donde dicha suma esta definida por la siguiente tabla.

ABY = A + B

La compuerta OR se representa del siguiente modo(aunque también puede tener más de dos entradas): 

Compuerta AND 

En una compuerta AND con entradas A y B, la salida Y resulta: Y = A * B, donde el producto se define por la siguiente tabla:

ABY = A * B

La compuerta AND se representa del siguiente modo(también puede tener más de dos entradas): 
Compuerta NOT 

En una compuerta NOT con entrada A, la salida Y resulta: Y = !A, donde el complemento se define por la siguiente tabla:

Y = !A

La compuerta NOT se representa del siguiente modo:
Compuertas NOR y NAND 

Las compuertas NOR y NAND no son básicas.  Una  compuerta NOR equivale a una compuerta OR seguida de una compuerta NOT.  Una  compuerta NAND equivale a una compuerta AND seguida de una compuerta NOT.

Por lo tanto, cuando las entradas son A y B, las salidas de estas compuertas resultan: 


miércoles, 22 de agosto de 2012

Introductory Assignment: One-time-pad

For the first assignment, we were tasked to do a one time pad program. I chose python as a programming language, and did a simple version of the one-time-pad, which I will explain later.


One-time-pad is a cipher method invented in 1917 by the Major Joseph Mauborgne and Gilbert Vernam from AT&T. Claude Shannon, 25 years later was in charge of proving with information theory that this cipher was infact a perfect secret, which means that the message content cannot give any useful information to the attacker.

In this cryptographic algorithm, plaintext is combined with a random key. It is the only known method to perform mathematically unbreakable encryption.
We can only talk about one-time pad if some important rules are followed. If these rules are applied correctly, the one-time pad can be proven unbreakable.

Even infinite computational power and infinite time cannot break one-time pad encryption, simply because it is mathematically impossible. However, if only one of these rules is disregarded, the cipher is no longer unbreakable.
  • The key is at least as long as the message or data that must be encrypted.
  • The key is truly random (not generated by a simple computer function or such)
  • Key and plain text are calculated modulo 10 (digits), modulo 26 (letters) or modulo 2 (binary)
  • Each key is used only once, and both sender and receiver must destroy their key after use.
  • There should only be two copies of the key: one for the sender and one for the receiver (some exceptions exist for multiple receivers.


First, we have or message, HELLO. Now we can get an integer number which would be the position of the letter in the alphabet. H would be 7, E is 4, L is 11, again L with 11, and finally O which is 14. Then we need to generate a random key, combining letters of the alphabet. This key needs to be the same size as our message, so it should be 5 characters long. Having the key, we add the position of the first letter of the message to the same of the key, then the second, third, etc, and save the values. Said values will be then normalized with the operation mod % 26 (26 letters in the alphabet), which will convert any number greater than 26 to a lesser number. Then this number will be the index of the corresponding letter to get a cipher text.

In the example bellow, we can see the process:

      H       E       L       L       O  message
   7 (H)   4 (E)  11 (L)  11 (L)  14 (O) message
+ 23 (X)  12 (M)   2 (C)  10 (K)  11 (L) key
= 30      16      13      21      25     message + key
=  4 (E)  16 (Q)  13 (N)  21 (V)  25 (Z) message + key (mod 26)
      E       Q       N       V       Z  → cipher text

Assuming this message is sent to someone who also knows the key(B), the person who encrypted this message(A) should delete the key. 

Then that B person, should be able to decrypt the message, doing the inverse operation ( substraction) and using the key.

       E       Q       N       V       Z  ciphertext
    4 (E)  16 (Q)  13 (N)  21 (V)  25 (Z) ciphertext
-  23 (X)  12 (M)   2 (C)  10 (K)  11 (L) key
= -19       4      11      11      14     ciphertext — key
=   7 (H)   4 (E)  11 (L)  11 (L)  14 (O) ciphertext — key (mod 26)
       H       E       L       L       O  → message

from sys import argv, stdout
from random import randint

def letter_pos(letter):
  for i in range(len(alphabet)):
    if alphabet[i] == letter:
 return i

def wiki_cipher(msg, key):
  cipher_msg = ""
  #print msg
  for i in range(len(msg)):
    #print letter_pos(msg[i]), letter_pos(key[i])
    cipher_msg += alphabet[(letter_pos(msg[i]) + letter_pos(key[i])) % 25]
  del key
  #print cipher_msg
  return cipher_msg

def wiki_decipher(cipher_msg, key):
  msg = ""
  for i in range(len(cipher_msg)):
    #print letter_pos(cipher_msg[i]), letter_pos(key[i])
    msg += alphabet[(letter_pos(cipher_msg[i]) - letter_pos(key[i])) % 25]
  del key
  #print msg
  return msg

def print_otp(ciphers):
  for cipher in ciphers:
  print ""

  filename = argv[1]
  f = open(filename, "r")
  msg = f.readlines()[0]
  msg.replace("\n", "")
  print "Message:"
  print msg
except IndexError as err:
  print "File not specified, using default message."
  msg = "Testing the one time pad encryption"
  print "Message: %s"%msg.lower()
alphabet = "abcdefghijklmnopqrstuvwxyz"
forbidden = " .,-!?$%&/()=+{{}}][:;_"
if " " in msg:
  words = [""]
  j = 0
  for i in range(len(msg)):
    if msg[i] in forbidden or msg[i] == "\n":
      words[j] += msg[i]
  del msg
  ciphers = []
  keys = []
  for word in words:
    key = ""
    for letter in word:
     key += alphabet[randint(0, 25)]
    if word != None:
      ciphers.append(wiki_cipher(word.lower(), key))
  print "Encrypted Message:"
  print ""
  msg = ""
  for i in range(len(ciphers)):
    msg += wiki_decipher(ciphers[i], keys[i]) + " "
  print "Decrypted message: %s"%msg

  cipher_msg = wiki_cipher(msg, key)
  del msg
  wiki_decipher(cipher_msg, key)
The python script bellow applies the previous algorithm to simple words or phrases. If specified, the script reads the message from a file, otherwise it will use a default message as an example.

An example run using a file named "msg" with a message "Hello my name is Emmanuel and this is a simple one-time-pad message" produces the following ouput:

As you can see, the message is changed to lowercase, and some "forbidden" characters are ignored replacing them for a space.


martes, 21 de agosto de 2012

Tarea 1: Magia

Para el primer post de laboratorio de Automatización y Control de Sistemas Dinámicos escogí comprobar por qué la siguiente afirmación es correcta:
Paso a Paso

La forma en la que la encontre fue muy sencilla, sustituyendo partes por otras equivalentes, como explicaré a continuación:

Para poder comprobar la afirmación, es necesario usar una de las fórmulas del Teorema de Euler:

La primera es la que debemos sustituir en la parte izquierda, quedandonos entonces de la forma siguiente:

De esta manera, podemos convertir la parte de la derecha en una de las definiciones de la exponencial, que es la siguiente:
Sustituyendo x por jΘ, la función nos queda como al inicio:
Comprobando esto mediante software, podemos calcular el exponencial de un número "x", mediante dos formas diferentes, una utilizando la librería default, y otra calculandolo usando el método de la sumatoria. El script en Python, es tan sencillo como lo siguiente:

from sys import argv
from math import pow, exp
x = int(argv[1])

def factorial(x):
  if x <= 1:
    return 1
    return x*factorial(x-1)

suma = 0.0
for i in range(100):
  suma += (pow(x, i)/factorial(i))
print "Resultado con Sumatoria: %s"%suma
print "Resultado con metodo exponencial de la libreria: %s"%exp(x)
print "Diferencia: %s"%abs(exp(x) - suma)

En exponenciales pequeños los resultados son bastante similares, pero aumentando "x",  el cálculo de la sumatoria empieza a ser diferente al de la librería,

Como se ve, aunque la diferencia de los primeros dígitos puede verse pequeña, estámos hablando de números increíblemente grandes, por lo cual una diferencia como esa, es bastante enorme. Pero esto es debido a que el método de la sumatoria es un cálculo muy pesado y en éste caso solo repito la sumatoria desde 0 hasta 100, por lo que la exactitud de los números es limitada. 

Aún así en los valores se puede observar la similaridad, por lo cual podemos decir que la afirmación anterior es correcta.

martes, 14 de agosto de 2012

Tarea 2: Tautología

Para la Tarea 1, debemos crear una expresión lógica que sea una tautología y que ademas cumpliera con ciertos requisitos. Los requisitos a cumplir son:

  • Que cuente por lo menos con 3 variables.
  • Usar por lo menos 4 ocurrencias conectivas ( ands, ors)
  • Tener por lo menos un ^(and), un v(or), y un ¬(not).

  • Tautología

    En lógica, una tautología es una fórmula bien formada de un sistema de lógica proposicional que resulta verdadera para cualquier interpretación; es decir, para cualquier asignación de valores de verdad que se haga a sus fórmulas.

    Un ejemplo sencillo de una tautología es la expresión (p ^ q) -> p, como se puede ver:

    "Mi Tautología"

    La tautología que encontré fue:

    ¬(a ^ (b v c)) v (a v b)  

    Para encontrarla, inicie con una expresión sencilla, y fui agregando más términos hasta completar con las restricciones, despúes comprobé con la tabla de verdad si era o no tautología. Inicialmente no lo fue, ya que mis primeros intentos no arrojaban verdadero para todas las combinaciones, pero modificando poco a poco la expresión, logré convertirla en tautología.

    Diagrama de Árbol

    Estructurando la expresión como un árbol:
    Tabla de Verdad

    La tabla de verdad de la expresión anterior es la siguiente:

     a    b    c     b v c   a ^ (b v c)     ¬(a ^ (b v c)     a v b     ¬(a ^ (b v c)) v (a v b)    
     0  0  0 0 0101

    Entonces sí S = ¬(a ^ (b v c)) v (a v b) es verdadera en todas las combinaciones posibles, podemos comprobar que:

      \vDash S 
    (S es una tautología)


    martes, 7 de agosto de 2012

    Reporte 1 - Verificación y Validación de Software

    Verificación y Validación de Software

    Conjunto de procesos de comprobación y análisis que aseguran que el software que se desarrolla está acorde a su especificación y cumple las necesidades de los clientes. 
    Existen actividades de Verificación y Validación en cada etapa del proceso de desarrollo del software.

    La Verificación de Software se hace la pregunta: ¿Estamos construyendo el producto correctamente?
    En ella se comprueba que el software cumple los requisitos funcionales y no funcionales de su especificación.

    La verificación de software es una disciplina más estrecha y compleja de la ingeniería de software cuyo objetivo es asegurar que el software pueda satisfacer completamente todos los requerimientos esperados.

    Hay dos acercamientos fundamentales en la verificación de software:

    • Verificación dinámica, también conocida como Prueba de Experimentación, útil para encontrar bugs.
    • Verificación estática, tambíen conocida como análisis. Ésta es útil para provar si un programa es correcto aunque puede resultar en falsos positivos.

    La Validación de Software se hace la pregunta: ¿Estamos construyendo el producto correcto?

    Comprueba que el software cumple las expectativas que el cliente espera. Nunca se va a poder demostrar que el software está completamente libre de defectos.
    Existen actividades de Verificación y Validación en cada etapa del proceso de desarrollo del software.

    Diferencia entre ambas

    La verificación y la validación no son la misma cosa:
    • Verificación: ¿Estamos construyendo el producto correctamente?
    El papel de la verificación comprende comprobar que el software está de acuerdo con su especificación. Se comprueba que el sistema cumple los requerimientos funcionales y no funcionales que se le han especificado.
    • Validación: ¿Estamos construyendo el producto concreto?
    La validación es un proceso mas general. Se debe asegurar que el software cumple las expectativas del cliente. Va mas allá de comprobar si el sistema está acorde con su especificación, para probar que el software hace lo que el usuario espera a diferencia de lo que se ha especificado.

    Es importante llevar a cabo la validación de los requerimientos del sistema de forma inicial. 

    Es fácil cometer errores y omisiones durante la fase de análisis de requerimientos del sistema y, en tales casos, el software final no cumplirá la expectativas de los clientes. Sin embargo, en la realidad, la validación de los requerimientos no puede descubrir todos los problemas que presenta la aplicación. Algunos defectos en los requerimientos solo pueden descubrirse cuando la implementación del sistema es completa.  

    Técnicas de comprobación y análisis
    • Las inspecciones del software analizan y comprueban las representaciones del sistema como el documento de requerimientos, los diagramas de diseño y y el código fuente del programa. Se aplica a todas las etapas del proceso de desarrollo. Las inspecciones se complementan  con algún tipo de análisis automático del texto fuente o de los documentos asociados. Las inspecciones del software y los análisis automatizados son técnicas de verificación y validación estáticas puesto que no requieren que el sistema se ejecute.
    • Las pruebas del software consiste en contrastar las respuestas de una implementación del software a series de datos de prueba y examinar las respuestas del software y su comportamiento operacional, para comprobar que se desempeñe conforme a lo requerido. Llevar a cabo las pruebas es una técnica dinámica de la verificación y validación ya que requiere disponer de un prototipo ejecutable del sistema.

    Al proceso de eliminación de los errores que se descubren durante las fases de prueba se denomina 
    depuración. Entonoces, sí la verificación y validación establece la existencia de defectos en el programa,la depuración es el proceso que localiza el origen y corrige estos defectos.

    Los mejores depuradores buscan patrones en los resultados de las pruebas donde el defecto se detecta, y para localizar el defecto utilizan el conocimiento que tienen sobre el tipo de defecto, el patrón de salida, así como del lenguaje y proceso de programación. El conocimiento del proceso es importante. Los depuradores conocen los errores de los programadores comunes (como olvidad incrementar un contador, errores de direccionamiento de punteros) y los comparan contra los patrones observados.

    Localizar los fallos es un proceso complejo porque los fallos no necesariamente se localizan cerca del punto en que se detectan. Para localizar un fallo de un programa el programador responsable de la depuración tiene que diseñar programas de prueba  adicionales que repitan el fallo original y que ayudan a descubrir el origen del fallo.

    Las herramientas de depuración son habitualmente parte de las herramientas de apoyo al lenguaje y que sirven de base al compilador. 

    ¿Por qué existe la Verificación y Validación de Software?

    Para un sistema de gran importancia, como uno de votación electrónica, es conveniente que una autoridad independiente lleve a cabo las pruebas de verificación. Para sistemas de menor importancia, la verificación puede realizarse internamente.

    Las pruebas de verificación de los programas pueden comprender lo siguiente:
    • Probar los programas para asegurar que reúnen los estándares exigidos y ejecutan las tareas esperadas, incluyendo auditorías de código (ver infra). 
    • Asegurar que la documentación del sistema es la adecuada y esté completa. 
    • Verificar que el sistema es capaz de funcionar bajo las condiciones normales esperadas y potenciales condiciones adversas. 
    • Garantizar que se cuenta con medidas de seguridad y que estas se ajustan a los estándares establecidos. 
    • Asegurar que se cuenta con las debidas medidas de control de calidad. 

    Idea de Proyecto - Where's Wally?

    Mi idea personal para proyecto de redes neuronales es una red neuronal que pueda detectar la presencia de el legendario Wally en una imágen.

    Típicamente el objetivo de este juego es encontrar al personaje en una imágen. La dificultad difiere, puede ser tan simple como encontrarlo entre algunas personas con ropas diferentes a él, o encontrarlo en un lugar lleno de colores y ropas similares a las suyas.

    Las características principales suelen ser, su camisa de rayas rojas con blancas, la gorra de invierno de los mismos colores, y su característica cara con gafas.