简体   繁体   English

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

[英]Generate all possible string from a given length

I would like to be able to generate all possible strings from a given length, and I frankly don't know how to code that. 我希望能够从给定的长度生成所有可能的字符串,坦率地说,我不知道如何编写代码。 So for further explanation, I and a friend would like to demonstrate some basic hacking techniques, so bruteforcing comes up. 因此,为了进一步解释,我和一位朋友想展示一些基本的黑客攻击技术,因此会出现强制攻击。 Of course, he will be my victim, no illegal thing there. 当然,他将是我的受害者,那里没有违法的东西。

However, the only thing he told me is that his PW will be 4-char-long, but I'm pretty sure his PW won't be in any dictionnary, that would be toi easy. 然而,他告诉我的唯一一件事是他的PW将是4个字符长,但我很确定他的PW不会出现在任何字典中,这很容易。

So I came up with the idea of generating EVERY 4-char-long-string possible, containing az characters (no caps). 所以我提出了生成每个4-char-long-string的想法,包含az字符(无上限)。

Would someone have a lead to follow to code such an algorithm ? 是否有人可以跟随代码编写这样的算法? I don't really bother with performances, if it takes 1 night to generate all PW, that's no problem. 我真的不打扰表演,如果需要1晚才能生成所有PW,那没问题。

Don't forget, that's only on demonstration purposes. 别忘了,这只是出于演示目的。

You can do it just how you'd do it with numbers. 您可以通过数字来完成它。 Start with aaaa. 从aaaa开始。 Then increment the 'least significant' part, so aaab. 然后增加“最不重要”的部分,所以aaab。 Keep going until you get to aaaz. 继续前进,直到你到达aaaz。 Then increment to aaba. 然后增加到aaba。 Repeat until you get to zzzz. 重复,直到你到达zzzz。

So all you need to do is implement is 所以你需要做的就是实现

String getNext(String current)

To expand on this; 为了扩展这个; It possibly isnt the quickest way of doing things, but it is the simplest to get right. 它可能不是最快捷的做事方式,但它是最简单的做法。

As the old adage goes - 'first make it right, then make it fast'. 正如古老的谚语所说的那样 - “首先使它成为正确的,然后让它快速”。 Getting a working implementation that passes all your tests (you do have tests, right?) is what you do first. 获得通过所有测试的工作实现(你确实有测试,对吗?)就是你先做的。 You then rewrite it to make it fast, using your tests as reassurance you're not breaking the core functionality. 然后你重写它以使其快速,使用你的测试作为保证你没有打破核心功能。

The absolutely simplest way is to use four nested loops: 绝对最简单的方法是使用四个嵌套循环:

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

This does not scale well, because adding extra characters requires adding a level of nesting. 这不能很好地扩展,因为添加额外的字符需要添加嵌套级别。 Recursive approach is more flexible, but it is harder to understand: 递归方法更灵活,但更难理解:

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

Call recursive method like this: 调用这样的递归方法:

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<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
    }
}

This is something you can do recursively. 这是你可以递归做的事情。

Lets define every (n) -character password the set of all (n-1) -character passwords, prefixed with each of the letters a thru z . 让每个(n)字符密码定义所有(n-1)字符密码的集合,以每个字母az为前缀。 So there are 26 times as many (n) -character passwords as there are (n-1) -character passwords. 所以(n)字符密码的数量是(n-1)字符密码的26倍。 Keep in mind that this is for passwords consisting of lower-case letters. 请记住,这是针对由小写字母组成的密码。 Obviously, you can increase the range of each letter quite easily. 显然,你可以很容易地增加每个字母的范围。

Now that you've defined the recursive relationship, you just need the terminating condition. 既然您已经定义了递归关系,那么您只需要终止条件。

That would be the fact that there is only one (0) -character password, that being the empty string. 那就是只有一个 (0)字符密码,即空字符串。

So here's the recursive function: 所以这是递归函数:

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

to be called with: 被称为:

printNCharacterPasswords ("", 4)

And, since Python is such a wonderful pseudo-code language, you can see it in action with only the first five letters: 而且,由于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)

which outputs: 哪个输出:

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

A aroth points out, using a digit counter approach is faster. Aroth指出,使用数字计数器方法更快。 To make this even faster, you can use a combination of an inner loop for the last digit and a counter for the rest (so the number of digits can be variable) 为了使速度更快,您可以使用最后一位数字的内循环和其余数字的计数器组合(因此数字位数可以变化)

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

}

prints 版画

Took 0.115 seconds to generate 308,915,776 combinations

Note: the loop is so simple, its highly likely that the JIT can eliminate key pieces of code (after in-lining newCombination) and the reason its so fast is its not really calculating every combination. 注意:循环非常简单,很可能JIT可以消除关键代码片段(在内联newCombination之后),并且它如此快速的原因是它没有真正计算每个组合。


A simpler way to generate combinations. 一种生成组合的简单方法。

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

prints 版画

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

It spends most of its time waiting for the screen to update. 它花费大部分时间等待屏幕更新。 ;) ;)

If you comment out the line which prints the combinations, and increase the count to 5 and 6 如果您注释掉打印组合的行,并将计数增加到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<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);
    }
}

Put all the characters you expect the password to contain into an array. 将您希望密码包含的所有字符放入数组中。 Write a stub function to test if your algorithm finds the correct password. 编写一个存根函数来测试您的算法是否找到了正确的密码。 Start with passwords of length 1, work your way up to 4 and see if your fake password is found on each iteration. 从长度为1的密码开始,最多运行4次,看看每次迭代是否找到了您的假密码。

you can use the following code for getting random string. 您可以使用以下代码获取随机字符串。 It will return you a string of 32 chars. 它会返回一串32个字符。 you can get string of desired length by using substring(). 你可以使用substring()获得所需长度的字符串。 Like if you want a string with 10 chars then: 就像你想要一个包含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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM