【问题标题】:Optimize a algorithm that find a specific Fibonacci number优化找到特定斐波那契数的算法
【发布时间】:2017-08-24 22:05:02
【问题描述】:

给定一个数字 A,我想找到 Ath 斐波那契数 是 3 的倍数,或者如果数字表示至少有 3 就可以了。

例子:

斐波那契 > 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, ...

输入:1,输出:3;

3 是第一个 3 的倍数或有 3 的斐波那契数 它。

输入:3,输出:21;

21 是第三个斐波那契数,它是 3 的倍数或有 3 它。

编辑: 变量类型更改为 unsigned long long int 并在 Fibonacci 生成器上调整。感谢 @rcgldr 和 @Jarod42 的帮助!

我的代码:

#include<bits/stdc++.h>

using namespace std;

int tem(unsigned long long int i){

    while(i != 0){
        if(i%10 == 3){
            return true;
        }
        i = i/10;
    }
    return false;
}

int main(){

    int a, count = 0;
    unsigned long long int f1 = 1, f2 = 1;

    while(scanf("%d", &a) != EOF){
        for(unsigned long long int i = 2; i > 0; i++){
            i = f1 + f2;
            f1 = f2;
            f2 = i;
                if((i%3 == 0) || tem(i)){
                    count++;
                    if(count == a){
                        cout << i << endl;
                        break;
                    }
                }

        }
    }
}

当 A > 20 时,它开始减速。有道理,因为它往往是指数的。我的代码效率不是很高,但是我没有找到更好的逻辑来使用。

我查看了这些链接,但没有找到结论:

1 - Recursive Fibonacci

2 - Fibonacci Optimization

有什么想法吗?感谢您的帮助!

【问题讨论】:

标签: c++ algorithm


【解决方案1】:

您可以使用此序列加速斐波那契部分

uint64_t f0 = 0;    // fib( 0)
uint64_t f1 = 1;    // fib(-1)
int n = ... ;       // generate fib(n)
    for(int i = 0; i < n; i++){
        std::swap(f0,f1);
        f0 += f1;
    }

注意 Fib(93) 是适合 64 位无符号整数的最大斐波那契数,它也有一个 3。 Fib(92) 是 3 的倍数的最大斐波那契数。

我使用此示例代码查找所有值(a 范围从 0 到 62),它似乎运行得相当快,所以我不确定问题出在哪里。是否启用优化?

#include <iostream>
#include <iomanip>

typedef unsigned long long uint64_t;

int tem(uint64_t i){
    while(i != 0){
        if(i%10 == 3)
            return true;
        i = i/10;
    }
    return false;
}

int main(){
    int a = 0, n;
    uint64_t f0 = 1, f1 = -1;  // fib(-1), fib(-2)

    for(n = 0; n <= 93; n++){
        std::swap(f0, f1);     // f0 = next fib
        f0 += f1;
        if((n&3) == 0 || tem(f0)){
            std::cout << std::setw( 2) << a << " " 
                      << std::setw( 2) << n << " "
                      << std::setw(20) << f0 << std::endl;
            a++;
        }
    }
    return 0;
}

根据编译器的不同,i%10 和 i/10 可以使用乘以“幻数”并移位来代替除以常数。 Visual Studio 2015 为 tem() 生成的代码,相当“聪明”:

tem     proc                                ;rcx = input
        test    rcx,rcx                     ; return false if rcx == 0
        je      SHORT tem1
        mov     r8, 0cccccccccccccccdh      ;magic number for divide by 10
tem0:   mov     rax, r8                     ;rax = magic number
        mul     rcx                         ;rdx = quotient << 3
        shr     rdx, 3                      ;rdx = quotient
        lea     rax, QWORD PTR [rdx+rdx*4]  ;rax = quotient*5
        add     rax, rax                    ;rax = quotient*10
        sub     rcx, rax                    ;rcx -= quotient * 10 == rcx % 10
        cmp     rcx, 3                      ;br if rcx % 10 == 3
        je      SHORT tem2
        mov     rcx, rdx                    ;rcx = quotient  (rcx /= 10)
        test    rdx, rdx                    ;loop if quotient != 0
        jne     SHORT tem0
tem1:   xor     eax, eax                    ;return false
        ret     0

tem2:   mov     eax, 1                      ;return true
        ret     0

tem     endp

【讨论】:

  • 感谢您的帮助!但是,我们在这里有一个误解。 "**or ** 如果数字表示上至少有一个 3。" 如果数字上有一个 3,比如 13,它也应该被接受。就像 3 的倍数一样。
  • @Katreque - 我错过了“或者有一个 3 ...”。我更新了我的答案。
  • int 的类型是个好点。如果我们考虑 A > 60,则数字开始通过 long long int。我将更新为 unsigned long long int。我什至不知道这个交换函数。比for(unsigned long long int i = 2; i &gt; 0; i++) i = f1 + f2; f1 = f2; f2 = i; 快吗?
  • @Katreque - 取决于编译器优化以及处理器是否有 swap 指令。假设 f0 和 f1 在寄存器中,对于 X86 或 X64,有一个 xchg 指令来执行交换,序列可能类似于xchg r8,r9 | add r8,r9,只有两条指令。
  • 我想我明白了。我对寄存器和低级信息的了解不是很好,但这是我进一步挖掘的起点。非常感谢分享!
【解决方案2】:

只是指出一些明显的编码错误

for(unsigned long long int i = 2; i > 0; i++)

是多余的。

for(;;){ 
  unsigned long long i = f1+f2;

应该足够了。其次

return 0; 

没有意义,因为它跳出了while循环。 break 会更好。

【讨论】:

  • 感谢您的建议!我不知道这个符号:for(;;).
【解决方案3】:

有一个聪明的方法来做斐波那契。

http://stsievert.com/blog/2015/01/31/the-mysterious-eigenvalue/

代码在 python 中,仅用于第 n 个数字,但我想你明白了。

def fib(n):
    lambda1 = (1 + sqrt(5))/2
    lambda2 = (1 - sqrt(5))/2
    return (lambda1**n - lambda2**n) / sqrt(5)
def fib_approx(n)
    # for practical range, percent error < 10^-6
    return 1.618034**n / sqrt(5)

【讨论】:

  • 感谢分享!我将尝试在 C++ 中实现,看看它有多快并与我的代码进行比较。
猜你喜欢
  • 1970-01-01
  • 2012-06-11
  • 1970-01-01
  • 1970-01-01
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
  • 2013-03-31
  • 2013-02-09
相关资源
最近更新 更多