Algoritmo codicioso (algoritmo codicioso)

Idea principal

Al resolver el problema, cada paso selecciona la solución óptima local , esperando finalmente obtener la solución óptima global.
(El resultado final del algoritmo codicioso no es necesariamente la solución óptima global, pero de hecho es la solución óptima aproximada ).


Problema clásico problema de cobertura de 1 juego

Hay n conjuntos, y cada conjunto contiene varios elementos. Para encontrar m conjuntos a partir de ellos, se requiere que contenga todos los elementos de los n conjuntos ym es el más pequeño.
Soluciones generales:
(1) Enumere todas las combinaciones de n conjuntos, porque cada conjunto puede estar en el conjunto o no en el conjunto, por lo que hay un total de 2 n 2 ^ n2n tipos de esquemas combinados.
(2) En estos esquemas de combinación, encuentre la combinación del conjunto que contiene todos los elementos y la combinación contiene el menor número de conjuntos.

La complejidad temporal de este método es O (2 n) O (2 ^ n)O ( 2n ), a medida que n aumenta, el tiempo aumentará bruscamente y dejará de estar disponible.

Ideas de algoritmos codiciosos (algoritmo aproximado):
(1) Seleccione un conjunto que contenga la mayor cantidad de elementos no contenidos.
(2) Repita el primer paso hasta que el conjunto seleccionado contenga todos los elementos.

La complejidad de tiempo del algoritmo codicioso es O (n 2) O (n ^ 2)O ( n2 ).
Ejemplo:
Hay varios conjuntos de la siguiente manera, resuelva
conjunto1 = {1, 2, 3};
conjunto2 = {1, 3, 4}; conjunto3
= {2, 5, 6};
conjunto4 = {2, 3} ;
conjunto5 = {6, 7};

Código:

public class GreedyAlgorithm {
    
    
    public Set<Integer> greedy(List<Set<Integer>> setList) {
    
    
    	//收集所有集合中的所有元素
        Set<Integer> needed = new HashSet<>();
        for (Set<Integer> set : setList) {
    
    
            for (int i : set) {
    
    
                needed.add(i);
            }
        }
        Set<Integer> result = new HashSet<>();
        while (!needed.isEmpty()) {
    
    
            int bestSet = 0;
            int bestCoveredSize = 0;
            for (int i = 0; i < setList.size(); i++) {
    
    
            	if (result.contains(i)) {
    
    
                    continue;
                }
                int coveredSize = 0;
                for (int j : setList.get(i)) {
    
    
                    if (needed.contains(j)) {
    
    
                        coveredSize++;
                    }
                }
                //体现出贪心算法,每次都选含有最多未包含元素的集合
                if (coveredSize > bestCoveredSize) {
    
    
                    bestSet = i;
                    bestCoveredSize = coveredSize;
                }
            }
            result.add(bestSet);
            needed.removeAll(setList.get(bestSet));

        }
        return result;
    }

    public static void main(String[] args) {
    
    
        List<Set<Integer>> setList = new ArrayList<>();
        setList.add(new HashSet<>(Arrays.asList(1, 2, 3)));
        setList.add(new HashSet<>(Arrays.asList(1, 3, 4)));
        setList.add(new HashSet<>(Arrays.asList(2, 5, 6)));
        setList.add(new HashSet<>(Arrays.asList(2, 3)));
        setList.add(new HashSet<>(Arrays.asList(6, 7)));
        System.out.println(new GreedyAlgorithm().greedy(setList));
    }
}

Problema clásico 2: problema del vendedor ambulante

Hay un agente de viajes que necesita viajar de la ciudad A a las otras n ciudades. Por favor, planifique su ruta de viaje para que el viaje total sea el más corto.

Solución general:
calcule n! N!n ! Recorra rutas y seleccione la ruta más corta entre ellas. La complejidad del tiempo esO (n!) O (n!)O ( n ! )

Idea de algoritmo codicioso:
cada vez que elija la siguiente ciudad a la que desea ir, elija la ciudad más cercana a la que no ha estado.

Código

public class GreedyAlgorithm2 {
    
    
    public static int[] greedyAlgorithm2(int[][] distance, int n) {
    
    
        int[] result = new int[n];
        Set<Integer> notBeenTo = new HashSet<>(n);
        for (int i = 1; i <= n; i++) {
    
    
            notBeenTo.add(i);
        }

        int from = 0;
        for (int i = 0; i < result.length; i++) {
    
    
            int next = 0;
            for (int j : notBeenTo) {
    
    
                //体现出贪心算法,每次都选还没去过的距离最近的城市
                if (next == 0 || distance[from][next] > distance[from][j]) {
    
    
                    next = j;
                }
            }
            result[i] = next;
            from = next;
            notBeenTo.remove(next);
        }

        return result;
    }

    //测试代码
    public static void main(String[] args) {
    
    
        int n = 4;
        int[][] distance = new int[n + 1][n + 1];
        //0表示旅行商当前所在城市A, 1...n表示要去的城市
        Random rnd = new Random();
        for (int i = 0; i < distance.length; i++) {
    
    
            for (int j = 0; j < i; j++) {
    
    
                if (i != j) {
    
    
                    distance[i][j] = distance[j][i] = rnd.nextInt(10) + 1;
                }
            }
        }
        for (int[] arr : distance) {
    
    
            System.out.println(Arrays.toString(arr));
        }
        System.out.println();
        int[] result = greedyAlgorithm2(distance, n);
        System.out.println(Arrays.toString(result));
    }
}

NP problema completo

En cuanto a cuál es el problema P, problema NP, problema NP completo (NPC), problema NP-difícil (NPH), por favor Baidu solo, pero no encontré una respuesta satisfactoria (no la entendí).

  • La definición simple de problemas NP-completos se conoce como problemas difíciles de resolver (problemas que no se pueden resolver en tiempo polinomial), como el problema que cubre el conjunto y el problema del viajante.
  • Al juzgar que un problema es NP-completo, no es necesario encontrar una solución perfecta, sino utilizar un algoritmo aproximado. Pero es difícil juzgar si un problema es NP-completo, porque la diferencia entre problemas fáciles de resolver y problemas NP-completos suele ser muy pequeña. Como "el problema del camino más corto entre dos puntos" y el "problema del viajante de comercio".

El siguiente método se puede utilizar para juzgar simplemente (no necesariamente) si el problema es NP-completo.

  • Cuando el número de elementos es pequeño, el algoritmo se ejecuta muy rápido, pero a medida que aumenta el número de elementos, la velocidad se vuelve muy lenta.
  • Los problemas que involucran "todas las combinaciones" suelen ser NP-completos.
  • El problema no se puede dividir en pequeños problemas, se deben considerar todas las situaciones posibles. Este puede ser un problema NP-completo.
  • Si el problema involucra una secuencia (como la secuencia de la ciudad en el problema del viajante de comercio) y es difícil de resolver, puede ser un problema NP completo.
  • Si el problema involucra una colección (como una colección de estaciones de radiodifusión) y es difícil de resolver, puede ser un problema NP-completo.
  • Si el problema se puede transformar en un problema de cobertura de conjunto o un problema de viajero, debe ser NP-completo.

Referencia: "Diagrama de algoritmo" Capítulo 8 Algoritmo codicioso

Supongo que te gusta

Origin blog.csdn.net/AmorFati1996/article/details/110998879
Recomendado
Clasificación