Project Euler # 25 The 1000 digit Fibonacci indexProject Euler 25 - 1000-digit Fibonacci NumberEuler #25 ScalaProject Euler 25 - x-digit Fibonacci NumberProject Euler Problems #1-#5Project Euler Problem #41Wascally WabbitsGeneralized Project Euler 2: A sledgehammer to crack a nutProject Euler #2: Even Fibonacci numbersProject Euler 2 - Fibonacci sequenceFinding large Fibonacci Number in Python
Argand formula and more for quaternions?
Why does the Rust compiler not optimize code assuming that two mutable references cannot alias?
Was Donald Trump at ground zero helping out on 9-11?
Why are we moving in circles with a tandem kayak?
Is every compact set is closed in any topological space?
How does the Thief's Fast Hands feature interact with mundane and magical shields?
how to understand the error info "Illegal parameter number in definition of reserved@a. ...t2+cdots+sqrt2}}_n项 , cdots 收敛.$}"
Convert graph format for Mathematica graph functions
Do we need to "Bulkify" Flows?
Omnidirectional LED, is it possible?
Composing fill in the blanks
Surviving a planet collision?
Wrapping IMemoryCache with SemaphoreSlim
Why did Windows 95 crash the whole system but newer Windows only crashed programs?
Is it unprofessional to mention your cover letter and resume are best viewed in Chrome?
8086 stack segment and avoiding overflow in interrupts
Did Vladimir Lenin have a cat?
Why does the Eurostar not show youth pricing?
Why does aggregate initialization not work anymore since C++20 if a constructor is explicitly defaulted or deleted?
Does Ubuntu reduces battery life?
Just how much information should you share with a former client?
Complaints from (junior) developers against solution architects: how can we show the benefits of our work and improve relationships?
What would the United Kingdom's "optimal" Brexit deal look like?
How do I make my photos have more impact?
Project Euler # 25 The 1000 digit Fibonacci index
Project Euler 25 - 1000-digit Fibonacci NumberEuler #25 ScalaProject Euler 25 - x-digit Fibonacci NumberProject Euler Problems #1-#5Project Euler Problem #41Wascally WabbitsGeneralized Project Euler 2: A sledgehammer to crack a nutProject Euler #2: Even Fibonacci numbersProject Euler 2 - Fibonacci sequenceFinding large Fibonacci Number in Python
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
$begingroup$
The Fibonacci sequence is defined by the recurrence relation:
Fn = Fn−1 + Fn−2, where F1
= 1 and F2 = 1.
Hence the first 12 terms will be:
F1 = 1
F2 = 1
F3 = 2
F4 = 3
F5 = 5
F6 = 8
F7 = 13
F8 = 21
F9 = 34
F10 = 55
F11 = 89
F12 = 144
The 12th term, F12, is the first term to contain three digits.
What is the index of the first term in the Fibonacci sequence to
contain 1000 digits?
from time import time
def fib(n):
"""returns index of the first fibonacci of length n digits."""
fibs = [0, 1]
while len(str(fibs[-1])) < n:
fibs.append(fibs[-1] + fibs[-2])
return len(fibs) - 1
if __name__ == '__main__':
time1 = time()
print(fib(1000))
print(f'Time: time() - time1 seconds.')
python beginner python-3.x programming-challenge fibonacci-sequence
$endgroup$
add a comment |
$begingroup$
The Fibonacci sequence is defined by the recurrence relation:
Fn = Fn−1 + Fn−2, where F1
= 1 and F2 = 1.
Hence the first 12 terms will be:
F1 = 1
F2 = 1
F3 = 2
F4 = 3
F5 = 5
F6 = 8
F7 = 13
F8 = 21
F9 = 34
F10 = 55
F11 = 89
F12 = 144
The 12th term, F12, is the first term to contain three digits.
What is the index of the first term in the Fibonacci sequence to
contain 1000 digits?
from time import time
def fib(n):
"""returns index of the first fibonacci of length n digits."""
fibs = [0, 1]
while len(str(fibs[-1])) < n:
fibs.append(fibs[-1] + fibs[-2])
return len(fibs) - 1
if __name__ == '__main__':
time1 = time()
print(fib(1000))
print(f'Time: time() - time1 seconds.')
python beginner python-3.x programming-challenge fibonacci-sequence
$endgroup$
4
$begingroup$
If you examine the Wikipedia article on Fibonacci numbers, in particular the section "Computation by Rounding, it should be possible to calculate the the first F_n > 10^1000 by taking base 10 logs of both sides.
$endgroup$
– James K Polk
Jul 20 at 13:27
$begingroup$
@JamesKPolk Exactly.
$endgroup$
– Eric Duminil
Jul 21 at 13:18
add a comment |
$begingroup$
The Fibonacci sequence is defined by the recurrence relation:
Fn = Fn−1 + Fn−2, where F1
= 1 and F2 = 1.
Hence the first 12 terms will be:
F1 = 1
F2 = 1
F3 = 2
F4 = 3
F5 = 5
F6 = 8
F7 = 13
F8 = 21
F9 = 34
F10 = 55
F11 = 89
F12 = 144
The 12th term, F12, is the first term to contain three digits.
What is the index of the first term in the Fibonacci sequence to
contain 1000 digits?
from time import time
def fib(n):
"""returns index of the first fibonacci of length n digits."""
fibs = [0, 1]
while len(str(fibs[-1])) < n:
fibs.append(fibs[-1] + fibs[-2])
return len(fibs) - 1
if __name__ == '__main__':
time1 = time()
print(fib(1000))
print(f'Time: time() - time1 seconds.')
python beginner python-3.x programming-challenge fibonacci-sequence
$endgroup$
The Fibonacci sequence is defined by the recurrence relation:
Fn = Fn−1 + Fn−2, where F1
= 1 and F2 = 1.
Hence the first 12 terms will be:
F1 = 1
F2 = 1
F3 = 2
F4 = 3
F5 = 5
F6 = 8
F7 = 13
F8 = 21
F9 = 34
F10 = 55
F11 = 89
F12 = 144
The 12th term, F12, is the first term to contain three digits.
What is the index of the first term in the Fibonacci sequence to
contain 1000 digits?
from time import time
def fib(n):
"""returns index of the first fibonacci of length n digits."""
fibs = [0, 1]
while len(str(fibs[-1])) < n:
fibs.append(fibs[-1] + fibs[-2])
return len(fibs) - 1
if __name__ == '__main__':
time1 = time()
print(fib(1000))
print(f'Time: time() - time1 seconds.')
python beginner python-3.x programming-challenge fibonacci-sequence
python beginner python-3.x programming-challenge fibonacci-sequence
edited Jul 20 at 2:27
200_success
135k21 gold badges173 silver badges443 bronze badges
135k21 gold badges173 silver badges443 bronze badges
asked Jul 20 at 0:32
emadboctoremadboctor
5731 silver badge11 bronze badges
5731 silver badge11 bronze badges
4
$begingroup$
If you examine the Wikipedia article on Fibonacci numbers, in particular the section "Computation by Rounding, it should be possible to calculate the the first F_n > 10^1000 by taking base 10 logs of both sides.
$endgroup$
– James K Polk
Jul 20 at 13:27
$begingroup$
@JamesKPolk Exactly.
$endgroup$
– Eric Duminil
Jul 21 at 13:18
add a comment |
4
$begingroup$
If you examine the Wikipedia article on Fibonacci numbers, in particular the section "Computation by Rounding, it should be possible to calculate the the first F_n > 10^1000 by taking base 10 logs of both sides.
$endgroup$
– James K Polk
Jul 20 at 13:27
$begingroup$
@JamesKPolk Exactly.
$endgroup$
– Eric Duminil
Jul 21 at 13:18
4
4
$begingroup$
If you examine the Wikipedia article on Fibonacci numbers, in particular the section "Computation by Rounding, it should be possible to calculate the the first F_n > 10^1000 by taking base 10 logs of both sides.
$endgroup$
– James K Polk
Jul 20 at 13:27
$begingroup$
If you examine the Wikipedia article on Fibonacci numbers, in particular the section "Computation by Rounding, it should be possible to calculate the the first F_n > 10^1000 by taking base 10 logs of both sides.
$endgroup$
– James K Polk
Jul 20 at 13:27
$begingroup$
@JamesKPolk Exactly.
$endgroup$
– Eric Duminil
Jul 21 at 13:18
$begingroup$
@JamesKPolk Exactly.
$endgroup$
– Eric Duminil
Jul 21 at 13:18
add a comment |
5 Answers
5
active
oldest
votes
$begingroup$
It's possible to calculate the nth Fibonacci number directly thanks to Binet's formula.
Due to floating point errors, the formula doesn't give correct values if n
is too large, but it works fine in order to calculate the number of digits.
from math import log10, floor, ceil
def fibonacci_digits(n):
if n < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return floor(n * log10(ϕ) - log10(5) / 2) + 1
It's possible to calculate the inverse of this function. This way, you can find the first Fibonacci with k
digits directly, without any loop:
def euler25(k):
if k < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return ceil((k + log10(5) / 2 - 1) / log10(ϕ))
It seems to return the correct answer for any k
in less than 1 μs :
>>> euler25(1000)
4782
>>> euler25(10_000)
47847
>>> euler25(100_000)
478495
>>> euler25(100_000_000_000_000_000)
478497196678166592
The digits are the same as in:
>>> 1 / log10(ϕ)
4.784971966781666
$endgroup$
1
$begingroup$
Oh god,φ
as an identifier. Can't wait to start seeing bugs likeφ = 1; assert ϕ == 1
...
$endgroup$
– Peilonrayz
Jul 21 at 4:06
$begingroup$
@Peilonrayz: I agree it can be dangerous. I used two different symbols at first, without noticing it. :-/ Your example isn't a bug, it's a feature!
$endgroup$
– Eric Duminil
Jul 21 at 7:28
2
$begingroup$
You say feature, I say death trap. ;) Yeah I know about NFKC normalization, that's how I made my example legit ;P
$endgroup$
– Peilonrayz
Jul 21 at 7:33
2
$begingroup$
@Peilonrayz:i
leads the pack with 21 collisions:"iᵢⁱℹⅈⅰⓘi𝐢𝑖𝒊𝒾𝓲𝔦𝕚𝖎𝗂𝗶𝘪𝙞𝚒"
. Next time I want to troll a colleague, I'll be sure to use every single synonym fori
in a script. :D
$endgroup$
– Eric Duminil
Jul 21 at 8:26
2
$begingroup$
You've gained my eternal ire and awe. :O Also, 𝚄𝓃𝙞𝙘𝔬de 𝒊𝘴 𝚝ⓗ𝕖 ⓝ𝔢𝑤 |𝟯³𝟳 ˢ𝒑ⅇ𝖆𝙠. - Yeah there's an impressive amount of collisions, which should make Unicode easier to support.
$endgroup$
– Peilonrayz
Jul 21 at 8:37
|
show 2 more comments
$begingroup$
Two comments:
There isn't any reason to keep a list of all the fib numbers. You only need the last two numbers to compute the next one.
A number with 1000 digits satisfies 1e1000 <= f < 1e1001. It might be faster to compare f to the numbers rather than to convert it to a string and get its length.
def fib_of_length(n):
"""returns index of the first fibonacci of length n digits.
for n > 0.
"""
bound = 10**(n-1)
fib, previous_fib = 1,0
index = 1
while fib < bound:
fib, previous_fib = fib + previous_fib, fib
index += 1
return index
$endgroup$
$begingroup$
It's reasonably fast, taking into account that you iterate over every Fibonacci number.
$endgroup$
– Eric Duminil
Jul 21 at 8:11
add a comment |
$begingroup$
This isn't a full review but something I found very interesting about this problem: I would have thought that keeping a list of every Fibonacci number would get expensive over larger numbers but that doesn't seem to be the case with my testing.
Instead it seems the bottleneck is the line:
len(str(fibs[-1])) < n:
I ran the function for n = 10,000 and it took roughly 32 seconds to run. If you instead do a comparison to a constant value such as:
threshold = 10 ** (n-1)
while fibs[-1] <= threshold:
The time drops to about a 10th of a second. If you change your list to only track the last three numbers that times is roughly halved again.
$endgroup$
1
$begingroup$
You should consider moving the actual computation out to a constant ...threshold = 10 ** (n - 1)
... then later you havefibs[-1] <= threshold
.... so you only have to do that computation once.
$endgroup$
– rolfl♦
Jul 22 at 11:26
1
$begingroup$
Instead of just tacking on text at the end, fix your initial answer to show the good way first. Some Python implementations would CSE that loop-invariant10 ** (n-1)
, but unlike C/C++ you apparently have to do that optimization manually. Anyway yes, converting a huge integer from binary to a base-10 string is very slow, requiring repeated division by 10 or equivalent.
$endgroup$
– Peter Cordes
Jul 22 at 19:59
add a comment |
$begingroup$
Project Euler has quite a few problems on the Fibonacci numbers. Other things also repeatedly crop up, like prime numbers, triangular numbers, and so on. In fact, quite a few problems build on earlier, easier, problems.
Because of this it is worth it to start designing your functions for reusability. Put these functions into modules, so you can reuse them later. This one should probably go to the sequences.py
module or just a general utils.py
.
Currently you have one function that does everything. Instead you will probably want a generator function that generates all Fibonacci numbers, which you can then use afterwards:
def fibonacci():
"""Yield the numbers from the Fibonacci sequence"""
a, b = 1, 1
while True:
yield a
a, b = b, a + b
The actual problem can then be solved in many ways, either like you did or by writing another generally applicable function, first
:
def first(it, condition=bool):
"""Return the first truthy (default) or first value satisfying `condition` from `it`."""
it = iter(it)
return next(filter(condition, it))
The itertools
module is well worth a read, although it is not needed here (I had used it in an earlier version). If you want to spend an entire afternoon, also have a look at the more_itertools
module.
The actual solution is then:
if __name__ == "__main__":
condition = lambda n: 10**999 <= n[1] < 10**1000
print(first(enumerate(fibonacci(), 1), condition)[0])
$endgroup$
add a comment |
$begingroup$
As others have pointed out, if you are brute-forcing this:
- there's no reason to keep the old values around in a list. You just need
a += b; b += a;
to go 2 steps, or in Pythona,b = b,a+b
to go one step. - binary integer -> base-10 digit string is very expensive, requiring repeated division by 10 or equivalent. (And that's division of the whole BigInteger by 10; it can't be done in chunks because the lowest decimal digit depends on all higher binary digits.) So compare against a threshold instead.
There's another trick nobody's mentioned:
Fib(n) grows fast enough that you can discard some of the the lowest digits occasionally without affecting the most-significant digits of your running total. Fib(n) grows close to exponentially; faster than any polynomial function.
For example, an Extreme Fibonacci code-golf question was to print the first 1000 digits of Fib(1 billion)
with a program that could actually finish in a reasonable amount of time. One Python2 answer from @AndersKaseorg used this code:
## golfed for minimum source-code size in bytes, not readability or performance
a,b=0,1
i=1e9
while i:
a,b=b,a+b;i-=1
if a>>3360:a/=10;b/=10
print a
# prints first 1000 digits of Fib(1**9),
# plus some (possibly wrong) extra trailing digits, as allowed by that question
which discards the low decimal digit of a
and b
if a > 2 ** 3360
. That's a 1012 digit number, so this keeps at least 1012 significant digits. (And slightly more in b
which is the larger of the 2).
For your case, you only care about the magnitude, so basically 1 significant digit of precision. You probably only have to keep about 12 or 13 significant digits to make sure the leading significant digit is always correct and doesn't grow too soon or too late. Maybe fewer; you might get away with keeping your running totals small enough to fit into 32-bit integers. Although I think Python on 64-bit machines will use 64-bit integers.
For your case, you do care about how many total digits there are. So you need to count how many times you truncate.
For efficiency, you probably want to set a higher truncation threshold and divide by 100 or 1000 so you don't have to do as many divisions. You might also be able to bit-shift (divide by a power of 2) instead of dividing by 10, if handle your final threshold appropriately, but that's trickier.
(That Python version takes about 12.5 minutes on a 4.4GHz Skylake x86-64 with Arch Linux CPython 2.7. My hand-written asm answer on the same question takes about 70 seconds by using base-10^9 extended precision, making it efficient to discard groups of 9 decimal digits.)
You can also unroll the loop so you only check for truncation every few additions because compare isn't free either. Perhaps have an efficient main loop that stops one or two orders of magnitude below the target digit-count, then go one step at a time without further truncation.
Working out the implementation details is left as an exercise for the reader (because the most efficient solution for large n is the closed-form FP math version). Keeping the numbers small should make the run time scale linearly with n
, instead of having each addition take longer as the numbers get larger.
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f224518%2fproject-euler-25-the-1000-digit-fibonacci-index%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
It's possible to calculate the nth Fibonacci number directly thanks to Binet's formula.
Due to floating point errors, the formula doesn't give correct values if n
is too large, but it works fine in order to calculate the number of digits.
from math import log10, floor, ceil
def fibonacci_digits(n):
if n < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return floor(n * log10(ϕ) - log10(5) / 2) + 1
It's possible to calculate the inverse of this function. This way, you can find the first Fibonacci with k
digits directly, without any loop:
def euler25(k):
if k < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return ceil((k + log10(5) / 2 - 1) / log10(ϕ))
It seems to return the correct answer for any k
in less than 1 μs :
>>> euler25(1000)
4782
>>> euler25(10_000)
47847
>>> euler25(100_000)
478495
>>> euler25(100_000_000_000_000_000)
478497196678166592
The digits are the same as in:
>>> 1 / log10(ϕ)
4.784971966781666
$endgroup$
1
$begingroup$
Oh god,φ
as an identifier. Can't wait to start seeing bugs likeφ = 1; assert ϕ == 1
...
$endgroup$
– Peilonrayz
Jul 21 at 4:06
$begingroup$
@Peilonrayz: I agree it can be dangerous. I used two different symbols at first, without noticing it. :-/ Your example isn't a bug, it's a feature!
$endgroup$
– Eric Duminil
Jul 21 at 7:28
2
$begingroup$
You say feature, I say death trap. ;) Yeah I know about NFKC normalization, that's how I made my example legit ;P
$endgroup$
– Peilonrayz
Jul 21 at 7:33
2
$begingroup$
@Peilonrayz:i
leads the pack with 21 collisions:"iᵢⁱℹⅈⅰⓘi𝐢𝑖𝒊𝒾𝓲𝔦𝕚𝖎𝗂𝗶𝘪𝙞𝚒"
. Next time I want to troll a colleague, I'll be sure to use every single synonym fori
in a script. :D
$endgroup$
– Eric Duminil
Jul 21 at 8:26
2
$begingroup$
You've gained my eternal ire and awe. :O Also, 𝚄𝓃𝙞𝙘𝔬de 𝒊𝘴 𝚝ⓗ𝕖 ⓝ𝔢𝑤 |𝟯³𝟳 ˢ𝒑ⅇ𝖆𝙠. - Yeah there's an impressive amount of collisions, which should make Unicode easier to support.
$endgroup$
– Peilonrayz
Jul 21 at 8:37
|
show 2 more comments
$begingroup$
It's possible to calculate the nth Fibonacci number directly thanks to Binet's formula.
Due to floating point errors, the formula doesn't give correct values if n
is too large, but it works fine in order to calculate the number of digits.
from math import log10, floor, ceil
def fibonacci_digits(n):
if n < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return floor(n * log10(ϕ) - log10(5) / 2) + 1
It's possible to calculate the inverse of this function. This way, you can find the first Fibonacci with k
digits directly, without any loop:
def euler25(k):
if k < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return ceil((k + log10(5) / 2 - 1) / log10(ϕ))
It seems to return the correct answer for any k
in less than 1 μs :
>>> euler25(1000)
4782
>>> euler25(10_000)
47847
>>> euler25(100_000)
478495
>>> euler25(100_000_000_000_000_000)
478497196678166592
The digits are the same as in:
>>> 1 / log10(ϕ)
4.784971966781666
$endgroup$
1
$begingroup$
Oh god,φ
as an identifier. Can't wait to start seeing bugs likeφ = 1; assert ϕ == 1
...
$endgroup$
– Peilonrayz
Jul 21 at 4:06
$begingroup$
@Peilonrayz: I agree it can be dangerous. I used two different symbols at first, without noticing it. :-/ Your example isn't a bug, it's a feature!
$endgroup$
– Eric Duminil
Jul 21 at 7:28
2
$begingroup$
You say feature, I say death trap. ;) Yeah I know about NFKC normalization, that's how I made my example legit ;P
$endgroup$
– Peilonrayz
Jul 21 at 7:33
2
$begingroup$
@Peilonrayz:i
leads the pack with 21 collisions:"iᵢⁱℹⅈⅰⓘi𝐢𝑖𝒊𝒾𝓲𝔦𝕚𝖎𝗂𝗶𝘪𝙞𝚒"
. Next time I want to troll a colleague, I'll be sure to use every single synonym fori
in a script. :D
$endgroup$
– Eric Duminil
Jul 21 at 8:26
2
$begingroup$
You've gained my eternal ire and awe. :O Also, 𝚄𝓃𝙞𝙘𝔬de 𝒊𝘴 𝚝ⓗ𝕖 ⓝ𝔢𝑤 |𝟯³𝟳 ˢ𝒑ⅇ𝖆𝙠. - Yeah there's an impressive amount of collisions, which should make Unicode easier to support.
$endgroup$
– Peilonrayz
Jul 21 at 8:37
|
show 2 more comments
$begingroup$
It's possible to calculate the nth Fibonacci number directly thanks to Binet's formula.
Due to floating point errors, the formula doesn't give correct values if n
is too large, but it works fine in order to calculate the number of digits.
from math import log10, floor, ceil
def fibonacci_digits(n):
if n < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return floor(n * log10(ϕ) - log10(5) / 2) + 1
It's possible to calculate the inverse of this function. This way, you can find the first Fibonacci with k
digits directly, without any loop:
def euler25(k):
if k < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return ceil((k + log10(5) / 2 - 1) / log10(ϕ))
It seems to return the correct answer for any k
in less than 1 μs :
>>> euler25(1000)
4782
>>> euler25(10_000)
47847
>>> euler25(100_000)
478495
>>> euler25(100_000_000_000_000_000)
478497196678166592
The digits are the same as in:
>>> 1 / log10(ϕ)
4.784971966781666
$endgroup$
It's possible to calculate the nth Fibonacci number directly thanks to Binet's formula.
Due to floating point errors, the formula doesn't give correct values if n
is too large, but it works fine in order to calculate the number of digits.
from math import log10, floor, ceil
def fibonacci_digits(n):
if n < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return floor(n * log10(ϕ) - log10(5) / 2) + 1
It's possible to calculate the inverse of this function. This way, you can find the first Fibonacci with k
digits directly, without any loop:
def euler25(k):
if k < 2:
return 1
ϕ = (1 + 5**0.5) / 2
return ceil((k + log10(5) / 2 - 1) / log10(ϕ))
It seems to return the correct answer for any k
in less than 1 μs :
>>> euler25(1000)
4782
>>> euler25(10_000)
47847
>>> euler25(100_000)
478495
>>> euler25(100_000_000_000_000_000)
478497196678166592
The digits are the same as in:
>>> 1 / log10(ϕ)
4.784971966781666
edited Jul 21 at 7:17
answered Jul 20 at 13:25
Eric DuminilEric Duminil
2,3621 gold badge7 silver badges15 bronze badges
2,3621 gold badge7 silver badges15 bronze badges
1
$begingroup$
Oh god,φ
as an identifier. Can't wait to start seeing bugs likeφ = 1; assert ϕ == 1
...
$endgroup$
– Peilonrayz
Jul 21 at 4:06
$begingroup$
@Peilonrayz: I agree it can be dangerous. I used two different symbols at first, without noticing it. :-/ Your example isn't a bug, it's a feature!
$endgroup$
– Eric Duminil
Jul 21 at 7:28
2
$begingroup$
You say feature, I say death trap. ;) Yeah I know about NFKC normalization, that's how I made my example legit ;P
$endgroup$
– Peilonrayz
Jul 21 at 7:33
2
$begingroup$
@Peilonrayz:i
leads the pack with 21 collisions:"iᵢⁱℹⅈⅰⓘi𝐢𝑖𝒊𝒾𝓲𝔦𝕚𝖎𝗂𝗶𝘪𝙞𝚒"
. Next time I want to troll a colleague, I'll be sure to use every single synonym fori
in a script. :D
$endgroup$
– Eric Duminil
Jul 21 at 8:26
2
$begingroup$
You've gained my eternal ire and awe. :O Also, 𝚄𝓃𝙞𝙘𝔬de 𝒊𝘴 𝚝ⓗ𝕖 ⓝ𝔢𝑤 |𝟯³𝟳 ˢ𝒑ⅇ𝖆𝙠. - Yeah there's an impressive amount of collisions, which should make Unicode easier to support.
$endgroup$
– Peilonrayz
Jul 21 at 8:37
|
show 2 more comments
1
$begingroup$
Oh god,φ
as an identifier. Can't wait to start seeing bugs likeφ = 1; assert ϕ == 1
...
$endgroup$
– Peilonrayz
Jul 21 at 4:06
$begingroup$
@Peilonrayz: I agree it can be dangerous. I used two different symbols at first, without noticing it. :-/ Your example isn't a bug, it's a feature!
$endgroup$
– Eric Duminil
Jul 21 at 7:28
2
$begingroup$
You say feature, I say death trap. ;) Yeah I know about NFKC normalization, that's how I made my example legit ;P
$endgroup$
– Peilonrayz
Jul 21 at 7:33
2
$begingroup$
@Peilonrayz:i
leads the pack with 21 collisions:"iᵢⁱℹⅈⅰⓘi𝐢𝑖𝒊𝒾𝓲𝔦𝕚𝖎𝗂𝗶𝘪𝙞𝚒"
. Next time I want to troll a colleague, I'll be sure to use every single synonym fori
in a script. :D
$endgroup$
– Eric Duminil
Jul 21 at 8:26
2
$begingroup$
You've gained my eternal ire and awe. :O Also, 𝚄𝓃𝙞𝙘𝔬de 𝒊𝘴 𝚝ⓗ𝕖 ⓝ𝔢𝑤 |𝟯³𝟳 ˢ𝒑ⅇ𝖆𝙠. - Yeah there's an impressive amount of collisions, which should make Unicode easier to support.
$endgroup$
– Peilonrayz
Jul 21 at 8:37
1
1
$begingroup$
Oh god,
φ
as an identifier. Can't wait to start seeing bugs like φ = 1; assert ϕ == 1
...$endgroup$
– Peilonrayz
Jul 21 at 4:06
$begingroup$
Oh god,
φ
as an identifier. Can't wait to start seeing bugs like φ = 1; assert ϕ == 1
...$endgroup$
– Peilonrayz
Jul 21 at 4:06
$begingroup$
@Peilonrayz: I agree it can be dangerous. I used two different symbols at first, without noticing it. :-/ Your example isn't a bug, it's a feature!
$endgroup$
– Eric Duminil
Jul 21 at 7:28
$begingroup$
@Peilonrayz: I agree it can be dangerous. I used two different symbols at first, without noticing it. :-/ Your example isn't a bug, it's a feature!
$endgroup$
– Eric Duminil
Jul 21 at 7:28
2
2
$begingroup$
You say feature, I say death trap. ;) Yeah I know about NFKC normalization, that's how I made my example legit ;P
$endgroup$
– Peilonrayz
Jul 21 at 7:33
$begingroup$
You say feature, I say death trap. ;) Yeah I know about NFKC normalization, that's how I made my example legit ;P
$endgroup$
– Peilonrayz
Jul 21 at 7:33
2
2
$begingroup$
@Peilonrayz:
i
leads the pack with 21 collisions: "iᵢⁱℹⅈⅰⓘi𝐢𝑖𝒊𝒾𝓲𝔦𝕚𝖎𝗂𝗶𝘪𝙞𝚒"
. Next time I want to troll a colleague, I'll be sure to use every single synonym for i
in a script. :D$endgroup$
– Eric Duminil
Jul 21 at 8:26
$begingroup$
@Peilonrayz:
i
leads the pack with 21 collisions: "iᵢⁱℹⅈⅰⓘi𝐢𝑖𝒊𝒾𝓲𝔦𝕚𝖎𝗂𝗶𝘪𝙞𝚒"
. Next time I want to troll a colleague, I'll be sure to use every single synonym for i
in a script. :D$endgroup$
– Eric Duminil
Jul 21 at 8:26
2
2
$begingroup$
You've gained my eternal ire and awe. :O Also, 𝚄𝓃𝙞𝙘𝔬de 𝒊𝘴 𝚝ⓗ𝕖 ⓝ𝔢𝑤 |𝟯³𝟳 ˢ𝒑ⅇ𝖆𝙠. - Yeah there's an impressive amount of collisions, which should make Unicode easier to support.
$endgroup$
– Peilonrayz
Jul 21 at 8:37
$begingroup$
You've gained my eternal ire and awe. :O Also, 𝚄𝓃𝙞𝙘𝔬de 𝒊𝘴 𝚝ⓗ𝕖 ⓝ𝔢𝑤 |𝟯³𝟳 ˢ𝒑ⅇ𝖆𝙠. - Yeah there's an impressive amount of collisions, which should make Unicode easier to support.
$endgroup$
– Peilonrayz
Jul 21 at 8:37
|
show 2 more comments
$begingroup$
Two comments:
There isn't any reason to keep a list of all the fib numbers. You only need the last two numbers to compute the next one.
A number with 1000 digits satisfies 1e1000 <= f < 1e1001. It might be faster to compare f to the numbers rather than to convert it to a string and get its length.
def fib_of_length(n):
"""returns index of the first fibonacci of length n digits.
for n > 0.
"""
bound = 10**(n-1)
fib, previous_fib = 1,0
index = 1
while fib < bound:
fib, previous_fib = fib + previous_fib, fib
index += 1
return index
$endgroup$
$begingroup$
It's reasonably fast, taking into account that you iterate over every Fibonacci number.
$endgroup$
– Eric Duminil
Jul 21 at 8:11
add a comment |
$begingroup$
Two comments:
There isn't any reason to keep a list of all the fib numbers. You only need the last two numbers to compute the next one.
A number with 1000 digits satisfies 1e1000 <= f < 1e1001. It might be faster to compare f to the numbers rather than to convert it to a string and get its length.
def fib_of_length(n):
"""returns index of the first fibonacci of length n digits.
for n > 0.
"""
bound = 10**(n-1)
fib, previous_fib = 1,0
index = 1
while fib < bound:
fib, previous_fib = fib + previous_fib, fib
index += 1
return index
$endgroup$
$begingroup$
It's reasonably fast, taking into account that you iterate over every Fibonacci number.
$endgroup$
– Eric Duminil
Jul 21 at 8:11
add a comment |
$begingroup$
Two comments:
There isn't any reason to keep a list of all the fib numbers. You only need the last two numbers to compute the next one.
A number with 1000 digits satisfies 1e1000 <= f < 1e1001. It might be faster to compare f to the numbers rather than to convert it to a string and get its length.
def fib_of_length(n):
"""returns index of the first fibonacci of length n digits.
for n > 0.
"""
bound = 10**(n-1)
fib, previous_fib = 1,0
index = 1
while fib < bound:
fib, previous_fib = fib + previous_fib, fib
index += 1
return index
$endgroup$
Two comments:
There isn't any reason to keep a list of all the fib numbers. You only need the last two numbers to compute the next one.
A number with 1000 digits satisfies 1e1000 <= f < 1e1001. It might be faster to compare f to the numbers rather than to convert it to a string and get its length.
def fib_of_length(n):
"""returns index of the first fibonacci of length n digits.
for n > 0.
"""
bound = 10**(n-1)
fib, previous_fib = 1,0
index = 1
while fib < bound:
fib, previous_fib = fib + previous_fib, fib
index += 1
return index
edited Jul 20 at 21:34
Stephen Rauch
3,8556 gold badges16 silver badges30 bronze badges
3,8556 gold badges16 silver badges30 bronze badges
answered Jul 20 at 1:32
RootTwoRootTwo
8693 silver badges6 bronze badges
8693 silver badges6 bronze badges
$begingroup$
It's reasonably fast, taking into account that you iterate over every Fibonacci number.
$endgroup$
– Eric Duminil
Jul 21 at 8:11
add a comment |
$begingroup$
It's reasonably fast, taking into account that you iterate over every Fibonacci number.
$endgroup$
– Eric Duminil
Jul 21 at 8:11
$begingroup$
It's reasonably fast, taking into account that you iterate over every Fibonacci number.
$endgroup$
– Eric Duminil
Jul 21 at 8:11
$begingroup$
It's reasonably fast, taking into account that you iterate over every Fibonacci number.
$endgroup$
– Eric Duminil
Jul 21 at 8:11
add a comment |
$begingroup$
This isn't a full review but something I found very interesting about this problem: I would have thought that keeping a list of every Fibonacci number would get expensive over larger numbers but that doesn't seem to be the case with my testing.
Instead it seems the bottleneck is the line:
len(str(fibs[-1])) < n:
I ran the function for n = 10,000 and it took roughly 32 seconds to run. If you instead do a comparison to a constant value such as:
threshold = 10 ** (n-1)
while fibs[-1] <= threshold:
The time drops to about a 10th of a second. If you change your list to only track the last three numbers that times is roughly halved again.
$endgroup$
1
$begingroup$
You should consider moving the actual computation out to a constant ...threshold = 10 ** (n - 1)
... then later you havefibs[-1] <= threshold
.... so you only have to do that computation once.
$endgroup$
– rolfl♦
Jul 22 at 11:26
1
$begingroup$
Instead of just tacking on text at the end, fix your initial answer to show the good way first. Some Python implementations would CSE that loop-invariant10 ** (n-1)
, but unlike C/C++ you apparently have to do that optimization manually. Anyway yes, converting a huge integer from binary to a base-10 string is very slow, requiring repeated division by 10 or equivalent.
$endgroup$
– Peter Cordes
Jul 22 at 19:59
add a comment |
$begingroup$
This isn't a full review but something I found very interesting about this problem: I would have thought that keeping a list of every Fibonacci number would get expensive over larger numbers but that doesn't seem to be the case with my testing.
Instead it seems the bottleneck is the line:
len(str(fibs[-1])) < n:
I ran the function for n = 10,000 and it took roughly 32 seconds to run. If you instead do a comparison to a constant value such as:
threshold = 10 ** (n-1)
while fibs[-1] <= threshold:
The time drops to about a 10th of a second. If you change your list to only track the last three numbers that times is roughly halved again.
$endgroup$
1
$begingroup$
You should consider moving the actual computation out to a constant ...threshold = 10 ** (n - 1)
... then later you havefibs[-1] <= threshold
.... so you only have to do that computation once.
$endgroup$
– rolfl♦
Jul 22 at 11:26
1
$begingroup$
Instead of just tacking on text at the end, fix your initial answer to show the good way first. Some Python implementations would CSE that loop-invariant10 ** (n-1)
, but unlike C/C++ you apparently have to do that optimization manually. Anyway yes, converting a huge integer from binary to a base-10 string is very slow, requiring repeated division by 10 or equivalent.
$endgroup$
– Peter Cordes
Jul 22 at 19:59
add a comment |
$begingroup$
This isn't a full review but something I found very interesting about this problem: I would have thought that keeping a list of every Fibonacci number would get expensive over larger numbers but that doesn't seem to be the case with my testing.
Instead it seems the bottleneck is the line:
len(str(fibs[-1])) < n:
I ran the function for n = 10,000 and it took roughly 32 seconds to run. If you instead do a comparison to a constant value such as:
threshold = 10 ** (n-1)
while fibs[-1] <= threshold:
The time drops to about a 10th of a second. If you change your list to only track the last three numbers that times is roughly halved again.
$endgroup$
This isn't a full review but something I found very interesting about this problem: I would have thought that keeping a list of every Fibonacci number would get expensive over larger numbers but that doesn't seem to be the case with my testing.
Instead it seems the bottleneck is the line:
len(str(fibs[-1])) < n:
I ran the function for n = 10,000 and it took roughly 32 seconds to run. If you instead do a comparison to a constant value such as:
threshold = 10 ** (n-1)
while fibs[-1] <= threshold:
The time drops to about a 10th of a second. If you change your list to only track the last three numbers that times is roughly halved again.
edited Jul 22 at 20:10
answered Jul 20 at 1:26
Ben HBen H
1716 bronze badges
1716 bronze badges
1
$begingroup$
You should consider moving the actual computation out to a constant ...threshold = 10 ** (n - 1)
... then later you havefibs[-1] <= threshold
.... so you only have to do that computation once.
$endgroup$
– rolfl♦
Jul 22 at 11:26
1
$begingroup$
Instead of just tacking on text at the end, fix your initial answer to show the good way first. Some Python implementations would CSE that loop-invariant10 ** (n-1)
, but unlike C/C++ you apparently have to do that optimization manually. Anyway yes, converting a huge integer from binary to a base-10 string is very slow, requiring repeated division by 10 or equivalent.
$endgroup$
– Peter Cordes
Jul 22 at 19:59
add a comment |
1
$begingroup$
You should consider moving the actual computation out to a constant ...threshold = 10 ** (n - 1)
... then later you havefibs[-1] <= threshold
.... so you only have to do that computation once.
$endgroup$
– rolfl♦
Jul 22 at 11:26
1
$begingroup$
Instead of just tacking on text at the end, fix your initial answer to show the good way first. Some Python implementations would CSE that loop-invariant10 ** (n-1)
, but unlike C/C++ you apparently have to do that optimization manually. Anyway yes, converting a huge integer from binary to a base-10 string is very slow, requiring repeated division by 10 or equivalent.
$endgroup$
– Peter Cordes
Jul 22 at 19:59
1
1
$begingroup$
You should consider moving the actual computation out to a constant ...
threshold = 10 ** (n - 1)
... then later you have fibs[-1] <= threshold
.... so you only have to do that computation once.$endgroup$
– rolfl♦
Jul 22 at 11:26
$begingroup$
You should consider moving the actual computation out to a constant ...
threshold = 10 ** (n - 1)
... then later you have fibs[-1] <= threshold
.... so you only have to do that computation once.$endgroup$
– rolfl♦
Jul 22 at 11:26
1
1
$begingroup$
Instead of just tacking on text at the end, fix your initial answer to show the good way first. Some Python implementations would CSE that loop-invariant
10 ** (n-1)
, but unlike C/C++ you apparently have to do that optimization manually. Anyway yes, converting a huge integer from binary to a base-10 string is very slow, requiring repeated division by 10 or equivalent.$endgroup$
– Peter Cordes
Jul 22 at 19:59
$begingroup$
Instead of just tacking on text at the end, fix your initial answer to show the good way first. Some Python implementations would CSE that loop-invariant
10 ** (n-1)
, but unlike C/C++ you apparently have to do that optimization manually. Anyway yes, converting a huge integer from binary to a base-10 string is very slow, requiring repeated division by 10 or equivalent.$endgroup$
– Peter Cordes
Jul 22 at 19:59
add a comment |
$begingroup$
Project Euler has quite a few problems on the Fibonacci numbers. Other things also repeatedly crop up, like prime numbers, triangular numbers, and so on. In fact, quite a few problems build on earlier, easier, problems.
Because of this it is worth it to start designing your functions for reusability. Put these functions into modules, so you can reuse them later. This one should probably go to the sequences.py
module or just a general utils.py
.
Currently you have one function that does everything. Instead you will probably want a generator function that generates all Fibonacci numbers, which you can then use afterwards:
def fibonacci():
"""Yield the numbers from the Fibonacci sequence"""
a, b = 1, 1
while True:
yield a
a, b = b, a + b
The actual problem can then be solved in many ways, either like you did or by writing another generally applicable function, first
:
def first(it, condition=bool):
"""Return the first truthy (default) or first value satisfying `condition` from `it`."""
it = iter(it)
return next(filter(condition, it))
The itertools
module is well worth a read, although it is not needed here (I had used it in an earlier version). If you want to spend an entire afternoon, also have a look at the more_itertools
module.
The actual solution is then:
if __name__ == "__main__":
condition = lambda n: 10**999 <= n[1] < 10**1000
print(first(enumerate(fibonacci(), 1), condition)[0])
$endgroup$
add a comment |
$begingroup$
Project Euler has quite a few problems on the Fibonacci numbers. Other things also repeatedly crop up, like prime numbers, triangular numbers, and so on. In fact, quite a few problems build on earlier, easier, problems.
Because of this it is worth it to start designing your functions for reusability. Put these functions into modules, so you can reuse them later. This one should probably go to the sequences.py
module or just a general utils.py
.
Currently you have one function that does everything. Instead you will probably want a generator function that generates all Fibonacci numbers, which you can then use afterwards:
def fibonacci():
"""Yield the numbers from the Fibonacci sequence"""
a, b = 1, 1
while True:
yield a
a, b = b, a + b
The actual problem can then be solved in many ways, either like you did or by writing another generally applicable function, first
:
def first(it, condition=bool):
"""Return the first truthy (default) or first value satisfying `condition` from `it`."""
it = iter(it)
return next(filter(condition, it))
The itertools
module is well worth a read, although it is not needed here (I had used it in an earlier version). If you want to spend an entire afternoon, also have a look at the more_itertools
module.
The actual solution is then:
if __name__ == "__main__":
condition = lambda n: 10**999 <= n[1] < 10**1000
print(first(enumerate(fibonacci(), 1), condition)[0])
$endgroup$
add a comment |
$begingroup$
Project Euler has quite a few problems on the Fibonacci numbers. Other things also repeatedly crop up, like prime numbers, triangular numbers, and so on. In fact, quite a few problems build on earlier, easier, problems.
Because of this it is worth it to start designing your functions for reusability. Put these functions into modules, so you can reuse them later. This one should probably go to the sequences.py
module or just a general utils.py
.
Currently you have one function that does everything. Instead you will probably want a generator function that generates all Fibonacci numbers, which you can then use afterwards:
def fibonacci():
"""Yield the numbers from the Fibonacci sequence"""
a, b = 1, 1
while True:
yield a
a, b = b, a + b
The actual problem can then be solved in many ways, either like you did or by writing another generally applicable function, first
:
def first(it, condition=bool):
"""Return the first truthy (default) or first value satisfying `condition` from `it`."""
it = iter(it)
return next(filter(condition, it))
The itertools
module is well worth a read, although it is not needed here (I had used it in an earlier version). If you want to spend an entire afternoon, also have a look at the more_itertools
module.
The actual solution is then:
if __name__ == "__main__":
condition = lambda n: 10**999 <= n[1] < 10**1000
print(first(enumerate(fibonacci(), 1), condition)[0])
$endgroup$
Project Euler has quite a few problems on the Fibonacci numbers. Other things also repeatedly crop up, like prime numbers, triangular numbers, and so on. In fact, quite a few problems build on earlier, easier, problems.
Because of this it is worth it to start designing your functions for reusability. Put these functions into modules, so you can reuse them later. This one should probably go to the sequences.py
module or just a general utils.py
.
Currently you have one function that does everything. Instead you will probably want a generator function that generates all Fibonacci numbers, which you can then use afterwards:
def fibonacci():
"""Yield the numbers from the Fibonacci sequence"""
a, b = 1, 1
while True:
yield a
a, b = b, a + b
The actual problem can then be solved in many ways, either like you did or by writing another generally applicable function, first
:
def first(it, condition=bool):
"""Return the first truthy (default) or first value satisfying `condition` from `it`."""
it = iter(it)
return next(filter(condition, it))
The itertools
module is well worth a read, although it is not needed here (I had used it in an earlier version). If you want to spend an entire afternoon, also have a look at the more_itertools
module.
The actual solution is then:
if __name__ == "__main__":
condition = lambda n: 10**999 <= n[1] < 10**1000
print(first(enumerate(fibonacci(), 1), condition)[0])
edited Jul 21 at 11:36
answered Jul 20 at 12:22
GraipherGraipher
29.3k5 gold badges46 silver badges103 bronze badges
29.3k5 gold badges46 silver badges103 bronze badges
add a comment |
add a comment |
$begingroup$
As others have pointed out, if you are brute-forcing this:
- there's no reason to keep the old values around in a list. You just need
a += b; b += a;
to go 2 steps, or in Pythona,b = b,a+b
to go one step. - binary integer -> base-10 digit string is very expensive, requiring repeated division by 10 or equivalent. (And that's division of the whole BigInteger by 10; it can't be done in chunks because the lowest decimal digit depends on all higher binary digits.) So compare against a threshold instead.
There's another trick nobody's mentioned:
Fib(n) grows fast enough that you can discard some of the the lowest digits occasionally without affecting the most-significant digits of your running total. Fib(n) grows close to exponentially; faster than any polynomial function.
For example, an Extreme Fibonacci code-golf question was to print the first 1000 digits of Fib(1 billion)
with a program that could actually finish in a reasonable amount of time. One Python2 answer from @AndersKaseorg used this code:
## golfed for minimum source-code size in bytes, not readability or performance
a,b=0,1
i=1e9
while i:
a,b=b,a+b;i-=1
if a>>3360:a/=10;b/=10
print a
# prints first 1000 digits of Fib(1**9),
# plus some (possibly wrong) extra trailing digits, as allowed by that question
which discards the low decimal digit of a
and b
if a > 2 ** 3360
. That's a 1012 digit number, so this keeps at least 1012 significant digits. (And slightly more in b
which is the larger of the 2).
For your case, you only care about the magnitude, so basically 1 significant digit of precision. You probably only have to keep about 12 or 13 significant digits to make sure the leading significant digit is always correct and doesn't grow too soon or too late. Maybe fewer; you might get away with keeping your running totals small enough to fit into 32-bit integers. Although I think Python on 64-bit machines will use 64-bit integers.
For your case, you do care about how many total digits there are. So you need to count how many times you truncate.
For efficiency, you probably want to set a higher truncation threshold and divide by 100 or 1000 so you don't have to do as many divisions. You might also be able to bit-shift (divide by a power of 2) instead of dividing by 10, if handle your final threshold appropriately, but that's trickier.
(That Python version takes about 12.5 minutes on a 4.4GHz Skylake x86-64 with Arch Linux CPython 2.7. My hand-written asm answer on the same question takes about 70 seconds by using base-10^9 extended precision, making it efficient to discard groups of 9 decimal digits.)
You can also unroll the loop so you only check for truncation every few additions because compare isn't free either. Perhaps have an efficient main loop that stops one or two orders of magnitude below the target digit-count, then go one step at a time without further truncation.
Working out the implementation details is left as an exercise for the reader (because the most efficient solution for large n is the closed-form FP math version). Keeping the numbers small should make the run time scale linearly with n
, instead of having each addition take longer as the numbers get larger.
$endgroup$
add a comment |
$begingroup$
As others have pointed out, if you are brute-forcing this:
- there's no reason to keep the old values around in a list. You just need
a += b; b += a;
to go 2 steps, or in Pythona,b = b,a+b
to go one step. - binary integer -> base-10 digit string is very expensive, requiring repeated division by 10 or equivalent. (And that's division of the whole BigInteger by 10; it can't be done in chunks because the lowest decimal digit depends on all higher binary digits.) So compare against a threshold instead.
There's another trick nobody's mentioned:
Fib(n) grows fast enough that you can discard some of the the lowest digits occasionally without affecting the most-significant digits of your running total. Fib(n) grows close to exponentially; faster than any polynomial function.
For example, an Extreme Fibonacci code-golf question was to print the first 1000 digits of Fib(1 billion)
with a program that could actually finish in a reasonable amount of time. One Python2 answer from @AndersKaseorg used this code:
## golfed for minimum source-code size in bytes, not readability or performance
a,b=0,1
i=1e9
while i:
a,b=b,a+b;i-=1
if a>>3360:a/=10;b/=10
print a
# prints first 1000 digits of Fib(1**9),
# plus some (possibly wrong) extra trailing digits, as allowed by that question
which discards the low decimal digit of a
and b
if a > 2 ** 3360
. That's a 1012 digit number, so this keeps at least 1012 significant digits. (And slightly more in b
which is the larger of the 2).
For your case, you only care about the magnitude, so basically 1 significant digit of precision. You probably only have to keep about 12 or 13 significant digits to make sure the leading significant digit is always correct and doesn't grow too soon or too late. Maybe fewer; you might get away with keeping your running totals small enough to fit into 32-bit integers. Although I think Python on 64-bit machines will use 64-bit integers.
For your case, you do care about how many total digits there are. So you need to count how many times you truncate.
For efficiency, you probably want to set a higher truncation threshold and divide by 100 or 1000 so you don't have to do as many divisions. You might also be able to bit-shift (divide by a power of 2) instead of dividing by 10, if handle your final threshold appropriately, but that's trickier.
(That Python version takes about 12.5 minutes on a 4.4GHz Skylake x86-64 with Arch Linux CPython 2.7. My hand-written asm answer on the same question takes about 70 seconds by using base-10^9 extended precision, making it efficient to discard groups of 9 decimal digits.)
You can also unroll the loop so you only check for truncation every few additions because compare isn't free either. Perhaps have an efficient main loop that stops one or two orders of magnitude below the target digit-count, then go one step at a time without further truncation.
Working out the implementation details is left as an exercise for the reader (because the most efficient solution for large n is the closed-form FP math version). Keeping the numbers small should make the run time scale linearly with n
, instead of having each addition take longer as the numbers get larger.
$endgroup$
add a comment |
$begingroup$
As others have pointed out, if you are brute-forcing this:
- there's no reason to keep the old values around in a list. You just need
a += b; b += a;
to go 2 steps, or in Pythona,b = b,a+b
to go one step. - binary integer -> base-10 digit string is very expensive, requiring repeated division by 10 or equivalent. (And that's division of the whole BigInteger by 10; it can't be done in chunks because the lowest decimal digit depends on all higher binary digits.) So compare against a threshold instead.
There's another trick nobody's mentioned:
Fib(n) grows fast enough that you can discard some of the the lowest digits occasionally without affecting the most-significant digits of your running total. Fib(n) grows close to exponentially; faster than any polynomial function.
For example, an Extreme Fibonacci code-golf question was to print the first 1000 digits of Fib(1 billion)
with a program that could actually finish in a reasonable amount of time. One Python2 answer from @AndersKaseorg used this code:
## golfed for minimum source-code size in bytes, not readability or performance
a,b=0,1
i=1e9
while i:
a,b=b,a+b;i-=1
if a>>3360:a/=10;b/=10
print a
# prints first 1000 digits of Fib(1**9),
# plus some (possibly wrong) extra trailing digits, as allowed by that question
which discards the low decimal digit of a
and b
if a > 2 ** 3360
. That's a 1012 digit number, so this keeps at least 1012 significant digits. (And slightly more in b
which is the larger of the 2).
For your case, you only care about the magnitude, so basically 1 significant digit of precision. You probably only have to keep about 12 or 13 significant digits to make sure the leading significant digit is always correct and doesn't grow too soon or too late. Maybe fewer; you might get away with keeping your running totals small enough to fit into 32-bit integers. Although I think Python on 64-bit machines will use 64-bit integers.
For your case, you do care about how many total digits there are. So you need to count how many times you truncate.
For efficiency, you probably want to set a higher truncation threshold and divide by 100 or 1000 so you don't have to do as many divisions. You might also be able to bit-shift (divide by a power of 2) instead of dividing by 10, if handle your final threshold appropriately, but that's trickier.
(That Python version takes about 12.5 minutes on a 4.4GHz Skylake x86-64 with Arch Linux CPython 2.7. My hand-written asm answer on the same question takes about 70 seconds by using base-10^9 extended precision, making it efficient to discard groups of 9 decimal digits.)
You can also unroll the loop so you only check for truncation every few additions because compare isn't free either. Perhaps have an efficient main loop that stops one or two orders of magnitude below the target digit-count, then go one step at a time without further truncation.
Working out the implementation details is left as an exercise for the reader (because the most efficient solution for large n is the closed-form FP math version). Keeping the numbers small should make the run time scale linearly with n
, instead of having each addition take longer as the numbers get larger.
$endgroup$
As others have pointed out, if you are brute-forcing this:
- there's no reason to keep the old values around in a list. You just need
a += b; b += a;
to go 2 steps, or in Pythona,b = b,a+b
to go one step. - binary integer -> base-10 digit string is very expensive, requiring repeated division by 10 or equivalent. (And that's division of the whole BigInteger by 10; it can't be done in chunks because the lowest decimal digit depends on all higher binary digits.) So compare against a threshold instead.
There's another trick nobody's mentioned:
Fib(n) grows fast enough that you can discard some of the the lowest digits occasionally without affecting the most-significant digits of your running total. Fib(n) grows close to exponentially; faster than any polynomial function.
For example, an Extreme Fibonacci code-golf question was to print the first 1000 digits of Fib(1 billion)
with a program that could actually finish in a reasonable amount of time. One Python2 answer from @AndersKaseorg used this code:
## golfed for minimum source-code size in bytes, not readability or performance
a,b=0,1
i=1e9
while i:
a,b=b,a+b;i-=1
if a>>3360:a/=10;b/=10
print a
# prints first 1000 digits of Fib(1**9),
# plus some (possibly wrong) extra trailing digits, as allowed by that question
which discards the low decimal digit of a
and b
if a > 2 ** 3360
. That's a 1012 digit number, so this keeps at least 1012 significant digits. (And slightly more in b
which is the larger of the 2).
For your case, you only care about the magnitude, so basically 1 significant digit of precision. You probably only have to keep about 12 or 13 significant digits to make sure the leading significant digit is always correct and doesn't grow too soon or too late. Maybe fewer; you might get away with keeping your running totals small enough to fit into 32-bit integers. Although I think Python on 64-bit machines will use 64-bit integers.
For your case, you do care about how many total digits there are. So you need to count how many times you truncate.
For efficiency, you probably want to set a higher truncation threshold and divide by 100 or 1000 so you don't have to do as many divisions. You might also be able to bit-shift (divide by a power of 2) instead of dividing by 10, if handle your final threshold appropriately, but that's trickier.
(That Python version takes about 12.5 minutes on a 4.4GHz Skylake x86-64 with Arch Linux CPython 2.7. My hand-written asm answer on the same question takes about 70 seconds by using base-10^9 extended precision, making it efficient to discard groups of 9 decimal digits.)
You can also unroll the loop so you only check for truncation every few additions because compare isn't free either. Perhaps have an efficient main loop that stops one or two orders of magnitude below the target digit-count, then go one step at a time without further truncation.
Working out the implementation details is left as an exercise for the reader (because the most efficient solution for large n is the closed-form FP math version). Keeping the numbers small should make the run time scale linearly with n
, instead of having each addition take longer as the numbers get larger.
answered Jul 22 at 20:41
Peter CordesPeter Cordes
1,7056 silver badges17 bronze badges
1,7056 silver badges17 bronze badges
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f224518%2fproject-euler-25-the-1000-digit-fibonacci-index%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
4
$begingroup$
If you examine the Wikipedia article on Fibonacci numbers, in particular the section "Computation by Rounding, it should be possible to calculate the the first F_n > 10^1000 by taking base 10 logs of both sides.
$endgroup$
– James K Polk
Jul 20 at 13:27
$begingroup$
@JamesKPolk Exactly.
$endgroup$
– Eric Duminil
Jul 21 at 13:18