簡體   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