Простой сбой программы Swift Фибоначчи (Project Euler 2)

Я пытаюсь решить вторую проблему на Project Euler. Проблема заключается в следующем:


Каждый новый член в последовательности Фибоначчи генерируется путем добавления двух предыдущих членов. Начиная с 1 и 2, первые 10 слагаемых будут: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... Рассматривая слагаемые в последовательности Фибоначчи, значения которых не превышают четыре миллиона, найти сумму четных членов.


Я думаю, что написал решение, но когда я пытаюсь запустить мой код, он ломает мою игровую площадку 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, он будет работать нормально. Явное задание двойных имен переменных не помогает. Кто-нибудь знает, что здесь происходит?

2 ответа

В этом нет ничего странного. Вы знаете, насколько велико число 400 Фибоначчи?

176023680645013966468226945392411250770384383304492191886725992896575345044216019675

стриж 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, который вычисляет следующее число Фибоначчи, пока не будет выполнено условие разрыва , значения которого не превышают четырех миллионов.

Числа Фибоначчи быстро становятся очень большими. Чтобы вычислить большие числа Фибоначчи, вам нужно реализовать 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://stackru.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
Другие вопросы по тегам