从给定长度生成所有可能的字符串

我希望能够从给定的长度生成所有可能的字符串,坦白说,我不知道如何编码。 因此,为了进一步解释,我和一位朋友想展示一些基本的黑客攻击技术,因此会出现强制攻击。 当然,他将是我的受害者,那里没有违法的东西。

然而,他告诉我的唯一一件事是他的PW将是4个字符长,但我很确定他的PW不会出现在任何字典中,这很容易。

所以我提出了生成每个4-char-long-string的想法,包含az字符(无上限)。

是否有人可以跟随代码编写这样的算法? 我真的不打扰表演,如果需要1晚才能生成所有PW,那没问题。

别忘了,这只是出于演示目的。

您可以通过数字来完成它。 从aaaa开始。 然后增加“最不重要”的部分,所以aaab。 继续前进,直到你到达aaaz。 然后增加到aaba。 重复,直到你到达zzzz。

所以你需要做的就是实现

String getNext(String current) 

为了扩展这个; 它可能不是最快捷的做事方式,但它是最简单的做法。

正如古老的谚语所说的那样 – “首先做正确的事,然后快速行动”。 获得通过所有测试的工作实现(你确实有测试,对吧?)就是你先做的。 然后你重写它以使其快速,使用你的测试作为保证你没有打破核心function。

最简单的方法是使用四个嵌套循环:

 char[] pw = new char[4]; for (pw[0] = 'a' ; pw[0] <= 'z' ; pw[0]++) for (pw[1] = 'a' ; pw[1] <= 'z' ; pw[1]++) for (pw[2] = 'a' ; pw[2] <= 'z' ; pw[2]++) for (pw[3] = 'a' ; pw[3] <= 'z' ; pw[3]++) System.out.println(new String(pw)); 

这不能很好地扩展,因为添加额外的字符需要添加嵌套级别。 递归方法更灵活,但更难理解:

 void findPwd(char[] pw, int pos) { if (pos < 0) { System.out.println(new String(pwd)); return; } for (pw[pos] = 'a' ; pw[pos] <= 'z' ; pw[pos]++) findPwd(pw, pos-1); } 

