Finding a perfect square smaller than an input number (Swift) This code runs slowly, even with numbers smaller than 1,000,000. Can someone help me optimize this thing?

// Finds the highest perfect square below a certain input (int)

let target: Int = 758865 // Swap this out for testing other numbers
var smlSqr = [Int]()
let findLimit = Int(Double(target).squareRoot().rounded(.up))   // Saves CPU time, because perfect squares less than
                                                                // number a but higher than a.squareRoot is impossible.

var index = 0
var maxSqr = 0 // Final result
var candidate = 0

repeat {
    smlSqr.append(index * index)
    index += 1
} while(index &lt; findLimit)

while candidate &lt; smlSqr.count {
    if smlSqr[candidate] &gt; maxSqr {
        maxSqr = smlSqr[candidate]
    }
    candidate += 1
}

print(String(maxSqr)) // For debugging, should print out 100
print(Int(Double(maxSqr).squareRoot())) // For debugging, making sure the number is actually a perfect square


I'm using Swift 5.1 on Xcode 11.
Also using a playground (if that helps)
Also using a playground (if that helps)


Well, that is the main reason for the slow execution. A playground is an interactive environment, and the code is not optimized. For all computations in the main playground file, intermediate results are displayed in the side bar (such as the values of the variables, or how often a loop has executed). This makes longer computations really slow.

For code that consists of many computation steps you better


either move it to a function in a separate playground source file, 
or use a compiled Xcode project instead of a playground.


I often prefer a compiled project over a playground because


you can debug your code (single-step, inspect variables, ...),
you can enable optimizations (compile with the “Release” configuration) for performance critical tasks.


But your code can also be simplified and optimized. This loop

var index = 0
var smlSqr = [Int]()
repeat {
    smlSqr.append(index * index)
    index += 1
} while(index &lt; findLimit)


appends one array element in each loop iteration, so that it can be written as a map operation:

let smlSqr = (0..&lt;findLimit).map { k in k * k }


(You may also think about better variable names: what does smlSqr stand for?)

And this

var maxSqr = 0 // Final result
var candidate = 0
while candidate &lt; smlSqr.count {
    if smlSqr[candidate] &gt; maxSqr {
        maxSqr = smlSqr[candidate]
    }
    candidate += 1
}


determines the largest array element. The is a dedicated method for that purpose:

let maxSqr = smlSqr.max()!


(We can force-unwrap here if we know that the array is not empty.) But the array elements are sorted in increasing order, so that the largest element is actually the last element:

let maxSqr = smlSqr.last!


So what we have so far is

let target = 758865
let findLimit = Int(Double(target).squareRoot().rounded(.up))
let smlSqr = (0..&lt;findLimit).map { k in k * k }
let maxSqr = smlSqr.last!


and now we can see that this can be simplified even further: The last element appended to the array is (findLimit - 1) * (findLimit - 1), so that we only need

let target = 758865
let findLimit = Int(Double(target).squareRoot().rounded(.up))
let maxSqr = (findLimit - 1) * (findLimit - 1)


(And this is so short and fast that you can execute it in in a playground directly, without any of the optimization tricks mentioned above.)

To make the code reusable, you should define it as a function:

func largestSquareRoot(lessThan n: Int) -&gt; Int {
    let root = Int(Double(n).squareRoot().rounded(.up)) - 1
    return root * root
}


Note that this gives a nonsensical result for \$ n = 0 \$ and crashes for \$ n &lt; 0 \$. You'll have to define what result you want in those cases.