tag grupo combo attribute algorithm go graph-algorithm

algorithm - grupo - ¿Agrupa números basados en ocurrencias?



option title attribute (4)

Cada una de sus tres secuencias se puede entender como una clique en un multigraph . Dentro de una camarilla, cada vértice está conectado a cada otro vértice.

El siguiente gráfico representa su caso de muestra con los bordes en cada camarilla de color rojo, azul y verde, respectivamente.

Como ya lo ha demostrado, podemos clasificar pares de vértices de acuerdo con el número de bordes entre ellos. En la ilustración, podemos ver que cuatro pares de vértices están conectados por dos bordes cada uno, y otros cuatro pares de vértices están conectados por un borde cada uno.

Podemos continuar clasificando vértices según la cantidad de camarillas en que aparecen. En cierto sentido, estamos clasificando vértices según su conexión. Se puede pensar que un vértice que aparece en k cliques está conectado en el mismo grado que otros vértices que aparecen en k cliques. En la imagen, vemos tres grupos de vértices: el vértice 3 aparece en tres cliques; los vértices 1, 2 y 4 aparecen en dos cliques; el vértice 5 aparece en una camarilla.

El programa Go a continuación calcula la clasificación de borde y la clasificación de vértices. La entrada al programa contiene, en la primera línea, el número de vértices n el número de cliques m . Suponemos que los vértices están numerados de 1 a n . Cada una de las siguientes líneas de entrada es una lista de vértices separados por espacios que pertenecen a una camarilla. Por lo tanto, la instancia de problema dada en la pregunta está representada por esta entrada:

5 3 1 2 3 4 4 3 5 2 1 3

El resultado correspondiente es:

Number of edges between pairs of vertices: 2 edges: (1, 2) (1, 3) (2, 3) (3, 4) 1 edge: (1, 4) (2, 4) (3, 5) (4, 5) Number of cliques in which a vertex appears: 3 cliques: 3 2 cliques: 1 2 4 1 clique: 5

Y aquí está el programa Go:

package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { // Set up input and output. reader := bufio.NewReader(os.Stdin) writer := bufio.NewWriter(os.Stdout) defer writer.Flush() // Get the number of vertices and number of cliques from the first line. line, err := reader.ReadString(''/n'') if err != nil { fmt.Fprintf(os.Stderr, "Error reading first line: %s/n", err) return } var numVertices, numCliques int numScanned, err := fmt.Sscanf(line, "%d %d", &numVertices, &numCliques) if numScanned != 2 || err != nil { fmt.Fprintf(os.Stderr, "Error parsing input parameters: %s/n", err) return } // Initialize the edge counts and vertex counts. edgeCounts := make([][]int, numVertices+1) for u := 1; u <= numVertices; u++ { edgeCounts[u] = make([]int, numVertices+1) } vertexCounts := make([]int, numVertices+1) // Read each clique and update the edge counts. for c := 0; c < numCliques; c++ { line, err = reader.ReadString(''/n'') if err != nil { fmt.Fprintf(os.Stderr, "Error reading clique: %s/n", err) return } tokens := strings.Split(strings.TrimSpace(line), " ") clique := make([]int, len(tokens)) for i, token := range tokens { u, err := strconv.Atoi(token) if err != nil { fmt.Fprintf(os.Stderr, "Atoi error: %s/n", err) return } vertexCounts[u]++ clique[i] = u for j := 0; j < i; j++ { v := clique[j] edgeCounts[u][v]++ edgeCounts[v][u]++ } } } // Compute the number of edges between each pair of vertices. count2edges := make([][][]int, numCliques+1) for u := 1; u < numVertices; u++ { for v := u + 1; v <= numVertices; v++ { count := edgeCounts[u][v] count2edges[count] = append(count2edges[count], []int{u, v}) } } writer.WriteString("Number of edges between pairs of vertices:/n") for count := numCliques; count >= 1; count-- { edges := count2edges[count] if len(edges) == 0 { continue } label := "edge" if count > 1 { label += "s:" } else { label += ": " } writer.WriteString(fmt.Sprintf("%5d %s", count, label)) for _, edge := range edges { writer.WriteString(fmt.Sprintf(" (%d, %d)", edge[0], edge[1])) } writer.WriteString("/n") } // Group vertices according to the number of clique memberships. count2vertices := make([][]int, numCliques+1) for u := 1; u <= numVertices; u++ { count := vertexCounts[u] count2vertices[count] = append(count2vertices[count], u) } writer.WriteString("/nNumber of cliques in which a vertex appears:/n") for count := numCliques; count >= 1; count-- { vertices := count2vertices[count] if len(vertices) == 0 { continue } label := "clique" if count > 1 { label += "s:" } else { label += ": " } writer.WriteString(fmt.Sprintf("%5d %s", count, label)) for _, u := range vertices { writer.WriteString(fmt.Sprintf(" %d", u)) } writer.WriteString("/n") } }

Dadas las siguientes tres secuencias de números, me gustaría descubrir cómo agrupar los números para encontrar las relaciones más cercanas entre ellos.

1,2,3,4 4,3,5 2,1,3 ...

No estoy seguro de cómo se llaman los algoritmos que estoy buscando, pero podemos ver relaciones más fuertes con algunos de los números que con otros.

Estos números aparecen juntos dos veces:

1 & 2 1 & 3 2 & 3 3 & 4

Juntos una vez:

1 & 4 2 & 4 3 & 5 4 & 5

Entonces, por ejemplo, podemos ver que debe haber una relación entre 1, 2, & 3 ya que todos aparecen juntos al menos dos veces. También podría decir que 3 & 4 están estrechamente relacionados ya que también aparecen dos veces. Sin embargo, el algoritmo podría elegir [1,2,3] (más de [3,4] ) ya que es una agrupación más grande (más inclusiva).

Podemos formar cualquiera de las siguientes agrupaciones si pegamos los números usados ​​con mayor frecuencia en un grupo:

[1,2,3] & [4,5] [1,2] & [3,4] & [5] [1,2] & [3,4,5] [1,2] & [3,4] & [5]

Si se permiten duplicados, incluso podría terminar con los siguientes grupos:

[1,2,3,4] [1,2,3] [3,4] [5]

No puedo decir qué agrupación es más "correcta", pero los cuatro de estos combos encuentran formas diferentes de agrupar los números de forma semi-correcta. No estoy buscando una agrupación específica, solo un algoritmo de clúster general que funciona bastante bien y es fácil de entender.

Estoy seguro de que hay muchas otras maneras de usar el conteo de ocurrencias para agruparlos también. ¿Cuál sería un buen algoritmo de agrupación base para estos? Se prefieren las muestras en Go, Javascript o PHP.


Como ya se mencionó, se trata de camarilla. Si quiere una respuesta exacta, se enfrentará al Problema de cliqueo máximo, que es NP-completo. Entonces, todo lo que sigue tiene sentido solo si el alfabeto de sus símbolos (números) tiene un tamaño razonable. En este caso, el algoritmo de estrecho avance, no muy optimizado para el Problema Máximo de la Camada en el pseudo-código sería

Function Main Cm ← ∅ // the maximum clique Clique(∅,V) // V vertices set return Cm End function Main Function Clique(set C, set P) // C the current clique, P candidat set if (|C| > |Cm|) then Cm ← C End if if (|C|+|P|>|Cm|)then for all p ∈ P in predetermined order, do P ← P / {p} Cp ←C ∪ {p} Pp ←P ∩ N(p) //N(p) set of the vertices adjacent to p Clique(Cp,Pp) End for End if End function Clique

Debido a Go es mi lenguaje de elección aquí es la implementación

package main import ( "bufio" "fmt" "sort" "strconv" "strings" ) var adjmatrix map[int]map[int]int = make(map[int]map[int]int) var Cm []int = make([]int, 0) var frequency int //For filter type resoult [][]int var res resoult var filter map[int]bool = make(map[int]bool) var bf int //For filter //That''s for sorting func (r resoult) Less(i, j int) bool { return len(r[i]) > len(r[j]) } func (r resoult) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func (r resoult) Len() int { return len(r) } //That''s for sorting //Work done here func Clique(C []int, P map[int]bool) { if len(C) >= len(Cm) { Cm = make([]int, len(C)) copy(Cm, C) } if len(C)+len(P) >= len(Cm) { for k, _ := range P { delete(P, k) Cp := make([]int, len(C)+1) copy(Cp, append(C, k)) Pp := make(map[int]bool) for n, m := range adjmatrix[k] { _, ok := P[n] if ok && m >= frequency { Pp[n] = true } } Clique(Cp, Pp) res = append(res, Cp) //Cleanup resoult bf := 0 for _, v := range Cp { bf += 1 << uint(v) } _, ok := filter[bf] if !ok { filter[bf] = true res = append(res, Cp) } //Cleanup resoult } } } //Work done here func main() { var toks []string var numbers []int var number int //Input parsing StrReader := strings.NewReader(`1,2,3 4,3,5 4,1,6 4,2,7 4,1,7 2,1,3 5,1,2 3,6`) scanner := bufio.NewScanner(StrReader) for scanner.Scan() { toks = strings.Split(scanner.Text(), ",") numbers = []int{} for _, v := range toks { number, _ = strconv.Atoi(v) numbers = append(numbers, number) } for k, v := range numbers { for _, m := range numbers[k:] { _, ok := adjmatrix[v] if !ok { adjmatrix[v] = make(map[int]int) } _, ok = adjmatrix[m] if !ok { adjmatrix[m] = make(map[int]int) } if m != v { adjmatrix[v][m]++ adjmatrix[m][v]++ if adjmatrix[v][m] > frequency { frequency = adjmatrix[v][m] } } } } } //Input parsing P1 := make(map[int]bool) //Iterating for frequency of appearance in group for ; frequency > 0; frequency-- { for k, _ := range adjmatrix { P1[k] = true } Cm = make([]int, 0) res = make(resoult, 0) Clique(make([]int, 0), P1) sort.Sort(res) fmt.Print(frequency, "x-times ", res, " ") } //Iterating for frequency of appearing together }

Y aquí puedes ver que funciona https://play.golang.org/p/ZiJfH4Q6GJ y jugar con datos de entrada. Pero una vez más, este enfoque es para un alfabeto de tamaño razonable (y datos de entrada de cualquier tamaño).


Este problema a menudo surge en el contexto de minería de reglas al analizar datos de ventas. (¿Qué artículos se compran juntos? Para que puedan colocarse uno al lado del otro en el supermercado)

Una clase de algoritmos que encontré es el aprendizaje de reglas de asociación . Y un paso inherente es encontrar conjuntos de elementos frecuentes que coincidan con su tarea. Un algoritmo es Apriori . Pero puedes encontrar mucho más cuando buscas esas palabras clave.


Sería mejor que describa el objetivo de dicha agrupación. Si no, puedo intentar sugerir el enfoque simple (como creo), y lo más limeted. No es adecuado si necesita contar una gran cantidad de números ampliados (como 1, 999999, 31) o grandes o no positivos. puede reorganizar los conjuntos de números en posiciones de arreglo de la siguiente manera:

|1|2|3|4|5|6| - numers as array positions ============== *1|1|1|1|1|0|0| *1 *2|0|0|1|1|1|0| *2 *4|1|1|1|0|0|0| *4 ============== +|2|2|3|2|1|0 - just a counters of occurence *|5|5|7|3|2|0 - so for first column number 1 mask will be: 1*1+1*4 = 5

aquí puedes ver en + fila que la combinación más frecuente es [3], luego [1,2] y [4] y luego [5], también puedes indicar y distinguir la coocurrencia de diferentes combinaciones

function grps(a) { var r = []; var sum = []; var mask = []; var max = 0; var val; for (i=0; i < a.length; i++) { for (j=0; j < a[i].length; j++) { val = a[i][j]; //r[i][val] = 1; sum[val] = sum[val]?sum[val]+1:1; mask[val] = mask[val]?mask[val]+Math.pow(2, i):1; if (val > max) { max = val; } } } for (j = 0; j < max; j++){ for (i = 0; i < max; i++){ r[sum[j]][mask[j]] = j; } } return r; }