Andrew
Andrew

Reputation: 551

Interpolate number from list of values in Swift

I am trying to calculate the value at any point using an interpolation between the nearest two points in a list. Is there a better / easier way to do this in Swift?

    func CalculateDelta( ratio: Double) -> Double {
    let ratioArray = [ 1.0, 1.05, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 500 ]
    let deltaArray = [ 0.15, 0.15, 0.103, 0.072, 0.053, 0.04, 0.03, 0.023, 0.017, 0.011, 0.006, 0.001, 0 ]

    var delta = deltaArray[0]
    if (ratio > ratioArray[ratioArray.count - 1] ) {
        delta = deltaArray[deltaArray.count - 1]
    } else {
        for var i=0; i<ratioArray.count-1; i++ {
            if (ratio > deltaArray[i]) {
                delta = deltaArray[i] + (((ratio - ratioArray[i])/(ratioArray[i+1] - ratioArray[i])) * (deltaArray[i+1] - deltaArray[i]));
                break;
            }
        }
    }
    return delta
}

Upvotes: 2

Views: 1742

Answers (1)

Daniel T.
Daniel T.

Reputation: 33967

If your code does what you want, then the following is more efficient:

func CalculateDelta(ratio: Double) -> Double {
    var result: Double = 0
    if ratio <= 500 {
        result = 0.15
    }
    return result
}

But I suspect your code is supposed to be doing something else. I suspect that the below code is more likely correct:

// returns the index of the first element that satisfies the predicate
func find<Seq: SequenceType>(seq: Seq, pred: (Seq.Generator.Element) -> Bool) -> Int? {
    for (index, obj) in enumerate(seq) {
        if pred(obj) {
            return index
        }
    }
    return nil
}

// returns the linear interpolation between two values
func lerp(start: Double, end: Double, percent: Double) -> Double {
    return start + percent * (end - start)
}

// returns the percent of value between start and end
func percent(start: Double, end: Double, value: Double) -> Double {
    assert(start <= value && value <= end)
    return (value - start) / (end - start)
}

func calculateDelta(ratio: Double) -> Double {
    let ratioArray = [1.0, 1.05, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 500]
    let deltaArray = [0.15, 0.15, 0.103, 0.072, 0.053, 0.04, 0.03, 0.023, 0.017, 0.011, 0.006, 0.001, 0]

    // if ratio is below range, return bottom of delta
    if ratio < ratioArray[0] {
        return deltaArray[0]
    }

    if let index = find(ratioArray, { ratio <= $0 }) {
        if ratioArray[index] == ratio {
            // if ratio is equal to element of range, return delta of same index
            return deltaArray[index]
        }
        else {
            // figure lerp between too low index and too high index
            let p = percent(ratioArray[index - 1], ratioArray[index], ratio)
            return lerp(deltaArray[index - 1], deltaArray[index], p)
        }
    }

    // if ratio is above range, return top of delta (0.0)
    return 0.0
}

Upvotes: 1

Related Questions