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

时间:2016-03-18 00:32:23

标签: ios swift

我有一个长字符串(有时超过1000个字符),我想将其转换为布尔值数组。它需要很快地完成这项工作。

let input: String = "001"
let output: [Bool] = [false, false, true]

我天真的尝试是这样的:

input.characters.map { $0 == "1" }

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

我觉得如果没有Swift&ObjC的开销,这将会很快。在C中,我认为这是一个简单的for循环,其中一个字节的内存与一个常量进行比较,但我不确定我应该看到的函数或语法是什么。

有没有办法更快地完成这项工作?

更新

我也试过了

output = []
for char in input.characters {
    output.append(char == "1")
}

它的速度提高了约15%。我希望能有更多的东西。

8 个答案:

答案 0 :(得分:12)

这更快:

// 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

// 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 :(得分:5)

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秒。

答案 2 :(得分:1)

这应该比[[255 255 255 106 214 113 73 224 83 164 68 195] [255 255 255 130 99 75 35 158 126 153 133 135] [255 255 255 152 79 15 7 101 175 68 45 71] [106 254 55 97 238 15 85 243 81 249 77 86]] [[255 255 255 106 214 113 73 224 83 164 68 195] [255 255 255 130 99 75 35 158 126 153 133 135] [255 255 255 152 79 15 7 101 175 68 45 71] [106 254 55 97 238 15 85 243 81 249 77 86]] Round-trip result: the arrays are equal. 版本快一点(对于500_000个交替的0和0而言为0.557s而对于来自diampiax的1.159s算法'A')

enumerate() where char == "1"

但它的可读性也差得多;-p

编辑: 两个版本都比地图版本慢,可能你忘了用优化编译?

答案 3 :(得分:1)

我猜这是尽可能快的:

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中一样快。

编辑在实际测试中,OP原始方法与此方法之间的时差是

之间的差异
13.3660290241241

0.219357967376709

这是一个相当戏剧性的加速。但是,我赶紧补充说,我已经排除时间测试的初始设置。这一行:

let inputchars = Array(input.characters)

......特别贵。

答案 4 :(得分:0)

还有一步应该加快速度。使用new_dataframe = df['A'].to_frame() new_dataframe.head() from pyspark.sql import SQLContext spDF = sqlContext.createDataFrame(new_dataframe) spDF.show() 将在循环开始之前调整一次数组的大小,而不是在循环运行时尝试执行此操作。

reserveCapacity

答案 5 :(得分:0)

使用withCString(_:)检索原始UnsafePointer<Int8>。迭代并比较49("1"的ascii值)。

答案 6 :(得分:0)

更实用的风格怎么样?它不是最快的(47毫秒),今天,肯定......

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

答案 7 :(得分:0)

我需要进行一些测试以确定,但我认为包括原始地图在内的许多方法存在的一个问题是,他们需要迭代字符串来计算字符数,然后第二次实际处理字符。

你试过了吗?

let output = [Bool](input.characters.lazy.map { $0 == "1" })

这可能只进行一次迭代。

另一件可以加快速度的事情是,如果你可以避免使用字符串,而是使用适当编码的字符数组(特别是如果是更固定大小的单位(例如UTF16或ASCII)。那么长度查找将是O (1)而不是O(n),迭代也可能更快

BTW始终在启用优化器的情况下测试性能,从不在Playground中测试性能,因为性能特征完全不同,有时是100倍。