我发现这个代码使用强力机制来解决背包问题(这主要是为了学习,所以不需要指出动态更有效)。我得到了代码工作,并了解其中的大部分内容。最。这是个问题:
我已经注意到这两个条件,我不知道它们是如何工作的以及为什么它们在代码中 - 我知道它们是至关重要的,因为我所做的任何改变都会导致算法产生错误结果:
// if bit not included then skip
if (((i >> j) & 1) != 1) continue;
// if bit match then add
if (((bestPosition >> j) & 1) == 1)
{
include.Add(Items[j]);
}
这是整个班级,以及我从主要方式调出来的方式:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace KnapSack2
{
class BruteForce
{
public double Capacity { get; set; }
public Item[] Items { get; set; }
public Data Run()
{
int bestValue = 0;
int bestPosition = 0;
int size = Items.Length;
var permutations = (long)Math.Pow(2,size);
for (int i = 0; i<permutations; i++)
{
int total = 0;
int weight = 0;
for (int j = 0; j<size; j++)
{
//jeżeli bit "not included" to omin"
if(((i>>j)&1)!=1)
continue;
total += Items[j].v;
weight += Items[j].w;
}
if (weight <= Capacity && total>bestValue)
{
bestPosition = i;
bestValue = total;
}
}
var include = new List<Item>();
for (int j = 0; j<size; j++)
{
// jeżeli bit pasuje, wtedy dodaj
if (((bestPosition>>j) & 1)==1)
include.Add(Items[j]);
}
return new Data { BestValue = bestValue, Include = include };
}//End of Run
}
}
在主要
中召唤var ks = new BruteForce
{
Capacity = MaxWeight,
Items = items.ToArray()
};
Data result = ks.Run();
Item class只是一个保存value,weight和ID的简单对象
答案 0 :(得分:20)
此&
是bitwise-AND
按位AND运算符将其第一个操作数的每个位与其进行比较 第二个操作数的对应位。如果两个位都是1,那么 对应的结果位设置为1.否则,对应 结果位设置为0。
虽然这个>>
是右移运算符
右移运算符(&gt;&gt;)将其第一个操作数右移 由其第二个操作数指定的位数。
话虽如此,让我们采取以下表达式
if (((i >> j) & 1) != 1) continue;
并尝试理解它。
最初,此i >> j
会将i
的位置向右移j
个位置。
例如,我们有以下任务:
int number = 5;
number
的二进制表示形式为:
0000 0000 0000 0000 0000 0000 0000 0101
如果我们将一个新整数定义为:
int shiftNumbersBitByOne = a >> 1;
然后shiftNumbersBitByOne
的二进制表示将是:
0000 0000 0000 0000 0000 0000 0000 0010
然后在这个操作的结果和1上,我们应用按位AND运算符。
这个运营商究竟是什么?
尽管定义很明确,但一个例子将使其更加清晰。
假设我们有二进制数a
和b
,那么a&b
的结果如下:
a = 0001 0100 1010 0001 1000 1010 1101 0011
b = 0010 1100 1111 0111 0011 1010 1011 0111
a & b = 0000 0100 1010 0001 0000 1010 1001 0011
话虽如此,在这个操作中(i >> j) & 1
我们在i >> j
的结果和1的二进制表示之间应用了bitwise-AND运算符
0000 0000 0000 0000 0000 0000 0000 0001
(i >> j) & 1
的结果是1?
当且仅当<{em>} i >> j
的最后一位数为1时,才会发生。
<强>更新强>
上面我们解决了如何部分 - 我不知道它们是如何工作的。现在我们将解决为什么部分 - 为什么它们在代码中。
让我们定义我们的问题,即背包问题。根据{{3}}
背包问题或背包问题是组合中的问题 优化:给定一组项目,每个项目都有质量和值, 确定要包含在集合中的每个项目的数量 总重量小于或等于给定限制和总数 价值尽可能大。
根据以上所述,
是直截了当的// This is the total weight limit.
public double Capacity { get; set; }
和
// This is an array with all the given items.
public Item[] Items { get; set; }
此外,根据您的代码,我们可以推断出每个项目的值和权重分别可以作为item.v
和item.w
访问。我建议你分别将它重命名为值和重量,以使你的代码更有意义。
显然,此int size = Items.Length;
是可用项目的数量。
部分从这里开始的重点:
var permutations = (long)Math.Pow(2,size);
什么是permutations
? permutations
代表什么?
在我们回答这个问题之前,让我们考虑如何在最终解决方案中表示项目集合中的哪些项目。我认为如果我们有n个项目,我们可以用n位数表示这个。怎么可能?如果n位数中的每个位指的是n个项中的一个,那么很明显我们可以这样做。如果n- th 项目不包含在最终解决方案中,则n- th 位的值将为0。虽然它的价值是1,但如果它将被包括在内。
所说的很清楚排列代表什么。 表示最终解决方案中项目的所有可能组合。这很清楚,因为每个位可以有2个值,0或1.鉴于我们有n位,可能组合的数量是2 ^ n。
实际上,由于这个原因,这个算法是一个强力算法(我们做了详尽的搜索)。我们访问所有可能的组合以找到最佳组合。在以下循环中:
for (int i = 0; i<permutations; i++)
{
// ...
}
你循环完成所有可能的组合。
然后foreach组合,你循环遍历项目集合:
for (int j = 0; j < size; j++)
{
// Here you check if the item in position j
// is included in the current combination.
// If it is not, you go to the next value of the loop's variable
// and you make the same check.
if(((i>>j)&1)!=1)
continue;
// The j-th item is included in the current combination.
// Hence we add it's
// value to the total value accumulator and it's weight to
// the total weight accumulator.
total += Items[j].v;
weight += Items[j].w;
}
现在,如果weight
小于极限值且,则总值大于当前最佳总值,我们选择此组合作为当前最佳值:
bestPosition = i;
bestValue = total;
最后,在完成所有可用组合后,我们将拥有最好的组合。
找到最佳组合后,我们必须遍历这些项目以查看其中包含哪些内容。
// The include is a list that will hold the items of the best combination.
var include = new List<Item>();
// We loop through all the available items
for (int j = 0; j<size; j++)
{
// If the items is included in the best combination,
// add this item to the include list.
if (((bestPosition>>j) & 1)==1)
include.Add(Items[j]);
}
答案 1 :(得分:9)
显然,相关的代码部分是对某个位置的检查,如注释所示。条件
((i >> j) & 1) != 1
当且仅当j
的{{1}}位为零时,才为真;条件
i
当且仅当((bestPosition >> j) & 1) == 1
的{{1}}位为1时,才为真。关于更大的图片,显然实现使用j
来建模一组项目,其中bestPosition
- 位设置当且仅当int
项包含在组;因此,会员资格测试可以通过比特检查来完成。该实现枚举了项目的所有子集(使用j
来表示它们)以执行穷举搜索。
请注意,集合的Delphi实现使用相同的方法,但隐藏了客户端代码的位索引。