Generación de todas las combinaciones de n Lista niveles de profundidad en Java

Alan cocción:

Estoy usando el siguiente código para generar una lista de combinaciones de tamaño s:

public static <T extends Comparable<? super T>> List<List<T>> combinations(List<T> items, int size) {
        if (size == 1) {
            List<List<T>> result = new ArrayList<>();
            for (T item : items) {
                result.add(Collections.singletonList(item));
            }
            return result ;
        }
        List<List<T>> result = new ArrayList<>();
        for (int i=0; i <= items.size() - size; i++) {
            T firstItem = items.get(i);
            List<List<T>> additionalItems = combinations(items.subList(i+1, items.size()), size-1) ;
            for (List<T> additional : additionalItems) {
                List<T> combination = new ArrayList<>();
                combination.add(firstItem);
                combination.addAll(additional);
                result.add(combination);
            }
        }
        return result ;
}

Dada una lista con los valores 1, 2 and 3con un tamaño de 2:

List<Integer> items = new ArrayList<Integer>();
items.add(1);
items.add(2);
items.add(3);

combinations(items, 2)

Esto produce las siguientes combinaciones:

[1, 2]
[1, 3]
[2, 3]

Estoy tratando de tomar esta salida y generar una tercera lista en la que cada una de las tres filas de salida anterior ahora se combina con vtas - sólo que esta orden de tiempo sensible y hasta 'd' niveles de profundidad. Estoy resultados similares a la siguiente salida esperando:

1 nivel profundo:

[1, 2]
[1, 3]
[2, 3]

2 niveles de profundidad:

[1, 2], [1, 3]
[1, 2], [2, 3]
[1, 3], [2, 3]
[1, 3], [1, 2]
[2, 3], [1, 2]
[2, 3], [1, 3]

3 niveles de profundidad:

[[1, 2], [1, 2], [1, 3]]
[[1, 2], [1, 2], [2, 3]]
[[1, 2], [1, 3], [1, 2]]
[[1, 2], [1, 3], [1, 3]]
[[1, 2], [1, 3], [2, 3]]
[[1, 2], [2, 3], [1, 2]]
[[1, 2], [2, 3], [1, 3]]
[[1, 2], [2, 3], [2, 3]]
[[1, 3], [1, 2], [1, 2]]
[[1, 3], [1, 2], [1, 3]]
[[1, 3], [1, 2], [2, 3]]
[[1, 3], [1, 3], [1, 2]]
[[1, 3], [1, 3], [2, 3]]
[[1, 3], [2, 3], [1, 2]]
[[1, 3], [2, 3], [1, 3]]
[[1, 3], [2, 3], [2, 3]]
[[2, 3], [1, 2], [1, 2]]
[[2, 3], [1, 2], [1, 3]]
[[2, 3], [1, 2], [2, 3]]
[[2, 3], [1, 3], [1, 2]]
[[2, 3], [1, 3], [1, 3]]
[[2, 3], [1, 3], [2, 3]]
[[2, 3], [2, 3], [1, 2]]
[[2, 3], [2, 3], [1, 3]]

4 niveles de profundidad:

