下一个最大的整数,有一些中间位与模式匹配?

Next largest integer with some middle bits matching a pattern?

本文关键字:中间 位与 模式匹配 整数 下一个      更新时间:2023-10-16

我的输入是:

  • 宽度n的位掩码mask和一些偏移k>=0
  • 位模式在某些(但不一定是全部)位掩码具有 1 的位置pattern1。
  • 整数val

我想找到下一个最大的整数result这样:

  • result > val
  • result & mask == pattern

例如,假设mask = 0xFF00pattern = 0x0100。然后我们期待以下结果:

NextLargest(mask, pattern, 0x00000) => 0x00100
NextLargest(mask, pattern, 0x000FF) => 0x00100
NextLargest(mask, pattern, 0x010FE) => 0x001FF
NextLargest(mask, pattern, 0x010FF) => 0x10100

另一个例子 - 说mask = 0xFpattern = 0xF。然后我们期望:

NextLargest(mask, pattern, 0x20) => 0x2F.

我尝试过诸如"去除mask关心的部分,增加它,或者回到pattern并返回"之类的方法,但我不断遇到边缘情况。这个问题有点像找到某个整数的下一个最大倍数的泛化。

这是我到目前为止的尝试(可运行的链接:https://ideone.com/AhXG5M):

#include <iostream>
using namespace std;
using uint32 = unsigned long;
uint32 NextLargest(int width, int offset, uint32 mask, uint32 pattern, uint32 val) {
unsigned long long ret = (val + 1) & ~mask;
if ((ret & ((1 << (offset + 1)) - 1)) == 0) {
// "carry" across the mask
ret += 1 << (offset + width);
}
return ret | pattern;
}
int main() {
// your code goes here
int width = 12;
int offset = 4;
uint32 significant_bits = (1 << (width + 1) - 1) << offset;
uint32 wanted_bits = 0xFFF << offset;
cout << hex;
// want 0xFFF1 -- correct
cout << NextLargest(width, offset, significant_bits, wanted_bits, 0) << endl;
// want 0xFFF2 -- correct
cout << NextLargest(width, offset, significant_bits, wanted_bits, 1) << endl;
// want 0x1FFFF0 -- incorrect, get 0xFFF0
cout << NextLargest(width, offset, significant_bits, wanted_bits, 0xF) << endl;
return 0;
}

我没有测试这个,但以下算法应该可以工作(伪代码):

let mask, pattern, and val be inputs
let fls be function that finds last bit set in word
let ffs be function that finds first bit set in a word
let applied be (val & ~mask) | pattern
if applied is greater than val then
return applied
let low_order_mask be (1 << ffs(mask)) - 1
if applied == val then
let flipped_low be (~value & low_order_mask)
if not flipped_low then
return applied + 1 // no need to carry
// need to carry
let set_low_zero be applied & ~low_order_mask
let carry be 1 << (fls(mask) + 1)
return set_low_zero + carry

flsffs由 POSIX 提供,但其他系统可能不会这样做。如果需要,SO 上有关于如何实现这些的答案。

想想value分成3。 掩码上方、掩码中和掩码下方的位。H(value)M(value)L(value)

我们知道M(result)==pattern. 我们有三名候选人。

C1 是H(value)+pattern+0

C2H(value)+pattern+L(value)+1

C3 是H(value)+pattern+X

X==(mask<<1)&~mask.这是高于mask的最低位。

如果pattern>M(value)我们可以使用 C1。 减少高位将得到一个数字<value,设置任何低位将增加数字。

如果pattern==M(value)那么我们可以尝试 C2 这实际上是value+1. 如果添加一个溢出到模式位,则会失败。

这意味着所有低位都已设置,要添加的下一个最低位置是掩码上方的第一个位。

unsigned next_masked(unsigned mask,unsigned pattern,unsigned value){
unsigned reduced_pattern=(mask&pattern);//May not be required...
unsigned over_add=(mask<<1)&~mask;
unsigned upper_mask=~(over_add-1);
unsigned cand=(value&upper_mask)|reduced_pattern;
if(cand>value){
return cand;
}
if((value&mask)==reduced_pattern){
unsigned scand=value+1;
if((scand&mask)==reduced_pattern){
return scand;
}
}   
return cand + over_add;
}

这里再次进行一些单元测试:

#include <iostream>
unsigned next_masked(unsigned mask,unsigned pattern,unsigned value){
unsigned reduced_pattern=(mask&pattern);//May not be required...
unsigned over_add=(mask<<1)&~mask;
unsigned upper_mask=~(over_add-1);
unsigned cand=(value&upper_mask)|reduced_pattern;
if(cand>value){
return cand;
}
if((value&mask)==reduced_pattern){
unsigned scand=value+1;
if((scand&mask)==reduced_pattern){
return scand;
}
}   
return cand + over_add;
}
bool invariant_next_masked(unsigned mask,unsigned pattern,unsigned value,unsigned result){
if((result&mask)!=(pattern&mask)){
return false;
}
if(result<=value){
return false;
}
for(unsigned test=result-1;test>value;--test){
if((test&mask)==(pattern&mask)){
return false;
}
}
return true;
}
int check_next_masked(unsigned mask,unsigned pattern,unsigned value,unsigned expect){
unsigned result=next_masked(mask,pattern,value);
if(result!=expect){
std::cout << std::hex << mask << ' ' << std::hex << pattern << ' ' << std::hex <<value << "==" << std::hex <<result << "!=" << std::hex <<expect <<'n';
return 1;
}
if(!invariant_next_masked(mask,pattern,value,result)){
return 1;
}
return 0;
}
int main() {
int errors=0;
errors+=check_next_masked(0xFF00,0x0100,0x0000,0x00100);
errors+=check_next_masked(0xFF00,0x0100,0x00FF,0x00100);
errors+=check_next_masked(0xFF00,0x0100,0x10FE,0x10100);        
errors+=check_next_masked(0xFF00,0x0100,0x1067,0x10100);
errors+=check_next_masked(0xFF00,0x0100,0x10123,0x10124);
errors+=check_next_masked(0xFF00,0x0100,0x110FF,0x20100);
errors+=check_next_masked(0xFF00,0x0100,0x102FF,0x20100);
errors+=check_next_masked(0xFF00,0x0100,0x101FF,0x20100);
errors+=check_next_masked(0x000F,0x0007,0x10123,0x10127);
errors+=check_next_masked(0x000F,0x0007,0x10128,0x10137);
errors+=check_next_masked(0x0FF0,0x0230,0x10128,0x10230);
errors+=check_next_masked(0x0FFF0,0x01230,0x01231,0x01232);
errors+=check_next_masked(0x0FFF0,0x01230,0x41237,0x41238);
errors+=check_next_masked(0x0FFF0,0x01230,0x4123F,0x51230);

if(errors>0){
std::cout << "Errors "<< errors << 'n';
return 1;
}
std::cout << "Successn";
return 0;
}

问题是计算最大最小的下一个值吗? 最大值似乎很奇怪。 如果要求是计算最小值,我认为这段代码应该可以工作: (在 GCC 7.1 上测试,假设目标是 64 位,sizeof(void *) == sizeof(size_t) == sizeof(uint64_t))

size_t next_smallest_value (size_t mask, size_t pattern, size_t x) {
assert(pattern & mask == pattern);
// only change bits within mask range to meet the requirement
auto y = x & ~mask | pattern;
if (y > x) {
// if the operation increased the value
// mask off all the lower bits
auto lsb_mask = __builtin_ctzll(mask);
return y & ~ones(lsb_mask);
} else {
// otherwise, the operation decreased or didn't change the value
// need to increase the fraction higher than the mask
auto msb_mask = 63 - __builtin_clzll(mask);
// higher part cannot be empty if the masked part decrease
assert(msb_mask < 63);
auto higher = ((y >> msb_mask) + 1) << msb_mask;
// also higher part cannot overflow
assert(higher != 0);
return y & mask | higher;
}
}

这个想法很简单:将位分成3个部分:上部,屏蔽部分,下部。 屏蔽部分可以直接导出并由maskpattern确定,它不能是其他值。

计算屏蔽位后,如果值增加,只需屏蔽下部的所有位。 否则,将较高部分增加 1(并屏蔽所有较低位)。

上面的代码不处理格式错误的输入,它会触发断言,但检查并没有用尽。

这是这个有点令人困惑的问题的两个函数 第一个函数给出满足结果要求的最大下一个整数。 第二个给出最小的下一个值。


1:得到满足result & mask == pattern的最大整数和result > val

unsigned NextLargest (unsigned mask, unsigned pattern, unsigned val) {
// zero "mask" bits and set "pattern" bits in largest (unsigned) int
unsigned const x = ~mask | pattern;
// if result is not greater than val, we can't satisfy requirements
if (x <= val) {
... report error, return error code or throw something
}
return x;
}

显然,这只返回满足要求的最高(无符号)整数值result & mask == patternresult > val.if 子句检查结果是否不会大于val,并且函数将失败。


2:获取满足要求的val后最小的下一个值:

unsigned NextSmallest (unsigned mask, unsigned pattern, unsigned val) {
unsigned const x = (val + mask + 1) & ~mask | pattern;
if (x <= val) {
... increment wrapped, can't give greater value
}
return x;
}

编辑:将(val|mask)更改为val+mask,因为结果必须仍大于val

此函数计算val + 1,并将溢出位携带超过mask位。 以下是该函数的几个示例,如果mask = 0x0ff00pattern = 0x00500

val        +mask    +1       &~mask     |pattern == result
0x00000    0x0ff00  0x0ff01  0x00001    0x00501
0x00001    0x0ff01  0x0ff02  0x00002    0x00502
0x000fe    0x0fffe  0x0ffff  0x000ff    0x005ff
0x000ff    0x0ffff  0x10000  0x10000    0x10500
0x00100    0x10000  0x10001  0x10001    0x10501
0x0f000    0x1ef00  0x1ef01  0x10001    0x10501
0x0ff00    0x1fe00  0x1fe01  0x10001    0x10501
0x0ffff    0x1feff  0x1ff00  0x10000    0x10500
0x10000    0x1ff00  0x1ff01  0x10001    0x10501
0x10001    0x1ff01  0x1ff02  0x10002    0x10502
0x100ff    0x1ffff  0x20000  0x20000    0x20500

经过长时间的编辑和重写,我仍然无法为这个问题给出足够好的答案。它的例子有奇怪的结果。如果有人发现这些功能或其部分有用,我仍然将其留在这里。我也没有实际测试计算机上的功能。