Detailed explanations and implementations of various maths concepts for writing high performance code/algorithms backed with Unit tests.

Overview

Maths for fast algorithms

Detailed explanations and implementations of various maths concepts which can help software Engineers write high performance code/algorithms backed with Unit tests.

Note: This repo is a work in progress, contributions are highly appreciated.

Arithmetic Sequence /Arithmetic Progression(AP)

Theory

Arithmetic Sequence is a sequence of numbers having a common/constant difference.

For example 1,2,3......n has a common difference (d) of 1

By using Gauss's technic for summing an arithmetic sequence all we need to know is the first term (A1) and the common difference (d) Or just the first term, nth term and the total number of terms (n).

In AP the nth term can be calculated as follows: -

An = A1 + (n-1)d

Thus using that we can have two ways of computing the sum fo the sequence, also known as Arithmetic Series (Sn) as follows: -

Sn = n/2(A1 + An)

If we substitute the value of An we can also deduce a second formula as follows: -

Sn = n/2[A1 + A1 + (n-1)d]

Therefore

Sn = n/2[2A1 + (n-1)d]

Note: For full derivations of the fomulas above see Suggested Learning Materials Section.

Code Samples & Complexity Analysis

Consider a list of natural numbers below: -

1,2,3,4,......................................1,000,000

Using the naive approach to find the sum of the numbers would be to iterate through all numbers and add them one by one. Here is an example of swift code for the naive approach.

This approach will given us O(n) run time.

Naive approach Swift sample code, O(n)

import Foundation

public struct NaiveSum {

    public func compute(firstTerm A1: Int, nthTerm n: Int, commonDifference d: Int) -> Int {
        var allTerms: [Int] = []
        var currentTerm: Int = A1
        for _ in 1...n {
            allTerms.append(currentTerm)
            currentTerm += d
        }

        var sum:Int = 0
        for term in allTerms {
            sum += term
        }
        
        return sum
    }

}

Gauss's Arithmetic Series sum, O(1)

Now let's try using technics for summing an arithmetic sequence to achive O(1) time complexity for summing a huge amount of numbers.

A swift implementation for arithmetic sum using Gauss's technic is as follows.

import Foundation

public struct GaussSum {

    public func compute(firstTerm a1: Int, nthTerm n: Int, commonDifference d: Int) -> Int {
       // Sn = n/2 (2A1 + (n - 1)d)
        let sum =  (2 * a1 + (n - 1) * d) *  n / 2
        return sum
    }

}

Unit tests to verify our implementation details

Note: Code snippet for this section can be run using Swift Playgrounds.You can find the source code here.

Let's write some unit tests for the above two implementations with different data sets, starting from small amount of data to a very large amount. We will try to increase the nth term and see if our functions give use expected output.

public final class ArithmeticSequenceImplementationTests: XCTestCase {

  // MARk: - Properties
  public var naiveSum: NaiveSum!
  public var gaussSum: GaussSum!

  // MARK: - Life Cycle
  public override func setUp() {
      super.setUp()
      naiveSum = NaiveSum()
      gaussSum = GaussSum()
  }

  public override func tearDown() {
      gaussSum = nil
      naiveSum = nil
      super.tearDown()
  }

  // Note: - I prefixed the tests with letters A,B,C... etc.. because Xcode run tests in alphabetic order by default,
  // So prefixing them makes it easy to debug time complexity of each test.

  // MARK: - Naive Tests
  func test_A_NaiveApproachSumTo5GivesCorrectAnswer() {
      // When
      let sumToFithTerm = naiveSum.compute(
          firstTerm: 1,
          nthTerm: 5,
          commonDifference: 2
      )

      // Then
      XCTAssertEqual(sumToFithTerm, 25)
  }

  func test_B_NaiveApproachSumTo100GivesCorrectAnswer() {
      // When
      let sumToOneHundred = naiveSum.compute(
          firstTerm: 1,
          nthTerm: 100,
          commonDifference: 1
      )

      // Then
      XCTAssertEqual(sumToOneHundred, 5050)
  }

