我必须通过爬山实现以下算法,该算法将在特定密码的密码分析中运行无数次。该算法产生标准字母表 {A,B,C,...,Y,Z} 的排列,其密钥 K 由相同字母表的 7 个字母组成,如下所示:
- 假设 K = INGXNDM = {9, 14, 7, 24, 14, 4, 13}
- 从右到左,在字母表上数 K1=9 个位置。到达 R,所以排列的第一个元素是 R:P = {R,...}
- 将 R 标记为已使用,稍后我们将不得不“跳过”它。
- 从 R 开始,向左数 K2=14 个位置,我们到达 D 并将其标记为已使用。P={R,D,...}
- 下一个计数是 7,当到达 A 时,我们循环并认为 Z 跟随 A,所以我们到达 W:将其标记为已使用,P={R,D,W,...}
- 下一个计数是 24,所以我们到达 V,因为我们跳过了 R、D 和 W。
- 以此类推……当 K7=13 已经使用时,我们以 K1=9 重新启动
- 得到的转置字母为:RDWVGBL ZHUKNFI PJSAMXT CQOYE
事实上,我需要解密代码中的逆排列。我的代码实现了一个链表来跳过使用过的字母。它以 0 为底,因此 A = 0, ... Z = 25 并返回逆排列 Pinv(i)=j,这意味着字母 i 位于位置 j。
#define ALPHALEN 26
void KeyToPermut(int *K, int * Pinv);
int previnit[ALPHALEN], prev[ALPHALEN], nextinit[ALPHALEN], next[ALPHALEN];
int main() {
int l, Pinv[ALPHALEN], K[7] = { 8, 13, 6, 23, 13, 3, 12 }, P[ALPHALEN];
// precalculate links between letters, ordered right to left , from Z to A
for (l = 0; l < ALPHALEN; l++) {
previnit[l] = l + 1; // prev[A] = B
if (previnit[l] >= ALPHALEN) previnit[l] -= ALPHALEN; // prev[Z] = A
nextinit[l] = l - 1; // next[B] = A
if (nextinit[l] < 0) nextinit[l] += ALPHALEN; // next[A] = Z
}
KeyToPermut(K, Pinv); // this is the code to be optimized
for (l = 0; l < ALPHALEN; l++) P[Pinv[l]] = l; // calculate direct permutation
for (l = 0; l < ALPHALEN; l++) printf("%c", P[l] + 65);
printf("\n");
}
void KeyToPermut(int *K, int * Permut) {
int l, keyptr=0, cnt=0, p=0;
// copy prev[] and next[] from precalculated arrays previnit[] and nextinit[]
for (l = 0; l < ALPHALEN; l++) {
prev[l] = previnit[l];
next[l] = nextinit[l];
}
while (1) {
for (l = 0; l <= K[keyptr] % (ALPHALEN-cnt); l++) p = next[p];
Permut[p] = cnt++;
if (cnt < ALPHALEN)
{
prev[next[p]] = prev[p]; // link previous and next positions
next[prev[p]] = next[p];
keyptr++;
if (keyptr >= 7) keyptr = 0; // re-use K1 after K7
}
else
break;
}
}
我有两个问题:
- 如何优化 KeyToPermut 中的代码?分析器显然表明跨链的 for 循环是瓶颈。可能有一种避免链表的方法......?
- 显然密钥空间不是26!但要小得多:26^7,所以只是 26 的一个子集!可以生成。你知道生成的排列有多具体吗?它们是否属于已知的排列类别?例如,(到目前为止)我无法识别这些排列循环中的任何模式。
我使用VS2013和C,项目的其他部分是CUDA代码。(x64 平台)
感谢您的关注。
背景信息:密码使用的加密方案使用 4 个长度为 7 的密钥 K。因此,寻找明文的理论密钥空间是 26^28 即 131 位。该方法可以使用其他密钥长度:从 1 到 25 的任何值都可以。