Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2005 Forums
 Transact-SQL (2005)
 fibonacci sequence

Author  Topic 

Transact Charlie
Master Smack Fu Yak Hacker

3451 Posts

Posted - 2009-08-05 : 12:32:57
Hi All,

this is completely useless but I was wondering how to produce an arbitrarily large list of fibonacci numbers

(0, 1, 1, 2, 3, 5, 8, 13, 21, 34.....)

take the 2 previous numbers and add them together to get the next number...

I came up with this simple CTE

;WITH fib AS (
SELECT
CAST(1 AS DECIMAL(38)) AS [value]
, CAST(0 AS DECIMAL(38)) AS [prec]
, CAST(1 AS INT) AS [Level]

UNION ALL SELECT
CAST(f.[value] + f.[prec] AS DECIMAL(38)) AS [value]
, f.[value] AS [prec]
, f.[level] + 1 AS [level]
FROM
fib f
WHERE
[level] < 183
)
SELECT * FROM fib
OPTION (MAXRECURSION 0)

However, as you can see you can only generate the first 183 numbers this way. any numbers bigger than this are too large to fit into a NUMERIC(38) datatype.

With FLOATS you can push that number up to 1476 numbers (but I'm not sure if the numbers are correct at the highest levels).


;WITH fib AS (
SELECT
CAST(1 AS FLOAT) AS [value]
, CAST(0 AS FLOAT) AS [prec]
, CAST(1 AS INT) AS [Level]

UNION ALL SELECT
CAST(f.[value] + f.[prec] AS FLOAT) AS [value]
, f.[value] AS [prec]
, f.[level] + 1 AS [level]
FROM
fib f
WHERE
[level] < 1476
)
SELECT * FROM fib
OPTION (MAXRECURSION 0)


So I had a think about the data - types and thought that with a string I could get a lot more levels. I came up with this

-- VARCHAR(MAX)
DECLARE @before VARCHAR(MAX)
DECLARE @current VARCHAR(MAX)
DECLARE @level INT
DECLARE @iterations INT

DECLARE @curCounter INT
DECLARE @befCounter INT

DECLARE @barrel VARCHAR(31)
DECLARE @next VARCHAR(MAX)
SET NOCOUNT ON

DECLARE @overflow INT
DECLARE @looper INT

DECLARE @fib TABLE (
[level] INT
, [value] VARCHAR(MAX)
)

SET @before = '0'
SET @current = '1'
SET @level = 1
SET @iterations = 3000

INSERT INTO @fib SELECT @level, @current

WHILE ( @level < @iterations ) BEGIN

SET @curCounter = LEN(@current)
SET @befCounter = LEN(@before)

SET @next = ''
SET @overFlow = 0
SET @looper = 1

WHILE ( @curCounter > 0 ) BEGIN

SET @barrel =
RIGHT(
'00000000000000000000000000000000'
+ CAST(
CAST(
SUBSTRING(
@current
, CASE WHEN @curCounter > 30 THEN @curCounter -29 ELSE 1 END
, CASE WHEN @curCounter > 30 THEN 30 ELSE @curCounter END
)
AS NUMERIC(31)
)

+ CASE
WHEN @befCounter < 1 THEN 0
ELSE CAST(
SUBSTRING(
@before
, CASE WHEN @befCounter > 30 THEN @befCounter -29 ELSE 1 END
, CASE WHEN @befCounter > 30 THEN 30 ELSE @befCounter END
)
AS NUMERIC(31)
)
END

+ CAST(@overFlow AS NUMERIC(31))

AS VARCHAR(31))
, 31)

SET @overFlow = CAST(LEFT(@barrel, 1) AS INT)

SET @curCounter = @curCounter - 30
SET @befCounter = @befCounter - 30

SET @next = @next + REVERSE(RIGHT(@barrel,30))
END

SET @next = @next + CAST(@overFlow AS CHAR(1))
SET @next = REVERSE(@next)
SET @next = SUBSTRING(@next, PATINDEX('%[^0]%', @next), LEN(@next) - PATINDEX('%[^0]%', @next) + 1)


SET @before = @current
SET @current = @next
SET @level = @level + 1

INSERT INTO @fib
SELECT @level, @current
END


-- Check the first 183 numbers to see if they are correct.
;WITH fib AS (
SELECT
CAST(1 AS DECIMAL(38)) AS [value]
, CAST(0 AS DECIMAL(38)) AS [prec]
, CAST(1 AS INT) AS [Level]

UNION ALL SELECT
CAST(f.[value] + f.[prec] AS DECIMAL(38)) AS [value]
, f.[value] AS [prec]
, f.[level] + 1 AS [level]
FROM
fib f
WHERE
[level] < 183
)
SELECT
wh.[level] AS [While Step]
, wh.[value] AS [While Value]
, cte.[value] AS [CTE Value]
, LEN(wh.[value]) AS [Decimal Places]

FROM
@fib wh
LEFT JOIN fib cte ON cte.[level] = wh.[level]

OPTION (MAXRECURSION 0)

Here bringing back the first 3000 numbers (I think -- I've only checked the first 183 against my first CTE).

Can anyone think of a faster or more elegant way to do this? Lets say to product the first 5000 fib numbers?


Charlie
===============================================================
Msg 3903, Level 16, State 1, Line 1736
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION

Lamprey
Master Smack Fu Yak Hacker

4614 Posts

Posted - 2009-08-05 : 12:56:52
I didn't spend too much time looking over your code, but it looks like you are chunking the string into numbers of a length at most 30 digists long and then doing some math and converting back to string. I don't see why this wouldn't work up to the VARCAHR 8000 maximum or even past that using VARCHAR(MAX). I'd have to play around with it.

I did find this article, it uses a function that "adds" the numbers a digit at a time, so I think your way would be better:[url]http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/MSSQLServer/sql-server-fibonacci-sequence[/url] Notice a mroe compact version of the function in the comments.

Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2009-09-01 : 09:07:40
Same idea
CREATE PROCEDURE dbo.uspFibonacci
(
@Fibs SMALLINT = 100
)
AS

SET NOCOUNT ON

SET @Fibs = COALESCE(ABS(@Fibs), 100)

CREATE TABLE #Fib
(
n INT IDENTITY(1, 1) PRIMARY KEY CLUSTERED,
c INT NOT NULL,
f VARCHAR(MAX) NOT NULL
)

IF @Fibs >= 1
INSERT #Fib
SELECT 1,
'0'

IF @Fibs >= 2
INSERT #Fib
SELECT 1,
'1'

DECLARE @f1 VARCHAR(MAX),
@f2 VARCHAR(MAX),
@f VARCHAR(MAX),
@Index INT,
@e TINYINT,
@m TINYINT

SELECT @f1 = '1',
@f2 = '0'

WHILE @Fibs >= 3
BEGIN
IF LEN(@f1) < LEN(@f2)
SET @f1 = REPLICATE('0', LEN(@f2) - LEN(@f1)) + @f1
ELSE IF LEN(@f2) < LEN(@f1)
SET @f2 = REPLICATE('0', LEN(@f1) - LEN(@f2)) + @f2

SELECT @f = '',
@e = 0,
@Index = LEN(@f1)

WHILE @Index >= 1
SELECT @m = (@e + SUBSTRING(@f1, @Index, 1) + SUBSTRING(@f2, @Index, 1)) % 10,
@e = (@e + SUBSTRING(@f1, @Index, 1) + SUBSTRING(@f2, @Index, 1)) / 10,
@f = CHAR(@m + 48) + @f,
@Index = @Index - 1

IF @e > 0
SET @f = CHAR(@e + 48) + @f

INSERT #Fib
VALUES (
DATALENGTH(@f),
@f
)

SELECT @f2 = @f1,
@f1 = @f,
@Fibs = @Fibs - 1
END

SELECT n,
c,
f
FROM #Fib
ORDER BY n


N 56°04'39.26"
E 12°55'05.63"
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2009-09-01 : 09:08:30
Btw, the 5000th Fibonacci number is 1045 character long and reads

2 397 334 346 100 631 452 333 336 800 023 778 743 396 400 988 090 212 332 865 227 234 032 387 117 767 626 167 465 060 795 065 595 580 850 691 237 390 963 845 987 165 478 074 085 124 644 348 902 530 685 083 246 709 423 858 342 692 329 718 110 162 972 268 152 200 857 232 686 119 638 781 547 238 020 078 362 945 470 777 668 711 057 069 618 425 746 387 920 931 255 084 621 360 135 655 698 456 629 322 111 614 827 324 455 767 748 623 844 363 426 260 372 374 195 153 577 101 298 837 831 208 580 530 677 289 982 029 527 164 306 876 024 342 838 547 454 228 388 796 380 077 029 917 639 469 963 653 048 076 473 269 452 943 584 037 848 773 158 456 736 367 057 460 079 075 603 072 996 653 089 318 046 279 296 240 100 777 360 367 200 040 226 807 430 924 334 616 931 577 257 195 085 793 060 133 817 911 514 540 227 011 756 335 999 604 550 121 968 663 793 604 830 945 238 116 686 325 506 344 893 928 776 515 696 088 851 468 818 023 735 825 546 502 317 562 957 459 506 612 704 850 760 351 077 006 532 507 519 813 600 498 603 205 937 022 956 740 021 970 327 599 548 184 626 715 032 015 801 445 754 074 519 753 924 901 317 605 013 561 516 613 650 173 445 818 028 242 577 356 369 143 977 719 495 739 428 130 191 089 993 769 093 308 407 443 558 168 431 535 751 910 046 557 480 949 313 497 996 285 124 526 992 631 353 143 367 314 930 548 703 966 553 707 195 171 094 152 730 704 138 121 243 470 432 644 848 607 501


N 56°04'39.26"
E 12°55'05.63"
Go to Top of Page

madhivanan
Premature Yak Congratulator

22864 Posts

Posted - 2009-09-01 : 09:55:31
Ok. Here are my methods with no loop,no recursion
http://sqlblogcasts.com/blogs/madhivanan/archive/2009/09/01/generate-fibonacci-series-no-loop-no-recursion.aspx

Madhivanan

Failing to plan is Planning to fail
Go to Top of Page

Transact Charlie
Master Smack Fu Yak Hacker

3451 Posts

Posted - 2009-09-01 : 10:10:56
Peso:

Nice -- a lot cleaner than mine.

We get different results for the 5000th value - so one of us (most likely myself is wrong)

My function spits out:

2397334346100631452333336800023778743396400988090212332865227234032387117767626167465060795065595580850691237390963845987165478074085124644348902530685083246709423858342692329718110162972268152200857232686119638781547238020078362945470777668711057069618425746387920931255084621360135655698456629322111614827324455767748623844363426260372374195153577101298837831208580530677289982029527164306876024342838547454228388796380077029917639469963653048076473269452943584037848773158456736367057460079075603072996653089318046279296240100777360367200040226807430924334616931577257195085793060133817911514540227011756335999604550121968663793604830945238116686325506344893928776515696088851468818023735825546502317562957459506612704850760351077006532507519813600498603205937022956740021970327599548184626715032015801445754074519753924901317605013561516613650173445818028242577356369143977719495739428130191089993769093308407443558168431535751910046557480949313497996285124526992631353143367314930548703966553707195171094152730704138121243470432644848607501

Which is the same length as yours but isn't remotely the same.

I've probably made a error somewhere!


Charlie
===============================================================
Msg 3903, Level 16, State 1, Line 1736
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION
Go to Top of Page

Transact Charlie
Master Smack Fu Yak Hacker

3451 Posts

Posted - 2009-09-01 : 10:16:38
quote:
Originally posted by Lamprey

I didn't spend too much time looking over your code, but it looks like you are chunking the string into numbers of a length at most 30 digists long and then doing some math and converting back to string. I don't see why this wouldn't work up to the VARCAHR 8000 maximum or even past that using VARCHAR(MAX). I'd have to play around with it.

I did find this article, it uses a function that "adds" the numbers a digit at a time, so I think your way would be better:[url]http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/MSSQLServer/sql-server-fibonacci-sequence[/url] Notice a mroe compact version of the function in the comments.




This link also produces the number that I get via my 30 digit addition code.


Charlie
===============================================================
Msg 3903, Level 16, State 1, Line 1736
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION
Go to Top of Page

Transact Charlie
Master Smack Fu Yak Hacker

3451 Posts

Posted - 2009-09-01 : 10:45:13
Ah -- I think I see what happened here:

Peso -- did you mispost the number?

after comparing your method with mine the figures all agree.

Whatever you posted as the 5000th number before isn't right. You've mangled it somehow.

I think you've cut out every 4th number? Where there are spaces in your posted number there should be numbers!

Oh well.....


Charlie
===============================================================
Msg 3903, Level 16, State 1, Line 1736
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2009-09-01 : 15:48:19
No, we're both right. It was my code to split the string that used 1 instead of 0 in the STUFF function.



N 56°04'39.26"
E 12°55'05.63"
Go to Top of Page

Arnold Fribble
Yak-finder General

1961 Posts

Posted - 2009-09-01 : 17:14:59
Haskell agrees!

fibs = 0 : scanl (+) 1 fibs
fib n = fibs !! (n-1) -- Haskell's list subscript operator is zero-baed

Go to Top of Page
   

- Advertisement -