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

Posted

技术标签:

【中文标题】简单的 Swift Fibonacci 程序崩溃(Project Euler 2)【英文标题】:Simple Swift Fibonacci program crashing (Project Euler 2) 【发布时间】:2016-01-10 16:01:54 【问题描述】:

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


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


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

Playground 执行中止:执行被中断,原因:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=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 无济于事。有人知道这里发生了什么吗?

【问题讨论】:

这可能会让你感兴趣:codereview.stackexchange.com/questions/60875/…. 同样的问题(在 32 位平台上):***.com/questions/31317369/…. @MartinR Codereview 是您想​​要改进工作代码时去的地方。崩溃的代码与 codereview 无关。 @MathiasEttinger:我知道。我的意图不是建议 OP 在 CR 上发布他的问题。它的意思是“看看这段代码(这是关于相同的PE问题)和评论,你可能会感兴趣。” @MartinR 哦,对不起。我读得有点快,并没有注意到它是指向实际问题的链接,而不是网站的主页。 【参考方案1】:

这没有什么奇怪。你知道 400 斐波那契数有多大吗?

176023680645013966468226945392411250770384383304492191886725992896575345044216019675

Swift Int64UInt64 根本无法处理这么大的数字。后者最大可以达到18446744073709551615 - 甚至不接近。

如果您将变量更改为双精度变量,它会起作用,但会不准确:

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

将产生

2.84812298108489e+83

这有点接近实际值

1.76e+83

幸运的是,您不需要获得那么大的值。我建议不要编写 for 循环,而是编写一个 while 循环,该循环计算下一个斐波那契数,直到满足 break 条件其值不超过四百万

【讨论】:

是的,这是一个巨大的数字,但我认为计算机擅长处理巨大的数字。如果我的理解正确,问题不在于我的代码,而是 Swift 无法处理如此大的数字这一事实? Project Euler 的问题本来就是要通过计算机程序来解决的,那么我该如何使用 Swift 来解决呢? @makenaw 首先:因为这个数字只是超过了内置整数可以容纳的数字。 期间。其次:如果你看一下我的最后一段,你可以很容易地快速计算出这个问题的答案。您的代码通常很好,是的。唯一的问题是 for 循环。 @makenaw:我做过一些PE题,几乎都有适合64位整数的解。 你也可以实现你自己的任意精度类(在这种情况下你只需要实现加法)。将整数表示为可变的数字数组。当然,您仍然受到限制,但不受 Uint 大小的限制。您受到计算机上可用内存量的限制。【参考方案2】:

斐波那契数很快变得非常大。要计算大的斐波那契数,您需要实现某种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://***.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

【讨论】:

以上是关于简单的 Swift Fibonacci 程序崩溃(Project Euler 2)的主要内容,如果未能解决你的问题,请参考以下文章

swift UIDynamicAnimator 崩溃

Swift:多次加载表格时崩溃

UIImagePickerController 崩溃应用程序 |斯威夫特3,Xcode8

Swift - 除非我关闭优化,否则发布构建崩溃

UISwitch 在值更改 IOS Swift 时崩溃

Swift 3.1:自定义错误转换为 NSError 以访问其域属性时崩溃