  func test_C_NaiveApproachSumTo_50000_GivesCorrectAnswer() {
      // When
      let sumToOneHundred = naiveSum.compute(
          firstTerm: 10,
          nthTerm: 50_000,
          commonDifference: 15
      )
      // Then
      XCTAssertEqual(sumToOneHundred, 18750125000)
  }

  // MARK: - Gauss Tests

  func test_D_GaussAproachSumTo5GivesCorrectAnswer() {
      // When
      let sumToFithTerm = gaussSum.compute(
          firstTerm: 1,
          nthTerm: 5,
          commonDifference: 2
      )
      // Then
      XCTAssertEqual(sumToFithTerm, 25)
  }

  func test_E_GaussAproachSumTo100GivesCorrectAnswer()  {
      // When
      let sum = gaussSum.compute(
          firstTerm: 1,
          nthTerm: 100,
          commonDifference: 1
      )
      // Then
      XCTAssertEqual(sum, 5050)
  }

  func test_F_GaussAproachSumTo_50000_GivesCorrectAnswer() {
      // When
      let sumToOneHundred = gaussSum.compute(
          firstTerm: 10,
          nthTerm: 50_000,
          commonDifference: 15
      )

      // Then
      XCTAssertEqual(sumToOneHundred, 18750125000)
  }

}

Running Implemnentation Tests

In the sources folder there is a playground file named ArithmeticSequence. All tests are triggered in that file as follows.

import Foundation
import XCTest

let testObserver = TestObserver()
XCTestObservationCenter.shared.addTestObserver(testObserver)
ArithmeticSequenceTests.defaultTestSuite.run()


// MARK: - Sample Outputs
/*

Test Suite 'ArithmeticSequenceTests' started at 2022-04-03 01:45:51.791
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_A_NaiveApproachSumTo5GivesCorrectAnswer]' started.
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_A_NaiveApproachSumTo5GivesCorrectAnswer]' passed (0.049 seconds).
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_B_NaiveApproachSumTo100GivesCorrectAnswer]' started.
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_B_NaiveApproachSumTo100GivesCorrectAnswer]' passed (0.016 seconds).
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_C_NaiveApproachSumTo_50000_GivesCorrectAnswer]' started.
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_C_NaiveApproachSumTo_50000_GivesCorrectAnswer]' passed (0.031 seconds).
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_D_GaussAproachSumTo5GivesCorrectAnswer]' started.
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_D_GaussAproachSumTo5GivesCorrectAnswer]' passed (0.001 seconds).
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_E_GaussAproachSumTo100GivesCorrectAnswer]' started.
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_E_GaussAproachSumTo100GivesCorrectAnswer]' passed (0.001 seconds).
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_F_GaussAproachSumTo_50000_GivesCorrectAnswer]' started.
Test Case '-[__lldb_expr_23.ArithmeticSequenceTests test_F_GaussAproachSumTo_50000_GivesCorrectAnswer]' passed (0.001 seconds).
Test Suite 'ArithmeticSequenceTests' passed at 2022-04-03 01:45:51.907.
    Executed 6 tests, with 0 failures (0 unexpected) in 0.099 (0.116) seconds
*/

Interpretations of the console output

  • The ouput above is for the implementation details of our algorithms, All tests passed so we can rest assured that our algorithms will give us correct results for any data sets.
  • Note that the time stemps printed on console above, it is not safe to use them as a measure of performance because other tasks are being done by Xcode such as cleaning up the tests before running each test etc.. So we are going to write better tests just for verifying the performance in the next section.

Performance Tests

Now let's write tests specifically for measuring performance of individual functions regardless of the IDE background tasks. This will give us more correct results to verify our hypothesis.

Note: Code snippet for this section too can be run using Swift Playgrounds. You can find the source code here.

import Foundation
import XCTest

public final class ArithmeticSequenceSpeedMeasurementsTests: XCTestCase {

