使用异或交换数据犯的错

异或



异或特性

  • 参与运算的两个值,如果两个相应位相同,则结果为 0,否则为 1

     0^0=0, 1^1=0
     1^0=1, 0^1=1
    
  • 任何数异或自己,等于把自己置 0

  • 对于任何数 x,都有 x^x=0,x^0=x

  • 1 异或任何数,其结果 = 任何数取反

  • 交换律:a ^ b = b ^ a

  • 结合律:(a^b)^c == a^(b^c)


异或交换数值

  • 交换方法

    A^=B
    B^=A
    A^=B
    
  • a=10100001, b=00000110

    a=a^b; //a=10100111  
    b=b^a; //b=10100001  
    a=a^b; //a=00000110  
    
  • 原理:数 a 两次异或同一个数 b(a=a^b^b) 仍然为原值 a .

  • 分析:

    a(新) = a(原) ^ b(原)  
    b(新) = b(原) ^ a(新) = b(原) ^ a(原) ^ b(原) = a(原) ^ b(原) ^ b(原) = a(原) ^ 0  = a(原)
    a(新2)= a(新) ^ b(新) = a(原) ^ b(原) ^ a(原) = b(原) ^ a(原) ^ a(原) = b(原) ^ 0  = b(原)
    

异或交换数据的坑

  • 在写快排算法时

    int test_arry[18] = {1, 3, 4, 5, 532, 0, 3, 19, 2, 19, 100, 128, 69, 39, 98, 99, 399, 5000};
    void swap(int *a, int *b) // Got a problem in quick sort when swap data
    {
        *a ^= (*b);
        *b ^= (*a);
        *a ^= (*b);
    }  
    
    void swap2(int *a, int *b)
    {
        int tmp = (*a);
        *a = (*b);
        *b = tmp;
    } 
    
    int partion(int arry[], int low, int high)
    {
        int pivot = arry[low];
        int start = low;
        while(low<high)
        {
            while((low<high) && (arry[high]>=pivot))
            {
                high--;
            }
    
            while((low<high) && (arry[low]<=pivot))
            {
                low++;
            }
            if(low>=high)
            {
                break;
            }
    
            swap(&arry[low], &arry[high]);
    
        } 
        swap(&arry[low], &arry[start]);
        return low;
    }
    
    
    void ns_qsort(int arry[], int low, int high)
    {
        if (low<high)
        {
            int pos = partion(arry, low, high);
            ns_qsort(arry, 0, pos-1);
            ns_qsort(arry, pos+1, high);
        }
    
    }
    
    void test_quick_sort(void)
    {
        print_int_arry(test_arry, 18);
        printf("Arry is sorted: %s\n", if_arry_sorted(test_arry, 18, mycompare) ? "Yes" : "No");
        ns_qsort(test_arry, 0, 17);
        print_int_arry(test_arry, 18);
        printf("Arry is sorted: %s\n", if_arry_sorted(test_arry, 18, mycompare) ? "Yes" : "No");
    }
    
    使用 swap() 的输出结果(排序后,结果出错):
        [1, 3, 4, 5, 532, 0, 3, 19, 2, 19, 100, 128, 69, 39, 98, 99, 399, 5000]
        Arry is sorted: No
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 128, 399, 532, 5000]
    
    
    使用 swap2() 的输出结果(排序后,结果正常):
        [1, 3, 4, 5, 532, 0, 3, 19, 2, 19, 100, 128, 69, 39, 98, 99, 399, 5000]
        Arry is sorted: No
        [0, 1, 2, 3, 3, 4, 5, 19, 19, 39, 69, 98, 99, 100, 128, 399, 532, 5000]
        Arry is sorted: Yes    
    
  • 原因分析

    • 在 swap() 中加入调试信息
      // 异或出现 0 值,只有自己把自己异或掉的时候才有这种情况
      void swap_t(int *a, int *b, int indexa, int indexb, int flag) // Got a problem in quick sort when swap data
      {
          // flag == 0 means using swap_t in while loop, flag == 1 means using swap_t outside while loop
          *a ^= (*b);
          *b ^= (*a);
          *a ^= (*b);
          if(a == b)
          {
              printf("swap: %p, %p; index_a = %d, index_b = %d; flag = %s\n",a,b, indexa, indexb, 0?"in":"out"); 
          }
      }         
      
      输出结果
          ...
          swap: 0x602088, 0x602088; index_a = 2, index_b = 2; flag = out
          swap: 0x602080, 0x602080; index_a = 0, index_b = 0; flag = out
          ...
      
      我们发现,有多次操作,我们 swap 操作了同一块内存,并且数组 index 相同,且都是位于 while 循环外调用的 swap_t , 原因在于
      while((low<high) && (arry[high]>=pivot))
      while((low<high) && (arry[low]<=pivot))
      其中的 >= 和 <= 符号的等号(=)
      即当 ns_qsort(arry, low, high) 对比到 (low + 1) == high 时,一个变量对自己亦或,就会是 0,注意,
      如果是两个值相同的不同变量采用异或交换法,结果是正确的,但此处,操作同一块内存,那么至始至终都是一个变量在对自己异或操作
      
  • 改进方法

    void swap(int *a, int *b) // Perfect
    {
        if (a == b)
        {
            return;
        }
        *a ^= (*b);
        *b ^= (*a);
        *a ^= (*b);
    }     
    
posted @ 2021-05-13 11:08  Brucegot9stars  阅读(77)  评论(0编辑  收藏  举报