像这样调用递归方法:

 char[] pw = new char[4]; findPwd(pw, 3); 
 private static void printAllStringsOfLength(int len) { char[] guess = new char[len]; Arrays.fill(guess, 'a'); do { System.out.println("Current guess: " + new String(guess)); int incrementIndex = guess.length - 1; while (incrementIndex >= 0) { guess[incrementIndex]++; if (guess[incrementIndex] > 'z') { if (incrementIndex > 0) { guess[incrementIndex] = 'a'; } incrementIndex--; } else { break; } } } while (guess[0] <= 'z'); } 
 public class GenerateCombinations { public static void main(String[] args) { List characters = new ArrayList(); for (char c = 'a'; c <= 'z'; c++) { characters.add(c); } List allStrings = new ArrayList(); for (Character c : characters) { for (Character d : characters) { for (Character e : characters) { for (Character f : characters) { String s = "" + c + d + e + f; allStrings.add(s); } } } } System.out.println(allStrings.size()); // 456 976 combinations } } 

这是你可以递归做的事情。

让每个(n)字符密码定义所有(n-1)字符密码的集合,以每个字母az为前缀。 所以(n)字符密码的数量是(n-1)字符密码的26倍。 请记住,这适用于由小写字母组成的密码。 显然,你可以很容易地增加每个字母的范围。

现在您已经定义了递归关系,您只需要终止条件。

那就是只有一个 (0)字符密码,即空字符串。

所以这是递归函数:

 def printNCharacterPasswords (prefix, num): if num == 0: print prefix return foreach letter in 'a'..'z': printNCharacterPasswords (prefix + letter, num - 1) 

被召唤:

 printNCharacterPasswords ("", 4) 

而且,由于Python是如此精彩的伪代码语言,你可以只用前五个字母来看它:

 def printNCharacterPasswords (prefix, num): if num == 0: print prefix return for letter in ('a', 'b', 'c', 'd', 'e'): printNCharacterPasswords (prefix + letter, num - 1) printNCharacterPasswords ("", 2) 

哪个输出:

 aa ab ac ad ae ba bb bc bd be ca cb cc cd ce da db dc dd de ea eb ec ed ee 

Aroth指出,使用数字计数器方法更快。 为了使速度更快,您可以使用最后一位数字的内部循环和其余数字的计数器组合(因此数字位数可以变化)

 public static void main(String... args) { long start = System.nanoTime(); int letters = 26; int count = 6; final int combinations = (int) Math.pow(letters, count); char[] chars = new char[count]; Arrays.fill(chars, 'a'); final int last = count - 1; OUTER: while (true) { for (chars[last] = 'a'; chars[last] <= 'z'; chars[last]+=2) { newComination(chars); chars[last]++; newComination(chars); } UPDATED: { for (int i = last - 1; i >= 0; i--) { if (chars[i]++ >= 'z') chars[i] = 'a'; else break UPDATED; } // overflow; break OUTER; } } long time = System.nanoTime() - start; System.out.printf("Took %.3f seconds to generate %,d combinations%n", time / 1e9, combinations); } private static void newComination(char[] chars) { } 

版画

 Took 0.115 seconds to generate 308,915,776 combinations 

注意:循环非常简单,很可能JIT可以消除关键代码片段(在内联newCombination之后),并且它如此快速的原因是它没有真正计算每个组合。


一种生成组合的简单方法。

 long start = System.nanoTime(); int letters = 26; int count = 6; final int combinations = (int) Math.pow(letters, count); StringBuilder sb = new StringBuilder(count); for (int i = 0; i < combinations; i++) { sb.setLength(0); for (int j = 0, i2 = i; j < count; j++, i2 /= letters) sb.insert(0, (char) ('a' + i2 % letters)); // System.out.println(sb); } long time = System.nanoTime() - start; System.out.printf("Took %.3f seconds to generate %,d combinations%n", time / 1e9, combinations); 

版画

 aaaa aaab aaac .... zzzx zzzy zzzz Took 0.785 seconds to generate 456,976 combinations 

它花费大部分时间等待屏幕更新。 ;)

如果您注释掉打印组合的行,并将计数增加到5和6

 Took 0.671 seconds to generate 11,881,376 combinations Took 15.653 seconds to generate 308,915,776 combinations 
 public class AnagramEngine { private static int[] anagramIndex; public AnagramEngine(String str) { AnagramEngine.generate(str); } private static void generate(String str) { java.util.Map anagram = new java.util.HashMap(); for(int i = 0; i < str.length(); i++) { anagram.put((i+1), str.charAt(i)); } anagramIndex = new int[size(str.length())]; StringBuffer rev = new StringBuffer(AnagramEngine.start(str)+"").reverse(); int end = Integer.parseInt(rev.toString()); for(int i = AnagramEngine.start(str), index = 0; i <= end; i++){ if(AnagramEngine.isOrder(i)) anagramIndex[index++] = i; } for(int i = 0; i < anagramIndex.length; i++) { StringBuffer toGet = new StringBuffer(anagramIndex[i] + ""); for(int j = 0; j < str.length(); j++) { System.out.print(anagram.get(Integer.parseInt(Character.toString(toGet.charAt(j))))); } System.out.print("\n"); } System.out.print(size(str.length()) + " iterations"); } private static boolean isOrder(int num) { java.util.Vector list = new java.util.Vector(); String str = Integer.toString(num); char[] digits = str.toCharArray(); for(char vecDigits : digits) list.add(Integer.parseInt(Character.toString(vecDigits))); int[] nums = new int[str.length()]; for(int i = 0; i < nums.length; i++) nums[i] = i+1; for(int i = 0; i < nums.length; i++) { if(!list.contains(nums[i])) return false; } return true; } private static int start(String str) { StringBuffer num = new StringBuffer(""); for(int i = 1; i <= str.length(); i++) num.append(Integer.toString(i)); return Integer.parseInt(num.toString()); } private static int size(int num) { int size; if(num == 1) { return 1; } else { size = num * size(num - 1); } return size; } public static void main(final String[] args) { final java.util.Scanner sc = new java.util.Scanner(System.in); System.out.printf("\n%s\t", "Entered word:"); String word = sc.nextLine(); System.out.printf("\n"); new AnagramEngine(word); } } 

将您希望密码包含的所有字符放入数组中。 编写一个存根函数来测试您的算法是否找到了正确的密码。 从长度为1的密码开始,最多运行4次,看看每次迭代是否找到了您的假密码。

您可以使用以下代码获取随机字符串。 它会返回一串32个字符。 你可以使用substring()获得所需长度的字符串。 就像你想要一个包含10个字符的字符串一样:

 import java.security.SecureRandom; import java.math.BigInteger; SecureRandom srandom = new SecureRandom(); String rand = new BigInteger(176, srandom).toString(32); rand.substring(0,7);