我正在尝试解决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无济于事。任何人都知道这里发生了什么?
答案 0 :(得分:3)
根本没有任何奇怪的。你知道400斐波纳契数是多大吗?
Int64
Swift UInt64
或18446744073709551615
根本无法处理那么大的数字。后者最多可以达到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