[[1, 2], [1, 2], [1, 2], [1, 3]]
[[1, 2], [1, 2], [1, 2], [2, 3]]
[[1, 2], [1, 2], [1, 3], [1, 2]]
[[1, 2], [1, 2], [1, 3], [1, 3]]
[[1, 2], [1, 2], [1, 3], [2, 3]]
[[1, 2], [1, 2], [2, 3], [1, 2]]
[[1, 2], [1, 2], [2, 3], [1, 3]]
[[1, 2], [1, 2], [2, 3], [2, 3]]
[[1, 2], [1, 3], [1, 2], [1, 2]]
[[1, 2], [1, 3], [1, 2], [1, 3]]
[[1, 2], [1, 3], [1, 2], [2, 3]]
[[1, 2], [1, 3], [1, 3], [1, 2]]
[[1, 2], [1, 3], [1, 3], [1, 3]]
[[1, 2], [1, 3], [1, 3], [2, 3]]
[[1, 2], [1, 3], [2, 3], [1, 2]]
[[1, 2], [1, 3], [2, 3], [1, 3]]
[[1, 2], [1, 3], [2, 3], [2, 3]]
[[1, 2], [2, 3], [1, 2], [1, 2]]
[[1, 2], [2, 3], [1, 2], [1, 3]]
[[1, 2], [2, 3], [1, 2], [2, 3]]
[[1, 2], [2, 3], [1, 3], [1, 2]]
[[1, 2], [2, 3], [1, 3], [1, 3]]
[[1, 2], [2, 3], [1, 3], [2, 3]]
[[1, 2], [2, 3], [2, 3], [1, 2]]
[[1, 2], [2, 3], [2, 3], [1, 3]]
[[1, 2], [2, 3], [2, 3], [2, 3]]
[[1, 3], [1, 2], [1, 2], [1, 2]]
[[1, 3], [1, 2], [1, 2], [1, 3]]
[[1, 3], [1, 2], [1, 2], [2, 3]]
[[1, 3], [1, 2], [1, 3], [1, 2]]
[[1, 3], [1, 2], [1, 3], [1, 3]]
[[1, 3], [1, 2], [1, 3], [2, 3]]
[[1, 3], [1, 2], [2, 3], [1, 2]]
[[1, 3], [1, 2], [2, 3], [1, 3]]
[[1, 3], [1, 2], [2, 3], [2, 3]]
[[1, 3], [1, 3], [1, 2], [1, 2]]
[[1, 3], [1, 3], [1, 2], [1, 3]]
[[1, 3], [1, 3], [1, 2], [2, 3]]
[[1, 3], [1, 3], [1, 3], [1, 2]]
[[1, 3], [1, 3], [1, 3], [2, 3]]
[[1, 3], [1, 3], [2, 3], [1, 2]]
[[1, 3], [1, 3], [2, 3], [1, 3]]
[[1, 3], [1, 3], [2, 3], [2, 3]]
[[1, 3], [2, 3], [1, 2], [1, 2]]
[[1, 3], [2, 3], [1, 2], [1, 3]]
[[1, 3], [2, 3], [1, 2], [2, 3]]
[[1, 3], [2, 3], [1, 3], [1, 2]]
[[1, 3], [2, 3], [1, 3], [1, 3]]
[[1, 3], [2, 3], [1, 3], [2, 3]]
[[1, 3], [2, 3], [2, 3], [1, 2]]
[[1, 3], [2, 3], [2, 3], [1, 3]]
[[1, 3], [2, 3], [2, 3], [2, 3]]
[[2, 3], [1, 2], [1, 2], [1, 2]]
[[2, 3], [1, 2], [1, 2], [1, 3]]
[[2, 3], [1, 2], [1, 2], [2, 3]]
[[2, 3], [1, 2], [1, 3], [1, 2]]
[[2, 3], [1, 2], [1, 3], [1, 3]]
[[2, 3], [1, 2], [1, 3], [2, 3]]
[[2, 3], [1, 2], [2, 3], [1, 2]]
[[2, 3], [1, 2], [2, 3], [1, 3]]
[[2, 3], [1, 2], [2, 3], [2, 3]]
[[2, 3], [1, 3], [1, 2], [1, 2]]
[[2, 3], [1, 3], [1, 2], [1, 3]]
[[2, 3], [1, 3], [1, 2], [2, 3]]
[[2, 3], [1, 3], [1, 3], [1, 2]]
[[2, 3], [1, 3], [1, 3], [1, 3]]
[[2, 3], [1, 3], [1, 3], [2, 3]]
[[2, 3], [1, 3], [2, 3], [1, 2]]
[[2, 3], [1, 3], [2, 3], [1, 3]]
[[2, 3], [1, 3], [2, 3], [2, 3]]
[[2, 3], [2, 3], [1, 2], [1, 2]]
[[2, 3], [2, 3], [1, 2], [1, 3]]
[[2, 3], [2, 3], [1, 2], [2, 3]]
[[2, 3], [2, 3], [1, 3], [1, 2]]
[[2, 3], [2, 3], [1, 3], [1, 3]]
[[2, 3], [2, 3], [1, 3], [2, 3]]
[[2, 3], [2, 3], [2, 3], [1, 2]]
[[2, 3], [2, 3], [2, 3], [1, 3]]

Nótese cómo en 2 niveles de profundidad de la combinación [1, 2], [1, 2]no se genera ya que no hay ningún conjunto de números diferentes en el medio, antes o después de ese conjunto. Sin embargo, a los 3 niveles de profundidad generamos la combinación [1, 2], [1, 3], [1, 2]ya que la combinación [1, 3]está presente entre los dos pares de [1, 2].

Del mismo modo, a los 4 niveles de profundidad, generamos la secuencia [1, 2], [1, 3], [1, 2], [1, 2]que es no equivalente a la secuencia [1, 2], [1, 3], [1, 2]ya que no hay secuencia adicional de [1, 2]después [1, 2], [1, 3], [1, 2]. No generamos la secuencia [1, 2], [1, 2], [1, 2], [1, 2]a los 4 niveles de profundidad ya que esta combinación es esencialmente equivalente a [1, 2]bec no hay un nuevo conjunto de números en el medio, antes o después de la combinación [1, 2].

En pocas palabras, ¿cómo combino una lista de listas de números - hasta cualquier número de niveles de profundidad (1-4 se usa sólo como ejemplo) pero esta vez el resultado que es objeto sensible (por lo que [1, 2], [1, 3]no es equivalente a [1, 3], [1, 2])? El resultado es probable que se almacena en una List<List<List<Integer>>>.

