简单的Swift Fibonacci程序崩溃(Project Euler 2)

时间:2016-01-10 16:01:54

标签: swift exc-bad-instruction

我正在尝试解决Project Euler上的第二个问题。问题如下:

Fibonacci序列中的每个新术语都是通过添加前两个术语生成的。从1和2开始,前10个术语将是: 1,2,3,5,8,13,21,34,55,89 ...... 通过考虑Fibonacci序列中的值不超过四百万的项,找到偶数项的总和。

我想我已经编写了一个解决方案,但是当我尝试运行我的代码时,它崩溃了我的Swift游乐场并给了我这个错误消息:

  

游乐场执行中止:执行被中断,原因:EXC_BAD_INSTRUCTION(代码= EXC_I386_INVOP,子代码= 0x0)

var prev = 0
var next = 1
var num = 0
var sum = 0

for var i = 1; i < 400; i++ {
    num = prev + next
    if next % 2 == 0 {
        sum += next
    }
    prev = next
    next = num
}
print(sum)

奇怪的是,如果我将循环中的计数器设置为小于93,它就可以正常工作。将变量名称显式设置为Double无济于事。任何人都知道这里发生了什么?

2 个答案:

答案 0 :(得分:3)

根本没有任何奇怪的。你知道400斐波纳契数是多大吗?

Int64

Swift UInt6418446744073709551615根本无法处理那么大的数字。后者最多可以达到var prev : Double = 0 var next : Double = 1 var num : Double = 0 var sum : Double = 0 - 甚至不能接近。

如果您将变量更改为双精度,则它可以正常运行但不准确:

JSONArray

将产生

  

2.84812298108489e + 83

有点接近

的实际值
  

1.76E + 83

幸运的是,你不需要获得那么大的价值。我建议不要写一个for循环但是一个while循环计算下一个fibonacci数,直到满足中断条件,其值不超过四百万

答案 1 :(得分:1)

斐波纳契数很快变得非常大。要计算较大的斐波那契数,您需要实现某种BigNum。这是制作BigNum的版本,该版本在内部实现为数字数组。例如,12345在内部被实现为[1, 2, 3, 4, 5]。这样可以轻松表示任意大的数字。

通过使两个数组具有相同的大小来实现加法,然后使用map添加元素,最后,carryAll函数将数组恢复为个位数。

例如12345 + 67

[1, 2, 3, 4, 5] + [6, 7]            // numbers represented as arrays
[1, 2, 3, 4, 5] + [0, 0, 0, 6, 7]   // pad the shorter array with 0's
[1, 2, 3, 10, 12]                   // add the arrays element-wise
[1, 2, 4, 1, 2]                     // perform carry operation

这是BigNum的实现。也可以使用CustomStringConvertible将结果打印为String

struct BigNum: CustomStringConvertible {
    var arr = [Int]()

    // Return BigNum value as a String so it can be printed
    var description: String { return arr.map(String.init).joined() }

    init(_ arr: [Int]) {
        self.arr = carryAll(arr)
    }

    // Allow BigNum to be initialized with an `Int`
    init(_ i: Int = 0) {
        self.init([i])
    }

    // Perform the carry operation to restore the array to single
    // digits
    func carryAll(_ arr: [Int]) -> [Int] {
        var result = [Int]()

        var carry = 0
        for val in arr.reversed() {
            let total = val + carry
            let digit = total % 10
            carry = total / 10
            result.append(digit)
        }

        while carry > 0 {
            let digit = carry % 10
            carry = carry / 10
            result.append(digit)
        }

        return result.reversed()
    }

    // Enable two BigNums to be added with +
    static func +(_ lhs: BigNum, _ rhs: BigNum) -> BigNum {
        var arr1 = lhs.arr
        var arr2 = rhs.arr

        let diff = arr1.count - arr2.count

        // Pad the arrays to the same length
        if diff < 0 {
            arr1 = Array(repeating: 0, count: -diff) + arr1
        } else if diff > 0 {
            arr2 = Array(repeating: 0, count: diff) + arr2
        }

        return BigNum(zip(arr1, arr2).map { $0 + $1 })
    }
}

// This function is based upon this question:
// https://stackoverflow.com/q/52975875/1630618

func fibonacci(to n: Int) {
    guard n >= 2 else { return }
    var array = [BigNum(0), BigNum(1)]
    for i in 2...n {
        array.append(BigNum())
        array[i] = array[i - 1] + array[i - 2]
        print(array[i])
    }
}

fibonacci(to: 400)

输出:

1
2
3
5
8
...
67235063181538321178464953103361505925388677826679492786974790147181418684399715449
108788617463475645289761992289049744844995705477812699099751202749393926359816304226
176023680645013966468226945392411250770384383304492191886725992896575345044216019675