求解位方程

时间:2015-02-01 23:08:32

标签: c++ algorithm pascal bit

我有一个等式:

x + y = x | y

如何解决这个等式?我需要找到方程所适用的第k个最小正整数y。也许有任何算法?我在哪里可以读到它? 因为我只是试图像这样解决它(在Pascal中):

uses crt;
var x,y,k,count:integer;

开始     readln(X,K);     计数:= 0;

for y:=1 to 10000 do
    if((x+y) = (x or y)) then 
    begin
        inc(count);
        if(count = k) then
        begin
            WriteLn('y= ',y); 
            break;
        end;
    end;

但代码很慢!

提前致谢!

4 个答案:

答案 0 :(得分:3)

可以通过在单个位值上对+|进行简单观察来解决此等式:

  • 如果两个值均为0,则两个操作都会生成0
  • 当值为1001时,两个操作都会产生1
  • 如果两个值均为1,则结果不同;另外,+产生一个“进位”,它会改变相邻位。

由于您正在寻找x + yx | y组合的相等性,因此您需要检查的是两个数字中都没有设置为1的位。换句话说,任何一对x, y使得x & y == 0会使你的方程成立,而任何一对x & y != 0都会使你的方程变为假。

为了查找给定k等式所适用的y最小x,您可以尝试y的所有值,递减k每次找到x & y == 0时。 k达到零后,打印当前值y

答案 1 :(得分:2)

解决方案的总数是x和y值的可能组合总数的3/4。这是因为只要x + y内没有进位,你的方程就会得到满足。因此,对于每个位,相应的x和y位00,01和10的三个组合不产生进位,只有11个产生进位。

答案 2 :(得分:0)

我知道有一个公认的解决方案,但是有一种更快的方法来找到这个解决方案所持有的第k个最小整数,而不是像它推荐的那样强制解决方案。

由于您提到您的原始代码(使用此方法)太慢,我认为您想要一个具有 O(整数类型中的位)运行时复杂性的解决方案 。有了它,我可以在我的i7上大约1/10秒内为x = 1234567890生成第一个500000解决方案(所有这些,而不仅仅是500000)(将输出重定向到/dev/null,否则成为瓶颈),虽然这当然比用于有用的基准测试的时间更少,并且能够以大致相同的速度生成每个个体,而计算y在这个例子中,蛮力方法中的第500000个解决方案将意味着检查超过5亿个数字。

关键的洞察力是,解决给定x + y = x | y的等式x数字是那些具有{{1}中的位子集的数字设置。因此,这成为找到第k个最小的这样的子集的问题,并且可以通过二进制搜索来完成 - 这使得O(位)复杂度成为可能。

换句话说,知道在解决方案中可以使用哪些位使我们能够从最高位向下构建第k个解决方案,因为在第i个解决方案中设置了第n个最低位(可以使用的位)(在它尚未设置)生成(i + 2 n-1 )解决方案。这意味着我们可以遍历可用位,决定是否在我们当前拥有的解决方案中设置它将生成序数大于k的解决方案,并根据它设置或不设置。

代码是C ++,因为问题标记为C ++,我比Pascal更喜欢它。

~x

答案 3 :(得分:-1)

最简单的答案是否定:

unsigned y = ~x;

因为(x & ~x) == 0

要获得k-th,您应该将k的位映射到y的1位。 这将只执行32(或使用x64时为64)步骤来完成。

unsigned find(unsigned y, unsigned k)
{
    int i = 0, j = 0;
    unsigned result = 0;
    for (i = 0; i < sizeof(unsigned)*8; ++i)
    {
        if (y & (1 << i))
        {
            if (k & (1 << j))
                result |= y & (1 << i);
            ++j;
            if (k < (1 << j))
                break; //we used all bits of k
        }
        if (y < (1 << i))
            break; //we used all 1-bits of y
    }
    return result;
}

可视化:

y:     110010011
k:         11001
       11  0  01 //we skip some position
r:     110000001

要获取拳头k数字列表,您可以循环播放:

for (unsigned i = 1; i <= k; ++i)
    std::cout << find(~x, i) << std::endl;