背包 - 蛮力算法

时间:2015-04-16 08:26:02

标签: c# algorithm brute-force knapsack-problem

我发现这个代码使用强力机制来解决背包问题(这主要是为了学习,所以不需要指出动态更有效)。我得到了代码工作,并了解其中的大部分内容。最。这是个问题:

我已经注意到这两个条件,我不知道它们是如何工作的以及为什么它们在代码中 - 我知道它们是至关重要的,因为我所做的任何改变都会导致算法产生错误结果:

// 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的简单对象

2 个答案:

答案 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运算符。

  

这个运营商究竟是什么?

尽管定义很明确,但一个例子将使其更加清晰。

假设我们有二进制数ab,那么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.vitem.w访问。我建议你分别将它重命名为值和重量,以使你的代码更有意义。

显然,此int size = Items.Length;是可用项目的数量。

部分从这里开始的重点

var permutations = (long)Math.Pow(2,size);

什么是permutationspermutations代表什么?

在我们回答这个问题之前,让我们考虑如何在最终解决方案中表示项目集合中的哪些项目。我认为如果我们有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实现使用相同的方法,但隐藏了客户端代码的位索引。