关于ios:将两个字符串转换为布尔数组的快速方法是什么?

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" }

但这比我想要的慢很多。我的分析表明,map正是减速的地方,但我不确定我能做到多简单。

我觉得如果没有斯威夫特/奥比奇的头顶,这会很快。在C语言中,我认为这是一个简单的for循环,在这个循环中,将一个字节的内存与一个常量进行比较,但我不确定应该看什么函数或语法。

有什么方法可以更快地做到这一点吗?

更新:

我也试过了

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
}

更新:input ="010101011010101001000100000011010101010101010101"

0.0741/0.0087,这种方法比作者的速度快8.46倍。数据相关性越大,则越为正。

另外,使用nulTerminatedUTF8速度稍微提高了一点,但并不总是比算法A的速度高:

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秒

Algorithm comparison graph


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

这应该很快。试试看。对于010101011010101001000100000011010101010101010101,需要0.039秒。


我想这是尽可能快的:

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)

…特别贵。


这应该比enumerate() where char =="1"版本快一点(50万个零一交替的0.557s,而Diamiax的1.159s算法‘a’)。

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