基于可变数量的内循环迭代

时间:2016-04-21 17:14:26

标签: swift swift2.2

在下面的代码中,我试图通过所有可能的字母组合来表示运行时变量的字符数。

这段代码的目的是构建一种密码破解器,它基本上是强力猜测字符串。我想使用循环,因为一旦正确的组合被击中,我将能够打破循环,从而节省了时间和资源,否则在我尝试在第一步中构建所有可能组合的数组时将需要这些。 / p>

我有一个静态代码,适用于5个字符长的字符串,但实际上我的字符串可以是任意长度。如何使我的代码可以使用任何长度的字符串?

let len = textField.text?.characters.count //Length of string
let charRange = "abcdefghijklmnopqrstuvwxyz" //Allowed characterset

for char1 in charRange.characters {
    for char2 in charRange.characters {
        for char3 in charRange.characters {
            for char4 in charRange.characters {
                for char5 in charRange.characters {
                     // Do whatever with all possible combinations
                }
            }
        }
    }
}

我想我必须以某种方式利用for totalChars in 1...len {,但无法弄清楚如何动态创建for循环?

3 个答案:

答案 0 :(得分:1)

根据 Martin R 的建议,您可以使用递归

这是函数

func visit(alphabet:[Character], combination:[Character], inout combinations:[String], length: Int) {
    guard length > 0 else {
        combinations.append(String(combination))
        return
    }

    alphabet.forEach {
        visit(alphabet, combination: combination + [$0], combinations: &combinations, length: length - 1)
    }
}

辅助函数

func combinations(alphabet: String, length: Int) -> [String] {
    var combinations = [String]()
    visit([Character](alphabet.characters), combination: [Character](), combinations: &combinations, length: length)
    return combinations
}

测试

现在,如果你想要3个字符的每个组合,并且你想要"ab"作为字母表,那么

combinations("ab", length: 3) // ["aaa", "aab", "aba", "abb", "baa", "bab", "bba", "bbb"]

重复

请注意,如果您在字母表中插入重复项,则会在结果中显示重复的元素。

时间复杂度

visit函数作为节点多次调用到高度为k-ary的完美h树中,其中:

  • kalphabet param
  • 中的元素数量
  • hlength param

这样的树有

enter image description here

节点。这是调用函数的确切次数。

空间复杂性

理论上 执行访问的同时分配的最大堆栈帧数为length

然而,由于Swift编译器确实实现了Tail Call Optimization,所分配的堆栈帧数仅为1.

最后,我们必须考虑combinations与结果数量一样大:alphabet^length

因此,时间复杂度是lengthelements into the result的最大值。

它是O(length + alphabet^length)

更新

事实证明你想要一个暴力密码破解者。

func find(alphabet:[Character], combination:[Character] = [Character](), length: Int, check: (keyword:String) -> Bool) -> String? {
    guard length > 0 else {
        let keyword = String(combination)
        return check(keyword: keyword) ? keyword : nil
    }

    for char in alphabet {
        if let keyword = find(alphabet, combination: combination + [char], length: length - 1, check: check) {
            return keyword
        }
    }

    return nil
}

最后一个参数check是一个闭包,用于验证当前单词是否是正确的密码。您将把逻辑放在此处,一旦找到密码,find就会停止。

实施例

find([Character]("tabcdefghil".characters), length: 3) { (keyword) -> Bool in
    return keyword == "cat" // write your code to verify the password here
}

答案 1 :(得分:1)

想法:使用字母表中的索引数组形成字符串;每次增加指数。

[0, 0, 0] -> [1, 0, 0] -> [2, 0, 0] ->
[0, 1, 0] -> [1, 1, 0] -> [2, 1, 0] ->
[0, 2, 0] -> [1, 2, 0] -> [2, 2, 0] ->
[0, 0, 1] ... [2, 2, 2]

这是一个使用长度为3且字母为abcd

的示例
let len = 3
let alphabet = "abcd".characters.map({ String($0) })
var allStrings = [String]()
let maxIndex = alphabet.endIndex
var indicies = Array(count: len, repeatedValue: 0)

outerLoop: while (true) {
    // Generate string from indicies
    var string = ""
    for i in indicies {
        let letter = alphabet[i]
        string += letter
    }
    allStrings.append(string)
    print("Adding \(string)")

    // Increment the index
    indicies[0] += 1

    var idx = 0
    // If idx overflows then (idx) = 0 and (idx + 1) += 1 and try next
    while (indicies[idx] == maxIndex) {
        // Reset current
        indicies[idx] = 0
        // Increment next (as long as we haven't hit the end done)
        idx += 1
        if (idx >= alphabet.endIndex - 1) {
            print("Breaking outer loop")
            break outerLoop
        }
        indicies[idx] += 1
    }
}

print("All Strings: \(allStrings)")

答案 2 :(得分:1)

替代递归;循环基数表示增量(重复)遍历您的字母

递归的替代方法是循环使用字母表的数字表示,使用代表不同数量字母的基数代表。这种方法的一个限制是echo "$GREEN$(ifconfig en0 | awk '/inet / {print $2}')$NC" 初始化程序最多允许base36数(基数36),即,你最多可以执行你的密码破解"使用一组具有唯一计数< = 36的字符。

帮助功能

String(_:,radix:)

主要基数暴力密码检查方法

// help function to use to pad incremental alphabeth cycling to e.g. "aa..."
let padToTemplate: (str: String, withTemplate: String) -> String = {
    return $0.characters.count < $1.characters.count
        ? String($1.characters.suffixFrom($0.characters.endIndex)) + $0
        : $0
}

使用示例

示例用法#1

// attempt brute-force attempts to crack isCorrectPassword closure
// for a given alphabet, suspected word length and for a maximum number of 
// attempts, optionally with a set starting point
func bruteForce(isCorrectPassword: (String) -> Bool, forAlphabet alphabet: [Character], forWordLength wordLength: Int, forNumberOfAttempts numAttempts: Int, startingFrom start: Int = 0) -> (Int, String?) {

    // remove duplicate characters (but preserve order)
    var exists: [Character:Bool] = [:]
    let uniqueAlphabet = Array(alphabet.filter { return exists.updateValue(true, forKey: $0) == nil })

    // limitation: allows at most base36 radix
    guard case let radix = uniqueAlphabet.count
        where radix < 37 else {
        return (-1, nil)
    }

    // begin brute-force attempts
    for i in start..<start+numAttempts {
        let baseStr = String(i, radix: radix).characters
            .flatMap { Int(String($0), radix: radix) }
            .map { String(uniqueAlphabet[$0]) }
            .joinWithSeparator("")

        // construct attempt of correct length
        let attempt = padToTemplate(str: baseStr,
            withTemplate: String(count: wordLength, repeatedValue: alphabet.first!))

        // log
        //print(i, attempt)

        // test attempt
        if isCorrectPassword(attempt) { return (i, attempt) }
    }
    return (start+numAttempts, nil) // next to test
}

示例用法#2(选择一个失败&#34;批次&#34;另一个)

// unknown content closure
let someHashBashing : (String) -> Bool = {
    return $0 == "ask"
}

// setup alphabet
let alphabet = [Character]("abcdefghijklmnopqrstuvwxyz".characters)

// any success for 500 attempts?
if case (let i, .Some(let password)) =
    bruteForce(someHashBashing, forAlphabet: alphabet,
               forWordLength: 3, forNumberOfAttempts: 500) {
    print("Password cracked: \(password) (attempt \(i))")
} /* Password cracked: ask (attempt 478) */