请利用动态规划算法、回溯法、分支限界法、蒙特卡洛算法以及融合回溯法的蒙特卡洛算法来求解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()