Experimento 4: algoritmo de programación dinámica y método de retroceso

Experimento 4: algoritmo de programación dinámica y método de retroceso

Use el algoritmo de programación dinámica para diseñar y realizar el problema de la mochila 0-1, use el método de retroceso para diseñar y realizar el problema de la mochila 0-1 y use diferentes volúmenes de datos para la comparación y el análisis experimentales.Se requiere analizar la complejidad del tiempo del algoritmo y presentar el código pertinente y otros documentos;

  1. Descripción del problema

(1) Problema de mochila

Dado un conjunto de artículos, cada artículo tiene su propio peso y precio, dentro del peso total limitado, ¿cómo elegimos hacer que el precio total de los artículos sea el más alto? El problema de la mochila también se puede describir como un problema decisivo, es decir, ¿puede el valor total llegar a V bajo la premisa de que el peso total no supera a W? Fue propuesto por Merkle y Hellman en 1978.

(2) algoritmo de programación dinámica

La programación dinámica (Dynamic Programming, DP) es una rama de la investigación de operaciones, que es el proceso de resolución de la optimización del proceso de toma de decisiones. A principios de la década de 1950, el matemático estadounidense Bellman (R. Bellman) y otros propusieron el famoso principio de optimización al estudiar el problema de optimización del proceso de toma de decisiones en varias etapas, creando así una programación dinámica. La aplicación de la programación dinámica es extremadamente amplia, incluida la tecnología de ingeniería, la economía, la producción industrial, el control militar y de automatización, etc. Se ha logrado un efecto significativo en

(3) Método de seguimiento

El método de backtracking (método de exploración y backtracking) es un método de búsqueda óptima, también conocido como método heurístico, que busca hacia adelante de acuerdo con las condiciones óptimas para lograr el objetivo. Pero cuando se explora un determinado paso, si la elección original no es óptima o no se puede alcanzar el objetivo, retroceda un paso y haga otra elección, lo que se denomina "punto de retroceso".

  1. Objetivo

Domine la programación dinámica y las ideas de diseño retrospectivas y el diseño de programas.

  1. Principio experimental

(1) Problema de mochila 0-1 basado en algoritmo de programación dinámica:

Dados n artículos y una mochila, el artículo i tiene peso wi, valor vi y capacidad de mochila c. Cómo elegir artículos para maximizar el valor total de los artículos en la mochila.

La solución es usar programación dinámica y recursividad para descomponer un problema grande en varios problemas pequeños.

① Propiedades óptimas de la subestructura:

El problema de la mochila 0-1 tiene propiedades de subestructura óptimas. Una razón es que el número total de elementos correspondientes a varias soluciones óptimas es el número total de elementos de la solución óptima que quedan después de restar el peso de una solución óptima.

②Relación recursiva:

Buscar repetidamente la solución óptima. La siguiente solución óptima es la anterior:

Si el artículo no está cargado, la solución óptima se omite directamente.

Si se planea cargar el artículo, tome el valor máximo del valor de carga y no carga.

 

El diseño de mi función es consistente con el del libro, pero está implementado en python, la razón se explicó en el experimento anterior. El diseño es el siguiente:

 

La complejidad temporal de la función Bag es O(nc)

La complejidad temporal de la función Show es O(n)

(2) El problema de la mochila 0-1 diseñado con base en el método backtracking:

Núcleo de retroceso:

Si puedes avanzar, avanzarás; si no puedes entrar, cambiarás; si no puedes cambiar, retrocederás. (Según la búsqueda en profundidad condicional, cuando se encuentra un determinado paso, si no es óptimo o no se puede alcanzar el objetivo, retrocede un paso y vuelve a elegir). El método de retroceso considerado actualmente no realiza una optimización secundaria del algoritmo, sino que solo escribe el código original.

 

La solución adopta un diseño de pensamiento orientado a objetos (de hecho, es porque otros esquemas de diseño son difíciles de resolver el problema)

 

Ideas para solucionar este problema:

Utilice la secuencia 0/1 para representar la ubicación de los elementos. Piense en la búsqueda como un árbol binario. La capa i-ésima del árbol binario representa el elemento i-ésimo. Si el espacio restante permite colocar el elemento i en la mochila, expanda el subárbol izquierdo. Si no se puede poner en la mochila, juzgue las condiciones de contorno y, si la expansión posterior puede obtener el valor óptimo, entonces expanda el subárbol derecho (es decir, no se coloca el elemento i, pero se consideran los elementos posteriores). Cuando la cantidad de capas alcance la cantidad de elementos, deje de expandirse y comience a retroceder.

 

Restricciones:

La masa total de artículos puestos en la mochila es menor o igual a la capacidad de la mochila.

 

Condiciones límite:

