Generating a random number in Swift - swift

I read in https://www.hackingwithswift.com/read/35/2/generating-random-numbers-in-ios-8-and-earlier that the best way to generate a random number is to use
let r = arc4random_uniform(UInt32(_names.count))
let name : String = _names[Int(r)]
but it seems odd that I have to cast twice to be able to get a random number, what should I do to avoid casting?

It really depends on how much casting you want to avoid. You could simply wrap it in a function:
func random(max maxNumber: Int) -> Int {
return Int(arc4random_uniform(UInt32(maxNumber)))
}
So then you only have to do the ugly casting once. Everywhere you want a random number with a maximum number:
let r = random(max: _names.count)
let name: String = _names[r]
As a side note, since this is Swift, your properties don't need _ in front of them.

I really like using this extension
extension Int {
init(random range: Range<Int>) {
let offset: Int
if range.startIndex < 0 {
offset = abs(range.startIndex)
} else {
offset = 0
}
let min = UInt32(range.startIndex + offset)
let max = UInt32(range.endIndex + offset)
self = Int(min + arc4random_uniform(max - min)) - offset
}
}
Now you can generate a random Int indicating the range
let a = Int(random: 1...10) // 3
let b = Int(random: 0..<10) // 6
let c = Int(random: 0...100) // 31
let d = Int(random: -10...3) // -4

Swift 4.2 and above
You can initial a random element:
let randomInt = Int.random(in: 0...1)
let randomDouble = Double.random(in: 0...1)
Also You can take a random element from a collection like
let randomInt = (0...9).randomElement()
Also there is another handy method that you can use on array for shuffling that you may need:
let shuffled = [1, 2, 3, 4].shuffled()

you can use gameplaykit
let random = GKRandomDistribution(lowestValue: 0, highestValue: 100)
let r = random.nextInt()

Modified answer from Luca written as extension in Swift 4
/// Returns random number within given range, upper bound included, eg. -1...0 = [-1, 0, 1]
extension CountableClosedRange where Bound == Int
{
var random: Int
{
let range = self
let offset: Int = range.lowerBound < 0 ? abs(range.lowerBound) : 0
let min = UInt32(range.lowerBound + offset)
let max = UInt32(range.upperBound + offset)
let randomNumber = Int(min + arc4random_uniform(max - min + 1)) - offset
return randomNumber
}
}
/// Returns random number within given range, upper bound not included, eg. -1...0 = [-1, 0]
extension CountableRange where Bound == Int
{
var random: Int
{
let range = self
let offset: Int = range.lowerBound < 0 ? abs(range.lowerBound) : 0
let min = UInt32(range.lowerBound + offset)
let max = UInt32(range.upperBound + offset)
let randomNumber = Int(min + arc4random_uniform(max - min)) - offset
return randomNumber
}
}
Examples:
(0...10).random
(0..<10).random

Or you could use
let name : String = _names[ Int(arc4random()) % _names.count ]

Related

How can I return a float or double from normal distribution in Swift 4?

