【问题标题】:Generate all possible string from a given length从给定长度生成所有可能的字符串
【发布时间】:2012-02-28 20:44:45
【问题描述】:

我希望能够从给定长度生成所有可能的字符串,但坦率地说,我不知道如何编码。所以为了进一步解释,我和一个朋友想演示一些基本的黑客技术,所以暴力破解出现了。当然,他会是我的受害者,没有违法的事情。

然而,他唯一告诉我的是他的密码长度为 4 个字符,但我很确定他的密码不会出现在任何字典中,这太容易了。

所以我想出了生成每个可能的 4 字符长字符串的想法,其中包含 a-z 字符(没有大写字母)。

会有人跟随他编写这样的算法吗?演技我倒是不介意,如果1个晚上就可以生成所有PW,那没问题。

别忘了,这只是为了演示。

【问题讨论】:

  • 您可以将这样的字符串视为基数中的数字[无论您想尝试多少不同的字符,例如 26 或 36 或 62]。从 0 开始并递增,直到再次为 0。

标签: java algorithm


【解决方案1】:

您可以像使用数字那样做。从 aaa 开始。然后增加“最不重要”的部分,所以 aaab。继续前进,直到到达 aaaz。然后递增到 aaba。重复直到你到达 zzzz。

所以你需要做的就是实现

String getNext(String current)

对此进行扩展;这可能不是最快的做事方式,但它是最简单的。

正如那句古老的格言所说——“先做对,再做快”。获得一个通过所有测试的工作实现(你确实有测试,对吗?)是你首先要做的。然后你重写它以使其更快,使用你的测试来保证你没有破坏核心功能。

【讨论】:

    【解决方案2】:

    这是您可以递归执行的操作。

    让我们定义每个(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
    

    【讨论】:

      【解决方案3】:

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

      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);
      

      【讨论】:

      • pwd 中的 System.out.println(new String(pwd)); 是什么?
      【解决方案4】:
      public class GenerateCombinations {
      
          public static void main(String[] args) {
              List<Character> characters = new ArrayList<Character>();
              for (char c = 'a'; c <= 'z'; c++) {
                  characters.add(c);
              }
              List<String> allStrings = new ArrayList<String>();
              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
          }
      }
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:
          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');
          }
          

          【讨论】:

            【解决方案7】:

            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
            

            【讨论】:

            【解决方案8】:
            public class AnagramEngine {
            
                private static int[] anagramIndex;
            
                public AnagramEngine(String str) {
                    AnagramEngine.generate(str);
                }
            
                private static void generate(String str) {
                    java.util.Map<Integer, Character> anagram = new java.util.HashMap<Integer, Character>();
                    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<Integer> list = new java.util.Vector<Integer>();
                    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);
                }
            }
            

            【讨论】:

              【解决方案9】:

              您可以使用以下代码获取随机字符串。它将返回一个 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);
              

              【讨论】:

              • 这到底是如何回答这个问题的?
              猜你喜欢
              • 1970-01-01
              • 2019-05-19
              • 1970-01-01
              • 1970-01-01
              • 2021-12-14
              • 1970-01-01
              • 2023-03-26
              • 2021-02-09
              • 1970-01-01
              相关资源
              最近更新 更多