C++ 求解bits方程

C++ 求解bits方程,c++,algorithm,pascal,bit,C++,Algorithm,Pascal,Bit,我有一个比特方程: x + y = x | y 如何解这个方程?我需要找到等式成立的第k个最小正整数y。也许有什么算法?我在哪里可以读到它? 因为我只是想这样解决它(用帕斯卡): 开始 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 Wr

我有一个比特方程:

x + y = x | y
如何解这个方程?我需要找到等式成立的第k个最小正整数y。也许有什么算法?我在哪里可以读到它? 因为我只是想这样解决它(用帕斯卡):

开始 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;
但是代码非常慢


提前谢谢

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

这个方程可以通过对单个位值进行简单的观察来解决:

  • 当两个值都为
    0
    时,两个操作都会产生
    0
  • 当值为
    1
    0
    0
    1
    时,两种操作都会产生
    1
  • 当两个值均为
    1
    时,结果不同;另外,
    +
    产生一个“进位”,它改变了相邻的位
因为您正在寻找
x+y
x | y
组合的相等性,所以您需要检查的是两个数字中没有设置为1的位。换句话说,任何一对
x,y
使得
x&y==0
都将使您的等式为真,而任何一对
x&y!=0
将使您的等式为假


为了找到方程对给定的
x
适用的
k
最小
y
,您可以尝试
y
的所有值,每次找到
x&y==0
时,将
k
递减。一旦
k
达到零,打印
y
的当前值最简单的答案是否定:

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;
}
要获取fist
k
编号列表,您可以执行以下操作:

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

