2017-05-18 73 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