Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 累积位运算_Java_Arrays_Algorithm_Bit Manipulation_Bit - Fatal编程技术网

Java 累积位运算

Java 累积位运算,java,arrays,algorithm,bit-manipulation,bit,Java,Arrays,Algorithm,Bit Manipulation,Bit,假设您有一个数组A=[x,y,z,…] 然后计算一个前缀/累积按位或数组p=[x,x | y,x | y | z,…] 如果我想在索引1和索引6之间找到元素的按位OR,我如何使用这个预计算的p数组来实现这一点?可能吗 我知道它在累积和中工作,以获得范围内的和,但我不确定是否使用位运算 编辑:在A中允许重复,因此A=[1,1,2,2,2,2,3]是一种可能性。不可能使用前缀/累积按位或数组来计算按位或某个随机范围,您可以尝试使用2个元素的简单情况并验证自己 然而,有不同的方法,即使用前缀和 假设我

假设您有一个数组
A=[x,y,z,…]

然后计算一个前缀/累积按位或数组
p=[x,x | y,x | y | z,…]

如果我想在索引
1
和索引
6
之间找到元素的按位OR,我如何使用这个预计算的
p
数组来实现这一点?可能吗

我知道它在累积和中工作,以获得范围内的和,但我不确定是否使用位运算


编辑:
A
中允许重复,因此
A=[1,1,2,2,2,2,3]
是一种可能性。

不可能使用前缀/累积按位或数组来计算按位或某个随机范围,您可以尝试使用2个元素的简单情况并验证自己

然而,有不同的方法,即使用前缀和

假设我们处理的是32位整数,我们知道,对于从x到y的按位或求和,如果范围(x,y)中存在一个数,且该数的第i位为1,则结果的第i位为1。所以通过反复回答这个问题:

  • 范围(x,y)中是否有
    ith
    位设置为1的数字
我们可以形成这个问题的答案

那么,如何检查范围(x,y)中是否至少有一个设置了位
ith
的数字?我们可以预处理并填充数组
pre[n][32]
,该数组包含数组中所有32位的前缀和

for(int i = 0; i < n; i++){
   for(int j = 0; j < 32; j++){
       //check if bit i is set for arr[i]
       if((arr[i] && (1 << j)) != 0){
           pre[i][j] = 1;
       }
       if( i > 0) {
           pre[i][j] += pre[i - 1][j];
       }
   }
}
重复此检查32次以计算最终结果:

