【问题标题】:Repetitively taking XOR of consecutive elements重复取连续元素的异或
【发布时间】:2020-05-01 12:55:36
【问题描述】:

给定一个大小为N的二进制数组

e.g. A[1:N] = 1 0 0 1 0 1 1 1

一个大小为N-1 的新数组将通过采用XOR 的2 个连续元素来创建。

A'[1:N-1] = 1 0 1 1 1 0 0

重复此操作,直到剩下一个元素。

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

我想找到剩下的最后一个元素(01

通过重复执行操作可以找到答案。这种方法将花费O(N*N) 时间。有没有办法更有效地解决问题?

【问题讨论】:

    标签: math xor bitwise-xor


    【解决方案1】:

    这个问题有一个非常有效的解决方案,只需要几行代码,但是解释起来相当复杂。反正我会去的。

    假设你需要减少一个列表,比如说,6 个数字,除了一个元素之外,这些数字都是零。通过对称性,只需考虑三种情况:

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

    在第一种情况下,边缘的单个“1”实际上并没有什么作用。它基本上只是保持不变。但在另外两种情况下,涉及的列表元素更多,情况也更复杂。列表的第二个元素中的“1”产生“1”的结果,但第三个元素中的“1”产生“0”的结果。是否有解释这种行为的简单规则?

    是的,有。看看这个:

    Row 0:             1
    Row 1:           1   1
    Row 2:         1   2   1
    Row 3:       1   3   3   1
    Row 4:     1   4   6   4   1
    Row 5:   1   5   10  10  5   1
    

    我相信你以前见过这个。这是帕斯卡三角形,其中每一行是通过添加从上面一行中提取的相邻元素获得的。三角形中间较大的数字反映了这样一个事实,即这些数字是通过将来自前面行的更广泛子集的值相加而获得的。

    请注意,在第 5 行中,中间的两个数字都是偶数,而其他数字都是奇数。这与上面显示的三个示例的行为完全匹配;偶数个“1”的异或积为零,奇数个“1”的异或积为“1”。

    为了让事情更清楚,让我们只考虑这个三角形中数字的奇偶性(即,“1”表示奇数,“0”表示偶数):

    Row 0:             1
    Row 1:           1   1
    Row 2:         1   0   1
    Row 3:       1   1   1   1
    Row 4:     1   0   0   0   1
    Row 5:   1   1   0   0   1   1
    

    这实际上称为Sierpinski triangle。在这个三角形中出现零的地方,它告诉我们你的列表在这个位置是 '1' 还是 '0' 无关紧要;它对结果值没有影响,因为如果您根据列表中的所有初始值写出显示最终结果值的表达式,该元素将出现偶数次。

    以第 4 行为例。除极端边缘外,每个元素都为零。这意味着如果您的列表有 5 个元素,则最终结果仅取决于列表中的第一个和最后一个元素。 (这同样适用于元素数量是 2 的幂次方大 1 的任何列表。)

    谢尔宾斯基三角形的行很容易计算。正如oeis.org/A047999中提到的:

    卢卡斯定理是 T(n,k) = 1 当且仅当 k 的二进制扩展中的 1 是 n 的二进制扩展中的 1 的子集;或等效地,k AND NOT n 为零,其中 AND 和 NOT 是按位运算符。


    所以,在冗长的解释之后,这是我的代码:

    def xor_reduction(a):
        n, r = len(a), 0
        for k in range(n):
            b = 0 if k & -n > 0 else 1
            r ^= b & a.pop()
        return r
    
    assert xor_reduction([1, 0, 0, 1, 0, 1, 1, 1]) == 1
    

    我说它很短。如果您想知道,第 4 行有 k & -n (k AND minus n) 而不是 k & ~n (k AND not n) 因为此函数中的 n 是列表中元素的数量,比行号,~(n-1)-n 相同(至少在 Python 中)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-09
      • 1970-01-01
      • 2019-07-27
      • 2019-08-01
      • 2019-09-30
      相关资源
      最近更新 更多