我正在尝试解决一个更大的问题,我认为该计划的一个重要部分是用于低效计算。
我需要计算给定数N,即区间[P,Q],其中P是最大的斐波那契数< =到N,而Q是最小的斐波那契数>> =到N
目前,我正在使用地图记录斐波纳契数的值。 查询通常涉及搜索最多为N的所有斐波那契数字,并且它不是非常节省时间,因为它涉及大量的比较。
这种类型的查询会在我的程序中经常出现,我对可以改进查找的方式感兴趣,最好是使用子线性复杂度。
答案 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映射来查找M
和P = F(N)
,两边最近的两个Fibonacci数,然后使用直接Fibonacci映射来计算Q = F(M)
和{{1}}。这涉及更多计算,但搜索次数较少。
答案 1 :(得分:9)
#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)
应该为您提供一个介于P
和Q
的两个整数索引之间的数字。然后,您可以使用封闭形式(如其他答案中所示)来提供实际数字P
和Q
。
请注意,根据您的初始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);
}