关于微信小程序使用JavaScript求解背包问题

拟解决生活中常见的问题之一:背包问题

该问题要求在一个物品集合中选择合适的物品放入背包,在放入背包中的物品总重量不超过背包容量的前提下,希望放入背包的物品总价值最大。根
据是否允许部分物品放入背包的要求,背包问题可以分为分数背包问
题和 0-1 背包问题。
对于分数背包问题,可以通过设计贪心算法得到问题实例的最优
解。对于 0-1 背包问题,该问题已经被证明为 NP-Hard,即不存在多
项式时间算法那求解,但可以通过贪心算法得到问题的近似解,或者
通过蛮力法、动态规划法得到问题的最优解。能分析各个算法所
使用的算法设计技术和时间复杂度。下列基本要求必须完成:
1、设计一个交互界面(例如菜单)供用户选择,如果可能,最好是
一个图形化用户界面;
2、能够人工输入一个背包问题具体实例,涉及物品个数、每个物品
的重量和价值,以及背包容量;
3、设计一个贪心算法求解分数背包问题给定实例的最优解,并分析
算法的时间复杂度;
4、设计一个贪心算法求解 0-1 背包问题给定实例的近似解,请提供
一个反例判断该算法不能总是能够给出最优解,并证明算法的解
和最优解的值的比值大于等于 1/2。
5、设计一个蛮力法算法求解 0-1 背包问题给定实例的最优解,并分
析算法的时间复杂度;
6、设计一个动态规划算法求解求解 0-1 背包问题给定实例的最优解,
并分析算法的时间复杂度;

wxml

<view class="box1">
  <text>请输入数量:</text>
  <input bindinput="numberInput"/>
  <text>请输入重量:</text>
  <input bindinput="weightInput"/>
  <text>请输入价值:</text>
  <input bindinput="valueInput"/>
  <text>请输入背包重量:</text>
  <input bindinput="maxWeightInput"/>
</view>
<view class="box4">
  <view>贪心法求分数背包</view>
  <text>{{total}}</text>
  <button type="primary" size="mini" class="btn2" bindtap="callTanxin_score">按我</button>
</view>
<view class="box4">
  <view>动态规划求01背包</view>
  <text>{{total1}}</text>
  <button type="primary" size="mini" class="btn2" bindtap="callKnapscak">按我</button>
</view>
<view class="box4">
  <view>穷举法求01背包</view>
  <text>{{total2}}</text>
  <button type="primary" size="mini" class="btn2" bindtap="callBruteForce">按我</button>
</view>
<view class="box4">
  <view>贪心法求01背包</view>
  <text>价值:{{total3}}</text>
  <text>重量:{{total3weight}}</text>
  <button type="primary" size="mini" class="btn2" bindtap="callTanxin_01">按我</button>
</view>

wxss

/* pages/home/home.wxss */
page{
  background: #eeeeee;
}
.show_array{
  min-height: 80rpx;
  width: 720rpx;
  line-height: 40rpx;
  word-wrap: break-word;
  word-break: break-all;
  padding: 12rpx 0;
  letter-spacing: 2rpx;
}
.box1{
  width: 704rpx;
  padding-left: 40rpx;
  padding-top: 10rpx;
  border-radius: 12rpx;
  background: #ffffff;
  margin: 14rpx auto;
  padding-bottom: 20rpx;
  font-size: 30rpx;
  line-height: 40rpx;
  color: #141414;
}
.box4{
  height: 130rpx;
  width: 704rpx;
  padding-left: 40rpx;
  padding-top: 10rpx;
  border-radius: 12rpx;
  background: #ffffff;
  margin: 14rpx auto;
  /* padding-bottom: 20rpx; */
  font-size: 30rpx;
  line-height: 40rpx;
  color: #141414;
}
.btn2{
  position: absolute;
  right: 30rpx;
}
.box4 text{
  display: inline-block;
  margin-right:40rpx;
}