El valor total de los artículos actualmente en la mochila (i y antes) + el valor total de los artículos después de i < el valor óptimo conocido En este caso, no hay necesidad de buscar

  1. diseño experimental

4.1 Problema de la mochila 0-1 (Programación dinámica)

La entrada es el número de elementos y el peso que puede soportar la mochila, ambos números enteros

También matrices de peso y valor (listas de python)

 

La salida es el valor óptimo, los elementos agregados (en forma de una lista numerada)

y el tiempo de ejecución del programa

 

Ejecute múltiples registros de datos y análisis gráfico.

 

4.2 Problema de la mochila 0-1 (solución por el método de retroceso)

Resuelto utilizando un enfoque orientado a objetos.

Debido a que la eficiencia del código de ejecución vs code es relativamente baja, esta vez se usa pycharm.

  1. Resultados experimentales y análisis.

5.1 Problema de la mochila 0-1 (Programación dinámica)

Primero intente ejecutar el programa de la siguiente manera:

No hay problema, genial, comienza la prueba de números aleatorios:

En primer lugar, existe una relación lógica entre n y c. Si c es demasiado grande, se seleccionarán la mayoría de los elementos. Si c es demasiado pequeño, el aumento de n no tendrá sentido. En cuanto al valor y peso del artículo, se toma un número aleatorio en el rango de 1-n por conveniencia.

Por defecto el tiempo de Bag and show toma el valor promedio de 3 veces, lo cual se refleja en la tabla

Ignore cualquier impacto de la impresión en el tiempo de ejecución

 

(1) Primero, no cambie n, y permita que c aumente en diferencia aritmética:

C norte c*n tiempo de bolsa (ns) mostrar tiempo (ns) bolsa1 detrás2 bolsa3 mostrar1 mostrar2 mostrar3
1000 50 50000 23246633.33 1364933.333 22898700 22937500 23903700 1102600 996800 1995400
1200 50 60000 28243233.33 984700 29920300 25927500 28881900 999100 957800 997200
1400 50 70000 33128433.33 979533.3333 35489100 30984900 32911300 995900 928300 1014400
1600 50 80000 38128733.33 959900 37474800 39948500 36962900 1002900 942000 934800
1800 50 90000 47240066.67 964266.6667 47907200 45941000 47872000 960000 933800 999000

Esta es una gráfica de c*n vs. bolsa, muestra el tiempo

Se puede ver que el aumento del valor de c*n hace que el tiempo de la bolsa aumente linealmente, pero el valor de mostrar casi no cambia, porque la complejidad de tiempo de la bolsa es O(cn), y la complejidad de tiempo de la muestra es O( n), mientras que el valor de n se controla sin cambios, solo se cambia el valor de c.

(2) Cambia el valor de n para que crezca aritméticamente, pero no cambies el valor de c:

C norte c*n tiempo de bolsa (ns) mostrar tiempo (ns) bolsa1 detrás2 bolsa3 mostrar1 mostrar2 mostrar3
2000 50 100000 49222800 961433.3 47931200 47829100 51908100 936100 998600 949600
2000 55 110000 56543767 955700 50886100 58842000 59903200 937000 997300 932800
2000 60 120000 61846667 985600 64827900 63827400 56884700 999300 996900 960600
2000 sesenta y cinco 130000 65841567 979866.7 66872700 64827600 65824400 944300 997400 997900
2000 70 140000 67170133 985833.3 66822400 65791100 68896900 956000 1003600 997900

El tiempo de presentación básicamente tiene una relación lineal con el aumento de n. Dado que la diferencia de datos es demasiado pequeña, esta relación lineal puede no ser muy obvia.

Captura de pantalla de una ejecución de N=50, c=1400:

Captura de pantalla de una ejecución con N=70, c=2000:

 

5.2 Problema de mochila 0-1 (método de retroceso)

Intenta ejecutar primero:

Tome una medida de aleatorización de datos:

(Nota: después de la prueba teórica y varias ejecuciones de prueba, la eficiencia de este algoritmo es mucho menor que la del algoritmo de programación dinámica, así que no tome un número demasiado grande)

Tome el peso de la mochila 200, el valor y el valor de un solo peso al azar del 1 al 50:

Número de productos

tiempo de ejecución (ns)

15

16806966

20

376673867

25

2410045300

30

14938310733

35

38633429100

 

  1. en conclusión

Usando el algoritmo de programación dinámica y el método de retroceso para resolver el problema de la mochila 0-1, la conclusión ha sido bien verificada. En el proceso de escribir el programa, consulté alguna información en Internet y libros, y me gustaría expresar mi gratitud.

 

  1. código fuente del programa

7.1 Algoritmo de programación dinámica:

import random
import time

