在一组数字中找到一个独特的位

时间:2011-11-27 13:40:06

标签: c bit-manipulation

解释这个的最好方法是演示。

有一组数字。它们可能会重复,所以:

1110,0100,0100,0010,0110 ......

我正在寻找的数字是一个有点设置的数字,不会出现在任何其他数字中。结果是数字(在这种情况下为1 - 第一个数字)和位位置(或掩码很好)所以1000(第4位)。可能有多个解决方案,但为此目的,它可能是贪婪的。

我可以通过迭代来做...对于每个数字N,它是:

N& 〜(其他数字或在一起)

但是比特的本质是,如果你在盒子外思考,总有一种更好的方法。例如,出现多次的数字永远不会有唯一的位,并且对ORing没有影响。

4 个答案:

答案 0 :(得分:5)

您只需要记录每个位是否已被看过一次或更多,以及是否已经看过两次或更多位。唯一位是那些已经被看过一次或多次而不是两次或更多次的位。这可以使用按位运算有效地完成。

count1 = 0
count2 = 0

for n in numbers:
    count2 |= count1 & n
    count1 |= n

for n in numbers:
    if n & count1 & ~count2:
        return n

如果您不想重复两次数字,可以跟踪您看到的包含每个位的数字。如果数字存储在磁盘上,这可能是一个很好的优化,因此流式传输需要磁盘访问,但当然它会使代码更复杂。

examples = [-1] * wordsize
count1 = 0
count2 = 0

for n in numbers:
    if n & ~count1:
        for i in xrange(wordsize):
            if n & (1 << i):
                examples[i] = n
    count2 |= count1 & n
    count1 |= n

for i in xrange(wordsize):
    if (count1 & ~count2) & (1 << i):
        return examples[i]

您可以使用技巧在设置示例的循环中更有效地提取位索引,但由于此代码在大多数“单词大小”时间执行,因此可能不值得。

这段代码很容易转换为C ...为了清楚起见,我只是用Python编写。

答案 1 :(得分:2)

(我在评论中写的长版本)

通过计算每个k的索引k处的位数是1的次数(有一个技巧比天真地更快,但它仍然是O(n)),你得到一个{{1}的列表计数器,其中计数为1意味着该位只有一次。因此,该计数器的索引(在O(1)中找到,因为您有一个固定的位数)是您想要的位位置。要找到该位设置的数字,只需再次迭代所有数字并检查它是否设置了该位(再次为O(n)),如果它确实是你想要的数字。

总计:O(n)与O(n 2 )检查每个数字与其他所有数字。

答案 2 :(得分:1)

此方法使用少于2次传递(但改变输入数组)

    #include <stdio.h>

    unsigned array[] = { 0,1,2,3,4,5,6,7,8,16,17 };
    #define COUNTOF(a) (sizeof(a)/sizeof(a)[0])
    void swap(unsigned *a, unsigned *b)
    {
        unsigned tmp;
        tmp = *a;
        *a = *b;
        *b = tmp;
    }

    int main(void)
    {
    unsigned idx,bot,totmask,dupmask;

    /* First pass: shift all elements that introduce new bits into the found[] array.
    ** totmask is a mask of bits that occur once or more
    ** dupmask is a mask of bits that occur twice or more
    */
    totmask=dupmask=0;
     for (idx=bot=0; idx < COUNTOF(array); idx++) {
         dupmask |= array[idx] & totmask;
         if (array[idx] & ~totmask) goto add;
         continue;

    add:
        totmask |= array[idx];
        if (bot != idx) swap(array+bot,array+idx);
        bot++;
        }
    fprintf(stderr, "Bot=%u, totmask=%u, dupmask=%u\n", bot, totmask, dupmask );

    /* Second pass: reduce list of candidates by checking if
    ** they consist of *only* duplicate bits */
    for (idx=bot; idx-- > 0 ; ) {
        if ((array[idx] & dupmask) == array[idx]) goto del;
        continue;
    del:
        if (--bot != idx) swap(array+bot,array+idx);

    }

    fprintf(stdout, "Results[%u]:\n", bot );
    for (idx=0; idx < bot; idx++) {
        fprintf(stdout, "[%u]: %x\n" ,idx, array[idx] );
        }
    return 0;
    }  

更新2011-11-28 另一个版本,不会改变原始数组。 (临时)结果保存在单独的数组中。

#include <stdio.h>
#include <limits.h>
#include <assert.h>

unsigned array[] = { 0,1,2,3,4,5,6,7,8,16,17,32,33,64,96,128,130 };
#define COUNTOF(a) (sizeof(a)/sizeof(a)[0])
void swap(unsigned *a, unsigned *b)
{
    unsigned tmp;
    tmp = *a, *a = *b, *b = tmp;
}


int main(void)
{
unsigned idx,nfound,totmask,dupmask;
unsigned found[sizeof array[0] *CHAR_BIT ];

/* First pass: save all elements that introduce new bits to the left
** totmask is a mask of bits that occur once or more
** dupmask is a mask of bits that occur twice or more
*/
totmask=dupmask=0;
 for (idx=nfound=0; idx < COUNTOF(array); idx++) {
     dupmask |= array[idx] & totmask;
     if (array[idx] & ~totmask) goto add;
     continue;

add:
    totmask |= array[idx];
    found[nfound++] = array[idx];
    assert(nfound <= COUNTOF(found) );
    }
fprintf(stderr, "Bot=%u, totmask=%u, dupmask=%u\n", nfound, totmask, dupmask );

/* Second pass: reduce list of candidates by checking if
** they consist of *only* duplicate bits */
for (idx=nfound; idx-- > 0 ; ) {
    if ((found[idx] & dupmask) == found[idx]) goto del;
    continue;
del:
    if (--nfound != idx) swap(found+nfound,found+idx);

}

fprintf(stdout, "Results[%u]:\n", nfound );
for (idx=0; idx < nfound; idx++) {
    fprintf(stdout, "[%u]: %x\n" ,idx, found[idx] );
    }
return 0;
}

答案 3 :(得分:0)

指出这不起作用:

您可以XOR将这些数字放在一起,结果将为您提供mask。 然后你必须找到第一个不给N & mask表达式0的数字。