I am trying to return a float or a double from a normal distribution with mean = 0 and standard deviation = 4 in Swift4. The closest I can come to getting what I need is by using GameplayKit -> GKGaussianDistribution as implemented in the code below:
func generateForecast() {
let gauss = GKGaussianDistribution(randomSource: self.source, mean: 0.0, deviation: 4.0)
self.epsilon = gauss.nextInt()
}
My problem is when I call
gauss.nextInt()
I obviously get an integer. And when I try
gauss.nextUniform()
I get a number between -1 and 1.
Is there a fairly simple way to return a float or double from a normal distribution in Swift4 instead of an Int or a float between -1 and 1?
import AppKit
import PlaygroundSupport
import GameplayKit
let nibFile = NSNib.Name(rawValue:"MyView")
var topLevelObjects : NSArray?
Bundle.main.loadNibNamed(nibFile, owner:nil, topLevelObjects: &topLevelObjects)
let views = (topLevelObjects as! Array<Any>).filter { $0 is NSView }
// Present the view in Playground
PlaygroundPage.current.liveView = views[0] as! NSView
let s = 0.001
var auto_corr: [Int] = []
class Market {
var numAgents: Int
var traders: [Agent] = []
var price: Double
var epsilon: Int
var priceHist: [Double] = []
var returnHist: [Double] = []
var returnRealHist: [Double] = []
var logReturn: Double = 0
var realReturn: Double = 0
let source = GKRandomSource()
init(numAgents: Int, price: Double, epsilon: Int) {
self.numAgents = numAgents
self.price = price
self.epsilon = epsilon
for _ in 1...numAgents {
self.traders.append(Agent(phi: 1, theta: 1))
}
}
func generateForecast() {
let gauss = GKGaussianDistribution(randomSource: self.source, mean: 0.0, deviation: 4.0)
self.epsilon = gauss.nextInt()
}
}
The documentation for GKGaussianDistribution does not mention that it overrides nextUniform() from the base class so don't assume it will return normally distributed values for you:
You can roll your own Gaussian Distribution using the Box-Muller Transformation:
class MyGaussianDistribution {
private let randomSource: GKRandomSource
let mean: Float
let deviation: Float
init(randomSource: GKRandomSource, mean: Float, deviation: Float) {
precondition(deviation >= 0)
self.randomSource = randomSource
self.mean = mean
self.deviation = deviation
}
func nextFloat() -> Float {
guard deviation > 0 else { return mean }
let x1 = randomSource.nextUniform() // a random number between 0 and 1
let x2 = randomSource.nextUniform() // a random number between 0 and 1
let z1 = sqrt(-2 * log(x1)) * cos(2 * Float.pi * x2) // z1 is normally distributed
// Convert z1 from the Standard Normal Distribution to our Normal Distribution
return z1 * deviation + mean
}
}
I intentionally did not subclass it from GKRandomDistribution since there are other methods I need to override but are not relevant to this question.

Rotate Swift Array

