简单的 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 Int64
或 UInt64
根本无法处理这么大的数字。后者最大可以达到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)的主要内容,如果未能解决你的问题,请参考以下文章