如何将整数分解为2的幂的数组?

时间:2018-08-30 13:47:11

标签: arrays swift math

我正在尝试实现类似二进制掩码的功能,其中将一个Int转换为其组成的数组(2的幂),例如:

69 = [64, 4, 1]

我不想使用二进制掩码和移位,因为我想对界面元素使用Swift枚举:

enum State: Int{
    case readyButton = 1
    case workingButton = 2
    case sleepingButton = 4
   //etc
}

then 7 = [.sleepingButton, .workingButton, .readyButton] // 7 = 4+2+1

Swift中有一些内置工具可以帮助我将Int转换为2个组件的力量吗?

2 个答案:

答案 0 :(得分:2)

您可能正在寻找类似的东西。在编写时,如果值不匹配,它将崩溃,但是您可以根据需要对其进行调整。尽管它确实使用了shift,但是它仍然可以很好地转换为您的枚举。

extension UInt32 {
    func state() -> [State] {
        var bitvals: [UInt32] = []
        var mask: UInt32 = 1
        while mask > 0 {
            if mask & self > 0 {
                bitvals.append(mask)
            }
            mask = mask << 1
        }
        let state = bitvals.map { State(rawValue: $0)! }
        return state
    }
}
enum State: UInt32 {
    case readyButton = 1
    case workingButton = 2
    case sleepingButton = 4
}

let val: UInt32 = 7
print(val.state())

这将打印出您在问题末尾给出的示例。

答案 1 :(得分:1)

更新后的答案,将任何Int转换为2的幂的数组。如果您希望它崩溃为负值,则取消注释致命错误

enum State: Int {
    case illegal = -1
    case readyButton = 1
    case workingButton = 2
    case sleepingButton = 4
}

这是将整数分解为2的幂的一般解决方案:

extension Int {

    func toPowersOf2() -> [Int] {
        guard self > 0 else {
//            fatalError("The number should be strictly positive")
            print("The number should be strictly positive")
            return [-1] //not really - use fatal error above to enforce crash if you don't want this behavior
        }
        var arrayOfPowers: [Int] = [] //Will hold the desired powers
        var remainder: Int = self   //We will substract found powers from the original number

        //Since Ints are coded on 64 bits (the 64th is for the sign)
        //Let's create an array of all the powers of 2 that
        //could be used to decompose an integer
        let powers = (0...62).map { NSDecimalNumber(decimal: pow(2.0, $0)).intValue }

        //Let's go from the highest to the lowest power
        for i in (0 ..< powers.count).reversed() {
            //Here we are looking for the power just smaller than the remainder
            if i < powers.count - 1, powers[i] <= remainder, powers[i + 1] > remainder {
                let p = powers[i]
                arrayOfPowers.append(p)
                remainder -= p
            }

                //if this is the biggest power and it is smaller than the number, then add it to arrayOfPowers
            else if i == powers.count - 1, powers[i] <= remainder {
                let p = powers[i]
                arrayOfPowers.append(p)
            }
        }
        return arrayOfPowers
    }

    func toStateArray() -> [State] {
        let array = self.toPowersOf2().map{ State(rawValue: $0) }.filter{ $0 != nil }
        return array as! [State]
    }
}

您可以像这样使用它:

(-1).toStateArray()//[illegal]
0.toStateArray()   //[illegal]
7.toPowersOf2()    //[4, 2, 1]
7.toStateArray()   //[sleepingButton, workingButton, readyButton]