Swift中双精度的LCM算法

时间:2015-02-05 17:04:36

标签: xcode swift int double

我需要将一个双打数组转换为整数,同时保持它们的比率相同并尽可能简单。例如,[0.7,0,-0.7]应该变为[1,0,-1]并且[24,12,0]应该变为[2,1,0]。我不确定这是否会涉及获得双打的最小公倍数,如果是这样的话怎么办呢?

1 个答案:

答案 0 :(得分:6)

首先,浮点数没有GCD或LCM。您 必须先将输入转换为 rational 数字。

这并不像听起来那么容易,因为像0.7这样的小数部分不能完全表示为二进制浮点数和 将在0.69999999999999996中存储为Double之类的内容。  所以如何从那里到7/10并不是很明显。

因此需要指定精度。然后你可以 使用 Continued Fractions 有效地创建(有限或无限)分数 h n / k n 的序列,它们是给定实数 X

以下是this JavaScript implementation到Swift的翻译:

typealias Rational = (num : Int, den : Int)

func rationalApproximationOf(x0 : Double, withPrecision eps : Double = 1.0E-6) -> Rational {
    var x = x0
    var a = floor(x)
    var (h1, k1, h, k) = (1, 0, Int(a), 1)

    while x - a > eps * Double(k) * Double(k) {
        x = 1.0/(x - a)
        a = floor(x)
        (h1, k1, h, k) = (h, k, h1 + Int(a) * h, k1 + Int(a) * k)
    }
    return (h, k)
}

示例:

rationalApproximationOf(0.7)      // (7, 10) i.e. 7/10
rationalApproximationOf(0.142857) // (1, 7)  i.e. 1/7

我已将默认精度设置为1.0E-6,但您可以调整它 满足您的需求。

然后你需要GCD的功能(最大公约数) 和LCM(最低公倍数)。这是一个简单的实现:

// GCD of two numbers:
func gcd(var a : Int, var b : Int) -> Int {
    while b != 0 {
        (a, b) = (b, a % b)
    }
    return abs(a)
}

// GCD of a vector of numbers:
func gcd(vector : [Int]) -> Int {
    return reduce(vector, 0) { gcd($0, $1) }
}

// LCM of two numbers:
func lcm(var a : Int, var b : Int) -> Int {
    return (a / gcd(a, b)) * b
}

// LCM of a vector of numbers:
func lcm(vector : [Int]) -> Int {
    return reduce(vector, 1) { lcm($0, $1) }
}

使用所有这些实用程序,您的功能现在可以实现为

func simplifyRatios(numbers : [Double]) -> [Int] {
    // Normalize the input vector to that the maximum is 1.0,
    // and compute rational approximations of all components:
    let maximum = maxElement(map(numbers) { abs($0) } )
    let rats = map(numbers) { rationalApproximationOf($0/maximum) }

    // Multiply all rational numbers by the LCM of the denominators:
    let commonDenominator = lcm(map(rats) { $0.den })
    let numerators = map(rats) { $0.num * commonDenominator / $0.den }

    // Divide the numerators by the GCD of all numerators:
    let commonNumerator = gcd(numerators)
    return map(numerators) { $0 / commonNumerator }
}

示例:

simplifyRatios([0.7, 0, -0.7])   // [1, 0, -1]
simplifyRatios([24, 12, 0])      // [2, 1, 0]
simplifyRatios([1.3, 0.26, 0.9]) // [65, 13, 45]