这里的诀窍是要注意所有四个密码选项只是彼此的旋转/移位。
也就是说,对于示例密码qr34 和您提到的模式,您正在查看:
qr34 %%@@ #Original potential password
4qr3 @%%@ #Rotate 1 place right
34qr @@%% #Rotate 2 places right
r34q %@@% #Rotate 3 places right
鉴于此,您可以使用与第一个问题相同的生成技术。
对于生成的每个潜在密码,检查潜在密码以及该密码的下三个班次。
请注意,以下代码依赖于 C/C++ 的一个有趣属性:如果可以提前推断出语句的真值,则不会进一步执行。也就是说,给定语句if(A || B || C),如果A 为假,则必须评估B;但是,如果 B 为真,则永远不会评估 C。
这意味着我们可以拥有A=CheckPass(pass) 和B=CheckPass(RotatePass(pass)) 和C=CheckPass(RotatePass(pass)),并保证密码只会根据需要轮换多次。
请注意,此方案要求每个线程都有自己的潜在密码的私有副本。
//Compile with, e.g.: gcc -O3 temp.c -std=c99 -fopenmp
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int PassCheck(char *pass){
return strncmp(pass, "4qr3", 4)==0;
}
//Rotate string one character to the right
char* RotateString(char *str, int len){
char lastchr = str[len-1];
for(int i=len-1;i>0;i--)
str[i]=str[i-1];
str[0] = lastchr;
return str;
}
int main(){
const char alph[27] = "abcdefghijklmnopqrstuvwxyz";
const char num[11] = "0123456789";
char goodpass[4] = "----"; //Provide a default password to indicate an error state
#pragma omp parallel for collapse(4)
for(int i = 0; i < 26; i++)
for(int j = 0; j < 26; j++)
for(int m = 0; m < 10; m++)
for(int n = 0; n < 10; n++){
char pass[4] = {alph[i],alph[j],num[m],num[n]};
if(
PassCheck(pass) ||
PassCheck(RotateString(pass,4)) ||
PassCheck(RotateString(pass,4)) ||
PassCheck(RotateString(pass,4))
){
//It is good practice to use `critical` here in case two
//passwords are somehow both valid. This won't arise in
//your code, but is worth thinking about.
#pragma omp critical
{
memcpy(goodpass, pass, 4);
//#pragma omp cancel for //Escape for loops!
}
}
}
printf("Password was '%.4s'.\n",goodpass);
return 0;
}
我注意到您正在使用
生成密码
pass[3] = num[i % 10];
pass[2] = num[i / 10 % 10];
pass[1] = alp[i / 100 % 26];
pass[0] = alp[i / 2600 % 26];
这种技术有时很有用,尤其是在科学编程中,但通常只是为了解决方便性和内存局部性问题。
例如,一个以a[y][x] 访问元素的数组数组可以写成一个以a[y*width+x] 访问元素的平面数组。这会提高速度,但这只是因为内存是连续的。
在您的情况下,此索引不会产生任何速度提升,但确实使您更难以推理您的程序是如何工作的。由于这个原因,我会避免它。
有人说“过早的优化是万恶之源”。对于微优化(例如您在此处尝试的优化)尤其如此。最大的速度提升来自高级算法决策,而不是来自繁琐的东西。 -O3 编译标志可以完成大部分您需要完成的工作,以使您的代码在此级别上快速运行。
微优化假设在您的高级代码中做一些复杂的事情会使您以某种方式超越编译器。这不是一个好的假设,因为编译器通常非常聪明,而且明天会更聪明。您的时间非常宝贵:除非您有明确的理由,否则不要将其用于这些东西。 (进一步讨论“过早优化”是here。)