js

Page({
  /**
   * 页面的初始数据
   */
  data: {
    number:0,
    weight:[],
    value:[],
    maxWeight:0,
    selected : [],
    F:[],
    total:'',//贪心分数
    total1:'',//动态规划
    total2:'',//穷举
    total3:'',//贪心01
    total3weight:'',//贪心01总重量
  },
numberInput(e){
  let testarray=e.detail.value
  this.data.number=parseInt(testarray) 
},
weightInput(e){
  let testarray=e.detail.value
  // this.data.weight=testarray.split(",")
  let nums=testarray.split(",")
  var turnNum=function(){
    for(let i=0;i<nums.length;i++){
      nums[i]=parseInt(nums[i])
    }
    return nums
  }
  this.data.weight=turnNum()
},
valueInput(e){
  let testarray=e.detail.value
  // this.data.value=testarray.split(",")
  let nums=testarray.split(",")
  var turnNum=function(){
    for(let i=0;i<nums.length;i++){
      nums[i]=parseInt(nums[i])
    }
    return nums
  }
  this.data.value=turnNum()
},
maxWeightInput(e){
  let testarray=e.detail.value
  this.data.maxWeight=testarray
},
// 贪心算法求分数背包  
		//贪心算法,只能算,可以分割的物品,如果不能分割物品,只能得到近似解,不分割物品,可以使用动态规划
		//1、计算每件商品的(价格/质量),即单位质量的价值
		//2、将单位质量价值排序
tanxin_score:function (){
  // let weights=[2,2,6,5,4]
  // let values=[6,3,5,4,6]
  // let capacity=10
  let weights=this.data.weight
  let values=this.data.value
  let capacity=this.data.maxWeight
	var list  = [];
	for(var i = 0,len = weights.length; i < len; i++){
		list.push({
			num:i+1,  //第几件商品
			w:weights[i], //重量
			v:values[i],
			rate:values[i]/weights[i]  
		});
	}
	list.sort(function(a,b){
		if(a.rate  > b.rate){
			return -1;
		}else{
			return 1;
		}
	});
	var selects = [];
	var total = 0;
	for(var i = 0,len = list.length; i < len; i++){
		var item = list[i];
		if(item['w'] <= capacity){
			selects.push({
				num:item.num,
				rate:1 ,       //完整的商品记录为1
				v:item.v,
				w:item.w
			});
			total = total + item.v;
			capacity = capacity - item.w;
		}else if(capacity > 0){
			//选取不完整的商品
			var rate = capacity/item['w'];
			var v = item.v*rate;
			selects.push({
				num:item.num,
				rate: rate,
				v:item.v*rate,
				w:item.w*rate
			});
			total = total + v;
			break;
		}else{
			break;
		}
	}
	return {
		selects,
		total
	}
},
callTanxin_score:function(){
  console.log(this.tanxin_score())
  this.setData({
    total:this.tanxin_score().total
  })
},
// 动态规划
knapsack:function (){
  let weights=this.data.weight
  let values=this.data.value
  let W=this.data.maxWeight
  var n = weights.length -1
  var f = [[]]  //记录结果矩阵
  for(var j = 0; j <= W; j++){
      if(j < weights[0]){ //如果容量不能放下物品0的重量,那么价值为0
         f[0][j] = 0
      }else{ //否则等于物体0的价值
         f[0][j] = values[0]
      }
  }
  for(var j = 0; j <= W; j++){
      for(var i = 1; i <= n; i++ ){
          if(!f[i]){ //创建新一行
              f[i] = []
          }
          if(j < weights[i]){ //等于之前的最优值
              f[i][j] = f[i-1][j]
          }else{
              f[i][j] = Math.max(f[i-1][j], f[i-1][j-weights[i]] + values[i]) 
          }
      }
      
  }
  console.log(f.concat())
  return f[n][W]
},
callKnapscak:function(){
  // this.knapsack()
  this.setData({
    total1:this.knapsack()
  })
},
// 递归
BFknapsack:function (n, W, weights, values, selected) {
  if (n == 0 || W == 0) {
      //当物品数量为0,或者背包容量为0时,最优解为0
      return 0;
  } else {
      //从当前所剩物品的最后一个物品开始向前,逐个判断是否要添加到背包中
      for (var i = n - 1; i >= 0; i--) {
          //如果当前要判断的物品重量大于背包当前所剩的容量,那么就不选择这个物品
          //在这种情况的最优解为f(n-1,C)
          if (weights[i] > W) {
              return this.BFknapsack(n - 1, W, weights, values, selected);
          } else {
              var a = this.BFknapsack(n - 1, W, weights, values, selected); //不选择物品i的情况下的最优解
              var b = values[i] + this.BFknapsack(n - 1, W - weights[i], weights, values, selected); //选择物品i的情况下的最优解
              //返回选择物品i和不选择物品i中最优解大的一个
              if (a > b) {
                  selected[i] = 0; //这种情况下表示物品i未被选取
                  return a;
              } else {
                  selected[i] = 1; //物品i被选取
                  return b;
              }
          }
      }
  }
},  
callBFknapsack:function(){
  let selected=this.data.selected
  let ws=[2,2,6,5,4]
  let vs=[6,3,5,4,6]
  console.log(this.BFknapsack(5,10,ws,vs,selected))
  selected.forEach(function(el,i){
  if(el){
      console.log("选择了物品"+i+ " 其重量为"+ ws[i]+" 其价值为"+vs[i])
  }
})
},
// 背包问题记忆化
MFKnapsack:function(i,j,F){
  var value = 0;
  // let F=this.data.F
  if(F[i][j] < 0){
    if (j < parseInt(this.pws[i])){
      value = this.MFKnapsack(i-1,j,F)
    }else{
      value = this.max(this.MFKnapsack(i-1,j,F),parseInt(this.pvs[i])+this.MFKnapsack(i-1,j-parseInt(this.pws[i]),F))
    }
    this.F[i][j] = value;
  }
  return this.F[i][j],F;
},
//求最大值
max:function(a,b){
  if(a>b){
    return a;
  }else{
    return b;
  }
},
//背包问题主函数
backpackmain:function(){
  // let pws=[2,2,6,5,4]
  // let pvs=[6,3,5,4,6]
  let pws=this.data.weight
  let pvs=this.data.value
  let F=this.data.F
  let pweight=''
  let pvalue=''
  let backpackweight=''
  let ts=[]
  //pws和pvs首端添加0,因为背包的记忆功能算法使用的是Weights[1..n]和Valuess[1..n]
  pws=pws.splice(0,0,0);
  pvs=pvs.splice(0,0,0);
  F = new Array(pws.length);
  //初始化值
  for (var i = 0; i < pws.length; i++) {
    F[i] = new Array(parseInt(backpackweight)+1)
    for (var j = 0; j <= parseInt(backpackweight); j++) {
      if(i==0 || j==0){
        F[i][j] = 0;
      }else{
        F[i][j] = -1;
      }
    }
  }
  this.MFKnapsack(pws.length -1,parseInt(backpackweight),F)
  //输出最优子集
   things = new Array();
  var j = parseInt(backpackweight);
  for (var i = pws.length-1; i > 0; i--) {
    if(F[i][j] == F[i-1][j]){
      while(F[i][j] == F[i-1][j]){
        i--;
      }
    }
    ts.push(i)
    j = j - pws[i];
  }
  console.log(F)
  console.log(ts)
},
// 穷举法
bruteForce:function(){
  // let weights=[7,3,4,5]
  // let values=[42,12,40,25]
  // let W=10
  // let n=4    
  let weights=this.data.weight
  let values=this.data.value
  let W=this.data.maxWeight
  let n=this.data.number
  let height= Math.pow(2,n)
  var arr =Array(height).fill().map(() => Array(n))
  for(var i =0;i <height;i++){  //总共有2^n个子集,需要进行2^n次循环,及数组A有2^n行
      var temp1 = i;
      for(var j =0;j < n;j++){    //数组A有n列,每一列代表一个物品
        var temp2 = parseInt(temp1 % 2);
        arr[i][j] = temp2;
        temp1 =parseInt(temp1 / 2);
      }
  }
  this.printArray(arr,weights,values,W);
  let maxSumValue=this.printArray(arr,weights,values,W)
  return maxSumValue
},
//输出穷举方案的背包实例的选择物品(0代表不包含该物品,1表示包含该物品)的总重量及总价值,并输出最优实例的总价值
printArray:function(arr,weight,value,maxWeight){
  // let arr = new Array(16).fill(new Array(4).fill(0))
  let len1 = arr.length      //二维数组的行数
  let len2 = arr[0].length      //二维数组的列数
  let maxSumValue=0
  for(let i=0;i<len1;i++){
      let tempWeight = 0;      //暂时计算当前选中背包实例物品的总重量,初始化为0
      let tempSumValue = 0;    //暂时计算当前选中背包实例物品的总价值,初始化为0
      let result=" "
      for(let j = 0;j < len2;j++){
        result+=arr[i][j]+" "
        tempWeight += arr[i][j]*weight[j]
        tempSumValue += arr[i][j]*value[j]
      }
      console.log(result)
      console.log("\t"+"总重量为:"+tempWeight);
      if(tempWeight <= maxWeight){
         console.log("\t"+"总价值为:"+tempSumValue)
      }
      else{
        console.log("\t"+"不可行(超出背包最大承重)")
      }
      if(tempWeight <= maxWeight && tempSumValue > maxSumValue)
          maxSumValue = tempSumValue;
      console.log();
  }
  console.log("穷举查找得知,最优解的总价值为:"+maxSumValue);
  return maxSumValue
},
callBruteForce:function(){
  this.bruteForce()
  this.setData({
    total2:this.bruteForce()
  })
},
//贪心法求01背包
//求价值
tanxin_01:function() {
  // let weights=[35, 30, 60, 50, 40, 10, 25]
  // let values=[10, 40, 30, 50, 35, 40, 30]
  // let W=150
  let weights=this.data.weight
  let values=this.data.value
  let W=this.data.maxWeight
  let n=this.data.number
  //存储累加的重量和价值
  let maxZhong=0;
  let maxJia=0;
  //存放密度
  let midu=new Array(n)
  //存放索引的数组
  let index=new Array(n)
  for (let i = 0; i < midu.length; i++)
  {
    midu[i]=parseFloat(values[i]/weights[i]);	
    index[i]=i;
  }
  //冒泡排序
  for (let i = 0; i < midu.length-1; i++)
  {
    for (let j = 0; j < midu.length-1-i; j++)
    {
      if (midu[j]<midu[j+1])
      {
        let temp =parseFloat(midu[j]);
        midu[j]=midu[j+1];
        midu[j+1]=temp;
        //索引跟着交换
        let temp1=index[j];
        index[j]=index[j+1];
        index[j+1]=temp1;
      }
    }
  }
  //计算最优解
  for (let i = 0; i < index.length; i++)
  {
    if (weights[index[i]]<=W)
    {
      maxZhong+=weights[index[i]];
      maxJia+=values[index[i]];
      W-=weights[index[i]];
    }
  }
  console.log("总重量:"+maxZhong);
  console.log("总价值:"+maxJia);
  return {maxZhong,maxJia}
},
callTanxin_01:function(){
  this.tanxin_01()
  this.setData({
    total3:this.tanxin_01().maxJia,
    total3weight:this.tanxin_01().maxZhong
  })
},

})
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值