for(unsigned i=1;i我知道有一个可接受的解决方案,但找到该解决方案适用的第k个最小整数的方法要比按照建议的方式强制执行解决方案快得多

因为您提到您的原始代码(使用这种方法)太慢了,所以我想您需要一个运行时复杂度O(整数类型的位)的解决方案对于
x=1234567890
,在i7上大约1/10秒内(将输出重定向到
/dev/null
,否则这将成为瓶颈),虽然这当然比创建一个有用的基准所需的时间要短,和am能够以大致相同的速度生成每个单独的基准,
y
,而在暴力方法中,计算到第500000个解意味着,在本例中,检查超过5亿个数字

关键的洞察是,对于给定的
x
解方程
x+y=x | y
的唯一数字是那些在
~x
集中有一个比特子集的数字。因此,这就成了一个寻找第k个最小的这样的子集的问题,这可以通过二进制搜索来完成——这就把O(比特)计算出来复杂性

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

<代码> C++,因为问题是C++的标记,我喜欢它比Pascal好。
#include <bitset>
#include <iostream>
#include <stdexcept>

// An artifact of the development process. I used it to test that
// the exception is thrown properly if there are less than k solutions.
enum { BITS_USED = 31 };

// Counts the bits set in an integer
unsigned bitcount(unsigned x)  {
  unsigned count = 0;

  while(x != 0) {
    ++count;
    x &= x - 1;
  }

  return count;
}

// Finds the highest bit set in x, starting to look at start
// (which will be the previously highest bit the way we use it)
unsigned highest_set_bit(unsigned x, unsigned start = 1 << (BITS_USED - 1)) {
  unsigned mask = start;

  while(mask != 0 && (x & mask) == 0) {
    mask >>= 1;
  }

  return mask;
}

// This function does the binary search.
unsigned find_kth_complement(unsigned x, unsigned k) {
  // (rest_mask) is the complement of (x), or at least the bits we take into
  // consideration (see comment on BITS_USED above).
  unsigned rest_mask = ~x & ((1u << BITS_USED) - 1);
  unsigned rest_bits = bitcount(rest_mask);
  unsigned bit       = highest_set_bit(rest_mask);

  // (curmask) will be updated to contain the bits we already know the
  // (k)th solution will have set. It will be built from the most significant
  // bit downwards and always be a solution itself (!). Setting new, ever
  // less significant bits in it will make it larger until it is the (k)th
  // solution.
  unsigned curmask   = 0;

  while(rest_mask != 0) {
    rest_mask &= ~bit;
    --rest_bits;

    // Here now the binary search: We know that (rest_bits) bits are
    // set in (rest_mask), which is (~x) without the bits we already
    // know the solution will have set. We know therefore that there
    // are (skip) = 2^(rest_bits) solutions that have the bit in (bit)
    // set, and equally many that have it unset.
    unsigned skip = 1u << rest_bits;

    // So: Setting the highest bit of the rest mask in (curmask) will
    // propel (curmask) (skip) solutions ahead. We can only do this if
    // we still have to skip more than that many solutions. (k) will
    // be adjusted to be the number of solutions left to skip.
    if(k >= skip) {
      curmask |= bit;
      k -= skip;
    }

    bit = highest_set_bit(rest_mask, bit);
  }

  // if (k) is not zero here, there were not enough solutions to skip.
  if(k != 0) {
    throw std::logic_error("There are less than k solutions for the given x");
  }

  return curmask;
}

int main() {
  unsigned x = 1234567890;
  unsigned y = ~x & 0xff;

  std::cout << std::bitset<BITS_USED>(x) << std::endl;

  // Taking it for a ride here: Generate the first 500k solutions.
  // Printing them is done in binary because it is easier to see that it
  // works that way.
  for(unsigned i = 0; i < 500000; ++i) {
    std::cout << std::bitset<BITS_USED>(find_kth_complement(x, i)) << "\n";
  }
}
#包括
#包括
#包括
//开发过程的一个工件。我用它来测试它
//如果存在少于k个解决方案,则会正确引发异常。
枚举{BITS_USED=31};
//对整数中设置的位进行计数
无符号位计数(无符号x){
无符号计数=0;
而(x!=0){
++计数;
x&=x-1;
}
返回计数;
}
//查找x中设置的最高位,开始查看开始
//(这将是我们使用它的方式之前的最高位)
无符号最高设置位(无符号x,无符号起始=1>=1;
}
返回掩码;
}
//此函数执行二进制搜索。
无符号find_kth_补码(无符号x,无符号k){
//(rest_mask)是(x)的补码,或者至少是我们考虑的位
//考虑因素(见上文对BITS_的评论)。

未签名的rest_掩码=~x&((1u这个方程有很多很多解。当你问如何解它时,你的意思是你想要所有的解吗?第一个也是最明显的解是x=0,y=0。或者你想要所有的解吗?好的,我需要找到第k个最小的正整数y,对于它,当
x+y
等于
x | y
时,
~(x | ~y)
zero@Columbo:或者,更简单地说,当
x&y
为0时。证明这一点的一种方法是证明
x+y==(x | y)+(x&y)
但是如果我知道k和x,我怎么才能找到方程所包含的第k个最小正整数y呢?@user2660964:一个简单的方法就是迭代,根据上面的约束检查每个值。谢谢!太好了!你是说
~
?即使这样,也能找到一些解,但不是所有解。任何x的最小y都是0.But OP不需要最小的答案,也不需要简单的答案。它是按顺序要求所有的答案。@yankes sizeof告诉您一个对象中有多少字节
for (unsigned i = 1; i <= k; ++i)
    std::cout << find(~x, i) << std::endl;
#include <bitset>
#include <iostream>
#include <stdexcept>

// An artifact of the development process. I used it to test that
// the exception is thrown properly if there are less than k solutions.
enum { BITS_USED = 31 };

// Counts the bits set in an integer
unsigned bitcount(unsigned x)  {
  unsigned count = 0;

  while(x != 0) {
    ++count;
    x &= x - 1;
  }

  return count;
}

// Finds the highest bit set in x, starting to look at start
// (which will be the previously highest bit the way we use it)
unsigned highest_set_bit(unsigned x, unsigned start = 1 << (BITS_USED - 1)) {
  unsigned mask = start;

  while(mask != 0 && (x & mask) == 0) {
    mask >>= 1;
  }

  return mask;
}

// This function does the binary search.
unsigned find_kth_complement(unsigned x, unsigned k) {
  // (rest_mask) is the complement of (x), or at least the bits we take into
  // consideration (see comment on BITS_USED above).
  unsigned rest_mask = ~x & ((1u << BITS_USED) - 1);
  unsigned rest_bits = bitcount(rest_mask);
  unsigned bit       = highest_set_bit(rest_mask);

  // (curmask) will be updated to contain the bits we already know the
  // (k)th solution will have set. It will be built from the most significant
  // bit downwards and always be a solution itself (!). Setting new, ever
  // less significant bits in it will make it larger until it is the (k)th
  // solution.
  unsigned curmask   = 0;

  while(rest_mask != 0) {
    rest_mask &= ~bit;
    --rest_bits;

    // Here now the binary search: We know that (rest_bits) bits are
    // set in (rest_mask), which is (~x) without the bits we already
    // know the solution will have set. We know therefore that there
    // are (skip) = 2^(rest_bits) solutions that have the bit in (bit)
    // set, and equally many that have it unset.
    unsigned skip = 1u << rest_bits;

    // So: Setting the highest bit of the rest mask in (curmask) will
    // propel (curmask) (skip) solutions ahead. We can only do this if
    // we still have to skip more than that many solutions. (k) will
    // be adjusted to be the number of solutions left to skip.
    if(k >= skip) {
      curmask |= bit;
      k -= skip;
    }

    bit = highest_set_bit(rest_mask, bit);
  }

  // if (k) is not zero here, there were not enough solutions to skip.
  if(k != 0) {
    throw std::logic_error("There are less than k solutions for the given x");
  }

  return curmask;
}

int main() {
  unsigned x = 1234567890;
  unsigned y = ~x & 0xff;

  std::cout << std::bitset<BITS_USED>(x) << std::endl;

  // Taking it for a ride here: Generate the first 500k solutions.
  // Printing them is done in binary because it is easier to see that it
  // works that way.
  for(unsigned i = 0; i < 500000; ++i) {
    std::cout << std::bitset<BITS_USED>(find_kth_complement(x, i)) << "\n";
  }
}