Algunos remedios informáticos, para los auditores de PCI en mi audiencia.
Te entrego un conjunto de enteros aleatorios. ¿Cómo puedes saber si el número tres está en él?
Bueno, hay una manera obvia: verifique los números secuencialmente hasta que encuentre el "3" o agote la matriz. Búsqueda lineal. Dados 10 números, debe suponer que podría tomar 10 pasos; N números, N pasos.
Imagen 1.png
La búsqueda lineal es mala. Es difícil hacer algo peor que lineal. Vamos a mejorarlo. Ordenar la matriz.
Imagen 2.png
Una matriz ordenada sugiere una estrategia diferente: saltar el centro de la matriz y ver si el valor que está buscando es menor que (a la izquierda) o mayor que (a la derecha). Repita, cortando la matriz por la mitad cada vez, hasta encontrar el valor.
Búsqueda binaria. Dados 10 números, se necesitarán hasta 3 pasos (log2 de 10) para encontrar uno de ellos en una matriz ordenada. O (log n) la búsqueda es increíble. Si tiene 65,000 elementos, solo tomará 16 pasos para encontrar uno de ellos. Duplique los elementos, y son 17 pasos.
Pero los arreglos ordenados apestan; Por un lado, la clasificación es más costosa que la búsqueda lineal. Entonces no usamos mucho la búsqueda binaria; en cambio, usamos árboles binarios.
Imagen 3.png
Para buscar un árbol binario, comienza en la parte superior y se pregunta "es mi clave menor que (izquierda) o mayor que (derecha) el nodo actual", y repite hasta que esté bien, está bien, ya sabe esto. Pero ese árbol es bonito, ¿no?
La búsqueda con un árbol binario (equilibrado) es O (log n), como la búsqueda binaria, que varía con el número de elementos en el árbol. Los árboles binarios son increíbles: obtienes una búsqueda rápida y un recorrido ordenado, algo que no obtienes de una tabla hash. Los árboles binarios son una mejor implementación de tabla predeterminada que las tablas hash. 2)
Pero los árboles binarios no son el único mecanismo de búsqueda estructurado en árbol. Los intentos de radix binarios, también llamados árboles PATRICIA, funcionan como árboles binarios con una diferencia fundamental. En lugar de comparar mayor que / menor que en cada nodo, verifica si hay un bit establecido, bifurcando a la derecha si está configurado y a la izquierda si no lo está.
Imagen 4.png
Estoy dejando de lado mucho sobre cómo funciona la bix radix. Esto es una pena, porque los intentos de radix están notoriamente subdocumentados: Sedgewick los jodió infamemente en "Algoritmos", y la página de Wikipedia para ellos apesta. ¡La gente todavía discute sobre cómo llamarlos! En lugar de una explicación de los vínculos de retroceso y los bordes etiquetados con posición de bits, aquí hay una pequeña implementación de Ruby.
He aquí por qué los intentos de radix son geniales:
Search performance varies with the key size, not the number of elements in the tree. With 16 bit keys, you’re guaranteed 16 steps
independientemente del número de elementos en el árbol, sin equilibrio.
More importantly, radix tries give you lexicographic matching, which is a puffed-up way of saying “search with trailing wildcard”, or
"Búsqueda de estilo de línea de comando-finalización". En un árbol de radix, puede buscar rápidamente "ro *" y obtener "rome" y "romulous" y "roswell".
3)
Te he perdido.
Pongamos esto en contexto. Los intentos son una estructura de datos crucial para el enrutamiento de Internet. El problema de enrutamiento es así:
You have a routing table with entries for “10.0.1.20/32 -> a” and “10.0.0.0/16 -> b”.
You need packets for 10.0.1.20 to go to “a”
You need packets for 10.0.1.21 to to to “b”
Este es un problema difícil de resolver con un árbol binario básico, pero con un radix trie, solo está pidiendo “1010.0000.0000.0000.0000.0001.0100” (para 10.0.1.20) y “1010.” (para 10.0.0.0 ) La búsqueda lexicográfica le brinda la "mejor coincidencia" para el enrutamiento. Puedes probarlo en el código Ruby anterior; agregue * "10.0.0.0" .to_ip al trie y busque "10.0.0.1" .to_ip.
La correspondencia entre el enrutamiento y los intentos de radix es tan fuerte que la biblioteca de radix trie de propósito general más popular (la de CPAN) en realidad es robada de GateD. Es un desastre, por cierto, y no lo uses.
Si comprende cómo funciona un trie, también comprende cómo funcionan las expresiones regulares. Los intentos son un caso especial de autómatas finitos deterministas (DFA), donde las ramificaciones se basan exclusivamente en comparaciones de bits y siempre se ramifican hacia adelante. Un buen motor regex es solo manejar DFA con más "características". Si mis imágenes tienen sentido para usted, las imágenes de este excelente artículo sobre el algoritmo de reducción NFA-DFA de Thompson también lo harán, y ese artículo lo hará más inteligente. 4)
Eres un operador de red en un backbone ISP Su mundo se compone principalmente de "prefijos": pares de red IP / máscara de red. Las máscaras de red en esos prefijos son muy importantes para usted. Por ejemplo, 121/8 pertenece a Corea; 121.128 / 10 pertenece a Korea Telecom, 121.128.10 / 24 pertenece a un cliente de KT y 121.128.10.53 es una computadora dentro de ese cliente. Si está rastreando una botnet o una operación de spam o propagación de gusanos, ese número de máscara de red es muy importante para usted.
Desafortunadamente, por importantes que sean, en ninguna parte de un paquete IP hay una "máscara de red" estampada: las máscaras de red son completamente un detalle de configuración. Entonces, cuando está viendo el tráfico, esencialmente tiene estos datos para trabajar:
ips.png
Sorprendentemente, dados suficientes paquetes para mirar, esta es información suficiente para adivinar las máscaras de red. Mientras trabajaba en Sony, Kenjiro Cho ideó una forma realmente elegante de hacerlo, basándose en intentos. Así es cómo:
Tome un bix radix trie básico, como los que usan los enrutadores de software. Pero limite el número de nodos en el árbol, digamos a 10,000. En un enlace troncal, registrando direcciones de encabezados IP, agotarás 10,000 nodos en unos instantes.
Almacene la lista de nodos en una lista, ordenada en orden LRU. En otras palabras, cuando hace coincidir una dirección IP con un nodo, "toque" el nodo, pegándolo en la parte superior de la lista. Gradualmente, las direcciones que se ven con frecuencia aparecen en la parte superior y los nodos que se ven con poca frecuencia se hunden en la parte inferior.
Imagen 6.png
Ahora el truco. Cuando se quede sin nodos y necesite uno nuevo, reclame desde el final de la lista. Pero cuando lo haga, enrolle los datos del nodo en su padre, así:
Imagen 5.png
10.0.1.2 y 10.0.1.3 son hermanos / 32s, las dos mitades de 10.0.1.2/31. Para reclamarlos, fusionarlos en 10.0.1.2/31. Si necesita reclamar 10.0.1.2/31, puede fusionarlo con 10.0.1.0/31 para formar 10.0.1.0/30.
Haga esto durante, digamos, un minuto, y las fuentes sobresalientes defenderán su posición en el árbol manteniéndose en la parte superior de la lista LRU, mientras que el ruido ambiental / 32 burbujea hasta / 0. Para obtener la lista sin procesar de las IP anteriores, con un árbol de 100 nodos, obtienes esto.
Cho llama a esto heurístico Aguri. 5)
Aguri tiene licencia BSD. Puede descargarlo y un programa de controlador que mira los paquetes a través de pcap, desde la antigua página de inicio de Cho. 6)
Voy a algún lado con esto, pero ahora tengo 1300 palabras en esta publicación, y si eres una persona de algoritmos, ya estás cansado de mí, y si no lo estás, estás cansado de mí. ahora. Entonces, deja que Aguri se hunda, y te daré algo genial e inútil para hacerlo más adelante esta semana.
Hay numerosos enlaces dispersos allí. Desafortunadamente, Archive.org no conserva las imágenes, solo el texto, por lo que se han perdido varias de ellas. Aquí están los que tiene archivados: