Search This Blog

Saturday, October 20, 2018

Eratosthenes Sieve in Swift

Task definition: Given a integer number n, calculate all primes smaller than or equal to n (It may be including or not including).

For example if we will not include upper bound, if n is 23, following prime numbers should be:
2, 3, 5, 7, 11, 13, 17, 19

Algorithm is following (for case when not including upper bound):


  1. Make an boolean array of size equals n, all values fill with true
  2. Assign elements at 0 and 1 indexes to false, because 0 and 1 is NOT prime numbers by default
  3. Since index 2 until last index of array if item at this index if true then make inner for loop. 
    1. Inner for loop should be for each multiples of current number. Values at these multiples indexes must be assigned to false
  4. Finally converts our array of boolean values to array of integer values. After all iterations of sieve we will have true values at indexes which are prime numbers. So if value of array item is true then its index is prime number.


// Not including max number
func sieveNotIncluding(max: Int) -> [Int] {
    guard max > 1 else { return [] }
    
    var sieve = [Bool](repeating: true, count: max)
    
    sieve[0] = false
    sieve[1] = false
    
    for i in 2..<max where sieve[i] == true {
        for j in stride(from: i*i, to: max, by: i) {
            sieve[j] = false
        }
    }
    
    return sieve.enumerated().compactMap { $1 == true ? $0 : nil }
}

sieveNotIncluding(max: 23) // [2, 3, 5, 7, 11, 13, 17, 19]


// Including max number
func sieveIncluding(max: Int) -> [Int] {
    guard max > 1 else { return [] }
    
    var sieve = [Bool](repeating: true, count: max + 1)
    
    sieve[0] = false
    sieve[1] = false
    
    for i in 2...max where sieve[i] == true {
        for j in stride(from: i*i, through: max, by: i) {
            sieve[j] = false
        }
    }
    
    return sieve.enumerated().compactMap { $1 == true ? $0 : nil }
}

sieveIncluding(max: 23) // [2, 3, 5, 7, 11, 13, 17, 19, 23]

GitHub source code: Eratosthenes Sieve in Swift 

Sunday, August 26, 2018

Test iOS Project for tutu.ru. Part 1

Description

Full task description can be found here on GitHub


What need to be done

1. Application should have TabBar with two tabs:
  1. Schedule ("Расписание") 
  2. About ("О приложении")

2. Schedule Screen ("Расписание")
The screen should allow you to select:
  1. Departure station(Станция "отправления")
  2. Arrival station(Станция "прибытия")
  3. Date of departure(Дату отправления)

3. Station List Screen ("Экран выбора станции")
The station selection screen must be built on the basis of UITableView and it should:
Contain the general list of stations (see the attached file), grouped by the value of "Country, City". A complete list of groups and elements should be presented on one screen, with the ability to scroll through the entire content.
Provide the ability to search for part of the name (both initial and incoming, regardless of the register). Search must be performed on the same screen, where the list of stations is presented, using the UISearchController.

4. Station Detail Screen ("Детальной информация о конкретной станции")
Display detailed information about a particular station (naming and its full address, including city, region and country).

5. About Screen ("О приложении")
In this section you need to post information about:
  1. Author
  2. Application Version
Input data
{
  "citiesFrom" : [  ], //массив пунктов отправления
  "citiesTo" : [  ] //массив пунктов назначения
}
City object
{
 "countryTitle" : "Россия", //название страны
 "point" : { //координаты города
  "longitude" : 50.64357376098633,
  "latitude" : 55.37233352661133
 },
 "districtTitle" : "Чистопольский район", //название района
 "cityId" : 4454, //идентификатор города
 "cityTitle" : "Чистополь", //название города
 "regionTitle" : "Республика Татарстан", //название региона
 "stations" : [...] //массив станций
}
Station object
{
 "countryTitle" : "Россия", //название страны (денормализация данных, дубль из города)
 "point" : { //координаты станции (в общем случае отличаются от координат города)
  "longitude" : 50.64357376098633,
     "latitude" : 55.37233352661133
 },
 
 "districtTitle" : "Чистопольский район", //название района
 "cityId" : 4454, //идентификатор города
 "cityTitle" : "город Чистополь", //название города
 "regionTitle" : "Республика Татарстан", //название региона
 
 "stationId" : 9362, //идентификатор станции
 "stationTitle" : "Чистополь" //полное название станции
}


Sunday, July 8, 2018

Swift Algorithms. Binary Search

How to solve following task: find element in the array. For this task we can use linear search algorithm - simply iterate over each element of the array until target element is found.

Linear Search

