(Helluva Boss)
Traducido del inglés
El grupo de I.M.P, con la posibilidad de ¡¿TETO!?
⯆⚉⯆
~Teteteto~
"No me digas 'linda', ¡soy gangsta!"
(Se recomienda usarlo con algún tipo de proxy. No sé si JLMM funcione, y me da pereza probarlo yo mismo)
Escenario uno:
Loona, Millie y Moxxie están huyendo de la policía.
Escenario dos:
Llevas un mes trabajando en I.M.P. Hoy todos tienen una reunión.
Escenario tres:
Estás enfermo. Loona está cuidando de ti. (pensado para ser tierno)
Escenario cuatro:
Tú y Millie están en una misión en la tierra.
Escenario cinco:
Uh-oh...
Escenario seis:
Ocurrió una gran pelea. Ve a consolar a Loona.
Escenario siete:
Tú y la Loon están siendo tontos.
Escenario ocho:
Loona, Millie, Moxxie y Blitzo quieren adoptar un nuevo HellHound. Ese eres tú, gemelo. (POV de Hellhound)
Escenario nueve:
Ingresa tu propio escenario.
Etiquetas:
Las computadoras no tienen sentido. Tira un poco de metal en una caja y boom. ¿Qué diablos está pasando aquí? Dentro de tu PC hay una unidad central de procesamiento o CPU. Básicamente es solo un trozo de silicio con miles de millones de interruptores microscópicos llamados transistores. Dependiendo del flujo de electricidad, pueden estar encendidos o apagados, algo así como una bombilla, lo que nos da dos estados, uno y cero. El valor de uno de estos interruptores se llama bit. Un bit por sí solo no hace mucho, pero si los juntas y... Un grupo de 8 bits se llama byte, y puede tener 256 combinaciones diferentes de 0s y 1s. ¡Felicidades! Ahora podemos almacenar información contando en un sistema llamado binario. Cada bit representa una potencia de 2, 1 significa que la potencia está incluida y 0 significa que no, así que este número tiene 1 por 64, 1 por 4, y 1 por 1, lo que suma 69. Esto está bien, pero para los humanos, el hexadecimal es incluso mejor. A menudo se denota con este 0x, y es simplemente un formato más legible que el binario. Cuatro bits binarios pueden tomar cualquier valor de 0 a 15. El hexadecimal usa de 0 a 9 y de A a F para representar esos valores, así que un grupo de cuatro bits puede ser reemplazado por un dígito hexadecimal. Bien, ahora que podemos almacenar números, solo necesitamos que las computadoras realmente, ya sabes, hagan algo con ellos. Usando transistores, puedes hacer puertas lógicas, que son circuitos electrónicos que encapsulan declaraciones lógicas. Puedes pensarlo como una bombilla con dos interruptores, donde la luz solo se enciende bajo ciertas condiciones, por ejemplo solo si A y B están encendidos. Combinando puertas lógicas de forma ingeniosa, puedes construir circuitos que realizan cálculos según el álgebra booleana, que es un sistema que formaliza las operaciones matemáticas en binario. Pero aunque las computadoras entienden ceros y unos, para los humanos no es realmente tan útil. Así que, usando una codificación de caracteres como ASCII, podemos asignar un número binario a cada carácter. Cuando escribes cualquiera en tu teclado, se traduce a este código binario, y en cuanto la computadora ve esto, dice, ah sí, esa es una A mayúscula, y la pega en la pantalla. Cómo encajan estos dispositivos entre sí lo maneja el kernel de un sistema operativo, como Windows, Linux o Mac, que se sitúa entre el hardware de la computadora y las aplicaciones y gestiona cómo funcionan todos juntos, por ejemplo, con los controladores de dispositivos. Los dispositivos de entrada te permiten dar instrucciones a la computadora con solo presionar un botón, pero a nivel más bajo, las computadoras solo entienden instrucciones en código máquina, que es código binario que le dice a la CPU qué hacer y qué datos usar. Cuando se trata de seguir estas instrucciones, la CPU es algo así como un genio, solo con la memoria de un pez dorado demenciado. Puede manejar cualquier instrucción, pero no puede almacenar ningún dato, así que solo es realmente útil con memoria de acceso aleatorio o RAM. Puedes imaginarla como una cuadrícula, donde cada casilla puede contener un byte de información, que puede ser datos o instrucciones, y tiene una dirección para que la CPU pueda acceder a ella en cuatro pasos. Obtener de la memoria, decodificar instrucciones y datos, y finalmente, ejecutar y almacenar el resultado. Este es un ciclo de máquina. Como un programa es básicamente solo una lista de instrucciones y memoria, para ejecutarlo, la CPU las ejecuta una por una en ciclos de máquina hasta que termina. Ah sí, ¿mencioné que esto sucede, como, muy rápido? Las CPU modernas pueden hacer miles de millones de ciclos por segundo, que son coordinados y sincronizados por un generador de reloj. La velocidad de este reloj se mide en gigahercios, y la gente a menudo hace overclocking de sus CPU para mejorar el rendimiento, lo cual está bien, pero podría incendiar tu PC. Lo que es aún más loco es que una CPU tiene múltiples núcleos, que pueden ejecutar diferentes instrucciones en paralelo, así que al mismo tiempo. Cada núcleo también puede dividirse en múltiples hilos, lo que permite que cada núcleo maneje múltiples instrucciones de forma concurrente, cambiando entre ellas muy rápidamente. Bien, eso es genial, pero no importa cuán poderosa sea una computadora si no tienes forma de darle instrucciones en primer lugar. Escribir código máquina a mano probablemente te volvería loco, pero por suerte, no tienes que hacerlo. El kernel está envuelto en un shell, que es solo un programa que expone el kernel al usuario, permitiendo instrucciones simples en una interfaz de línea de comandos con entradas de texto. Pero la mejor manera de hacer que una computadora haga algo útil es con un lenguaje de programación, que usa abstracción para que en lugar de esto puedas escribir código que se ve así, que luego se convierte en código máquina para ti. Algunos lenguajes como Python usan un intérprete, que intenta ejecutar directamente el código fuente línea por línea. Otros lenguajes como C o Go usan un compilador, que convierte todo el programa en código máquina antes de ponerlo en un archivo que la CPU pueda ejecutar. Ahora, cada lenguaje de programación tiene una sintaxis diferente, pero hay algunas herramientas básicas que casi todos tienen. La forma más básica de usar datos es con variables, que asignan un valor a un nombre que luego puede reutilizarse y modificarse. Dependiendo del valor, las variables pueden tener diferentes tipos de datos. Para texto, están los caracteres individuales y las cadenas de múltiples caracteres. Para números, están los enteros, que también pueden tener signo si son negativos, y los números de punto flotante para valores decimales. Se llaman de punto flotante porque el punto decimal puede flotar para intercambiar precisión por rango. Esto es posible porque usan notación científica. Es un número multiplicado por una potencia que te dice dónde poner el punto decimal, que es exactamente cómo se ven bajo el capó. El número real se almacena con fracciones binarias. Algunas fracciones, como un tercio, solo pueden aproximarse en binario con una suma infinita. Pero, como la memoria no es infinita, tienes que cortarla en algún punto, lo que lleva a errores de redondeo, causando cálculos bastante extraños a veces. Si esto no es suficiente, long y double usan el doble de memoria para duplicar el rango de ints y floats. Algunos lenguajes como Python descubren automáticamente qué tipo es una variable, pero en un lenguaje como C, tienes que declarar explícitamente el tipo de una variable. El valor de una variable se almacena en alguna dirección en memoria. Los punteros son variables cuyo valor es la dirección de memoria de otra variable, que se denota con este ampersand. Así que en realidad, un puntero es solo un trozo de memoria que apunta a otro trozo de memoria. Como una dirección de memoria es solo un número, puedes sumarle y restarle para navegar por bytes individuales de memoria. Esto se llama aritmética de punteros. En algunos lenguajes de bajo nivel como C, tienes que asignar y liberar memoria manualmente una vez que ya no se usa. Todo esto sucede en el heap, que es una parte de la memoria que puede crecer y encogerse dinámicamente según lo exija el programa, lo que permite más control pero hace que sea increíblemente fácil romper tu código por completo. Podrías tocar memoria que no deberías o que simplemente no existe, lo que se conoce como fallo de segmentación. Pero también, si hay algún trozo de memoria que ya no se usa y olvidas liberarlo o ya no tienes forma de acceder a él, esa memoria deja de ser utilizable. Esto se llama fuga de memoria y puede hacer que el programa se vuelva lento y eventualmente se bloquee. Para evitar este desastre, los lenguajes de alto nivel como Python tienen recolectores de basura integrados que gestionan la memoria por ti. Diferentes tipos de datos ocupan una cantidad diferente de memoria. Los enteros suelen ocupar 4 bytes de memoria, un solo carácter suele ocupar 1 byte de memoria, y una cadena es solo múltiples bytes de caracteres con un carácter nulo para señalar el final de la cadena. Almacenar múltiples elementos en un trozo contiguo de memoria como este es la idea de un array. Más en general, es una lista de elementos con el mismo tipo de datos, con cada elemento teniendo un índice numérico, que la mayoría de las veces empieza en 0. Como los elementos están uno al lado del otro en memoria, al conocer la dirección del primer elemento puedes indexar rápidamente cualquier elemento del array usando aritmética de punteros. Los arrays son lo que se conoce como una estructura de datos, que es solo una forma de organizar datos para que sea más fácil trabajar con ellos. Recuperar valores de un array es increíblemente rápido, pero el tamaño de un array a menudo es fijo al crearlo, así que cuando está lleno no puedes añadir nada, y si no lo usas todo, es solo memoria desperdiciada, así que una opción más flexible es una lista enlazada. Usa nodos que contienen un valor y un puntero al siguiente nodo, lo que permite que estén separados en memoria. Además, puede crecer y encogerse dinámicamente ya que puedes añadir y eliminar cualquier nodo, y puedes reordenarla simplemente reorganizando los punteros. Esto es genial, pero pueden ser poco prácticas ya que no tienes forma de acceder al último nodo excepto si recorres todos los anteriores. Pero aun así, tanto los arrays como las listas enlazadas son útiles ya que te permiten crear colas y pilas. Una pila sigue el principio de último en entrar, primero en salir, igual que tomar un panqueque de una pila. En la práctica, solo imagina un puntero que siempre apunta al elemento que se añadió por última vez a la estructura, entonces puedes hacer pop, es decir, eliminar el último elemento, lo que incrementa el puntero hacia atrás. Una cola sigue el principio de primero en entrar, primero en salir y usa dos punteros, uno para el primer elemento que se añadió y otro para el último. Cualquier elemento nuevo se añade después del puntero del último, que luego se actualiza, y el desencolado empieza en el primer puntero. Otra estructura de datos útil es un hash map, que es solo una colección de pares clave-valor. Funciona algo así como un array, pero usa una función hash para tomar una clave y asignarla a un índice, donde luego se almacena su valor. Pero a veces, dos claves diferentes pueden asignarse al mismo índice, lo que se llama colisión. Hay diferentes formas de lidiar con esto, pero una forma es crear una lista enlazada en esa posición del array, lo que hace que buscar ese valor sea un poco más lento, pero aun así, los HashMap son increíblemente útiles porque puedes definir las claves que apuntan a cada valor, y como se basan en arrays, recuperarlos es increíblemente rápido. Para muchos problemas, puede ser muy útil representar la relación entre diferentes puntos de datos como una estructura de datos. Si tomas los nodos de una lista enlazada, pero permites que cualquier nodo apunte a cualquier otro nodo, obtienes un grafo, donde los nodos están conectados por aristas que pueden ser dirigidas, no dirigidas, e incluso pueden llevar un peso, que puede representar cualquier métrica, como distancia o costo. Los grafos son útiles para analizar grupos dentro de redes o encontrar el camino más corto entre dos puntos, por ejemplo en Google Maps. Hay dos formas principales de buscar en un grafo. La búsqueda en amplitud empieza en un nodo y avanza capa por capa hasta encontrar el nodo objetivo por primera vez. La búsqueda en profundidad explora cada camino completamente hasta llegar a un callejón sin salida, luego retrocede al último nodo con un camino diferente y continúa desde allí de la misma manera hasta encontrar el nodo objetivo. Un grafo donde dos nodos cualesquiera están conectados por exactamente un camino se llama árbol y representa una jerarquía, por ejemplo el sistema de archivos de tu computadora. Un árbol empieza en la raíz y se ramifica en subárboles que terminan en nodos hoja. Los nodos padres pueden tener cualquier número de nodos hijos, pero a menudo los árboles binarios son los más útiles. Por ejemplo, un árbol binario donde todos los valores a la izquierda de cualquier nodo son menores y todos los valores a la derecha son mayores se llama árbol de búsqueda binaria, lo que hace que encontrar valores específicos sea súper rápido. Si quieres encontrar un valor objetivo, solo empieza en la raíz. Si el objetivo es menor que ese nodo, ve a la izquierda, si es mayor, ve a la derecha, y repite esto hasta encontrar el objetivo. Lo que acabamos de usar es un algoritmo muy simple, que es solo un conjunto de instrucciones que resuelve un problema paso a paso. La forma más simple de escribir un algoritmo es en forma de función. Toma entradas, hace algo con ellas, y devuelve una salida. Igual que con las variables, luego puedes llamar a una función por su nombre y pasarle diferentes argumentos. Cuando se llama a una función, la llamada a la función se apila en la pila de llamadas, que es memoria a corto plazo usada para ejecutar código, y como su nombre indica, se basa en una estructura de datos tipo pila, lo que significa último en entrar, primero en salir. Para implementar algoritmos, a menudo tienes que comparar dos valores, lo que puedes hacer con operadores como mayor que o igualdad, y expresiones lógicas como and, or y not. Expresiones como estas son simples, pueden ser verdaderas o falsas, que son los valores posibles del tipo de datos booleano y te permiten escribir declaraciones condicionales. Si alguna condición es verdadera, haz esto, si no, haz aquello. Los booleanos también pueden usarse para recorrer ciertas partes del código. Una forma es con un bucle while. Mientras esta condición sea verdadera, este código se ejecutará. Otra forma es con un bucle for, que puede iterar sobre cada elemento dentro de una estructura de datos como un array, pero también puede hacer un bucle durante un número específico de iteraciones estableciendo un valor inicial, incrementándolo después de cada iteración, y estableciendo un límite superior con una condición. Las funciones también pueden llamarse a sí mismas, lo que se conoce como recursión. Esto es útil cuando un problema puede descomponerse en problemas idénticos más pequeños, como calcular el factorial de 5, que es simplemente 5 por el factorial de 4, que es simplemente 4 por... Pero, por defecto, una función recursiva simplemente seguirá llamándose a sí misma para siempre. Esto significa que sigue apilando más llamadas a funciones en la pila de llamadas hasta que se supera la memoria de la pila en un desbordamiento de pila. Eso no es óptimo. Para detener esto, tienes que añadir una condición base a una función recursiva que defina cuándo parar. Solo entonces las llamadas a funciones se ejecutarán sin bloquear tu PC. La recursión es genial, pero puede ser bastante costosa en tiempo y espacio. Así que, para minimizar la cantidad de cálculos necesarios, los resultados anteriores pueden guardarse en una caché para que si vuelven a aparecer, la computadora no tenga que recalcularlos desde cero. Esto se llama memoización. Hablando de rendimiento, para juzgar qué tan bueno es un algoritmo, puedes mirar la complejidad temporal y espacial, es decir, cuánto tiempo o espacio se requiere para ejecutarlo. Esto se mide en notación Big O, que describe la relación entre el crecimiento del tamaño de entrada y el número de operaciones necesarias para ejecutar el algoritmo. Por ejemplo, sumar 1 a cada número dentro de un array es O, porque el número de operaciones aumenta de forma lineal a medida que el array crece. Lo relevante no es el número exacto de operaciones, sino la tendencia a medida que el tamaño de entrada tiende a infinito. Verás, algo como n factorial crece mucho más rápido que cualquier función lineal podría. Así que mientras la complejidad temporal sea algún tipo de relación lineal, se simplifica a O, y lo mismo ocurre con cualquier otro grupo. Al escribir un algoritmo, siempre hay diferentes enfoques. Un enfoque de fuerza bruta sería simplemente comprobar cada elemento hasta encontrar un objetivo en una lista. Pero si quieres que tu programa corra más rápido que un caracol de jardín, necesitas un enfoque más sofisticado como divide y vencerás. Por ejemplo, en la búsqueda binaria, compruebas repetidamente el elemento del medio para ver en qué lado está el objetivo y sigues buscando en esa mitad hasta encontrarlo. Además, hay diferentes enfoques para escribir código en general, que se llaman paradigmas de programación. La programación declarativa describe qué hace el código pero no cómo exactamente debería hacerlo la computadora, mientras que la programación imperativa describe explícitamente cómo la computadora debe lograr un resultado con instrucciones detalladas. Una extensión de la programación imperativa es la programación orientada a objetos, donde puedes definir clases como planos para objetos, que son unidades individuales que consisten en datos en forma de propiedades y comportamientos en forma de métodos. Para codificar una clase, empiezas definiendo las propiedades como variables y los métodos como funciones. Después de encapsular propiedades y métodos en una clase, puedes instanciar un objeto y usar la notación de punto para trabajar con sus propiedades y métodos. Las clases facilitan organizar y reutilizar código, porque puedes definir subclases que heredan propiedades y comportamientos de una superclase pero también pueden extenderlos y sobrescribirlos. Por ejemplo, una subclase de pato de goma podría implementar quack con un chirrido en su lugar. Como resultado, los patos de goma pueden tratarse como objetos de la clase pato pero comportarse de manera diferente cuando se llama a quack, que es el concepto de polimorfismo. Pero para algunos problemas, ninguno de estos paradigmas tradicionales funcionará. Digamos que quieres hacer que una computadora reconozca cuál de estas imágenes es una abeja. El problema es que realmente no puedes describir cómo se ve una abeja con código. Aquí es donde entra el aprendizaje automático, también conocido como enseñar a una computadora a hacer una tarea sin programarla explícitamente para hacer esa tarea. Primero, necesitas muchos datos que puedas dividir en datos de entrenamiento y datos de prueba. Luego, eliges un algoritmo que pueda cambiar sus parámetros con el tiempo, por ejemplo una red neuronal donde los pesos puedan actualizarse para lograr un resultado diferente. Al alimentar este algoritmo con muchísimos datos de entrenamiento, puedes construir un modelo cuya precisión luego puedes comprobar con los datos de prueba. Si no es del todo correcto, el modelo puede mejorar con el tiempo comparando la salida con lo que debería haber sido, capturando la diferencia en una función de error y ajustando sus parámetros para minimizar la diferencia. Pero no importa cuán futurista, vanguardista, increíblemente rápido y optimizado sea, si quieres que la gente realmente use la aplicación que escribiste, probablemente deberías saber sobre internet. Es una red de computadoras de todo el mundo conectadas por cables. O sea, literalmente, internet es solo un montón de cables gruesos que recorren el fondo del océano junto con instalaciones como los proveedores de servicios de internet que te conectan con tu destino. Estas computadoras se comunican con la suite de protocolos de internet. Cada computadora en la red tiene una dirección IP única, y dos computadoras pueden entonces transferir datos con un protocolo de control de transmisión. Divide los mensajes en un montón de paquetes, los envía a través de una red de cables, antes de que el receptor vuelva a armar el mensaje. Si tienes una mala conexión a internet, es posible que hayas experimentado pérdida de paquetes, que es simplemente que algunos de estos paquetes se pierden en el camino. Si internet es el hardware, entonces la web es el software, que puedes usar con un navegador. Cada página en la web tiene una URL. Cuando la escribes en tu navegador, busca la dirección IP del servidor que aloja este sitio web con un sistema de nombres de dominio, que es como un diccionario que asigna nombres de dominio a direcciones IP de servidores reales. Después de conectarse a él vía TCP, tu navegador, llamado el cliente, usa el protocolo de transferencia de hipertexto para enviar una solicitud HTTP al servidor, que luego da una respuesta, idealmente conteniendo el contenido de la página web. El sitio web real a menudo consiste en tres partes. Un archivo HTML contiene todo el contenido de un sitio web y es básicamente solo una colección de elementos, que pueden ser texto, enlaces, botones, etcétera. Un archivo CSS controla lo visual y hace que un sitio web se vea bien. Pero un sitio web es inútil si presionar botones bonitos no hace nada, así que un lenguaje como JavaScript añade funcionalidad. Pero a veces, las cosas pueden salir mal. Con cada respuesta HTTP viene un código de respuesta, que lleva información sobre el estado de la respuesta. Por ejemplo, 200 significa OK, y cualquier cosa que empiece con 4 es un error, el más famoso siendo el 404, página no encontrada. Las solicitudes HTTP pueden llevar diferentes métodos, por ejemplo, get, post, put y delete, es decir, recuperar, añadir, actualizar y eliminar información. Estos son usados a menudo por las interfaces de programación de aplicaciones, que conectan dos aplicaciones y les permiten interactuar entre sí. Por ejemplo, almacenar y recuperar datos de una base de datos. El tipo de base de datos más común es una base de datos relacional, que usa tablas para almacenar datos. Las columnas de una tabla contienen diferentes atributos, y las filas representan puntos de datos individuales. Además, cada tabla tiene un atributo único llamado clave primaria. Una clave foránea es la clave primaria de otra tabla que establece una relación entre las dos. En este caso, cada libro está conectado a un autor. Con un lenguaje como SQL, puedes escribir declaraciones para trabajar con datos de estas tablas. Podrías buscar los títulos y autores de todos los libros cuyo título empiece con H, pero para eso tenemos que unir la tabla de autores con la tabla de libros en la clave coincidente para combinar dos atributos de diferentes tablas en uno, dándonos esta salida. Estas declaraciones son útiles, pero tienes que tener cuidado o podrías simplemente borrar una base de datos entera con una línea de código. Pero no te preocupes, eso nunca ha pasado antes. Detrás de cada página de inicio de sesión hay una base de datos con nombres de usuario y contraseñas. Cuando un usuario intenta iniciar sesión, a menudo se usa una consulta SQL para comprobar si la entrada del usuario coincide con una entrada en la base de datos. Eso está bien. Pero un actor malicioso podría escribir algo como esto, que cambia la consulta terminando la cadena antes de tiempo y comentando el resto, lo que significa que mientras este nombre de usuario exista en la base de datos, se concede el acceso. Esto se llama ataque de inyección SQL, y es una de las formas más fáciles en que los hackers llegan a lugares donde no deberían. Escuchar sobre estos conceptos es una cosa, pero para realmente aprenderlos tienes que verlos en acción y usarlos tú mismo, que es exactamente lo que puedes hacer con Brilliant, que tiene miles de lecciones interactivas para todo, desde matemáticas y ciencia de datos hasta programación e IA. Hacen que el conocimiento se quede, con lecciones visuales y problemas interactivos, que no solo son divertidos y desarrollan habilidades intuitivas de resolución de problemas, sino que también está demostrado que son seis veces más efectivos que simplemente ver horas de conferencias. Sé que encontrar tiempo para aprender nuevas habilidades puede ser difícil, pero Brilliant lo hace muy fácil. Puedes construir conocimiento valioso desde cero en solo un par de minutos al día, con lecciones del tamaño de un bocado desde cualquier dispositivo, en cualquier lugar, en cualquier momento. Pasarás de lo básico de la ciencia de datos a analizar conjuntos de datos reales de Spotify en absolutamente nada de tiempo, lo que puedes complementar con fundamentos de matemáticas y cursos de programación en Python para construir uno de los conjuntos de habilidades más demandados de nuestro tiempo. ¿La mejor parte? Puedes probar todo lo que Brilliant tiene para ofrecer gratis durante 30 días completos visitando brilliant.org slash wackyscience. También obtienes un 20% de descuento en una suscripción premium anual. Gracias a Brilliant por patrocinar este video.
Creado 30 ene
Actualizado 9 ago
Publicado 30 ene