    func test_A_NaiveSumSpeedForSmallDataSets() {
        measureMetrics(
            [.wallClockTime],
            automaticallyStartMeasuring: false
        ) {
            startMeasuring()
            let _ = computeSumOfNaturalNumbersUsingNaiveApproach(
                firstTerm: 1,
                nthTerm: 10,
                commonDifference: 1
            )

            stopMeasuring()
        }
    }

    func test_B_NaiveSumSpeedForLargeDataSets() {
        measureMetrics(
            [.wallClockTime],
            automaticallyStartMeasuring: false
        ) {
            startMeasuring()
            let _ = computeSumOfNaturalNumbersUsingNaiveApproach(
                firstTerm: 1,
                nthTerm: 100_000,
                commonDifference: 1
            )

            stopMeasuring()
        }
    }

    func test_C_GaussSpeedForSmallDataSets() {
        measureMetrics(
            [.wallClockTime],
            automaticallyStartMeasuring: false
        ) {
            startMeasuring()
            let _ = computeSumOfNaturalNumbersUsingGaussApproach(
                firstTerm: 1,
                nthTerm: 10,
                commonDifference: 1
            )

            stopMeasuring()
        }
    }

    func test_D_GaussSumSpeedForLargeDataSets() {
        measureMetrics(
            [.wallClockTime],
            automaticallyStartMeasuring: false
        ) {
            startMeasuring()
            let _ = computeSumOfNaturalNumbersUsingGaussApproach(
                firstTerm: 1,
                nthTerm: 100_000,
                commonDifference: 1
            )

            stopMeasuring()
        }
    }

}

Running Performance measurement Tests

Similar to implementation tests above tests for this section can also be triggered from the ArithmeticSequence playground included in Sources folder as follows.

:0: Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_B_NaiveSumSpeedForLargeDataSets]' measured [Time, seconds] average: 0.053, relative standard deviation: 8.010%, values: [0.054845, 0.053265, 0.063672, 0.053840, 0.054468, 0.052207, 0.048998, 0.048461, 0.049184, 0.050229], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , polarity: prefers smaller, maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_B_NaiveSumSpeedForLargeDataSets]' passed (0.782 seconds). Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_C_GaussSpeedForSmallDataSets]' started. :0: Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_C_GaussSpeedForSmallDataSets]' measured [Time, seconds] average: 0.000, relative standard deviation: 42.445%, values: [0.000002, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , polarity: prefers smaller, maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_C_GaussSpeedForSmallDataSets]' passed (0.253 seconds). Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_D_GaussSumSpeedForLargeDataSets]' started. :0: Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_D_GaussSumSpeedForLargeDataSets]' measured [Time, seconds] average: 0.000, relative standard deviation: 42.183%, values: [0.000004, 0.000002, 0.000002, 0.000001, 0.000001, 0.000001, 0.000001, 0.000002, 0.000001, 0.000001], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , polarity: prefers smaller, maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100 Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_D_GaussSumSpeedForLargeDataSets]' passed (0.252 seconds). Test Suite 'ArithmeticSequenceSpeedMeasurementsTests' passed at 2022-04-03 03:31:02.273. Executed 4 tests, with 0 failures (0 unexpected) in 1.552 (1.567) seconds */ ">
import Foundation
import XCTest

let testObserver = TestObserver()
XCTestObservationCenter.shared.addTestObserver(testObserver)
ArithmeticSequenceSpeedMeasurementsTests.defaultTestSuite.run()

