寻找最接近的斐波纳契数

时间:2011-10-20 22:31:45

标签: c++ algorithm math complexity-theory discrete-mathematics

我正在尝试解决一个更大的问题,我认为该计划的一个重要部分是用于低效计算。

我需要计算给定数N,即区间[P,Q],其中P是最大的斐波那契数< =到N,而Q是最小的斐波那契数>> =到N

目前,我正在使用地图记录斐波纳契数的值。 查询通常涉及搜索最多为N的所有斐波那契数字,并且它不是非常节省时间,因为它涉及大量的比较。

这种类型的查询会在我的程序中经常出现,我对可以改进查找的方式感兴趣,最好是使用子线性复杂度。

11 个答案:

答案 0 :(得分:30)

Fibonacci数字由Binet公式给出

F(n) = ( phi^n - (1-phi)^n ) / \sqrt{5}

其中phi是黄金比例,

phi = (1 + \sqrt{5}) / 2. 

这可以直接实现(Python示例):

<<fibonacci_binet.py>>=
phi = (1 + 5**0.5) / 2

def fib(n):
    return int(round((phi**n - (1-phi)**n) / 5**0.5))

由于浮点舍入错误,但这只能为n < 70提供正确的结果。

Binet的公式可以通过忽略(1-phi)^n项来反转,n项在大F(n)时消失。因此,我们可以定义反Fibonacci函数,当给定n时,返回F(1) = F(2)(忽略<<fibonacci_binet.py>>= from math import log def fibinv(f): if f < 2: return f return int(round(log(f * 5**0.5) / log(phi))) ):

N

这里使用舍入对我们有利:它消除了我们修改Binet公式所引入的错误。实际上,当给定任何Fibonacci数时,该函数将返回正确答案,该数字可以存储为计算机内存中的精确整数。另一方面,它不验证给定的数字实际上是斐波纳契数;输入一个大的Fibonacci数或任何接近它的数字都会得到相同的结果。因此,您可以使用此想法查找最接近给定数字的斐波纳契数。

然后,我的想法是应用反Fibonacci映射来查找MP = F(N),两边最近的两个Fibonacci数,然后使用直接Fibonacci映射来计算Q = F(M)和{{1}}。这涉及更多计算,但搜索次数较少。

答案 1 :(得分:9)

我在 https://ideone.com/H6SAd

上发布了完整的Proof-Of-Concept实施方案
  • 这是非常快的
  • 它使用adhoc二进制搜索
  • 在阅读其他回复后,
  • 编辑,我感觉那里概述的数学思想(PengOne)将导致更快的查找(基本上:反转公式加上一层的计算()/ ceil()调用?)

#include <cmath>
#include <iostream>

const double pheta = 0.5*(std::sqrt(5)+1);

double fib(unsigned int n)
{
    return (std::pow(pheta, n) - std::pow(1 - pheta, n)) / std::sqrt(5);
}

unsigned int fibo_lowerbound(double N, unsigned min=0, unsigned max=1000)
{
    unsigned newpivot = (min+max)/2;
    if (min==newpivot)
        return newpivot;

    if (fib(newpivot) <= N)
        return fibo_lowerbound(N, newpivot, max);
    else
        return fibo_lowerbound(N, min, newpivot);
}

std::pair<double, double> fibo_range(unsigned int n)
{
    unsigned int lbound = fibo_lowerbound(n);
    return std::make_pair(fib(lbound), fib(lbound+1));
}

void display(unsigned int n)
{
    std::pair<double, double> range = fibo_range(n);
    std::cout << "Fibonacci range wrapping " << n << " is "
              << "[" << (unsigned long long) range.first << ", " << (unsigned long long) range.second << "]"
              << std::endl;
}

int main()
{
    display(1044);
    display(8999913);
    display(7);
    display(67);
}

输出结果为:

Fibonacci range wrapping 1044 is [987, 1597]
Fibonacci range wrapping 8999913 is [5702887, 9227465]
Fibonacci range wrapping 7 is [5, 8]
Fibonacci range wrapping 67 is [55, 89]

答案 2 :(得分:5)

您可以使用斐波那契数字的closed-form expression

由于它中的第二项非常小,你只能用第一项来近似它,所以n可以找到基黄色比率对数。

答案 3 :(得分:3)

答案 4 :(得分:3)

我刚刚做了一个CodeChef拼图就是这个问题(http://www.codechef.com/problems/DPC204)。我简单地计算了从0到范围结束的斐波纳契数列,并计算了在范围开始之后的数量。我对其样本输入的测试分别为2.6M和0.00s,因此nieve解决方案足够快。

基本上,我创建了一个由unsigned int[333]组成的大型unsigned-int类,并为每个循环计算两个数字,以避免交换。

start with A=0,B=1;
A+=B;B+=A; 
now A==1,B==2, the next two Fib. numbers, with no swaps.
A+=B;B+=A; 
now A==3,B==5, the next two Fib. numbers, with no swaps.

由于你必须停下来检查一个或两个数字是否在范围内,但A是否有点复杂

我在CodeChef上的解决方案时间为0.00秒,所以我觉得这个方法应该足够快,你只需编写一个函数,将uint[333]加到另一个uint[333]上。 (使用全部32位,每个十进制数字只有字符)

答案 5 :(得分:1)

由于您只考虑64位整数,因此最多需要考虑约100个Fibonacci数。您可以使用它们的定义F n = F n-1 + F n-2 预先计算它们。

然后预先计算另一个表,该表将前导零位的数量映射到斐波那契数字表中的索引,到第一个具有多个前导零位的数字。

现在找到间隔使用你的数字的前导零位数(这可以快速计算,因为许多处理器有一个特殊的指令),使用第二个表找到一个起点,并线性搜索第一个表的间隔。由于在相邻的2的幂之间最多有两个Fibonacci数,因此最多需要2步。

这样做的好处是它只使用整数运算,这是精确的并且往往比浮点计算更快。

答案 6 :(得分:0)

使用此处的最后一个表格进行反向,您可以找到当前数字周围的Fib数字的两个索引。 http://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding

log(N * sqrt(5)) / log((1+sqrt(5))/2)应该为您提供一个介于PQ的两个整数索引之间的数字。然后,您可以使用封闭形式(如其他答案中所示)来提供实际数字PQ

请注意,根据您的初始Fib条件,您可能会一个人离开。

答案 7 :(得分:0)

  

我认为该计划的一个重要部分是用于低效计算。

你有profiled你的代码吗?作为一般原则,不要过早优化;衡量哪些部分正在放慢速度。这样,当您尝试优化时,您可以判断优化是否有帮助或受到影响(通常,声音良好的优化会使其运行更糟;因为编译器无法进行优化或者您无法使用cpu的寄存器/缓存最佳)。

如果这会减慢你的速度,我会做类似彭的伟大解决方案,但预先计算所有的Fib数到你的最大值并将它们存储到一个数组中,该数组由关闭的相应的expoential(n)索引 - form(phi^**n - (1-phi)**n)/sqrt(5)。他的方法会使用浮点运算错误计算大n的Fib数;除非你使用任意高精度(慢)。所以你的起始数组是fib_array = [0,1,1,2,3,5,8,13,... ]。然后忽略小(1-phi)**n术语,反转fib找到n(例如,Peng的fib_inv),并将fib_array[n]作为你的第一个界限。如果这个界限小于(大于)你的值;你已经找到了下限(上限),因此另一个边界应该是fib_array[n+1]fib_array[n-1]) 或者如果你想计算它,使用给定的N比Binet的公式更好。 http://en.literateprograms.org/Fibonacci_numbers_%28Python%29

就个人而言,我会检查以确保第二个边界位于该术语的另一侧作为第一个边界(在极少数情况下我们不应该忽略(1-phi)**n术语;您可能如果该术语受例如fib_array[n+1]fib_array[n+2]的限制,则可以进行另一次查找。 (这种检查可能是多余的;但你必须首先证明这一点,而且在我的书中,一个额外的比较是安全的。)

答案 8 :(得分:0)

构建一个适合8个字节的Fibonacci数表;只有94.这将节省你通过每次迭代计算它们。这里不需要浮点数学。

然后使用二进制搜索在当时的数字下方和上方找到数字。这将节省您比较所有数字,并将您的搜索减少到一个恒定的搜索时间。

这符合您的要求,但请注意,您的要求未指定N应返回的内容,以便在64位整数空间中没有Q,即N&gt; 12,200,160,415,121,876,738。如果您关心这一点,请决定如何处理它。 :)

#include "stdint.h"
#include "stdio.h"
#include "stdlib.h"
#include "time.h"

/* build a table of all fibonacci numbers that fit in a uint64_t. */
static const int fibonacciCount = 94;
uint64_t fibonacciSequence[fibonacciCount];
static void precalc(void) {
    fibonacciSequence[0] = 0;
    fibonacciSequence[1] = 1;
    for (int i = 2; i < fibonacciCount; ++i) {
        fibonacciSequence[i] = fibonacciSequence[i-2] + fibonacciSequence[i-1];
    }
}

