【问题标题】:How does XOR really works, and what is the magic behind it?XOR 是如何真正起作用的,它背后的魔力是什么?
【发布时间】:2017-09-26 06:21:22
【问题描述】:

这个问题可能有点误导,但我不知道如何用另一种方式问它。 在hackerrank中存在如下问题:

考虑一个整数数组,其中除一个整数之外的所有整数 成对出现。换句话说,每个元素恰好出现两次 除了一个独特的元素。

给定数组查找并打印唯一元素。 [2,3,1,3,2] -> 结果是 1

我这样解决了这个问题:

private static int lonelyInteger(int[] a) {
        if(a==null)
            return 0;

         if(a.length<2)
             return a.length;

        Set<Integer> set = new HashSet<>();

        for(int i : a){

            if(set.contains(i)){
                set.remove(i);
            }else{
                set.add(i);
            }            
        }

        return (Integer) set.toArray()[0];
    }

然而,我们发现这个问题有一个巧妙的解决方案:

private static int lonelyInteger(int[] a) {
         int b = a[0];

         for(int i = 1; i < a.length; i++){
             b ^= a[i];
         }
        return b;       
    }

问题是我不知道它为什么起作用?! 我了解它是如何工作的,但不明白它为什么起作用? 为了理解我做了一个小程序来输出每一步的结果:

public class BitwiseOperator {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] a = new int[n];
        int sum = 0;
        in.nextLine();
        String line = in.nextLine();
        String[] numbers = line.split(" ");
        for (int i = 0; i < numbers.length; i++) {
            a[i] = Integer.valueOf(numbers[i]);
        }

        for (int i = 0; i < a.length; i++) {
            binary(sum, a[i]);
            sum ^= a[i];
            binary(sum);
            System.out.println();
            System.out.println();
            System.out.println();

        }
        System.out.println(sum);
    }


    private static void binary(int sum) {
        System.out.println(String.format("%16s", Integer.toBinaryString(sum)).replace(' ', '0') + " ->" + sum);
    }

    private static void binary(int sum, int i) {
        System.out.println(String.format("%16s", Integer.toBinaryString(sum)).replace(' ', '0') + " ->" + sum);
        System.out.println("XOR");
        System.out.println(String.format("%16s", Integer.toBinaryString(i)).replace(' ', '0') + " ->" + i);
        System.out.println("--------");
    }


}

如果输入以下输入:

5

2 3 2 1 3

输出是:

0000000000000000 ->0
XOR
0000000000000010 ->2
--------
0000000000000010 ->2



0000000000000010 ->2
XOR
0000000000000011 ->3
--------
0000000000000001 ->1



0000000000000001 ->1
XOR
0000000000000010 ->2
--------
0000000000000011 ->3



0000000000000011 ->3
XOR
0000000000000001 ->1
--------
0000000000000010 ->2



0000000000000010 ->2
XOR
0000000000000011 ->3
--------
0000000000000001 ->1



1

所以该程序确实有效但我真的需要了解为什么?

【问题讨论】:

  • A xor A == 0 这就是为什么重复取消;如果我们只有一个项目是重复的,那么异或这些项目将返回不同的项目
  • @DmitryBychenko 您的评论应该是一个答案,因为它简洁、简短、全面且正确。
  • @DmitryBychenko 是的,我明白,但是当它是 A XOR B XOR C XOR A XOR B 为什么它也是 0 时会发生什么?
  • @Adelin 因为 XOR 事物的顺序无关紧要,因为 XOR 只是没有携带的加法。还有(0 XOR A) = A,所以A XOR B XOR C XOR A XOR B = (B XOR B) XOR (C XOR X) XOR A = 0 XOR 0 XOR A = 0 XOR A = A。所有出现两次的都取消为零。

标签: java algorithm binary bitwise-operators


【解决方案1】:

恕我直言,一个准确的证明涉及群论(您可以基于xor 构建abelian 群):

  1. xor是群操作
  2. 0 是一个群组0
  3. A 是一个逆元素(所以任何 A 都是它自身的逆元素)。

当然我们要证明(A xor B) xor C == A xor (B xor C)

由于A xor B == B xor A 我们有一个abelian 群,这就是为什么可以重新组合 任何顺序的项目:

  A XOR B XOR C XOR A XOR B ==
 (A XOR A) XOR (B XOR B) XOR C ==
  C

一般情况:

   A xor B xor ... xor Z ==
  (A xor A) xor (B xor B) xor ... xor (distinct Item) ==
   0 xor 0 xor ... xor (distinct Item) ==
   distinct Item

【讨论】:

  • @DmitryBychenko 正是我一直在寻找的东西,现在我将去阅读有关阿贝尔群的信息,以消除我数学技能上的锈迹:D
【解决方案2】:

考虑一个整数数组,其中除一个整数之外的所有整数都成对出现。换句话说,其中的每个元素都恰好出现两次,除了一个唯一元素。现在假设您将所有这些数字相加。

如果这个和是偶数,那意味着什么?这意味着出现一次的数字必须是偶数。如果这个和是奇数,这意味着什么?这意味着出现一次的数字必须是奇数。想一想,直到你理解为止。

现在想象一下,我们没有对它们求和,而是跟踪总和是偶数还是奇数。所以如果前两个数字是 3 和 7,总和就是 10,但我们只需要记住它是偶数。这仍然有效。最终的答案将是即使出现一次的数字是偶数,如果出现一次的数字是奇数,则最终答案是奇数。想一想,直到你理解为止。

这就是我们如何使它适用于其中的一个数字。但我们也可以同时对所有位执行此操作,跟踪每个位位置的总数是奇数还是偶数。完成后,我们将知道每个位位置出现一次的数字是奇数还是偶数,这就是我们所需要的。因为我们是用二进制做的,所以那个位置唯一的奇数是 1,唯一的偶数是 0。想想这个直到你理解为止。

【讨论】:

  • 对不起,我忘了说限制,我已经编辑了问题并添加了限制,请相应地编辑你的答案:)
  • @Adelin 不需要。答案仍然完全有效。
【解决方案3】:

我会尽量保持简短。

异或同一个数返回 0

示例: 23 ^ 23 = 0 -325 ^ -325 = 0

因此,当您通过 XOR 循环运行时,出现两次的数字将自行抵消,最后您将获得唯一的数字。

【讨论】:

    猜你喜欢
    • 2012-05-15
    • 2012-11-30
    • 2011-04-02
    • 2019-11-08
    • 2010-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    相关资源
    最近更新 更多