特定有限整数集的有效映射

R..*_*R.. 5 c mapping unicode bit-manipulation

我正在寻找下面的整数列表和0-127范围的子集之间的小的,快速的(双向)双射映射:

0x200C, 0x200D, 0x200E, 0x200F,
0x2013, 0x2014, 0x2015, 0x2017,
0x2018, 0x2019, 0x201A, 0x201C,
0x201D, 0x201E, 0x2020, 0x2021,
0x2022, 0x2026, 0x2030, 0x2039,
0x203A, 0x20AA, 0x20AB, 0x20AC,
0x20AF, 0x2116, 0x2122
Run Code Online (Sandbox Code Playgroud)

一个明显的解决方案是

y = x>>2 & 0x40 | x & 0x3f;
x = 0x2000 | y<<2 & 0x100 | y & 0x3f;
Run Code Online (Sandbox Code Playgroud)

编辑:我错过了一些值,特别是0x20Ax,这些值不适用于上述.

另一个显而易见的解决方案是查找表,但不会使其不必要地大,查找表无论如何都需要一些位重排,我怀疑通过简单的位重排可以更好地完成整个任务.

对于好奇的人来说,这些神奇的数字是传统ISO-8859和Windows代码页中唯一出现的"大"Unicode代码点.

650*_*502 1

我知道它很难看,但除了最后一个值之外,如果您考虑最低 6 位,那么所有其他值都已经是唯一的,因此您可以构建和逆映射:

int ints[] = {0x200C, 0x200D, 0x200E, 0x200F,
              0x2013, 0x2014, 0x2015, 0x2017,
              0x2018, 0x2019, 0x201A, 0x201C,
              0x201D, 0x201E, 0x2020, 0x2021,
              0x2022, 0x2026, 0x2030, 0x2039,
              0x203A, 0x20AA, 0x20AB, 0x20AC,
              0x20AF, 0x2116, 0x2122};

int invmap[64];

void mkinvmap()
{
    for (int i=0; i<26; i++)
        invmap[ints[i]&63] = ints[i];
    invmap[0] = 0x2122;
}
Run Code Online (Sandbox Code Playgroud)

在这个逆映射计算之后,两个变换函数是

int direct(int x)  { return x==0x2122 ? 0 : (x & 63); }
int inverse(int x) { return invmap[x]; }
Run Code Online (Sandbox Code Playgroud)

该函数direct(x)将返回 0 到 63 之间的数字,而inverse(x)给定 0 到 63 之间的数字的函数将返回一个整数。对于列表中的所有 27 个值inverse(direct(x)) == x