Java中的排列而不使用数组

时间:2018-10-30 15:20:04

标签: java permutation

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

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

2 个答案:

答案 0 :(得分:1)

您只需列举从0到无穷大并以36为底的数字toString即可枚举所有可能的包含字母和数字的密码。

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个基数,也就是说,它适用于您的情况,但是如果要包括特殊字符,则必须创建自己的数字, -password功能(考虑除法和取模),其余部分保持不变。

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


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

0

输出:

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

答案 1 :(得分:0)

除了tobias的答案。

这是一种您可以使用指定字母遍历它的所有可能组合(直到一定长度)并对其进行操作而无需存储它们的一种方法,除非您检测到它是一个匹配。

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

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