/* do a binary search for the Fibonacci numbers >= N and <= N */
static void find_closest_fibonacci(uint64_t N, uint64_t *P, uint64_t *Q) {
    int upper = fibonacciCount;
    int lower = 0;
    do {
        int mid = ((upper - lower) >> 1) + lower;
        uint64_t midValue = fibonacciSequence[mid];
        if ( midValue > N ) {
            upper = mid;
        } else if ( midValue < N ) {
            lower = mid + 1;
        } else {
            *P = fibonacciSequence[ mid ];
            *Q = fibonacciSequence[ mid ];
            return;
        }
    } while ( upper > lower );
    *P = fibonacciSequence[ lower - 1 ];
    *Q = fibonacciSequence[ lower ];
}

/* hacked together 64 bit random number generator,
 used just in tester only */
static uint64_t rand64(void) {
    /* totally flawed as a random number generator,
     but that's not the point here. */
    uint64_t v = 0;
    for (int i = 0; i < 8; ++i) {
        v = (v << 8) + (rand() % 256);
    }
    return v;
}

int main (int argc, const char * argv[]) {
    srand( (unsigned)time( NULL ) );

    precalc(); /* do this once only */

    uint64_t upperBound = fibonacciSequence[fibonacciCount - 1];
    printf( "Upper bound is %qu\n", upperBound );

    /* build a sample to run against the algorithm
     we favor mostly numbers below RAND_MAX, because
     if we test across all of UINT64_MAX the results are
     pretty boring. */
    static const int sampleCount = 100;
    static const int normalSampleCount = 90;
    uint64_t numbers[sampleCount];
    for (int i = 0; i < normalSampleCount; ++i) {
        numbers[i] = rand();
    }
    for (int i = normalSampleCount; i < sampleCount; ++i) {
        uint64_t number;
        do {
            number = rand64();
        } while ( number > upperBound );
        numbers[i] = number;
    }

    /* use described algorithm */
    for (int i = 0; i < 100; ++i) {
        uint64_t P;
        uint64_t Q;
        uint64_t N = numbers[i];
        find_closest_fibonacci(N, &P, &Q);
        printf( "%qu [%qu,%qu]\n", N, P, Q );
    }

    return 0;
}

将您拥有的任何其他算法放在同一个文件中,然后针对同一个测试人员运行它。

答案 9 :(得分:0)

在Scala上,找到壁橱的斐波那契数字也很简单:

val phi = (1 + sqrt(5))/2
def fib(n: Long): Long =  round( ( pow(phi,n) -  pow((1-phi),n) ) / sqrt(5)  )
def fibinv(f: Long): Long =  if (f < 2) f else round( log(f * sqrt(5) ) /log(phi))

答案 10 :(得分:-2)

当且仅当(5 * n ^ 2 + 4)或(5 * n ^ 2 - 4)中的一个或两个是完美正方形时,数字是斐波那契。我正在使用这个前提来验证输入数字是否属于斐波纳契系列。

#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

typedef struct node{

    int64_t value;
    struct node *next;

}Node;

Node *head ;

void readElements(int);
int isPerfectSquare(int64_t sqrValue);

int main(){

    int input_count , flag=0;
    Node *temp_node = NULL;
    int64_t sqrValue = 0;

    scanf("%d" , &input_count);

    if((input_count < 1 )||(input_count > 100000)){
        printf("Total number of Inputs out of Range ..!!\n");
        return 1;
    }

    readElements(input_count);

    /*Reading the elements from the list*/

    temp_node = head;

    while(temp_node != NULL){

        sqrValue = 5*pow(temp_node->value , 2);
        flag = (isPerfectSquare(sqrValue+4) || isPerfectSquare(sqrValue-4));

        if(flag == 1){
            printf("IsFibo\n");
        }
        else{
            printf("IsNotFibo\n");
        }

        temp_node = temp_node->next;

    }   



    return 0;

}


void readElements(int input_count){

    int temp = 0;
    int64_t val = 0;
    Node *temp_node =NULL , *cur = NULL;
    char b[20];


    while (temp < input_count) {

        scanf("%s" , b);
        val = atol(b);

        if(val < 0 || val >10000000000)
            continue;

        temp_node = (Node*) malloc(sizeof(Node));

        temp_node->value = val;
        temp_node->next = NULL;

        if(head == NULL){
            head = cur = temp_node;
        }
        else{
            cur->next = temp_node;
            cur = temp_node;
        }

        temp++;

    }

}

int isPerfectSquare(int64_t sqrValue){

    int64_t s = 0;

    s = sqrt(sqrValue);

    return(s*s == sqrValue);

}
相关问题