He buscado en todo en StackOverflow y he visto varios hilos en la generación de combinaciones (como éste y éste ), pero no abordó la situación exacta se describe anteriormente.

Gracias

a pescado:

Creo que hice lo que busca. El código se divide en cuatro métodos independientes (que no hizo ninguna distinción si tenía que ser solamente 1):

public static <T extends Comparable<? super T>> List<List<List<T>>> level(List<List<T>> items, int level) {
    List<List<List<T>>> result = new ArrayList<>();
    if(level == 1) {
        for(List<T> item : items) {
            result.add(Collections.singletonList(item));
        }
        return result;
    }

    for(int i = 0; i < level; i++) {
        if(i == 0) {
            for(List<T> item : items)
                result.add(Collections.singletonList(item));
            continue;
        }

        List<List<List<T>>> newResult = new ArrayList<>();
        for(List<List<T>> item : result) {
            List<List<List<T>>> combined = new ArrayList<>();
            List<T> first = item.get(0);
            for(int j = 0; j < items.size(); j++) {
                List<List<T>> current = new ArrayList<>();
                List<T> it = items.get(j);
                current.addAll(item);
                current.add(it);

                combined.add(current);
            }

            newResult.addAll(combined);
        }

        result = newResult;
    }

    clean(result);
    return result;
}

Esto es lo que hace la mayor parte del algoritmo. En primer lugar, al igual que en la función que nos ha facilitado, se comprueba para ver si el nivel es 1, en cuyo caso sólo se puede devolver una lista de la lista, al igual que en el método dado. Después de eso, sin embargo bucle de más de muchos niveles que tenemos. En primer lugar, comprobamos si el nivel actual es 1, en cuyo caso se acaba de hacer lo mismo que si el método se llama con un nivel de 1. A continuación, creamos una nueva lista llamada newResult. Esta variable es básicamente un valor temporal del resultpara evitar una excepción de la modificación concurrente. A continuación, recorrer todos los valores que ya están en result. Creamos unos valores nuevos basados en cualquier valor que tenemos, y añadirlos a combined. A continuación, añadir combinedennewResultY el algoritmo es básicamente terminado. Ahora, esos bucles no cuentan para cuando todos los valores son los mismos, por ejemplo, [1, 2], [1, 2], [1, 2], [1, 2]. Así que llamamos el cleanmétodo para eliminar esos casos.

public static <T extends Comparable<? super T>> void clean(List<List<List<T>>> list) {
    List<List<List<T>>> removals = new ArrayList<>();
    for(List<List<T>> item : list) {
        if(!check(item))
            removals.add(item);
    }
    for(List<List<T>> item : removals) {
        list.remove(item);
    }
}

Este método se aplica a todo el interior de la lista dada, y se ejecuta el checkmétodo en el elemento. Ese método se explica más abajo. Si el elemento no es válido, se marca para la eliminación, y se retira en el siguiente bucle.

public static <T extends Comparable<? super T>> boolean check(List<List<T>> list) {
    if(list.size() < 2) return true;

    for(int i = 1; i < list.size(); i++) {
        List<T> previous = list.get(i-1);
        List<T> item = list.get(i);

        if(notEqual(previous, item)){
            return true;
        }
    }

    return false;
}

Este bucle comprueba si una lista dada es válida, mediante la comparación de una lista contra otro, hasta que encuentra dos que no son los mismos. Cuando eso sucede, la lista es válida, y devuelve verdadero. Si no, nunca volverá, se romperá fuera de onda, y volver falsa.

public static <T extends Comparable<? super T>> boolean notEqual(List<T> a, List<T> b) {
    for(int i = 0; i < Math.min(a.size(), b.size()); i++) {
        T ao = a.get(i);
        T bo = b.get(i);

        if(ao.compareTo(bo) != 0)
            return true;
    }

    return false;
}

Este método toma dos listas de entrada, y se comprueba para ver si los elementos dentro de ellos no son iguales. Se realiza un bucle sobre ambas listas, obtener los elementos en el mismo índice, y los compara entre sí. Si no son iguales, devuelve verdadero, y por otra parte, que termina el bucle y vuelve falsa.

Tenga en cuenta que esto es simplemente una prueba de concepto y no una versión final. Una gran cantidad de aspectos de la misma, sin duda podría ser mejorado, pero esta es una versión de trabajo de lo que pidió.

Enlace a la versión de trabajo en jDoodle

Si usted tiene alguna pregunta acerca de cualquier aspecto de la misma, o quiere nada aclarado, no dude en preguntar!

Editar: He revisado el algoritmo para incorporar lo que has pedido. Aquí está el nuevo código:

