繁体   English   中英

Java中的排列而不使用数组

[英]Permutations in Java without using an array

下面的代码循环遍历所有提交的字符,将它们放入字符串数组,然后对它们进行散列,然后对照最初输入的散列密码进行检查。 此逻辑适用于最多5个字符的密码,但是当我输入6个字符时,排列的数目(36 ^ 6)太大,以至于由于数组的大小太大而引发了内存异常。

因此,我试图仅使用字符串而不是数组来修改它来停止此内存问题,但无法完全使它工作,有什么明显的建议吗?

您可以通过从0计数到无穷大和转换的数量只是列举含有字母和数字的所有可能的密码toString与基地36。

long k = 0;
while (true) {
    String pwd = Long.toString(k++, 36);
    if (SHA1(pwd).equals(hashedPassword)) {
        System.out.println(pwd);
    }
}

在这里, toString使用[0-9a-z]最多可使用36个基数,也就是说,它适用于您的情况,但是,如果要包括特殊字符,则必须创建自己的数字转密码功能(请考虑除法和取模),但其余部分保持不变。

这样,内存要求为O(1),但是对于最多n个字符的密码,复杂度当然仍为O(36 n )。


这种方法的一个问题是-与所有数字表示一样,前导零将被忽略,因此Long.toString将永远不会生成以0开头的密码(除了0本身)。 为了解决这个问题,您可以使用两个嵌套循环-外循环迭代密码中的位数,而内循环迭代数字直至36 d,并用前导零填充字符串,或者从36 d循环成2 * 36 d并切掉第一个(非零)数字。 这似乎比以前需要做更多的工作,但实际上它只产生两倍的密码。

for (int d = 1; d < 3; d++) {
    long p = pow(36, d);
    for (long n = p; n < 2 * p; n++) {
        String pwd = Long.toString(n, 36).substring(1);
        System.out.println(n + " " + pwd);
    }
}

输出:

0
1
...
z
00
01
...
zz

除了托比亚斯的答案。

您可以通过这种方式使用指定的字母递归遍历所有可能的组合(直到一定长度),然后对每个字母进行操作而不存储它们,除非您检测到匹配。

// Depth first combinations
private static void combineRecurse(String base, char[] alphabet, int maxLen,
        Consumer<String> consumer) {
    if (base.length() < maxLen - 1) {
        for (int i = 0; i < alphabet.length; i++) {
            String newPermutation = base + alphabet[i];
            consumer.accept(newPermutation);
            combineRecurse(newPermutation, alphabet, maxLen, consumer);
        }
    } else {
        // the new permutiation will be the maxlength
        for (int i = 0; i < alphabet.length; i++) {
            consumer.accept(base + alphabet[i]);
        }
    }
}

public static void forEachCombination(char[] alphabet, int maxLen, Consumer<String> consumer) {
    if(alphabet == null || consumer == null || maxLen < 1) return;
    combineRecurse("", alphabet, maxLen, consumer);
}

public static void main(String[] args) {

    char[] alphabet = { ... };
    final String hashedPassword = SHA1(password);
    final int maxlen = 16;

    List<String> possiblePasswords = new ArrayList<>();
    forEachCombination(alphabet, maxlen, s -> {
        // System.out.println(s);
        if (SHA1(s).equals(hashedPassword)) {
            possiblePasswords.add(s);
        }
    });
}

进行这种迭代而不是递归可能会更好,但是目前,递归易于实现,对于初学者来说更容易理解。

暂无
暂无

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

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