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

时间:2012-02-07 12:06:24

标签: java algorithm

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

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

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

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

不要忘记,这只是出于演示目的。

9 个答案:

答案 0 :(得分:8)

你可以用你的数字来做这件事。从aaaa开始。然后增加“最不重要”的部分,所以aaab。继续前进,直到你到达aaaz。然后增加到aaba。重复,直到你到达zzzz。

所以你需要做的就是实现

String getNext(String current)

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

正如古老的谚语所说的那样 - “首先要做对,然后快点”。获得通过所有测试的工作实现(你确实有测试,对吗?)就是你先做的。然后重写它以使其快速,使用您的测试确保您不会破坏核心功能。

答案 1 :(得分:5)

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

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

答案 2 :(得分: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');
}

答案 3 :(得分:2)

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

答案 4 :(得分:1)

这是你可以递归的。

让每个(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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

答案 8 :(得分:-1)

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