I am trying to solve this question I found on a coding challenge website using Swift 3.
I'm sure most of you have seen it before, but in case you haven't here it is...
The idea is you take a string and rotate it x number of times. So using their example "12345" rotated 2x would be "34512"
I wrote this, but it when I print it out in Playground it just prints out the exact same string I entered.
func rotateSring(originalString: String, numberOfRotations: Int) -> String {
var tempArray: [String] = []
tempArray.append(originalString)
let count = numberOfRotations
for _ in 1...count {
for letter in tempArray {
tempArray.remove(at: 0)
tempArray.append(letter)
}
}
let newString = tempArray.joined(separator: "")
return newString
}
I also tried a variation
func rotateSring(originalString: String, numberOfRotations: Int) -> String {
var tempArray: [String] = []
tempArray.append(originalString)
let count = numberOfRotations
for _ in 1...count {
let test =tempArray.remove(at: 0)
tempArray.append(test)
}
let newString = tempArray.joined(separator: "")
return newString
}
Neither produce the desired result when I say
let testRun = rotateSring(originalString: "12345", numberOfRotations: 2)
I would like the "34512" but instead I get "12345"
If I had to guess what I am doing wrong, I think that I am just rotating the entire array from start to finish so it does move but it moves full circle.
If somebody could explain what I am doing wrong, and how I can fix it that would be great. Thank you
I have gone through your solution and found few mistakes. The below implementation will work.
func rotateSring(originalString: String, numberOfRotations: Int) -> String {
var tempArray: [Character] = Array(originalString.characters)
let count = numberOfRotations
for _ in 1...count {
let letter = tempArray.removeFirst()
tempArray.append(letter)
}
let newString = String(tempArray)
return newString
}
let testRun = rotateSring(originalString: "12345", numberOfRotations: 2)
Now let me explain the changes:
var tempArray: [String] = []
tempArray.append(originalString)
// to
var tempArray: [Character] = Array(originalString.characters)
In Swift, String doesn't conform to Sequence type protocol and so you need to use Character array and so when you were trying to loop over letters, you were actually looping over the whole string i.e. 12345.
// tempArray = ["12345"] not ["1", "2", "3", "4", "5"]
for letter in tempArray {
tempArray.remove(at: 0)
tempArray.append(letter)
}
func rotateSring(originalString: String, numberOfRotations: UInt) -> String {
if numberOfRotations == 0 {
return originalString
}
return rotateSring(originalString: originalString[originalString.index(after: originalString.startIndex)..<originalString.endIndex] + String(originalString[originalString.startIndex]),
numberOfRotations: numberOfRotations - 1)
}
The native String's padding function can do that for you quite efficiently :
let string = "12345"
let rotation = 2
let rotated = "".padding(toLength: string.characters.count, withPad: string, startingAt: rotation % string.characters.count)
if you also need to support negative rotation values, you simply need to calculate the appropriate positive offset:
let string = "12345"
let rotation = -3
let offset = ( rotation % string.characters.count + string.characters.count ) % string.characters.count
let rotated = "".padding(toLength: string.characters.count, withPad: string, startingAt: offset)
What you are doing wrongly in both tries is that you used a [String] with only one element in it - originalString. So when you remove the element at index 0, the array becomes empty.
Here is a solution of mine:
func rotateSring(originalString: String, numberOfRotations: Int) -> String {
var str = originalString
for _ in 0..<numberOfRotations {
let firstChar = str.characters.first! // temporarily store the first char
var c = str.characters.dropFirst() // remove the first char from the string
c.append(firstChar) // add the first char back to the end
str.characters = c
}
return str
}
Rotation by using substring(to:) and substring(from:)
The accepted answer have already covered fixing your own solution; I'll pitch in with another alternative, making use of the substring(to:) and substring(from:) methods of String to rotate a given string a supplied number of characters. The supplied String will be left-rotated (<- shift) for positive rotation numbers, and right-rotated for (-> shift) for negative numbers.
// "left-rotate" supplied string using substring concenation
// (negative supplied rotations will be applied as "right-rotations")
func rotateString(originalString: String, numberOfRotations: Int) -> String {
// rotation is a non-changing operation upon empty or single-character strings
guard case let charCount = originalString.characters.count,
charCount > 1 else { return originalString }
// remove redundant full cycle rotation, and change rotation
// direction (left -> right) in case the supplied rotations are negative.
let numberOfRotations = numberOfRotations % charCount
+ (numberOfRotations < 0 ? 1 : 0) * charCount
// use substring methods to construct the "rotated" String
if numberOfRotations != 0 {
let splitIndex = originalString
.index(originalString.startIndex, offsetBy: numberOfRotations)
return originalString.substring(from: splitIndex) +
originalString.substring(to: splitIndex)
}
return originalString
}
Example usage:
let str = "1πŸ‡―πŸ‡΅345"
// left rotations
print(rotateString(originalString: str, numberOfRotations: 1)) // πŸ‡―πŸ‡΅3451
print(rotateString(originalString: str, numberOfRotations: 2)) // 3451πŸ‡―πŸ‡΅
print(rotateString(originalString: str, numberOfRotations: 6)) // πŸ‡―πŸ‡΅3451
// right rotations
print(rotateString(originalString: str, numberOfRotations: -2)) // 451πŸ‡―πŸ‡΅3
print(rotateString(originalString: str, numberOfRotations: -6)) // 51πŸ‡―πŸ‡΅34
// no rotations (/ only full cycles)
print(rotateString(originalString: str, numberOfRotations: 5)) // 1πŸ‡―πŸ‡΅345
print(rotateString(originalString: str, numberOfRotations: -5)) // 1πŸ‡―πŸ‡΅345
print(rotateString(originalString: str, numberOfRotations: 0)) // 1πŸ‡―πŸ‡΅345
Or, as a String extension:
extension String {
func rotated(by numberOfRotations: Int) -> String {
guard case let charCount = characters.count,
charCount > 1 else { return self }
let numberOfRotations = numberOfRotations % charCount
+ (numberOfRotations < 0 ? 1 : 0) * charCount
if numberOfRotations != 0 {
let splitIndex = index(startIndex, offsetBy: numberOfRotations)
return substring(from: splitIndex) + substring(to: splitIndex)
}
return self
}
}
/* example usage */
let str = "1πŸ‡―πŸ‡΅345"
// left rotations
print(str.rotated(by: 1)) // πŸ‡―πŸ‡΅3451
print(str.rotated(by: 2)) // 3451πŸ‡―πŸ‡΅
print(str.rotated(by: 6)) // πŸ‡―πŸ‡΅3451
// right rotations
print(str.rotated(by: -2)) // 451πŸ‡―πŸ‡΅3
print(str.rotated(by: -6)) // 51πŸ‡―πŸ‡΅34
// no rotations (/ only full cycles)
print(str.rotated(by: 5)) // 1πŸ‡―πŸ‡΅345
print(str.rotated(by: -5)) // 1πŸ‡―πŸ‡΅345
print(str.rotated(by: 0)) // 1πŸ‡―πŸ‡΅345
code
extension String {
mutating func rotate(by count_: Int) {
guard count_ != 0 else { return }
if count_ < 0 {
let count = -count_ % self.count
let tailRange = index(endIndex, offsetBy: -count)..<endIndex
let tail = String(self[tailRange])
removeSubrange(tailRange)
self = tail + self
} else {
let count = count_ % self.count
let headRange = startIndex..<index(startIndex, offsetBy: count)
let head = String(self[headRange])
removeSubrange(headRange)
self = self + head
}
}
mutating func rotated(by count: Int) -> String {
rotate(by: count)
return self
}
}
Although it's not the most performant way, you can solve this with a recursive function as well:
mutating func rotate(by count_: Int) {
guard count_ >= 0 else { return rotate(by: count - (-count_ % count)) }
let count = count_ % self.count
let headRange = startIndex..<index(startIndex, offsetBy: count)
let head = String(self[headRange])
removeSubrange(headRange)
self = self + head
}
usage
var english = "ABCDEFGHIJ"
var hangeul = "ㅁλͺ¨λͺΈλ§ˆλ§˜λ«„λ«”"
english.rotate(by: 3)
print(english) //DEFGHIJABC
print(hangeul.rotated(by: 1)) //λͺ¨λͺΈλ§ˆλ§˜λ«„뫔ㅁ

