在Java中,我如何创建一个位图来解决“背包窘境”

时间:2012-02-06 21:20:28

标签: java bitmap knapsack-problem

我正处于我的第一个编程课程中,现在我很困难。基本上,我们正在做的是从文本文件中获取16个值(在第一行代码中),并且在第二行代码上有一个值。我们将这16个值读入数组,并将第2行值设置为目标。我对那部分没有任何问题。

但是,我遇到麻烦的是创建一个位图来测试16个值的每个可能子集,它们等于目标数量。

IE,说我们有这些数字:

12   15   20   4   3   10   17   12   24   21   19   33   27   11   25   32

然后我们将每个值对应一个位图

 0    1    1   0   0    0    0    1    1    1    0    1    0    0    1    0

然后我们只接受以“1”

为基础的值
     15   20                     12   24   21        33             25

然后我们测试该子集以查看它是否等于“目标”数字。

我们只允许在问题中使用一个数组,并且我们不允许使用数学类(还没有使用它)。

我理解这个概念,我知道我需要实现移位运算符和逻辑&符号,但我真的很茫然。我非常沮丧,我只是想知道是否有人可以给我任何提示。

5 个答案:

答案 0 :(得分:0)

要在int中生成所有可能的位模式,因此该位图定义的所有可能子集都只需要在1处启动int并将其递增到unsigned short int可以容纳的最高值(全1) )。在每个内循环的末尾,将总和与目标进行比较。如果它匹配,你有一个解决方案子集 - 打印出来。如果没有,请尝试下一个子集。 有人可以帮忙解释如何去做吗?我理解这个概念,但缺乏如何实现它的知识。

答案 1 :(得分:0)

好的,所以你可以获得一个阵列。据推测,该数组包含第一组数据。 所以你的方法不需要任何额外的数组。

在这种情况下,位向量只是一个心理模型构造。这个想法是这样的:如果你尝试了所有可能的组合(注意,不是排列),那么你将找到与你的目标最接近的总和。所以假设你有N个数字。这意味着您有2^N种可能的组合。

位向量方法是将每个组合编号为02^N - 1,然后尝试每个组合。

假设你在数组中少了32个数字,你基本上就有这样一个外部循环:

int numberOfCombinations = (1 << numbers.length - 1) - 1;
for (int i = 0; i < numberOfCombinations; ++i) { ... }

对于i的每个值,您需要检查numbers中的每个数字,根据i的移位和位掩码决定添加或跳过。

答案 2 :(得分:0)

因此,任务是给定一组A非负数和目标值k的算法,确定是否存在A的子集,以便其元素之和为k

我使用A上的感应来接近这一点,跟踪哪些数字&lt; = k是到目前为止处理的元素集的子集的总和。那就是:

boolean[] reachable = new boolean[k+1];
reachable[0] = true;
for (int a : A) {
    // compute the new reachable
    // hint: what's the relationship between subsets of S and S \/ {a} ?
}
return reachable[k];

从数学上讲,位图是将一系列数字映射到{0,1}的函数。 boolean[]将数组索引映射到布尔值。所以可以调用boolean[]位图。

使用boolean[]的一个不利之处是您必须单独处理每个数组元素。相反,可以使用长保持64位,并使用位移和屏蔽操作一次处理64个“数组”元素。但是这种微优化很容易出错而且很复杂,所以在代码中通常不应该做到可靠和可维护。

答案 3 :(得分:0)

我认为你需要这样的东西:

public boolean equalsTarget( int bitmap, int [] numbers, int target ) {
  int sum = 0; // this is the variable we're storing the running sum of our numbers
  int mask = 1; // this is the bitmask that we're using to query the bitmap
  for( int i = 0; i < numbers.length; i++ ) { // for each number in our array
    if( bitmap & mask > 0 ) { // test if the ith bit is 1
        sum += numbers[ i ]; // and add the ith number to the sum if it is
    } 
    mask <<= 1; // shift the mask bit left by 1
  }
  return sum == target; //if the sum equals the target, this bitmap is a match
}

您的其余代码非常简单,您只需将位图的每个可能值(1..65535)提供给此方法并对结果执行操作。

P.s。:请确保您完全理解解决方案,而不仅仅是复制它,否则您只是在欺骗自己。 :)

Pps:在这种情况下使用int有效,因为int是32位宽,我们只需要16。如果需要所有位,请注意按位操作,因为所有原始整数类型(byte,short,int,long)用Java签名。

答案 4 :(得分:0)

解决这个问题有几个步骤。首先,您需要枚举所有可能的位图。正如其他人指出的那样,你可以通过递增0到2 ^ n - 1的整数来轻松完成。

一旦你有了这个,就可以遍历所有可能的位图,你只需要一种方法来获取该位图并将其“应用”到一个数组中,以生成地图所代表的所有索引上的元素之和。以下方法是如何执行此操作的示例:

private static int bitmapSum(int[] input, int bitmap) {
    // a variable for holding the running total
    int sum = 0;

    // iterate over each element in our array
    // adding only the values specified by the bitmap
    for (int i = 0; i < input.length; i++) {
        int mask = 1 << i;
        if ((bitmap & mask) != 0) {
            // If the index is part of the bitmap, add it to the total;
            sum += input[i];
        }
    }

    return sum;
}

此函数将采用整数数组和位图(表示为整数),并返回数组中所有元素的总和,其索引存在于掩码中。

此功能的关键是能够确定给定的索引是否实际位于位图中。这是通过首先为所需索引创建一个位掩码,然后将该掩码应用于位图来测试是否设置了该值来实现的。

基本上我们想要构建一个整数,其中只有一位被设置而所有其他位都为零。然后,我们可以使用位图按位AND该掩码,并通过将结果与0进行比较来测试是否设置了特定位置。

假设我们有一个如下所示的8位地图:

map:       1 0 0 1 1 1 0 1
           ---------------
indexes:   7 6 5 4 3 2 1 0

要测试索引4的值,我们需要一个如下所示的位掩码:

mask:      0 0 0 1 0 0 0 0
           ---------------
indexes:   7 6 5 4 3 2 1 0

要构建蒙版,我们只需从1开始并将其移动N:

1:            0 0 0 0 0 0 0 1
shift by 1:   0 0 0 0 0 0 1 0
shift by 2:   0 0 0 0 0 1 0 0
shift by 3:   0 0 0 0 1 0 0 0
shift by 4:   0 0 0 1 0 0 0 0

一旦我们有了这个,我们可以将蒙版应用于地图,看看是否设置了值:

map:             1 0 0 1 1 1 0 1
mask:            0 0 0 1 0 0 0 0
                 ---------------
result of AND:   0 0 0 1 0 0 0 0

由于结果是!= 0,我们可以判断索引4是否包含在地图中。