// MARK: - Sample Output
/*

Test Suite 'ArithmeticSequenceSpeedMeasurementsTests' started at 2022-04-03 03:31:00.706
Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_A_NaiveSumSpeedForSmallDataSets]' started.
:0: Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_A_NaiveSumSpeedForSmallDataSets]' measured [Time, seconds] average: 0.000, relative standard deviation: 154.337%, values: [0.000144, 0.000015, 0.000013, 0.000012, 0.000012, 0.000012, 0.000012, 0.000012, 0.000012, 0.000012], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , polarity: prefers smaller, maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_A_NaiveSumSpeedForSmallDataSets]' passed (0.266 seconds).
Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_B_NaiveSumSpeedForLargeDataSets]' started.
:0: Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_B_NaiveSumSpeedForLargeDataSets]' measured [Time, seconds] average: 0.053, relative standard deviation: 8.010%, values: [0.054845, 0.053265, 0.063672, 0.053840, 0.054468, 0.052207, 0.048998, 0.048461, 0.049184, 0.050229], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , polarity: prefers smaller, maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_B_NaiveSumSpeedForLargeDataSets]' passed (0.782 seconds).
Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_C_GaussSpeedForSmallDataSets]' started.
:0: Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_C_GaussSpeedForSmallDataSets]' measured [Time, seconds] average: 0.000, relative standard deviation: 42.445%, values: [0.000002, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , polarity: prefers smaller, maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_C_GaussSpeedForSmallDataSets]' passed (0.253 seconds).
Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_D_GaussSumSpeedForLargeDataSets]' started.
:0: Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_D_GaussSumSpeedForLargeDataSets]' measured [Time, seconds] average: 0.000, relative standard deviation: 42.183%, values: [0.000004, 0.000002, 0.000002, 0.000001, 0.000001, 0.000001, 0.000001, 0.000002, 0.000001, 0.000001], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , polarity: prefers smaller, maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[ArithmeticSequence_Sources.ArithmeticSequenceSpeedMeasurementsTests test_D_GaussSumSpeedForLargeDataSets]' passed (0.252 seconds).
Test Suite 'ArithmeticSequenceSpeedMeasurementsTests' passed at 2022-04-03 03:31:02.273.
    Executed 4 tests, with 0 failures (0 unexpected) in 1.552 (1.567) seconds
*/

Demistifying Console Outputs

  • Naive sum performance results.

    • test_A_NaiveSumSpeedForSmallDataSets took 0.266 seconds.
    • test_B_NaiveSumSpeedForLargeDataSets took 0.782 seconds (Almost 3 times the small data sets) -> O(n) runtime.
  • Gauss Arithmetic sum performance results.

    • test_C_GaussSpeedForSmallDataSets took 0.253 seconds.
    • test_D_GaussSumSpeedForLargeDataSets Surprisingly took 0.252 seconds which is the almost exactly similar ( O(1) run time) to the method for small data sets above.

Other useful formulas related to Arithmetic Progression

  • Sum of Natural Numbers (Sn) => n/2(n+1)
  • Sum of Square Series (Sn2) => n/6(n+1)(2n+1)
  • Sum of Cubic Series (Sn3) => (n/2(n+1))2

Suggested Learning Materials

If you are new to Arithmetic Progression (AP) or you just need to review the concepts, I recommend the following materials.

Contributing

This is a work in progress so, I welcome any contributions which involves usage of maths theorem/formulas to achieve high performance algorithms. Please see the CONTRIBUTING for how to get involved.

You might also like...
Snapshot view unit tests for iOS

iOSSnapshotTestCase (previously FBSnapshotTestCase) What it does A "snapshot test case" takes a configured UIView or CALayer and uses the necessary UI

I built this application with unit testing and test-driven development to understand TDD theory and practice
I built this application with unit testing and test-driven development to understand TDD theory and practice

TestDrivenDevelopment Description I built this application with unit testing and test-driven development to understand TDD theory and practice, to wri

A Mac and iOS Playgrounds Unit Testing library based on Nimble.
A Mac and iOS Playgrounds Unit Testing library based on Nimble.

Spry Spry is a Swift Playgrounds Unit Testing library based on Nimble. The best thing about Spry is that the API matches Nimble perfectly. Which means

Runtime introspection and unit testing of SwiftUI views

ViewInspector 🕵️‍♂️ for SwiftUI ViewInspector is a library for unit testing SwiftUI views. It allows for traversing a view hierarchy at runtime provi

Erik is an headless browser based on WebKit. An headless browser allow to run functional tests, to access and manipulate webpages using javascript.
Erik is an headless browser based on WebKit. An headless browser allow to run functional tests, to access and manipulate webpages using javascript.

