Quick question please about the efficiency of higher order swift functions with large input data. During a recent test I had a question about finding 'equlibirum indexes' in arrays- i.e. the index of an array where the sum of all elements below the index equals the sum of all elements above the index
An equilibrium index of this array is any integer P such that 0 ≤ P <
N and the sum of elements of lower indices is equal to the sum of
elements of higher indices, i.e.
A[0] + A[1] + ... + A[P−1] = A[P+1] + ... + A[N−2] + A[N−1].
The challenge was to write a short function which computed the first (or any) index which was considered 'equilibirum'.
I put together a simple snippet which scored highly but failed some of the 'performance' tests which used large input data (array sizes around 100,000).
Here's the code
public func solution(inout A : [Int]) -> Int {
var index = 0;
for _ in A {
let sumBefore = A[0...index].reduce(0) { $0 + $1 }
let sumAfter = A[index...A.count-1].reduce(0) { $0 + $1 }
if (sumBefore == sumAfter) { return index; }
index += 1;
}
return -1;
}
Would anyone please be able to explain why the code performs so poorly with large sets of data, or any recommended alternatives?
Here, for example is a description of a failing perfomance test:
Large performance test, O(n^2) solutions should fail.
✘ TIMEOUT ERROR
running time: >6.00 sec., time limit: 0.22 sec.
It looks like the challenge is failing because your solution is O(n^2).
Your for loop, along with 2 sequential reduces inside, make your solution ~ O(2*n^2) since reduce goes through all the elements again.
A simpler solution is to first compute the whole sum, and then iterate through the elements once, subtracting each value from the whole sum, one by one, thus having access to the left and right sums, for comparison.
Using Swift 3.0, Xcode 8:
func findEquilibriumIndex(in array: [Int]) -> Int? {
var leftSum = 0
var rightSum = array.reduce(0, combine: +)
for (index, value) in array.enumerated() {
rightSum -= value
if leftSum == rightSum {
return index
}
leftSum += value
}
return nil
}
let sampleArray = [-7, 1, 5, 2, -4, 3, 0]
findEquilibriumIndex(in: sampleArray)
The problem is not that "the built-in functions perform so poorly."
Your solution is slow because in each iteration, N elements are
added (N being the length of the array). It would be more efficient
to compute the total sum once and update the "before sum"
and "after sum" while traversing through the array. This reduces
the complexity from O(N^2) to O(N):
public func solution(A : [Int]) -> Int {
var sumBefore = 0
var sumAfter = A.reduce(0, combine: +)
for (idx, elem) in A.enumerate() {
sumAfter -= elem
if sumBefore == sumAfter {
return idx
}
sumBefore += elem
}
return -1
}
(Swift 2.2, Xcode 7.3.1)
Remarks:
There is no reason to pass the array as inout parameter.
An operator (in this case +) can be passed as a argument to the reduce() function.
enumerate() returns a sequence of array indices together with
the corresponding element, this saves another array access.
Note also that a more "Swifty" design would be to make the return type
an optional Int? which is nil if no solution was found.
The incrementalSums extension
If you define this extension
extension Array where Element : SignedInteger {
var incrementalSums: [Element] {
return Array(reduce([0]) { $0.0 + [$0.0.last! + $0.1] }.dropLast())
}
}
given an array of Int(s) you can build an array where the Int at the n-th position represents the sum of the values from 0 to (n-1) in the original array.
Example
[1, 2, 3, 10, 2].incrementalSums // [0, 1, 3, 6, 16]
The equilibriumIndex function
Now you can build a function like this
func equilibriumIndex(nums: [Int]) -> Int? {
let leftSums = nums.incrementalSums
let rightSums = nums.reversed().incrementalSums.reversed()
return Array(zip(leftSums, rightSums)).index { $0 == $1 }
}
Here is a functional version of the solution in Swift 3
let total = sampleArray.reduce(0,+)
var sum = 0
let index = sampleArray.index{ v in defer {sum += v}; return sum * 2 == total - v }
If I understand correctly the element at the resulting index is excluded from the sum on each side (which I'm not certain the other solutions achieve)
Related
I have an array of floating values:
let array:[Double] = [2270.87, 2285.15, 2273.49, 2312.89, 2323.07, 2336.14, 2355.09, 2633.0, 2671.34]
I need and single line logic using swift higher-order functions or array extension to find the minimum difference value from all differences between the values.
I tried but I'm unable to move further:
let array = [2270.87, 2285.15, 2273.49, 2312.89, 2323.07, 2336.14, 2355.09, 2633.0, 2671.34]
let minDiff = array.map( { *All differences between array of values* } ).reduce(0, min)
Actually I am showing these values in a graph. So I want the minimum absolute fluctuation between the values. in the above example like 2323.07 and 2336.14 have minimum fluctuation 10.18.
You can zip two arrays where second one doesn't have first element. Then you can get abs value of subtraction value with index x + 1 from that with index x, and using map() you can get the minimum value from all of those absolute values.
zip(array, array.dropFirst()).map { abs($1 - $0) }.min() // 10.180000000000291
If you want to find the minimum absolute difference between two consecutive elements of your array, you can use below extension, which maps over the indices of the array, to access 2 consecutive elements at a time.
dropLast is important to ensure that we stop iteration before the last element (since we calculate the diff between the penultimate and last element before reaching the last index).
extension Array where Element: Comparable, Element: SignedNumeric {
func minConsecutiveDiff() -> Element? {
indices.dropLast().map { abs(self[$0] - self[$0+1])}.min()
}
}
let array:[Double] = [2270.87, 2285.15, 2273.49, 2312.89, 2323.07, 2336.14, 2355.09, 2633.0, 2671.34]
array.minConsecutiveDiff() // 10.18
If you were interested in the diff between any two elements of the array, not just consecutive ones, you could get that by first sorting the Array and then calculating the diff between the consecutive elements of the sorted array as #MartinR pointed out in comments.
extension Array where Element: Comparable, Element: SignedNumeric {
func minDiff() -> Element? {
sorted().minConsecutiveDiff()
}
}
Here is one way using forEach
var min: Double = array.max()!
var previous: Double?
array.forEach {
if let prev = previous, min > abs($0 - prev) {
min = abs($0 - prev)
}
previous = $0
}
another option is to use reduce(into:) with a tuple
let min = array.reduce(into: (Double, Double)(array.first!, 0)) {
if abs($1 - $0.1) < $0.0 {
$0.0 = abs($1 - $0.1)
}
$0.1 = $1
}.0
which both gives 10.18 as the smallest difference.
This is a follow up on this thread, which main issue was to iterate over all permutations of an array, that is given ["a","b","c"], obtain ["bca","acb".. etc], using an iterator.
Thanks to Martin R's insights, and also his inputs in another thread, I came up with another possible solution for the 'Sequence-Based enumeration of permutations' problem using iterators. The issue is that I'm not sure I have all permutations although there are good indications they're all there. The algorithm is guaranteed to provide n! permutations at the most, with no duplicates.
The idea behind this approach is the following, say there's an array a=["a","b","c"...], of size n. Listing all permutations can be viewed as picking elements from bags:
■
■
■
■ ■
■ ■ ■
■ ... ■ ■ ■
0 ... n-3 n-2 n-1
So the algorithm takes the initial array, and removes a row, pass it recursively until there is no row left. At this point, if an iterator can be found, all individual permutations can be addressed independently. The iterator is hidden in FactorialSequence below, where a method next() allows to move from adjacents points.
public struct FactorialSequence : Sequence, IteratorProtocol {
private var current: [Int]
public init(size:Int) {
self.current = Array(repeating: 0, count: size)
}
public mutating func next() -> [Int]? {
return self.__next();
}
private mutating func __next() -> [Int]? {
var next = current
defer {
current = next;
}
for i in self.current.indices.reversed() {
if next[i] < current.count - i - 1 {
next[i] += 1
return next;
}
next[i] = 0
}
return nil
}
}
func permute(seq:[String],at:[Int]) -> String? {
if seq.count > 0 {
var ss = seq;
let uu = seq[at.first!]
var cc = at;
_ = ss.remove(at: cc.first!)
_ = cc.remove(at: 0);
return uu + (permute(seq:ss,at:cc) ?? "")
}
return nil ;
}
the permute() function is called passing the iterator (an array) calculated from FactorialSequence:
var fs = FactorialSequence(size: 3)
print("\(fs.current):\(permute(seq:["a","b","c"], at: fs.current)!)")
while let uu = fs.next() {
print("\(uu):\(permute(seq:["a","b","c"], at: uu)!)")
}
and gives (in flatten string format):
[-0.000][-0.000][171] [0, 0, 0]:abc
[0.0009][0.0009][174] [0, 1, 0]:acb
[0.0016][0.0007][174] [1, 0, 0]:bac
[0.0024][0.0008][174] [1, 1, 0]:bca
[0.0032][0.0008][174] [2, 0, 0]:cab
[0.0040][0.0008][174] [2, 1, 0]:cba
Note on 'no duplicates': Since permutations are accessed using an array (the iterator), if two iterators differ by one elements, they point to two different permutations. Although a little thin, I take this as an argument for not having duplicates.
The only question remaining is 'are they all there?'. One could say that there are n! distinct arrays pointing at a given permutation, but I'm not too sure about the validity of that argument, since it comes from a 'drawing'... Pointers welcome.
I didn't thoroughly scrub SO to check if this had been already formulated this way or in a similar way (although links in the original thread use other approaches). Apologies if it did.
For a given size N the FactorialSequence produces a sequence of all arrays
[ i.0, i.1, ..., i.(N-1) ]
such that
0 <= i.0 < N, 0 <= i.1 < N-1, ..., 0 <= i.(N-1) < 1
that are exactly
N * (N-1) * ... * 1 = N!
elements. The permute() function then picks the element with index i.0
from the given array with N elements, then the element with i.1 from
the remaining N-1 elements, and so on.
So yes, this indeed produces all possible permutations of the array.
However, the code can be simplified a bit. First, FactorialSequence
does not return the initial array [ 0, ..., 0 ], corresponding to
the identity permutation. Also the separate __next() method seems
unnecessary.
If we change the code to
public struct FactorialSequence : Sequence, IteratorProtocol {
private var current: [Int]
private var firstIteration = true
public init(size:Int) {
self.current = Array(repeating: 0, count: size)
}
public mutating func next() -> [Int]? {
if firstIteration {
firstIteration = false
return current
}
for i in self.current.indices.reversed() {
if current[i] < current.count - i - 1 {
current[i] += 1
return current;
}
current[i] = 0
}
return nil
}
}
then all permutations are returned (including the initial identity), and
the defer statement is no longer necessary.
The permute() function can be simplified slightly, and made generic:
func permute<E>(array: [E], at: [Int]) -> [E] {
if at.isEmpty { return [] }
var at = at
var array = array
let firstIndex = at.remove(at: 0)
let firstElement = array.remove(at: firstIndex)
return [firstElement] + permute(array: array, at: at)
}
Now
let array = ["a", "b", "c"]
let fs = FactorialSequence(size: 3)
for p in fs {
print(permute(array: array, at: p).joined())
}
produces the expected output.
Note however that permute() produces a lot of intermediate arrays,
therefore I assume it to be less efficient than the other methods
that you referenced.
An alternative would be to swap the picked element to its new
place, this avoids recursion and temporary arrays:
func permute<E>(array: [E], at: [Int]) -> [E] {
var result = array
for (i, j) in at.enumerated() {
result.swapAt(i, i + j)
}
return result
}
(It produces the permutations in a different order, though.)
So say I have an array:
var stringArray = ["a","b","c","d","e","f","g","h","i","j"]
Now, how do I delete "a", "c", "e", "g", and "i" (all the even number indexes from the array)?
Thanks!
Instead of using C-style for-loops (which are set to be deprecated in an upcoming version of Swift), you could accomplish this using strides:
var result = [String]()
for i in stride(from: 1, through: stringArray.count - 1, by: 2) {
result.append(stringArray[i])
}
Or for an even more functional solution,
let result = stride(from: 1, to: stringArray.count - 1, by: 2).map { stringArray[$0] }
Traditional
var filteredArray = []
for var i = 1; i < stringArray.count; i = i + 2 {
filteredArray.append(stringArray[i])
}
Functional alternative
var result = stringArray.enumerate().filter({ index, _ in
index % 2 != 0
}).map { $0.1 }
enumerate takes a array of elements and returns an array of tuples where each tuple is an index-array pair (e.g. (.0 3, .1 "d")). We then remove the elements that are odd using the modulus operator. Finally, we convert the tuple array back to a normal array using map. HTH
There are a bunch of different ways to accomplish this, but here are a couple that I found interesting:
Using flatMap() on indices:
let result: [String] = stringArray.indices.flatMap {
if $0 % 2 != 0 { return stringArray[$0] }
else { return nil }
}
Note: result needs to be defined as a [String] otherwise the compiler doesn't know which version of flatMap() to use.
Or, if you want to modify the original array in place:
stringArray.indices.reverse().forEach {
if $0 % 2 == 0 { stringArray.removeAtIndex($0) }
}
In this case you have to call reverse() on indices first so that they're enumerated in reverse order. Otherwise by the time you get to the end of the array you'll be attempting to remove an index that doesn't exist anymore.
Swift 4.2
A function accepting generics and producing reduced result
func stripElements<T>(in array:[T]) -> [T] {
return array.enumerated().filter { (arg0) -> Bool in
let (offset, _) = arg0
return offset % 2 != 0
}.map { $0.element }
}
I'm trying to iterate through an array and sum up all the values using generics like so:
func reduceDaArray <T, U>(a: [T], startingValue: U, summed: (U, T) -> U) -> U {
var sum = 0
for number in a {
sum = sum + number
}
return sum
}
reduceDaArray([2,3,4,5,6], 2, +) //(22)
It's giving me the following errors:
Binary operator '+' cannot be applied to operands of type 'Int' and 'A' with regards to the line sum = sum + number
and
Int is not convertible to 'U' with regards to the line return sum
I know this is accomplished better with the reduce method, but I wanted to complete the task using iteration for this instance to get some practice. Why are these errors occurring? I never explicitly stated that T is an Int.
In your reduceDaArray() function,
var sum = 0
declares an integer instead of using the given startingValue.
And
sum = sum + number
tries to add a generic array element to that integer, instead of using
the given summed closure.
So what you probably meant is
func reduceDaArray <T, U>(a: [T], startingValue: U, summed: (U, T) -> U) -> U {
var sum = startingValue
for number in a {
sum = summed(sum, number)
}
return sum
}
which compiles and works as expected:
let x = reduceDaArray([2, 3, 4, 5, 6], 2, +)
println(x) // 22
let y = reduceDaArray([1.1, 2.2], 3.3, *)
println(y) // 7.986
let z = reduceDaArray(["bar", "baz"], "foo") { $0 + "-" + $1 }
println(z) // foo-bar-baz
I'm trying to learn how to assign a range of numbers 1 to 4 to an array with each number printed twice. I can't figure out how to use the random range to print numbers specific amounts of times.
Not exactly sure if this is even right. I've haven't really working with for loops, but i did learn them. Not even complete because of the roadblock of how to do this.
By the way, also might help to say this is a card matching game I'm making, so thats why i only need to print twice.
/*for index in imageArray
{
imageArray[index] =
}*/
To assign numbers one through four to an array:
let numbers = Array(1...4)
To assign the numbers one through four twice to an array:
let numbers = Array(1...4) + Array(1...4)
To shuffle those numbers:
var shuffledNumbers = numbers.shuffled()
In Swift 4.2 and later, you can use the built-in shuffled method. In earlier versions, you'd have to write your own, e.g. using the Fisher-Yates algorithm:
// see http://stackoverflow.com/a/24029847/1271826
extension MutableCollection {
/// Shuffle the elements of `self` in-place.
mutating func shuffle() {
if count < 2 { return } // empty and single-element collections don't shuffle
for i in 0 ..< count - 1 {
let j = Int(arc4random_uniform(UInt32(count - i)))
if j != 0 {
let current = index(startIndex, offsetBy: i)
let swapped = index(current, offsetBy: j)
swapAt(current, swapped)
}
}
}
/// Return shuffled collection the elements of `self`.
func shuffled() -> Self {
var results = self
results.shuffle()
return results
}
}