背包问题详解

请利用动态规划算法、回溯法、分支限界法、蒙特卡洛算法以及融合回溯法的蒙特卡洛算法来求解0-1背包问题,利用贪心算法来实现特殊类型的0-1背包问题,比较不同算法的时间复杂度和空间复杂度。


import random
import sys
import time
import heapq


import numpy as np

sys.setrecursionlimit(100000)  # 递归深度限制  似乎没啥用


def Greedy(C, N, goods):
    '''
    价值率最大贪心 小数背包问题
    '''
    start_time = time.time()

    Max_Value = 0
    leftc = C
    mark = 0
    while mark < N and goods[mark][0] <= leftc:
        Max_Value += goods[mark][1]
        leftc -= goods[mark][0]
        mark += 1
    if mark < N:
        Max_Value += goods[mark][-1]*leftc

    end_time = time.time()
    print("Greedy: Maximum Value : %.4f   time : %.4f" %
          (Max_Value, end_time-start_time))


def Brach_And_Bround(C, N, goods):
    '''分支界限法 量一大就崩了'''
    start_time = time.time()
    leftc = C
    bestp = 0
    presentvalue = 0

    def Bound(mark):
        '''返回标号为mark的上界价值O(n)'''
        cw = leftc
        cv = presentvalue
        while mark < N and goods[mark][0] <= cw:
            cv += goods[mark][1]
            cw -= goods[mark][0]
            mark += 1
        if mark < N:
            cv += goods[mark][2]*cw
        return cv

    def BrachBrounding(mark):
        nonlocal leftc, presentvalue, bestp
        status = []
        heapq.heapify(status)  # 对status堆化
        upper = Bound(mark)
        # 当选择左儿子结点时,上界约束up不用关心,重量约束wt需要考虑。因为上界约束跟父节点相同。
        # 当选择右儿子结点时,上界约束up需要考虑,重量约束不需要考虑。因为父节点和该结点重量相同。
        while mark != N:
            if goods[mark][0] <= leftc:
                if presentvalue+goods[mark][1] > bestp:
                    bestp = presentvalue + goods[mark][1]
                '''上界值取反人小根堆作为大根堆'''
                heapq.heappush(status, (-upper, mark, presentvalue +
                                        goods[mark][1], leftc-goods[mark][0]))
            upper = Bound(mark+1)
            if upper >= bestp:
                heapq.heappush(status, (-upper, mark, presentvalue, leftc))
            # 从优先队列中选择价值上界最大的结点成为扩展结点
            upper, mark, presentvalue, leftc = heapq.heappop(status)
            mark += 1
            upper = -upper

    BrachBrounding(0)
    end_time = time.time()
    print("Brach_And_Bround: Maximum value : %d  time : %.4f" %
          (bestp, end_time-start_time))


def BackTracking(C, N, goods):
    '''
    回溯法求解0-1背包问题
    时间复杂度O(n2^n)
    '''
    start_time = time.time()

    presentvalue = 0  # 当前价值
    status = []
    leftc = C
    bestp = 0  # 最大价值

    def Bound(mark):
        '''返回标号为mark的上界价值O(n)'''
        cw = leftc
        cv = presentvalue
        while mark < N and goods[mark][0] < cw:
            cv += goods[mark][1]
            cw -= goods[mark][0]
            mark += 1
        if mark < N:
            cv += goods[mark][2]*cw
        return cv

    def Backtrack(mark):
        '''递归函数O(2^n)'''
        nonlocal presentvalue, leftc, bestp
        if mark >= N:
            bestp = presentvalue
            return
        if goods[mark][0] <= leftc:
            status.append(mark)
            leftc -= goods[mark][0]
            presentvalue += goods[mark][1]
            Backtrack(mark+1)
            presentvalue -= goods[mark][1]
            leftc += goods[mark][0]
            status.pop(-1)
        if Bound(mark+1) > bestp:  # 符合条件搜索右子树
            Backtrack(mark+1)

    Backtrack(0)
    end_time = time.time()
    # print(status)
    print("BackTracking : Maximum Value : %d    time : %.4f" %
          (bestp, end_time-start_time))


