2017-05-18 97 views
4

我试图把一个十六进制字符串,并插入所有其他角色之间的连字符(如“b201a968”到“b2-01-a9-68”)。我发现有几种方法可以做到这一点,但问题是我的字符串相当大(8066个字符),而且我能够最快速地运行它仍然需要几秒钟的时间。这些是我尝试过的方式,以及他们正在接受的时间。任何人都可以帮我优化这个功能吗?优化加入破折号长斯威夫特字符串

//42.68 seconds 
    func reformatDebugString(string: String) -> String 
    { 
     var myString = string 
     var index = 2 
     while(true){ 
      myString.insert("-", at: myString.index(myString.startIndex, offsetBy: index)) 
      index += 3 
      if(index >= myString.characters.count){ 
       break 
      } 
     } 

     return myString 
    } 

//21.65 seconds 
    func reformatDebugString3(string: String) -> String 
    { 
     var myString = "" 
     let length = string.characters.count 
     var first = true 
     for i in 0...length-1{ 
      let index = string.index(myString.startIndex, offsetBy: i) 
      let c = string[index] 

      myString += "\(c)" 
      if(!first){ 
       myString += "-" 
      } 
      first = !first 
     } 

     return myString 
    } 

//11.37 seconds 
    func reformatDebugString(string: String) -> String 
    { 
     var myString = string 
     var index = myString.characters.count - 2 
     while(true){ 
      myString.insert("-", at: myString.index(myString.startIndex, offsetBy: index)) 
      index -= 2 
      if(index == 0){ 
       break 
      } 
     } 

     return myString 
    } 
+0

这似乎是一个总理我们并行化的情况 - 尝试dispatch_apply,也许。 –

回答

2

正如,你应该避免这两个东西:

  • 计算每个指标与string.index(string.startIndex, offsetBy: ...)
  • 修改大字符串insert(_:at:)

所以,这可以是另一个方式:

func reformatDebugString4(string: String) -> String { 
    var result = "" 

    var currentIndex = string.startIndex 
    while currentIndex < string.endIndex { 
     let nextIndex = string.index(currentIndex, offsetBy: 2, limitedBy: string.endIndex) ?? string.endIndex 
     if currentIndex != string.startIndex { 
      result += "-" 
     } 
     result += string[currentIndex..<nextIndex] 
     currentIndex = nextIndex 
    } 

    return result 
} 
+0

好主意切分中间人物 - 这比我的速度快(2.08s和3.06s在快速基准测试中)。 – Hamish

+0

@Hamish,感谢您的评价。在我的旧款MacBook上,我无法找到与您的MacBook显着不同的区别。但是在某些情况下'enumerated()'的开销可能会更大,所以值得尝试避免它。 – OOPer

+0

在这种情况下,使用'enumerated()'实际上对性能没有任何明显的影响 - 我期望这一点,因为编译器应该能够将其优化为仅仅是一个简单的递增值循环。速度提升你的实现看起来主要来自中间字符的切片(做两个单独的附加操作会再次减慢它)。 – Hamish

7

与所有你的三个方法的问题是,为了获得当前字符的索引在循环使用index(_:offsetBy:)。这是一个O(n)运算,其中n是偏移的距离 - 因此,所有三个函数都以二次方式运行。

此外,为了解决方案#1和#3,将插入到所得的字符串是一个为O​​(n)操作中,当将插入点之后的所有字符都被向上移动以容纳附加字符。在这种情况下,从头开始构建字符串通常会更便宜,因为我们可以在字符串的末尾添加给定字符,如果字符串具有足够的容量,则为O(1),否则为O(n)。

也为解决方案#1,说myString.characters.count是O(n)的操作,所以你要在循环的每次迭代做不是。

所以,我们要从头开始构建的字符串,并避免索引和计算循环内的字符数。下面是这样做的一种方式:

extension String { 

    func addingDashes() -> String { 

     var result = "" 

     for (offset, character) in characters.enumerated() { 

      // don't insert a '-' before the first character, 
      // otherwise insert one before every other character. 
      if offset != 0 && offset % 2 == 0 { 
       result.append("-") 
      } 

      result.append(character) 
     } 
     return result 
    } 
} 

// ... 

print("b201a968".addingDashes()) // b2-01-a9-68 

您的最佳解决方案(#3)在一份新闻稿中构建了我的电脑上37.79s,上面的方法了0.023s。在哈米什的答案已经指出

+0

简单,优雅,快速。 EZ PZ。 – NSGangster

+0

@Hamish,我在下面的链接中分享了我的Xcode操场。在我的系统中,我的解决方案需要12.6秒左右,你的时间是20.5秒,而OOper的时间约为15.3秒。你可以试试你的系统吗? https://drive.google.com/open?id=0Bz423-2RSnjHMzlGbWNpQy1LSG8 –

+0

@RyanTensmeyer不要使用游乐场 - 他们真的越野车和不可靠的。如果你的代码放到一个实际的项目(MacOS的命令行工具模板是一个很好的地方去),你应该可以看到正确的结果(另外,这是最好的标杆发布版本,以占编译器的优化 - 而还要确保编译器不会完全优化该方法)。 [这是我的基准代码](https://gist.github.com/hamishknight/d1e983e0445084cce314a50333a4d32e)。 – Hamish