如何找到偶数或奇数“1”位[重复]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何找到偶数或奇数“1”位[重复]相关的知识,希望对你有一定的参考价值。

这个问题在这里已有答案:

是否可以说在单个测试中C中的int的二进制表示中是否存在偶数或奇数个“1”位?如果没有最快的方法呢?

答案

也许不是最快的,但它的工作原理非常简单:

int evenNumberOfOnes(unsigned int num)
{
    int n=0;
    while(num > 0) {
        n ^= num;
        num = num >> 1;
    }
    return n & 1;
}

感谢@EricPostpischil提供有关改进的提示。

另一答案

解决这个问题的一种方法是计算位数并检查最低有效位以查看该值是奇数还是分别为10。给定一个32位整数,我们可以通过一些巧妙的位操作来计算其二进制表示1Hamming Weights的数量。有一个很好的答案找到汉明重量已经here,它也谈到效率。

采用这种算法通过横向累积加法找到汉明重量。如果hasOddCount有奇数1s,x会返回1

int hasOddCount(int x) {
    int first = 0x11 | (0x11 << 8);
    int second = first | (first << 16);
    int mask = 0xf | (0xf << 8);
    int count = 0;
    int sum = second & x;
    sum = (x>>1 & second) + sum;
    sum = (x>>2 & second) + sum;
    sum = (x>>3 & second) + sum;

    sum = sum + (sum >> 16);

    sum = ((sum & mask) + (mask & (sum >> 4)));
    count = (((sum >> 8) + sum) & 0x3f);

    //count is the Hamming weight, & by 0x1 to see if it's odd
    return count & 0x1;
}
另一答案

我确信这不是最不可能的代码,但它简短易懂;

#include <stdio.h>

int main(int argc, const char * argv[]) {
    unsigned x;
    int count;

    scanf("%u
",&x);

    for(count=0;x;x>>=1)
        if(x&1)
            ++count;

    puts(count&1?"odd":"even");

    return(0);
}

顺便说一句:我正在编写血液酒精含量为.05的代码,我没有检查过,如果我把这个简单的任务搞错了,我明天可能会感到尴尬。请不要将此代码放入任何使用热核武器或无人驾驶汽车的地方。

编辑:看到其他答案后也可能是正确的&|棘手的;最快的代码真的取决于架构。模数在某些CPU上速度非常快,而在其他CPU上则会占用很多周期。一切都确实在大约相同的几个周期内发生变化。

另一答案
int parity(int n) {
   int p = 0;

   for (;n;n>>=1) {
      p = (n&1) ? 1-p : p;
   }

   return p;
}

如果参数n中的1的数量是偶数,则返回零。简单测试:

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

int main() {
   srand(time(NULL));
   for (int i=0;i<100;i++) {   
      int n = rand();
      printf("%d has an %s number of 1's 
", n, parity(n)  ? "odd" : "even");
   }
}
另一答案

我发布这个作为一个新的答案,因为它有点hackish和我的其他答案有点不同。

int evenNumberOfOnesFast(unsigned int num)
{
    unsigned char *b;
    b = (unsigned char *)&num;
    unsigned char par[4]={0};

    for(int i=0; i<8; i++) {
        for( int j=0; j<4; j++) {
            par[j]^=b[j];
            b[j]=b[j] >> 1;
        }
    }
    return (par[0] ^ par[1] ^ par[2] ^ par[3]) & 1;
}

在这里,我假设int是4个字节。我希望这会使cpu在管道中并行执行一些操作。我不确定是不是这样,但这比我以前的版本快两倍。

以下是测试它的主要功能:

#define N 10000000
int main()
{
    int * arr = malloc(N*sizeof(*arr));
    int * par = malloc(N*sizeof(*par));

    // Random values to prevent optimizer from just removing the code                                              
    srand(time(NULL));

    for(int i=0; i<N; i++)
        arr[i]=rand();

    // Correctness check                                                                                           
    for(int i=0; i<N; i++)
        if(evenNumberOfOnes(arr[i]) != evenNumberOfOnesFast(arr[i])) {
            perror ("Error");
            exit(EXIT_FAILURE);
        }

    clock_t begin, end;
    double time_spent;

    begin = clock();

    for(int i=0; i<N; i++)
        par[i]=evenNumberOfOnes(arr[i]);

    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf(" Time: %f
", time_spent);

    begin = clock();

    for(int i=0; i<N; i++)
        par[i]=evenNumberOfOnesFast(arr[i]);

    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf(" Time: %f
", time_spent);
}

结果如下:

$ cc r.c -O3 -Wall -Wextra
$ ./a.out 
 Time: 0.201635
 Time: 0.093152

但是,我还测试了@ Miket25提供的解决方案hasOddCount,它实际上比我的快速变体快十倍。

以上是关于如何找到偶数或奇数“1”位[重复]的主要内容,如果未能解决你的问题,请参考以下文章

算法异或 偶数数组中找到一个唯一奇数

一个数字,它只是另一个数字的奇数(或偶数)位

在MySQL中仅选择偶数/奇数行[重复]

在数字列表中查找下一个奇数或下一个偶数

位运算:判断奇偶数

如何检查循环计数? (以及它是偶数还是奇数)[重复]