def DP(C, N, goods):
    '''
    动态规划0-1背包问题
    返回背包容量C可容纳的最大价值
    '''
    start_time = time.time()
    dp = np.zeros((N, C+1), dtype=int)
    for i in goods:
        # 标号为i个物品
        for j in range(1, C+1):
            # 背包容量
            if j >= goods[i][0]:
                    # 背包容量可以放下标号i的物品
                dp[i][j] = max(dp[i-1][j], goods[i][1]+dp[i-1][j-goods[i][0]])
            else:
                dp[i][j] = dp[i-1][j]
    end_time = time.time()
    # print(dp)
    print("DP : Maximum value:%d    time: %.4f" %
          (dp[N-1][C], end_time-start_time))


def MonteCarlo(C, N, goods):
    '''蒙特卡洛算法'''
    #不是很敢确定
    start_time = time.time()
    bestp = 0
    for i in range(20000):
        leftc = C
        item = list(goods.keys())
        presentvalue = 0
        while True:
            key = random.choice(item)
            item.remove(key)
            if goods[key][0] <= leftc:
                presentvalue += goods[key][1]
                leftc -= goods[key][0]
            else:
                break
        if presentvalue > bestp:
            bestp = presentvalue
    end_time = time.time()
    print("MonteCarlo : Maximum value:%d    time: %.4f" %
          (bestp, end_time-start_time))


def Monte_Back(C, N, goods):
    '''蒙特卡洛+回溯'''
    #总觉得有点儿问题
    start_time = time.time()

    bestp = 0
    leftc = C
    presentvalue = 0
    status = []

    def MonteCarlo(n):
        nonlocal bestp, leftc, presentvalue
        for i in range(10000):
            leftc = C
            item = [j for j in range(n)]
            presentvalue = 0
            while True:
                key = random.choice(item)
                item.remove(key)
                if goods[key][0] <= leftc:
                    presentvalue += goods[key][1]
                    leftc -= goods[key][0]
                else:
                    break
            if presentvalue > bestp:
                bestp = presentvalue

    def Bound(mark):
        '''返回标号为mark的上界价值O(n)'''
        cw = leftc
        cv = presentvalue
        while mark < N and goods[mark][0] < cw:
            cv += goods[mark][1]
            cw -= goods[mark][0]
            mark += 1
        if mark < N:
            cv += goods[mark][2]*cw
        return cv

    def Backtrack(mark):
        '''递归函数O(2^n)'''
        nonlocal presentvalue, leftc, bestp
        if mark >= N:
            bestp = presentvalue
            return
        if goods[mark][0] <= leftc:
            status.append(mark)
            leftc -= goods[mark][0]
            presentvalue += goods[mark][1]
            Backtrack(mark+1)
            presentvalue -= goods[mark][1]
            leftc += goods[mark][0]
            status.pop(-1)
        if Bound(mark+1) > bestp:  # 符合条件搜索右子树
            Backtrack(mark+1)

    MonteCarlo(int(N/2))
    Backtrack(int(N/2))

    end_time = time.time()
    print("MonteCarlo_Back : Maximum value:%d    time: %.4f" %
          (bestp, end_time-start_time))


def main():
    Backpack_Capacity = int(
        input("Please input the capacity of the knapsack :"))
    Num_items = int(input("Please input the number of items:"))
    items = []
    for i in range(Num_items):
        weight = random.randint(1, 50)
        value = random.randint(1, 200)
        items.append((weight, value, value/weight))
    # 随机生成物品(重量,价值,单价)
    items.sort(key=lambda good: good[-1], reverse=True)
    # 按物品单价排序
    goods = dict(zip(range(Num_items), items))

    print(goods)
    print("\n")
    DP(Backpack_Capacity, Num_items, goods)
    BackTracking(Backpack_Capacity, Num_items, goods)
    Brach_And_Bround(Backpack_Capacity, Num_items, goods)
    Greedy(Backpack_Capacity, Num_items, goods)
    MonteCarlo(Backpack_Capacity, Num_items, goods)
    Monte_Back(Backpack_Capacity, Num_items, goods)

 
if __name__ == '__main__':
    main()

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值