def bag(n, c, w, v):
    # 置零,表示初始状态
    value = [[0 for j in range(c + 1)] for i in range(n + 1)]
    for i in range(1, n + 1):
        for j in range(1, c + 1):
            value[i][j] = value[i - 1][j]
            # 背包总容量够放当前物体,遍历前一个状态考虑是否置换
            if j >= w[i - 1] and value[i][j] < value[i - 1][j - w[i - 1]] + v[i - 1]:
                value[i][j] = value[i - 1][j - w[i - 1]] + v[i - 1]
    print("weight: ")
    print(w)
    print("value: ")
    print(v)
    # for x in value:
    #     print(x)
    return value

def show(n, c, w, value):
    print('the best value is ', value[n][c])
    x = [False for i in range(n)]
    j = c
    for i in range(n, 0, -1):
        if value[i][j] > value[i - 1][j]:
            x[i - 1] = True
            j -= w[i - 1]
    print('the things in the bag: ')
    for i in range(n):
        if x[i]:
            print('NO. ', i+1, ' thing,', end='')

n = 70  # 物品的数量,
c = 2000 # 书包能承受的重量,
w=[0 for _ in range(n)] # the weight of goods
v=[0 for _ in range(n)] # the values of goods
for k in range(n):
    w[k]=random.randint(1,n)
    v[k]=random.randint(1,n)

# 乱码严重,改为英文输出(utf-8 也会乱码我也是醉了)
t1=time.time_ns()
total = bag(n,c,w,v)
e1=time.time_ns()

t2=time.time_ns()
show(n,c,w,total)
e2=time.time_ns()

print("\n===============================")
print("time of function bag :"+str(e1-t1)+" ns")
print("time of function show :"+str(e2-t2)+" ns")

 

7.2 Método de seguimiento

import random
import time
import numpy

class BackSack():  # 定义背包类
    def __init__(self, capacity):  # 类的初始化
        self.capacity = capacity  # 背包最大容量(重量)
        self.currentWeight = 0  # 背包当前重量
        self.bestValue = 0  # 背包可容纳货物的最大价值,最优值
        self.currentValue = 0  # 背包内当前已装货物的价值

    def Backtrack(self, i):  # 遍历解空间寻找最优值,I:当前搜索的深度
        global length, weight, value, goods  # 全局变量
        if (i > length):
            if self.currentValue > self.bestValue:  # 更新最优值
                self.bestValue = self.currentValue
                self.currentCapacity = self.currentWeight  # 当前最优解下的背包重量
                self.bestgoods = goods[0:10]
                print('best:', self.bestgoods)  # 输出当前的最优解,最后一次输出即是最终的最优解
            return
        if self.currentWeight + weight[i] <= self.capacity:  # 进入左子树,即选取goods[i]放入背包
            goods[i] = 1
            self.currentWeight = self.currentWeight + weight[i]
            self.currentValue = self.currentValue + value[i]
            self.Backtrack(i + 1)
            self.currentValue = self.currentValue - value[i]  # 进入右子树,即舍弃goods[i],不放入背包
            self.currentWeight = self.currentWeight - weight[i]
            goods[i] = 0
        self.Backtrack(i + 1)


def main():
    global length, weight, value, goods  # 全局变量,分别表示货物数目,货物的重量数组,价值数组,货物的选取即0-1值
    # currentWeight = 0
    # bestValue = 0
    # currentValue = 0
    capacity = 200
    number_goods = 35
    value_max = 50
    weight_max = 50

    weight = [0 for _ in range(number_goods)]
    for index1 in range(number_goods):
        weight[index1] = random.randint(1, weight_max)
    print(weight)

    # weight = [2, 2, 6, 5, 4]

    value = [0 for _ in range(number_goods)]
    for index2 in range(number_goods):
        value[index2] = random.randint(1, value_max)
    print(value)

    # value = [6, 3, 5, 4, 6]

    goods = [0 for _ in range(number_goods)]

    length = len(weight) - 1
    backsack = BackSack(capacity)

    start_time = time.time_ns()
    backsack.Backtrack(0)
    end_time = time.time_ns()

    # backsack.Backtrack=Backtrack
    print("===============================================")
    print("Bag weight: " + str(capacity))
    print("Number of goods: " + str(capacity // 2))
    print("Best value: " + str(backsack.bestValue))
    print("Current weight: " + str(backsack.currentCapacity))  # 输出最优值和背包内物品的总重量
    print("Total time: " + str(end_time - start_time) + " ns.")
    print(backsack.bestgoods)  # 输出最优解

    return end_time - start_time


times = [0, 0, 0]
for ix in range(3):
    ctime = main()
    times[ix] = ctime
print("################################################################")
print(numpy.mean(times))

Código fuente del proyecto: dirección de github

Supongo que te gusta

Origin blog.csdn.net/qq_37387199/article/details/109722375
Recomendado
Clasificación