Aquí está mi problema: Tengo una secuencia S de (no vacíos pero posiblemente no distintos) establece s_i, y para cada s_i necesito saber cuántos conjuntos s_j en S (i ≠ j) son subconjuntos de s_i.Generar un DAG desde un poset utilizando estrictamente la programación funcional
También necesito un rendimiento incremental: una vez que tenga todos mis conteos, puedo reemplazar un conjunto s_i por un subconjunto de s_i y actualizar los recuentos de forma incremental.
Realizar todo esto usando un código puramente funcional sería una gran ventaja (código I en Scala).
Como la inclusión del conjunto es un pedido parcial, pensé que la mejor manera de resolver mi problema sería construir un DAG que representara el diagrama de Hasse de los conjuntos, con bordes que representan inclusión y unir un valor entero a cada nodo representando el tamaño del sub-dag debajo del nodo más 1. Sin embargo, he estado estancado durante varios días tratando de desarrollar el algoritmo que construye el diagrama de Hasse a partir del ordenamiento parcial (¡no hablemos de incrementalidad!), aunque pensé sería un material estándar de pregrado.
Aquí es mi estructura de datos:
case class HNode[A] (
val v: A,
val child: List[HNode[A]]) {
val rank = 1 + child.map(_.rank).sum
}
Mi DAG se define por una lista de raíces y un poco de orden parcial:
class Hasse[A](val po: PartialOrdering[A], val roots: List[HNode[A]]) {
def +(v: A): Hasse[A] = new Hasse[A](po, add(v, roots))
private def collect(v: A, roots: List[HNode[A]], collected: List[HNode[A]]): List[HNode[A]] =
if (roots == Nil) collected
else {
val (subsets, remaining) = roots.partition(r => po.lteq(r.v, v))
collect(v, remaining.map(_.child).flatten, subsets.filter(r => !collected.exists(c => po.lteq(r.v, c.v))) ::: collected)
}
}
estoy bastante atascado aquí. La última vez que vine para añadir un nuevo valor v para el DAG es:
- encuentra toda rs_i "subconjuntos de raíz" de v en el DAG, es decir, subconjuntos de V tal que ningún superconjunto de rs_i es un subconjunto de v. Esto se puede hacer bastante fácilmente realizando una búsqueda (BFS o DFS) en el gráfico (función
collect
, posiblemente no óptima o incluso defectuosa). - construye el nuevo nodo n_v, cuyos hijos son los previamente encontrados rs_i.
- Ahora, descubramos dónde debe adjuntarse n_v: para una lista dada de raíces, descubra los superconjuntos de v. Si no encuentra ninguno, agregue n_v a las raíces y elimine los subconjuntos de n_v de las raíces. De lo contrario, realice el paso 3 recursivamente en los hijos de los superconjuntos.
Todavía no he implementado completamente este algoritmo, pero parece ser intrincado y no óptimo para mi problema aparentemente simple. ¿Hay algún algoritmo más simple disponible (Google no tenía ni idea de esto)?
Ese algoritmo me parece terriblemente simple, no innecesariamente intrincado. ¿Cuál es el problema exactamente? El código de Scala apenas será más largo que tu descripción. (Aunque no creo que lo hayas descrito del todo completamente). –
Bueno, dado que he entrado en la programación funcional (~ 6 meses atrás), he estado acostumbrado a frases sencillas cuando se trata de estructuras de datos recursivas. Se siente incómodo desarrollar un algoritmo de tres pasos, que no reside en una sola llamada recursiva (el paso 1. está desconectado del paso 3.) Además, este algoritmo comprueba los subconjuntos dos veces (paso 1 y 3), lo cual se siente incorrecto. – scand1sk
Como referencia, recientemente implementé un montón binomial, que se sintió mucho más fácil (aunque probablemente se deba a que los algoritmos estaban mejor definidos). – scand1sk