int result = 0;
for (int i = 0; i < 32; i++){
   if((pre[y][i] - (i > 0 ? pre[x - 1][i] : 0)) > 0){
       result |= (1 << i);
   }
}
return result;
int结果=0;
对于(int i=0;i<32;i++){
如果((pre[y][i]-(i>0?pre[x-1][i]:0))>0){

结果|=(1普通前缀数组不起作用,因为为了支持任意范围查询,它要求元素具有相对于运算符的逆,因此例如,对于加法,逆是否定的,对于异或,逆是元素本身,对于按位或无逆

基于同样的原因,二叉索引树也不起作用

但是横向堆确实可以工作,其代价是存储大约2*n到4*n个元素(取决于取整为2的幂所增加的元素),比32*n小得多的扩展。这不会使横向堆发挥最令人兴奋的作用,但它避免了显式链接树的问题:大块节点对象(每个节点约32字节)和指针追踪。可以使用常规隐式二叉树,但这会使其索引与原始数组中的索引更难关联。横向堆类似于完整的二叉树,但从概念上讲,没有根-实际上我们在这里有根,即存储的最高级别上的单个节点。类似于常规隐式二叉树y树侧面堆是隐式链接的,但规则不同:

  • left(x)=x-((x&-x)>>1)
  • right(x)=x+((x&-x)>>1)
  • parent(x)=(x&(x-1))|((x&-x)3和3->7。然后,找到端点的最低共同祖先(或从最高节点开始),并递归定义:

    rangeOR(i, begin, end):
        if leftmostLeaf(i) >= begin and rightmostLeaf(i) <= end
            return data[i]
        L = 0
        R = 0
        if rightmostLeaf(left(i)) >= begin
            L = rangeOR(left(i), begin, end)
        if leftmostLeaf(right(i)) <= end
            R = rangeOR(right(i), begin, end)
        return L | R
    
    另一种策略是从底部开始向上移动,直到双方相遇,同时在向上移动的过程中收集数据。当从
    begin
    开始时,其父项位于其右侧,则父项的右子项的索引高于
    begin
    ,因此它是查询范围的一部分,除非父项是公共的a两条向上“链”的电容器。例如(未测试):


    考虑a=(1,2,x,x,x,4)和p= [1,3,x,x,x,x,7 ]。你的建议是p [6 ] -p[0 ]=6,但是你怎么知道两者之间的值没有一个最低的位集?“这是可能的吗?”不,因为按位或丢失有关原始值的信息。假设您知道
    x
    1
    ,并且
    x | y
    也是
    1
    ,那么
    y
    的值是多少?它可以是
    0
    1
    ,但这两个选项都不可能说是正确的。@m69,我不确定。我有非常基本的知识位的边缘运算。我不是在建议P[6]-P[0],我是在问我们是否有类似的东西可以用于位运算,或者在一个范围内用于位运算array@Andreas,是否有一种有效的方法来使用预处理或其他方法,以便能够按位或在[l,r]范围内进行计算对于给定的数组
    a
    ?@BrijendarBakchodia我没有跟上你?你现在是在问如何从
    a
    高效地计算
    P
    ?而不是问从
    P
    反向计算
    a
    。很好。唯一的缺点似乎是空间。如果原始数组很大,你现在需要32 huge arrays.Hmm,减少空间的一种可能的方法是使用二叉搜索树来存储位的位置,这也会增加时间复杂度,因为
    log n
    如果只存储每个32位的前缀和,那么您将节省大量空间,并且您的查询只会因为一个小的常量因子而变得更昂贵。@MattTimmermans,那就像是两个答案的组合?@m69,我不这么认为。你的答案没有抓住关键点——如果你必须组合32个前缀和才能得到结果,那么这项改进所需要的32个左右的额外原始项也不是一个很大的额外工作。
    rangeOR(i, begin, end):
        if leftmostLeaf(i) >= begin and rightmostLeaf(i) <= end
            return data[i]
        L = 0
        R = 0
        if rightmostLeaf(left(i)) >= begin
            L = rangeOR(left(i), begin, end)
        if leftmostLeaf(right(i)) <= end
            R = rangeOR(right(i), begin, end)
        return L | R
    
    int[] data;
    
    public int rangeOR(int begin, int end) {
        return rangeOR(data.length >> 1, 2 * begin + 1, 2 * end + 1);
    }
    
    private int rangeOR(int i, int begin, int end) {
        // if this node is fully covered by [begin .. end], return its value
        int leftmostLeaf = i - (i & -i) + 1;
        int rightmostLeaf = i + (i & -i) - 1;
        if (leftmostLeaf >= begin && rightmostLeaf <= end)
            return data[i];
    
        int L = 0, R = 0;
        // if the left subtree contains the begin, query it
        if (begin < i)
            L = rangeOR(i - (Integer.lowestOneBit(i) >> 1), begin, end);
        // if the right subtree contains the end, query it
        if (end > i)
            R = rangeOR(i + (Integer.lowestOneBit(i) >> 1), begin, end);
        return L | R;
    }
    
    public int rangeOR(int begin, int end) {
        int i = begin * 2 + 1;
        int j = end * 2 + 1;
        int total = data[i];
        // this condition is only to handle the case that begin == end,
        // otherwise the loop exit is the `break`
        while (i != j) {
            int x = (i & (i - 1)) | (Integer.lowestOneBit(i) << 1);
            int y = (j & (j - 1)) | (Integer.lowestOneBit(j) << 1);
            // found the common ancestor, so done
            if (x == y) break;
            // if the low chain took a right turn, the right child is part of the range
            if (i < x)
                total |= data[x + (Integer.lowestOneBit(x) >> 1)];
            // if the high chain took a left turn, the left child is part of the range
            if (j > y)
                total |= data[y - (Integer.lowestOneBit(y) >> 1)];
            i = x;
            j = y;
        }
        return total;
    }
    
    public BitwiseORRangeTree(int[] input) {
        // round length up to a power of two, then double it
        int len = input.length - 1;
        len |= len >> 1;
        len |= len >> 2;
        len |= len >> 4;
        len |= len >> 8;
        len |= len >> 16;
        len = (len + 1) * 2;
    
        this.data = new int[len];
    
        // copy input data to leafs, odd indexes
        for (int i = 0; i < input.length; i++)
            this.data[i * 2 + 1] = input[i];
    
        // build higher levels of the tree, level by level
        for (int step = 2; step < len; step *= 2) {
            for (int i = step; i < this.data.length; i += step) {
                this.data[i] = this.data[i - (step >> 1)] | this.data[i + (step >> 1)];
            }
        }
    }