Erik Erik is a headless browser based on WebKit and HTML parser Kanna. An headless browser allow to run functional tests, to access and manipulate web

UITest helper library for creating readable and maintainable tests

UITestHelper General information When creating UI tests you will see that you are often repeating the same pieces of code. The UITestHelper library wi

The XCTest Project, A Swift core library for providing unit test support

XCTest The XCTest library is designed to provide a common framework for writing unit tests in Swift, for Swift packages and applications. This version

Swift Package with examples of how to tests iOS apps

GimmeTheCodeTDD A swift package with examples of unit tests in iOS development. Requirements Xcode 13 Content Dependency Injection Constructor Injecti

Small library to easily run your tests directly within a Playground
Small library to easily run your tests directly within a Playground

[] (https://developer.apple.com/swift/) Build Status Branch Status master develop About PlaygroundTDD enables you to use TDD directly on Xcode Playgro

Comments
  • Add a simple roadmap section.

    Add a simple roadmap section.

    I thought it would be useful to include a simple road map section (What's next section), So I have added it to help others to have an informed decision before making contributions.

    opened by MussaCharles 0
  • Refactor AP documentation to make it easy to read.

    Refactor AP documentation to make it easy to read.

    Organized documentation for Arithmetic progression section to make it easy to read. I have also added link to online learning material about the sum of AP.

    opened by MussaCharles 0
  • Refactor sample code and documentation for better readability.

    Refactor sample code and documentation for better readability.

    As I was reviewing the docs, I found it was somewhat hard to read the code so I decided to refactor all implementations, Deleted no longer needed files and update README file to reflect changes.

    opened by MussaCharles 0
Owner
Mussa Charles
iOS Engineer
Mussa Charles
XCTestExtensions is a Swift extension that provides convenient assertions for writing Unit Test.

XCTestExtensions Features XCTAssertEventually (that convenient assertions for writing Unit Test). Use "XCTAssertEventually", you can write asynchronou

shindyu 22 Dec 1, 2022
Swift framework containing a set of helpful XCTest extensions for writing UI automation tests

AutoMate • AppBuddy • Templates • ModelGenie AutoMate AutoMate is a Swift framework containing a set of helpful XCTest extensions for writing UI autom

PGS Software 274 Dec 30, 2022
A collection of useful test helpers designed to ease the burden of writing tests for iOS applications.

MetovaTestKit is a collection of useful test helpers designed to ease the burden of writing tests for iOS applications. Requirements Installation Usag

null 23 Aug 29, 2021
Gauntlet is a collection of testing utility methods that aims to make writing great tests easier.

Gauntlet What is Gauntlet? Gauntlet is a collection of testing utility methods that aims to make writing great tests easier and with more helpful and

null 11 Dec 17, 2022
Marvel - Marvel Characters App using MVVM, and including unit tests

Marvel About The purpose of this project is to develop a app using MVVM, and inc

null 1 Mar 20, 2022
Mockit is a Tasty mocking framework for unit tests in Swift 5.0

Mockit Introduction Mockit is a Tasty mocking framework for unit tests in Swift 5.0. It's at an early stage of development, but its current features a

Syed Sabir Salman-Al-Musawi 118 Oct 17, 2022
Trying to implement Unit Tests for @Binding properties in a ViewModel

BindingTester Trying to implement Unit Tests for @Binding properties in a ViewModel ViewModel to be tested class SheetViewModel: ObservableObject {

Raphael Guye 0 Oct 22, 2021
Library for unifying the approach to network mocking in iOS unit- & UI-tests.

TinkoffMockStrapping Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installati

Online financial ecosystem 22 Jan 3, 2023
Catching fatal errors in unit tests

Precondition Catching When running tests which hit fatal errors, often preconditions the built-in support with XCTest. One package which supports cach

Brennan Stehling 0 Nov 28, 2021
Write unit tests which test the layout of a view in multiple configurations

Overview This library enables you to write unit tests which test the layout of a view in multiple configurations. It tests the view with different dat

LinkedIn 565 Nov 16, 2022