public static <T extends Comparable<? super T>> List<List<List<T>>> level(List<List<T>> items, int minLevel, int maxLevel) {
    List<List<List<T>>> result = new ArrayList<>();

    for(int i = minLevel; i < maxLevel+1; i++) {
        result.addAll(level(items, i));
    }

    return result;
}

Este es el método sobrecargado que le permitirá especificar el rango de niveles que desea. Dado un nivel máximo y mínimo, devolverá una nueva lista que contiene todos los niveles dentro de ese rango, ambos inclusive. Como usted ha dicho, es relativamente trivial, como un bucle simple.

public static <T extends Comparable<? super T>> List<List<List<T>>> level(List<List<T>> items, int level) {
    List<List<List<T>>> result = new ArrayList<>();
    if(level == 1) {
        for(List<T> item : items) {
            result.add(Collections.singletonList(item));
        }
        return result;
    }

    for(int i = 0; i < level; i++) {
        if(i == 0) {
            for(List<T> item : items)
                result.add(Collections.singletonList(item));
            continue;
        }

        List<List<List<T>>> newResult = new ArrayList<>();
        for(List<List<T>> item : result) {
            if(item.size() < i)
                continue;

            List<List<List<T>>> combined = new ArrayList<>();
            List<T> first = item.get(0);
            for(int j = 0; j < items.size(); j++) {
                List<List<T>> current = new ArrayList<>();
                List<T> it = items.get(j);
                current.addAll(item);
                current.add(it);

                combined.add(current);
            }

            newResult.addAll(combined);
        }

        result = newResult;
    }

    List<List<List<T>>> removals = new ArrayList<>();
    for(List<List<T>> item : result) {
        if(!check(item))
            removals.add(item);
    }
    for(List<List<T>> item : removals) {
        result.remove(item);
    }

    return result;
}

Aquí está el método revisado. Quité el cleanmétodo, y acaba de ponerlo dentro del levelmétodo, ya que sólo se llama una vez. No creo que en realidad es posible, al menos con el código actual para ejecutar ese cleanmétodo durante el algoritmo, porque en este punto en el tiempo, la forma en que funciona es que genera todas las combinaciones posibles para un nivel dado, luego va a la el proximo. Si se eliminaron combinaciones que son el mismo, en el siguiente nivel, no serían añadidos esas combinaciones.

Aquí está un ejemplo: Digamos que tengo [1, 2], [1, 3], [2, 3]. Si fuera a nivel dos, me gustaría tener las combinaciones especificadas en su pregunta. Bastante obvio ¿verdad? Bueno, si luego pasó al nivel 3, utilizando sólo los resultados de nivel 2, que se perderían todas las combinaciones que contienen [1, 2], [1, 2] [...], ya que no está en la lista dada. Este es un problema con el algoritmo, y sin duda podría ser mejorado.

Mi plan es aún más esta refactorización, para que sea comprobar el interior del algoritmo, pero me puede tardar un minuto caliente para hacer eso.

Nueva versión de trabajo en jDoodle

Edición 2: La incorporación del cleanmétodo dentro del algoritmo era en realidad mucho más simple que se pensaba inicialmente. Aquí está el nuevo código con un par de comentarios:

public static <T extends Comparable<? super T>> List<List<List<T>>> level(List<List<T>> items, int level) {
    List<List<List<T>>> result = new ArrayList<>();
    for(int i = 0; i < level; i++) {
        if(i == 0) { // If level is 0, we can just add the items as singleton lists to the result
            for(List<T> item : items)
                result.add(Collections.singletonList(item));
            continue;
        }

        List<List<List<T>>> newResult = new ArrayList<>(); // Temporary items that will be added
        for(List<List<T>> item : result) {
            if(item.size() < i) // Make sure we are manipulating items that are on the previous level
                continue;

            List<List<List<T>>> combined = new ArrayList<>(); // The temporary values for this specific item
            for(int j = 0; j < items.size(); j++) {
                List<List<T>> current = new ArrayList<>(); // The current list with the value
                current.addAll(item); // Add the current items from result to the list
                current.add(items.get(j)); // Add the current item from items to the list

                if (i == level-1 && !check(current)) { // If this is the last level, and the current list shouldn't be added, skip adding
                    continue;
                }

                combined.add(current); // Add the current list to the combined values
            }

            newResult.addAll(combined); // Add all of the lists in combined to the new result
        }

        result = newResult; // Make result equal to the new result
    }

    return result;
}

Ahora lo que hace es, al agregar una nueva combinación a la lista, se comprueba en primer lugar si el nivel actual es el definitivo. Si es así, en realidad se comprueba la lista, y si no es válido, se salta en realidad la adición.

nuevo planeo volver a escribir por completo el algoritmo en un formato mucho más inteligente, pero este código funciona completamente en este momento.

Trabajando en la versión jDoodle

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=175407&siteId=1
Recomendado
Clasificación