In Swift, how to modify a character in string with subscript?

Like in C, we can simply do
str[i] = str[j]
But how to write the similar logic in swift?
Here is my code, but got error:
Cannot assign through subscript: subscript is get-only
let indexI = targetString.index(targetString.startIndex, offsetBy: i)
let indexJ = targetString.index(targetString.startIndex, offsetBy: j)
targetString[indexI] = targetString[indexJ]
I know it may work by using this method, but it's too inconvenient
replaceSubrange(, with: )
In C, a string (char *) can be treated as an array of characters. In Swift, you can convert the String to an [Character], do the modifications you want, and then convert the [Character] back to String.
For example:
let str = "hello"
var strchars = Array(str)
strchars[0] = strchars[4]
let str2 = String(strchars)
print(str2) // "oello"
This might seem like a lot of work for a single modification, but if you are moving many characters this way, you only have to convert once each direction.
Reverse a String
Here's an example of a simple algorithm to reverse a string. By converting to an array of characters first, this algorithm is similar to the way you might do it in C:
let str = "abcdefg"
var strchars = Array(str)
var start = 0
var end = strchars.count - 1
while start < end {
let temp = strchars[start]
strchars[start] = strchars[end]
strchars[end] = temp
start += 1
end -= 1
}
let str2 = String(strchars)
print(str2) // "gfedcba"
Dealing with String with Swift is major pain in the a**. Unlike most languages I know that treat string as an array of characters, Swift treats strings as collection of extended grapheme clusters and the APIs to access them is really clumsy. Changes are coming in Swift 4 but that manifesto lost me about 10 paragraphs in.
Back to your question... you can replace the character like this:
var targetString = "Hello world"
let i = 0
let j = 1
let indexI = targetString.index(targetString.startIndex, offsetBy: i)
let indexJ = targetString.index(targetString.startIndex, offsetBy: j)
targetString.replaceSubrange(indexI...indexI, with: targetString[indexJ...indexJ])
print(targetString) // eello world
I was quite shocked as well by the fact that swift makes string indexing so damn complicated. For that reason, I have built some string extensions that enable you to retrieve and change parts of strings based on indices, closed ranges, and open ranges, PartialRangeFrom, PartialRangeThrough, and PartialRangeUpTo. You can download the repository I created here
You can also pass in negative numbers in order to access characters from the end backwards.
public extension String {
/**
Enables passing in negative indices to access characters
starting from the end and going backwards.
if num is negative, then it is added to the
length of the string to retrieve the true index.
*/
func negativeIndex(_ num: Int) -> Int {
return num < 0 ? num + self.count : num
}
func strOpenRange(index i: Int) -> Range<String.Index> {
let j = negativeIndex(i)
return strOpenRange(j..<(j + 1), checkNegative: false)
}
func strOpenRange(
_ range: Range<Int>, checkNegative: Bool = true
) -> Range<String.Index> {
var lower = range.lowerBound
var upper = range.upperBound
if checkNegative {
lower = negativeIndex(lower)
upper = negativeIndex(upper)
}
let idx1 = index(self.startIndex, offsetBy: lower)
let idx2 = index(self.startIndex, offsetBy: upper)
return idx1..<idx2
}
func strClosedRange(
_ range: CountableClosedRange<Int>, checkNegative: Bool = true
) -> ClosedRange<String.Index> {
var lower = range.lowerBound
var upper = range.upperBound
if checkNegative {
lower = negativeIndex(lower)
upper = negativeIndex(upper)
}
let start = self.index(self.startIndex, offsetBy: lower)
let end = self.index(start, offsetBy: upper - lower)
return start...end
}
// MARK: - Subscripts
/**
Gets and sets a character at a given index.
Negative indices are added to the length so that
characters can be accessed from the end backwards
Usage: `string[n]`
*/
subscript(_ i: Int) -> String {
get {
return String(self[strOpenRange(index: i)])
}
set {
let range = strOpenRange(index: i)
replaceSubrange(range, with: newValue)
}
}
/**
Gets and sets characters in an open range.
Supports negative indexing.
Usage: `string[n..<n]`
*/
subscript(_ r: Range<Int>) -> String {
get {
return String(self[strOpenRange(r)])
}
set {
replaceSubrange(strOpenRange(r), with: newValue)
}
}
/**
Gets and sets characters in a closed range.
Supports negative indexing
Usage: `string[n...n]`
*/
subscript(_ r: CountableClosedRange<Int>) -> String {
get {
return String(self[strClosedRange(r)])
}
set {
replaceSubrange(strClosedRange(r), with: newValue)
}
}
/// `string[n...]`. See PartialRangeFrom
subscript(r: PartialRangeFrom<Int>) -> String {
get {
return String(self[strOpenRange(r.lowerBound..<self.count)])
}
set {
replaceSubrange(strOpenRange(r.lowerBound..<self.count), with: newValue)
}
}
/// `string[...n]`. See PartialRangeThrough
subscript(r: PartialRangeThrough<Int>) -> String {
get {
let upper = negativeIndex(r.upperBound)
return String(self[strClosedRange(0...upper, checkNegative: false)])
}
set {
let upper = negativeIndex(r.upperBound)
replaceSubrange(
strClosedRange(0...upper, checkNegative: false), with: newValue
)
}
}
/// `string[...<n]`. See PartialRangeUpTo
subscript(r: PartialRangeUpTo<Int>) -> String {
get {
let upper = negativeIndex(r.upperBound)
return String(self[strOpenRange(0..<upper, checkNegative: false)])
}
set {
let upper = negativeIndex(r.upperBound)
replaceSubrange(
strOpenRange(0..<upper, checkNegative: false), with: newValue
)
}
}
}
Usage:
let text = "012345"
print(text[2]) // "2"
print(text[-1] // "5"
print(text[1...3]) // "123"
print(text[2..<3]) // "2"
print(text[3...]) // "345"
print(text[...3]) // "0123"
print(text[..<3]) // "012"
print(text[(-3)...] // "345"
print(text[...(-2)] // "01234"
All of the above works with assignment as well. All subscripts have getters and setters.
a new extension added,
since String conforms to BidirectionalCollection Protocol
extension String{
subscript(at i: Int) -> String? {
get {
if i < count{
let idx = index(startIndex, offsetBy: i)
return String(self[idx])
}
else{
return nil
}
}
set {
if i < count{
let idx = index(startIndex, offsetBy: i)
remove(at: idx)
if let new = newValue, let first = new.first{
insert(first, at: idx)
}
}
}
}
}
call like this:
var str = "fighter"
str[at: 2] = "6"

rawValue ++max is deprecated swift 3

I have this problem I can not solve. paste my code
static let count: Int = {
var max: Int = 0
while let _ = PDFList(rawValue: ++max) {}
return max
}()
}
I tried to write this but it does not work
rawValue: (max += 1)) {}
if you help me please. Thank you
+= is not an expression like ++, it's a statement. That is, it doesn't evaluate to a value that can be used in assignment, as a parameter, etc.
You have to split it apart, then use max directly:
static let count: Int = {
var max = 0
while let _ = PDFList(rawValue: max) { max += 1 }
return max
}()
This is more clear, IMO:
static let count: Int = {
var max = 0
while PDFList(rawValue: max) != nil { max += 1 }
return max
}()
The functional way to do this is:
static let count: Int = {
let max = (1 ... Int.max)
.first { PDFList(rawValue: $0) == nil }
return max!
}()
The original code never checks 0 and I am not sure whether that's correct. If it's not correct, change the lower bound of the range to 0.

swift string advancedBy is slow?

So I am writing in swift to practice some online judge.
Here's the issue: Longest Palindromic Substring
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
So I am using dp to solve it in swift:
class Solution {
func longestPalindrome(s: String) -> String {
var hash = Array(count: s.characters.count, repeatedValue: Array(count: s.characters.count, repeatedValue: false))
for i in 0 ..< s.characters.count {
hash[i][i] = true
}
var maxStart = 0
var maxEnd = 0
var maxCount = 1
for i in 1.stride(through: s.characters.count - 1, by: 1) {
for j in 0 ..< s.characters.count - 1 {
if j + i < s.characters.count {
if isValidPalindrome(j, j + i, s, hash) {
hash[j][j + i] = true
if maxCount < i + 1 {
maxCount = i
maxStart = j
maxEnd = j + i
}
}
}
else {
break
}
}
}
// construct max palindrome string, swift string is so dummy
var str = ""
for i in maxStart...maxEnd {
let index = s.characters.startIndex.advancedBy(i)
str += String(s.characters[index])
}
return str
}
func isValidPalindrome(start: Int, _ end: Int, _ s: String, _ hash: [[Bool]]) -> Bool {
// end <= s's length - 1
let startIndex = s.startIndex.advancedBy(start)
let endIdnex = s.startIndex.advancedBy(end)
if end - start == 1 {
return s[startIndex] == s[endIdnex]
}
else {
let left = start + 1
let right = end - 1
return s[startIndex] == s[endIdnex] && hash[left][right]
}
}
}
I am thinking it's a correct one, but when I submit, always time exceeded for long strings like:
"kyyrjtdplseovzwjkykrjwhxquwxsfsorjiumvxjhjmgeueafubtonhlerrgsgohfosqssmizcuqryqomsipovhhodpfyudtusjhonlqabhxfahfcjqxyckycstcqwxvicwkjeuboerkmjshfgiglceycmycadpnvoeaurqatesivajoqdilynbcihnidbizwkuaoegmytopzdmvvoewvhebqzskseeubnretjgnmyjwwgcooytfojeuzcuyhsznbcaiqpwcyusyyywqmmvqzvvceylnuwcbxybhqpvjumzomnabrjgcfaabqmiotlfojnyuolostmtacbwmwlqdfkbfikusuqtupdwdrjwqmuudbcvtpieiwteqbeyfyqejglmxofdjksqmzeugwvuniaxdrunyunnqpbnfbgqemvamaxuhjbyzqmhalrprhnindrkbopwbwsjeqrmyqipnqvjqzpjalqyfvaavyhytetllzupxjwozdfpmjhjlrnitnjgapzrakcqahaqetwllaaiadalmxgvpawqpgecojxfvcgxsbrldktufdrogkogbltcezflyctklpqrjymqzyzmtlssnavzcquytcskcnjzzrytsvawkavzboncxlhqfiofuohehaygxidxsofhmhzygklliovnwqbwwiiyarxtoihvjkdrzqsnmhdtdlpckuayhtfyirnhkrhbrwkdymjrjklonyggqnxhfvtkqxoicakzsxmgczpwhpkzcntkcwhkdkxvfnjbvjjoumczjyvdgkfukfuldolqnauvoyhoheoqvpwoisniv"
I can get the correct result qahaq after some time, and I am wondering why it's so slow. If I write it in other language, not so bad.
I suspect the API s.startIndex.advancedBy(start) is causing it, but I checked the doc, no time complexity and no other ways to turn an int to the startIndex type?
Any ideas to replace advancedBy? Thank in advance.
For those having the same issue: I turned swift String into Array, and it gets much faster.
I also looked into Swift source code about the advancedBy implementation, it's a O(n) opreation, that's why it's slow.
For whom is interested in the implementation, take a look at https://github.com/apple/swift/blob/8e12008d2b34a605f8766310f53d5668f3d50955/stdlib/public/core/Index.swift
You will see advancedBy is merely multiple successor():
#warn_unused_result
public func advanced(by n: Distance) -> Self {
return self._advanceForward(n)
}
/// Do not use this method directly; call advanced(by: n) instead.
#_transparent
#warn_unused_result
internal func _advanceForward(_ n: Distance) -> Self {
_precondition(n >= 0,
"Only BidirectionalIndex can be advanced by a negative amount")
var p = self
var i : Distance = 0
while i != n {
p._successorInPlace()
i += 1
}
return p
}
This should do the trick. Before implementing it, I recommend checking out some explanations such as this guy's. https://www.youtube.com/watch?v=obBdxeCx_Qs. I'm not affiliated with him, though I do believe his video is somewhat useful.
func longestPalindrome(_ s: String) -> String {
var charArray = [Character("$"), Character("#")]
for i in s.characters {
charArray += [i, Character("#")]
}
charArray += [Character("#")]
var mir = 0, c = 0, r = 0, longestPalindromeIndex = 0, longestPalindromeLength = 0, ss = "", returnString = ""
var p = [Int]()
//MARK: For loop
for i in 0...(charArray.count - 1) {
p += [0, 0]
mir = 2 * c - i
if i < r {
p[i] = min(r - i, p[mir])
}
if i - (1 + p[i]) >= 0 && i + (1 + p[i]) < charArray.count - 1 {
while charArray[i + (1 + p[i])] == charArray[i - (1 + p[i])] {
p[i] += 1
}
}
if p[i] > longestPalindromeLength {
longestPalindromeIndex = i
longestPalindromeLength = p[i]
}
if i + p[i] > r {
c = i
r = i + p[i]
}
}//for loop
for i in Array(charArray[(longestPalindromeIndex - longestPalindromeLength)...(longestPalindromeIndex + longestPalindromeLength)]) {
ss = String(i)
if ss != "#" && ss != "$" && ss != "#" {
returnString += ss
}
}
return returnString
}//func