找到最大号码的最快,最有效的方法。这可以通过执行按位和数组的2个DISTINCT元素来获得

时间:2014-07-27 07:22:11

标签: c arrays bit-manipulation

给定一组非负整数,找到最大值的最快和最有效的方法是什么。可以通过在数组的2个DISTINCT元素上执行按位和(即&运算符)来获得?

这是我的代码,直到现在:

max = 0
for(i=0; i<n; i++)
{
    for(j=i+1; j<n; j++)
    {
        temp = a[i] & a[j];
        if(temp > max)
            max = temp
    }
 }

这当然是天真的方法。我正在寻找更有效的解决方案。

也许类似于使用trie(实际上是二叉树)来查找数组元素的最大XOR。有关max XOR解决方案的说明,请访问http://threads-iiith.quora.com/Tutorial-on-Trie-and-example-problems?share=1

3 个答案:

答案 0 :(得分:4)

我希望我的问题是正确的。这是我的解决方案:

你有一个整数数组,说它们是无符号整数,因为我们处理的是按位运算。让我们把它们看作是一串零和一的二进制表示,然后把它们放在一起。

我们现在将它们相应的位垂直对齐。让我们从最左边的列开始绘制垂直线。如果我们在列中遇到多于或等于两个1,则排除没有1 s的每一行。在绘制我们的垂直线时,我们将忽略被排除的那些。

你看到这是怎么回事?

这将继续下去,直到我们只剩下2行并没有被排除。如果我们最终得到的不是2,那么这意味着出了问题:

  • 少于2意味着我们最初只有不到2行
  • 超过2意味着......
    • 如果少于我们最初的那些,那么剩下的应该都是相同的
    • 如果与我们最初的数量完全一样多,那么它们可能都是相同的,或者每个可能的对都是按位不同的,这意味着每一对产生0

这是我编写的代码,遵循我上面描述的逻辑:

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

#define bit(_x_) (1U << (_x_))

void randomfillarray( unsigned int * arr, size_t size ) {
    srand( time( NULL ) );
    for ( int i = 0; i < size; i++ )
        arr[i] = rand( );
}

int main( ) {
    unsigned int arr[10];
    size_t size = sizeof arr / sizeof * arr;
    randomfillarray( arr, size );

    unsigned int * resultantcouple = malloc( sizeof arr );
    memcpy( resultantcouple, arr, sizeof arr );

    for ( int i = 0; i < size; i++ )
        printf( i ? " %u" : "%u", arr[i] );
    putchar( '\n' );

    int success = 0;

    for ( unsigned int thebit = bit( sizeof( int ) * 8 - 1 ); thebit; thebit >>= 1 ) {
        int count = 0;
        int * indices = NULL;
        for ( int i = 0; i < size; i++ ) {
            if ( resultantcouple[i] & thebit ) {
                indices = realloc( indices, ++count * sizeof * indices );
                indices[count - 1] = i;
            }
        }
        if ( count >= 2 ) {
            size = count;
            for ( int i = 0; i < size; i++ )
                resultantcouple[i] = resultantcouple[indices[i]];
            resultantcouple = realloc( resultantcouple, size * sizeof * resultantcouple );
        }
        if ( size == 2 ) {
            success = 1;
            break;
        }
        free( indices );
    }

    if ( success )
        printf( "Success! %u and %u are the ones.", resultantcouple[0], resultantcouple[1] );
    else
        printf( "Failure! Either all pairs are bitwise distinct, or there are less than 2 elements, or something else..." );


    putchar( '\n' );
    return 0;
}

行动期间的情况相同:http://ideone.com/hRA8tn

我不确定这是否是最好的,但它应该比全部测试更好。

答案 1 :(得分:0)

首先看一下并理解heapsort算法。

将数组转换为堆,使您可以访问两个最大的元素。这是在线性时间O(n)内完成的。

取两个最大的元素,x =最大,y =第二大。如果y = 0,则解为0.如果x中的最高位和y中的最高位相同,则解是x&amp;年。否则,清除x中的最高位,修复堆,然后重试。最后一步采用O(log n)步,如果使用k位整数,如32或64,则最多重复k次。

不需要额外的空间和线性时间。

伪代码:

If n ≤ 1 there is no solution. 
Turn a [0] to a [n-1] into a heap with a [0] as the largest element. 
Repeat
    Let x = a [0].
    Let y = a [1].
    If n ≥ 3 and a [2] > a [1] then let y = a [2].
    If y = 0 then the solution is 0.
    Determine b = the highest bit of x.
    If (y & b) != 0 then the solution is x & y.
    Replace a [0] with x & (~ b)
    Turn a [0] to a [n-1] into a heap again by moving a [0] down. 

如果i≠j,则假设a [i]和[j]被认为是“不同的数组元素”。如果你需要a [i]≠a [j]那么事情就会略有不同。您必须删除数组中的重复条目,但是如果最大元素是例如31和15,则您不希望清除31中的最高位,然后将其作为重复删除!所以代码更难。

Let mask = ~0. In the following, when creating a heap compare a [i] & mask, not a [i].
Turn a [0] to a [n-1] into a heap with a [0] as the largest element. 
Repeat
    If n ≤ 1 then there is no solution.
    Let x = a [0].
    Let y = a [1].
    If n ≥ 3 and a [2] & mask > y & mask then let y = a [2].

    If x = y then let n = n - 1, let a [0] = a [n], restore the heap, and continue.
    If (y & mask) = 0 then the solution is 0.
    Determine b = the highest bit of x & mask.
    If (y & b) != 0 then the solution is x & y.
    Replace mask with mask & ~b.
    Restore the heap and continue. 

最坏的情况是O(n log n),例如,如果除0之外的所有元素都是1。

答案 2 :(得分:0)

以下内容适用于我our_n uint our_a[our_n] uint result ; uint passes ; uint msb ; uint pn ; at->start_clock = times(&at->start_tms) ; result = 0 ; passes = 0 ; msb = (UINT_MAX >> 1) + 1 ; pn = our_n ; do { uint seen_once ; uint seen_again ; passes += 1 ; seen_once = 0 ; seen_again = 0 ; for (uint i = 0 ; i < pn ; ++i) { uint a ; a = our_a[i] ; if ((a & result) == result) { seen_again |= (a & seen_once) ; seen_once |= a ; } ; } ; assert((seen_again & result) == result) ; seen_again ^= result ; while (msb > seen_again) msb >>= 1 ; result |= msb ; } while (msb > 1) ; ,但不更改数组或复制数据或其他任何内容。本质上是在一次向下传递数组时,它标识了到目前为止可以添加到结果的下一位。每次传递仅考虑包含到目前为止结果的所有位的值:

      k = 0 ;
      for (uint i = 0 ; i < pn ; ++i)
        {
          uint a ;

          a = our_a[i] ;

          if ((a & result) == result)
            {
              our_a[k++] = a ;

              seen_again |= (a & seen_once) ;
              seen_once  |= a ;
            } ;
        } ;
      pn = k ;

所以,这是O(p * n),其中p是通过次数:1..32。

如果可以销毁数组的内容,那么内部循环可以更改为:

{{1}}

当然,第一遍现在正在做更多的工作而不是需要,所以单独这样做可以节省更多。