What is a fast way to convert a string of two characters to an array of booleans?
我有一个很长的字符串(有时超过1000个字符),我想将其转换为布尔值数组。它需要做很多次,非常快。
1 2 | let input: String ="001" let output: [Bool] = [false, false, true] |
我天真的尝试是这样的:
1 | input.characters.map { $0 =="1" } |
但这比我想要的慢很多。我的分析表明,
我觉得如果没有斯威夫特/奥比奇的头顶,这会很快。在C语言中,我认为这是一个简单的
有什么方法可以更快地做到这一点吗?
更新:
我也试过了
1 2 3 4 | output = [] for char in input.characters { output.append(char =="1") } |
速度快了15%。我希望更多。
这是更快的:
1 2 3 4 5 6 | // Algorithm 'A' let input ="0101010110010101010" var output = Array<Bool>(count: input.characters.count, repeatedValue: false) for (index, char) in input.characters.enumerate() where char =="1" { output[index] = true } |
更新:
0.0741/0.0087,这种方法比作者的速度快8.46倍。数据相关性越大,则越为正。
另外,使用
1 2 3 4 5 6 | // Algorithm 'B' let input ="10101010101011111110101000010100101001010101" var output = Array<Bool>(count: input.nulTerminatedUTF8.count, repeatedValue: false) for (index, code) in input.nulTerminatedUTF8.enumerate() where code == 49 { output[index] = true } |
结果图显示,输入长度2196,其中第一个和最后一个0..1,A–第二个,B–第三个点。A:0.311秒,B:0.304秒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import Foundation let input:String ="010101011001010101001010101100101010100101010110010101010101011001010101001010101100101010100101010101011001010101001010101100101010100101010" var start = clock() var output = Array<Bool>(count: input.nulTerminatedUTF8.count, repeatedValue: false) var index = 0 for val in input.nulTerminatedUTF8 { if val != 49 { output[index] = true } index+=1 } var diff = clock() - start; var msec = diff * 1000 / UInt(CLOCKS_PER_SEC); print("Time taken \(Double(msec)/1000.0) seconds \(msec%1000) milliseconds"); |
这应该很快。试试看。对于
我想这是尽可能快的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | let targ = Character("1") let input: String ="001" // your real string goes here let inputchars = Array(input.characters) var output:[Bool] = Array.init(count: inputchars.count, repeatedValue: false) inputchars.withUnsafeBufferPointer { inputbuf in output.withUnsafeMutableBufferPointer { outputbuf in var ptr1 = inputbuf.baseAddress var ptr2 = outputbuf.baseAddress for _ in 0..<inputbuf.count { ptr2.memory = ptr1.memory == targ ptr1 = ptr1.successor() ptr2 = ptr2.successor() } } } // output now contains the result |
原因是,由于使用了缓冲区指针,我们只是在连续内存中循环,就像通过增加C数组的指针循环C数组一样。因此,一旦我们通过了初始设置,这应该和在C中一样快。
在实际测试中,操作的原始方法与此方法之间的时间差是
1 | 13.3660290241241 |
和
1 | 0.219357967376709 |
这是一个非常戏剧性的加速。但是,我很快补充说,我已经从计时测试中排除了初始设置。这条线:
1 | let inputchars = Array(input.characters) |
…特别贵。
这应该比
1 2 3 4 5 6 7 | let input = inputStr.utf8 let n = input.count var output = [Bool](count: n, repeatedValue: false) let one = UInt8(49) // 1 for (idx, char) in input.enumerate() { if char == one { output[idx] = true } } |
但它的可读性也差得多;-p
编辑:两个版本都比map变量慢,也许你忘了用优化进行编译?
我需要做一些测试来确定,但是我认为许多方法(包括原始映射)的一个问题是,它们需要迭代字符串来计算字符数,然后第二次实际处理字符。
你试过了吗?
1 | let output = [Bool](input.characters.lazy.map { $0 =="1" }) |
这可能只进行一次迭代。
另一个可以加快速度的方法是,如果可以避免使用字符串,而是使用适当编码的字符数组(尤其是更固定大小的单位(如UTF16或ASCII)。然后,长度查找将是O(1)而不是O(n),迭代也可能更快。
btw总是在启用乐观主义者的情况下测试性能,而从不在操场上测试,因为性能特征完全不同,有时是100倍。
更实用的款式怎么样?它不是最快的(47毫秒),今天,当然…
1 2 3 4 5 6 7 8 | import Cocoa let start = clock() let bools = [Bool](([Character] ("010101011001010101001010101100101010100101010110010101010101011001010101001010101100101010100101010101011001010101001010101100101010100101010".characters)).map({$0 =="1"})) let msec = (clock() - start) * 1000 / UInt(CLOCKS_PER_SEC); print("Time taken \(Double(msec)/1000.0) seconds \(msec%1000) milliseconds"); |
使用EDCOX1×1来检索原始EDCOX1×2。迭代并比较49(EDCOX1的ASCII值为3)。
另外一步应该加快速度。在循环开始之前,使用EDCOX1 0将调整数组大小,而不是试图在循环运行时执行数组。
1 2 3 4 5 | var output = [Bool]() output.reserveCapacity(input.characters.count) for char in input.characters { output.append(char =="1") } |