Unsorted array of integers.
let array = [45, 42, 75, 61, 37, 95, 30, 19, 81, 77, 44, 54, 8, 88, 85, 46, 39, 35, 65, 3]
Algorithm of linear search.
func linearSearch(array: [Int], target: Int) -> Bool {
    for i in 0..<(array.count - 1) {
        if array[i] == target {
            return true
        }
    }
    return false
}
Test results.
linearSearch(array: array, target: 65) // true
linearSearch(array: array, target: 68) // false
assert(true == linearSearch(array: array, target: 65), "Error happened")
assert(false == linearSearch(array: array, target: 68), "Error happened")
Time Complexity of Linear Search is O(n).


Binary Search Algorithm

How we can increase time from linear search? The solution is using of Binary Search. The main idea is to splitting the array by half until the target is found. But there is one requirement - array must be sorted.

Steps of Binary Search algorithm is following:
  1. Split the array by half and define whether the target, in the left half or in the right half.
  2. If the target is in the left half, you repeat the process there: split the left half into two even smaller pieces and look in which piece the target is. (The same for the case when it's the right half.)
  3. These steps repeats until the target is found. If the array cannot be split one more time, it means that target is not in the array

Time Complexity

What is the time complexity of Binary Search. How express what number of operations that required to find target in array? For example, there is array with 1000 elements, how many time we need to divide 1000 by 2 to get 1 value?

1000 / 2
500 / 2
250 / 2
125 / 2
65 / 2
33 / 2
17 / 2
9 / 2
5 / 2
3 / 2
1

Or it can be expresses as how many times we need to multiply 1 by 2 to get 1000?
N = 1*2ˆK 
It gives us the following K = logN, because logN is the number of times we need to raise 2 to the power of K to get N.
So, for finding target in array of 1000 elements we need maximum 9 operations instead of 1000 for linear search.

Recursive Implementation

Let's provide recursive implementation of Binary Search. There is entry point.
func binarySearch(array: [Int], target: Int) -> Bool {
    let left = 0
    let right = array.count - 1
    return binarySearchRecursive(array: array, target: target, left: left, right: right)
}
There is recursive function.
func binarySearchRecursive(array: [Int], target: Int, left: Int, right: Int) -> Bool {
    if left > right {
        return false
    }
    let midIndex = (left + right) / 2
    let midValue = array[midIndex]
    if midValue == target {
      return true
    } else if (target > midValue) {
        let newLeft = midIndex + 1
        return binarySearchRecursive(array: array, target: target, left: newLeft, right: right)
    } else {
        let newRight = midIndex - 1
        return binarySearchRecursive(array: array, target: target, left: left, right: newRight)
    }
}
Test results.
let sortedArray = [3, 8, 19, 30, 35, 37, 39, 42, 44, 45, 46, 54, 61, 65, 75, 77, 81, 85, 88, 95]
binarySearch(array: sortedArray, target: 88) // true
binarySearch(array: sortedArray, target: 89) // false
assert(true == binarySearch(array: sortedArray, target: 88), "Error happened")
assert(false == binarySearch(array: sortedArray, target: 89), "Error happened")

Problem with overflow

There is one thing that can cause error. The expression let midIndex = (left + right) / 2 could fail because of overflow when left and right are close to the max values of type Int.  To prevent his situation we can present this expression through difference between right and left. Algorithm is following:
  1. midIndex = (left + right) / 2
  2. (left + right) / 2 = (left + left - left+ right + right - right) / 2
  3. (left + left - left+ right + right - right) / 2 = (right - left + 2*left + (right - right)) / 2
  4. (right - left + 2*left + (right - right)) / 2 = (right - left + 2*left) / 2
  5. (right - left + 2*left) / 2 = (right - left) / 2 + left

So, we will replace this unsafe calculation of mid index
let midIndex = (left + right) / 2
to this safe calculation
let midIndex = left + (right - left) / 2
update algorithm
func binarySearchRecursive(array: [Int], target: Int, left: Int, right: Int) -> Bool {
    if left > right {
        return false
    }
    let midIndex = left + (right - left) / 2
    let midValue = array[midIndex]
    if midValue == target {
      return true
    } else if (target > midValue) {
        let newLeft = midIndex + 1
        return binarySearchRecursive(array: array, target: target, left: newLeft, right: right)
    } else {
        let newRight = midIndex - 1
        return binarySearchRecursive(array: array, target: target, left: left, right: newRight)
    }
}

With Range instead of Left and Right

Instead of two parameters left and right we can use one - range of type Range<Int>. Type range has two variable lowerBound and upperBound.
func binarySearchRecursive(array: [Int], target: Int, range: Range<Int>) -> Bool {
    if range.lowerBound >= range.upperBound {
        return false
    }
    let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2
    print(midIndex)
    let midValue = array[midIndex]
    if midValue == target {
        return true
    } else if (target > midValue) {
        let newRange: Range<Int> = (midIndex + 1)..<range.upperBound
        return binarySearchRecursive(array: array, target: target, range: newRange)
    } else {
        let newRange: Range<Int> = (range.lowerBound)..<midIndex
        return binarySearchRecursive(array: array, target: target, range: newRange)
    }
}

//let sortedArray = [3, 8, 19, 30, 35, 37, 39, 42, 44, 45, 46, 54, 61, 65, 75, 77, 81, 85, 88, 95]
binarySearchRecursive(array: sortedArray, target: 88, range: 0..<sortedArray.count) // true
binarySearchRecursive(array: sortedArray, target: 89, range: 0..<sortedArray.count) // false

Making Generic Function

We can make our function more universal by making it generic. Generic function can be applied to any array where elements are conform to protocol Comparable. For example, we can apply it to array of strings.
func binarySearchRecursive<T: Comparable>(array: [T], target: T, range: Range<Int>) -> Bool {
    if range.lowerBound >= range.upperBound {
        return false
    }
    let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2
    print(midIndex)
    let midValue = array[midIndex]
    if midValue == target {
        return true
    } else if (target > midValue) {
        let newRange: Range<Int> = (midIndex + 1)..<range.upperBound
        return binarySearchRecursive(array: array, target: target, range: newRange)
    } else {
        let newRange: Range<Int> = (range.lowerBound)..<midIndex
        return binarySearchRecursive(array: array, target: target, range: newRange)
    }
}

binarySearchRecursive(array: sortedArray, target: 81, range: 0..<array.count)
let strings = ["aa", "bb", "cc", "dd", "ee"]
binarySearchRecursive(array: strings, target: "dd", range: 0..<strings.count)

Iterative Implementation

Instead of using recursive calls we can implement Binary Search algorithm using iterative approach and while loop. On each iteration of loop we just will change lower or upper bounds of range.
func binarySearchIterative<T: Comparable>(array: [T], target: T) -> Bool {
    var lower = 0
    var upper = array.count
    while (lower <= upper) {
        let midIndex = lower + (upper - lower) / 2
        let midValue = array[midIndex]
        if midValue == target {
            return true
        } else if target > midValue {
            lower = midIndex + 1
        } else {
            upper = midIndex - 1
        }
    }
    return false
}

let sortedArray = [3, 8, 19, 30, 35, 37, 39, 42, 44, 45, 46, 54, 61, 65, 75, 77, 81, 85, 88, 95]
binarySearchIterative(array: sortedArray, target: 81) // true
binarySearchIterative(array: sortedArray, target: 82) // false

Making Extension

Now, using iterative solution that we provided above we can easily make extension for all Array where elements of array conform to Comparable protocol.
extension Array where Element: Comparable {
    
    func binarySearch(target: Element) -> Bool {
        var lower = 0
        var upper = self.count
        while (lower <= upper) {
            let midIndex = lower + (upper - lower) / 2
            let midValue = self[midIndex]
            if midValue == target {
                return true
            } else if target > midValue {
                lower = midIndex + 1
            } else {
                upper = midIndex - 1
            }
        }
        return false
    }
}

let sortedArray = [3, 8, 19, 30, 35, 37, 39, 42, 44, 45, 46, 54, 61, 65, 75, 77, 81, 85, 88, 95]
sortedArray.binarySearch(target: 81) // true
sortedArray.binarySearch(target: 82) // false

Source Code

Source code for this post could be found here on GitHub

Thursday, July 5, 2018

Swift Algorithms. Bubble Sort

Bubble Sort is the simplest sorting algorithm which iterates over elements of the array and swap adjacent elements if they are in wrong order. It repeat iterations until all elements will be in right order.

Example of unsorted and sorted array
let array = [19, 2, 66, 4, 45, 99, 101, 3, 5, 7, 17, 35, 42]
let sortedArray = [2, 3, 4, 5, 7, 17, 19, 35, 42, 45, 66, 99, 101]
First Version
We just iterate over elements and compare adjacent. And swap them in case of wrong order.
Also we use special variable counter for counting number of operations.
func bubbleSort1(array: [Int]) -> [Int] {
    var result = array
    let n = result.count
    var counter = 0
    for _ in 0..<(n - 1) {
        for j in 0..<(n - 1) {
            counter += 1
            if result[j] > result[j + 1] {
                let temp = result[j]
                result[j] = result[j + 1]
                result[j + 1] = temp
            }
        }
    }
    print("Count of operations in bubbleSort1: \(counter)")
    return result
}
Check the result of the algorithm. Time complexity of this algorithm is O(nˆ2) because of two for loops.
let result1 = bubbleSort1(array: array)
// Count of operations in bubbleSort1: 144
assert(result1 == sortedArray, "Error happened!")
Second Version
We can improve first solution. Because after each iteration over the array the biggest element moving to the end of the array. So, after first iteration we can compare only before n-2, after second iteration only before n-3 and etc. So we can change upper limit for inner for loop.
func bubbleSort2(array: [Int]) -> [Int] {
    var result = array
    let n = result.count
    var counter = 0
    for i in 0..<(n - 1) {
        for j in 0..<(n - i - 1) {
            counter += 1
            if result[j] > result[j + 1] {
                result.swapAt(j, j + 1)
            }
        }
    }
    print("Count of operations in bubbleSort2: \(counter)")
    return result
}
After optimisation we can see that number of operation is decreased, now it takes 78 instead of 144 in first solution. But still time complexity of algorithm is O(nˆ2) because there are two for loops.
let result2 = bubbleSort2(array: array)
// Count of operations in bubbleSort2: 78
assert(result2 == sortedArray, "Error happened!")
Third Version
We can optimise our solution even more. If after next iteration there were no swapping operations happened it means that array is sorted and we can exit from for loop. It gives us linear time complexity O(n) in case of array is already sorted, because we will have only one loop over n-elements for comparing elements.
func bubbleSort3(array: [Int]) -> [Int] {
    var result = array
    let n = result.count
    var counter = 0
    var isSwapped: Bool
    for i in 0..<(n - 1) {
        isSwapped = false
        for j in 0..<(n - i - 1) {
            counter += 1
            if result[j] > result[j + 1] {
                result.swapAt(j, j + 1)
                isSwapped = true
            }
        }
        if !isSwapped {
            break
        }
    }
    print("Count of operations in bubbleSort3: \(counter)")
    return result
}
We can see that count of operation is slightly decreased, now it is 63 instead of 78.
let result3 = bubbleSort3(array: array)
// Count of operations in bubbleSort3: 63
assert(result3 == sortedArray, "Error happened!")
But count of operations will be decreased significantly for case when array is sorted: from 63 to 12
print("Comparing, unsored array: \(bubbleSort3(array: array))")
// Count of operations in bubbleSort3: 63
print("Comparing, sorted array: \(bubbleSort3(array: sortedArray))")
// Count of operations in bubbleSort3: 12
Fourth Version
The same optimised algorithm but written with using while loop.
func bubbleSort4(array: [Int]) -> [Int] {
    var result = array
    var counter = 0
    let n = result.count
    var lastUnsorted = n - 1
    var isSorted = false
    while (!isSorted) {
        isSorted = true
        for i in 0..<(lastUnsorted) {
            counter += 1
            if result[i] > result[i + 1] {
                result.swapAt(i, i + 1)
                isSorted = false
            }
         }
        lastUnsorted -= 1
    }
    print("Count of operations in bubbleSort4: \(counter)")
    return result
}
Result of work
let result4 = bubbleSort4(array: array)
// Count of operations in bubbleSort3: 63
assert(result4 == sortedArray, "Error happened!")

Making Generic Function 

We can make generic function and use this algorithm for arrays with any type. Main condition that elements of the array are confirmed to Comparable protocol. Now we can sort array of strings, for example.
// Comparable:
// A type that can be compared using the relational operators <, <=, >=, and >.
func genericBubbleSort<T: Comparable>(list: [T]) -> [T] {
    var result = list
    let n = result.count
    var lastUnsorted = n - 1
    var isSorted = false
    while (!isSorted) {
        isSorted = true
        for i in 0..<lastUnsorted {
            if result[i] > result[i + 1] {
                result.swapAt(i, i + 1)
                isSorted = false
            }
        }
        lastUnsorted -= 1
    }
    return result
}

let stringsArray = ["China", "Japan", "Brazil", "Russia", "Ireland"]
genericBubbleSort(list: stringsArray)
// ["Brazil", "China", "Ireland", "Japan", "Russia"]

Making Extension

We can also make extension for more handy usage of our algorithm.
extension Array where Element: Comparable {
    
    func bubbleSort() -> [Element] {
        var result = self
        let n = result.count
        var lastUnsorted = n - 1
        var isSorted = false
        while (!isSorted) {
            isSorted = true
            for i in 0..<lastUnsorted {
                if result[i] > result[i + 1] {
                    result.swapAt(i, i + 1)
                    isSorted = false
                }
            }
            lastUnsorted -= 1
        }
        return result
    }
}

[19, 2, 66, 4, 45, 99, 101, 3, 5, 7, 17, 35, 42].bubbleSort()
// [2, 3, 4, 5, 7, 17, 19, 35, 42, 45, 66, 99, 101]
["China", "Japan", "Brazil", "Russia", "Ireland"].bubbleSort()
// ["Brazil", "China", "Ireland", "Japan", "Russia"]

Source Code

Source code for this post could be found here on GitHub