Python while loop not working as intended - python

I don't know how to get over this problem with while loop.
So basically I want to return the number of zeros at the end of a number's factorial.
import math
def zeros(n):
total_zero = 0
n = math.factorial(n)
while str(n)[-1] == '0': # to check if the last number is 0 or not
n = n * 0.1
total_zero += 1
return total_zero
output = zeros(30)
print(output)
After the while loop runs only 1 time, it breaks; I don't know why.
Help would be very appreciated. Thanks!

After multiplying your value by 0.1 it becomes a float, and it's string representation becomes the scientific notation 2.6525285981219107e+31 which doesn't end by a 1
You'd better do the integer division by 10 to keep an int
def zeros(n):
total_zero = 0
n = math.factorial(n)
while str(n)[-1] == '0': # to check if the last number is 0 or not
n = n // 10
total_zero += 1
print(f"testting {str(n)}")
return total_zero
>> zeros(30)
testting 26525285981219105863630848000000
testting 2652528598121910586363084800000
testting 265252859812191058636308480000
testting 26525285981219105863630848000
testting 2652528598121910586363084800
testting 265252859812191058636308480
testting 26525285981219105863630848
7
Better You can also use str.rstrip : you remove the leading zeros and check the length difference
def zeros(n):
value = str(math.factorial(n))
return len(value) - len(value.rstrip("0"))

My first answer above was literally solving the question that the user asked: "Why doesn't my code work?" But there is a much much simpler way of solving the question "How many zeros does n! have at the end" which is so simple you can do the math in your head.
Look at the prime factorization of any number f. To get a "0" at the end of a number, you must have 2 x 5 in the prime factorization. So the number of zeros at the end of f is just the minimum of the number of 2s and the number of 5s in the prime factorization. For factorials, you always get more factors of 2 than of 5, so we're down to the question: How many 5s are there in the prime factorization of n!
That's easy! Legendre's formula says it is:
floor(n/5) + floor(n/25) + floor(n/125) + ...
and although this is an infinite series, after a couple of terms, they're all zero. For 30!, you get 6 + 1 + 0 + 0 + ... = 7.
If asked how many 0s there are at the end of 1000!, the answer is `200 + 40 + 8 + 1 = 249'

Why didn't you bother to do any debugging? See this lovely debugging site for help. A simple print to trace n shows the problem.
You're doing a float computation on an integer; the result you get back is not guaranteed to be exact. Instead, use
n = n // 10

After you multiply by .1, you have a floating point number. You will be losing precision with a number as large as 30! You want to divide by 10 using //.
Also rather than looking at the last digit in the string, you can just look at the number mod 10. Much faster

In python, the * operator on an int and float outputs a float. Casting to str converts long floats into scientific notation. Therefore on your second iteration you have:
> str(math.factorial(30)*.1)
'2.6525285981219107e+31'
> str(math.factorial(30)*.1)[-1]
'1'
Since math.factorial always returns an int, which str converts to a string of the full integer value, you might try first converting the output of math.factorial to a string, and then iterating backward through that string. Something like:
def count_zeros(n):
count = 0
n = str(math.factorial(n))
while n[-1] == '0':
count += 1
n = n[:-1]
return count

This could be solved a bit more compactly as follows
from math import factorial
from itertools import takewhile
def zeros(n):
return sum(1 for _ in takewhile(lambda i: i == '0', reversed(str(factorial(n)))))
For example
>>> zeros(30)
7
This basically computes the factorial, converts that to a string, then counts the '0' characters from the string backwards until it encounters a non-zero character.

Related

Can I make this recursion work with negative numbers?

I wrote this code and it's alright with positive numbers, but when I tried negative numbers it crashes. Can you give any hints on how to make it work with negative numbers as well? It needs to be recursive, not iterative, and to calculate the sum of the digits of an integer.
def sum_digits(n):
if n != 0:
return (n % 10 + sum_digits(n // 10))
else:
return 0
if __name__=='__main__':
print(sum_digits(123))
Input: 123
Output: 6
On the assumption that the 'sum' of the three digits of a negative number is the same as that of the absolute value of that number, this will work:
def sum_digits(n):
if n < 0:
return sum_digits(-n)
elif n != 0:
return (n % 10 + sum_digits(n // 10))
else:
return 0
That said, your actual problem here is that Python's handling of modulo for a negative number is different than you expect:
>>> -123 % 10
7
Why is that? It's because of the use of trunc() in the division. This page has a good explanation, but the short answer is that when you divide -123 by 10, in order to figure out the remainder, Python truncates in a different direction than you'd expect. (For good, if obscure, reasons.) Thus, in the above, instead of getting the expected 3 you get 7 (which is 10, your modulus, minus 3, the leftover).
Similarly, it's handling of integer division is different:
>>> -123 // 10
-13
>>> 123 // 10
12
This is un-intuitively correct because it is rounding 'down' rather than 'towards zero'. So a -12.3 rounds 'down' to -13.
These reasons are why the easiest solution to your particular problem is to simply take the absolute value prior to doing your actual calculation.
Separate your function into two functions: one, a recursive function that must always be called with a non-negative number, and two, a function that checks its argument can calls the recursive function with an appropriate argument.
def sum_digits(n):
return _recursive_sum_digits(abs(n))
def _recursive_sum_digits(n):
if n != 0:
return (n % 10 + sum_digits(n // 10))
else:
return 0
Since _recursive_sum_digits can assume its argument is non-negative, you can dispense with checking its sign on every recursive call, and guarantee that n // 10 will eventually produce 0.
If you want to just sum the digits that come after the negative sign, remove the sign by taking the absolute value of the number. If you're considering the first digit of the negative number to be a negative digit, then manually add that number in after performing this function on the rest of the digits.
Here is your hint. This is happening because the modulo operator always yields a result with the same sign as its second operand (or zero). Look at these examples:
>>> 13 % 10
3
>>> -13 % 10
7
In your specific case, a solution is to first get the absolute value of the number, and then you can go on with you approach:
def sum_digits(n):
n = abs(n)
if n != 0:
return (n % 10 + sum_digits(n // 10))
else:
return 0

calculating the sum of digits in a list in python

I am new to math problems in python but basically i have the following code:
a = list(range(1,10000))
str(a)
sum_of_digits = sum(int(digit) for digit in str(a[9998]))
print(sum_of_digits)
this allows me to calculate the sum of the digits of a given number in the list a. but instead of feeding numbers into this one by one, i want an efficient way to calculate the sum of the digits of all the numbers in a and print them all out at once. I can't seem to figure out a solution but i know the answer is probably simple. any help is appreciated!
edit: i didnt know this post would get this much attention, for those wanting more clarification i basically want to know which digits in the list of range 1,9999 has a sum of 34 or more. i think everyone thought i simply wanted to take the sum of digits of each list element and then compile a total sum. in any case, that method helped me solve the actual problem
A good, straightforward way to do this is to use the modulo % operator, along with floor division \\:
total_sum = 0
for num in a:
sum_of_digits = 0
while (num != 0):
sum_of_digits = sum_of_digits + (num % 10)
num = num//10
total_sum = total_sum + sum_of_digits
print total_sum
Here, the expression n % 10 returns the remainder of dividing n by 10, or in other words, it returns the digit in the units place of that number. What the while loop is doing is repeatedly dividing the number by 10, then adding the number in the units place to the total.
Note that the \\ (floor division) is important here, as it gets rid of any decimal value in the number, which is needed for modulo % to work properly.
Note: This solution is massively more efficient than any algorithm which relies on str().
i want an efficient way to calculate the sum of the digits of all the numbers in a
If you truly want an efficient way, do not calculate the sum of the digit sum of all the individual numbers. Instead, calculate the total digit sum of the entire range1 at once.
For example, in the range up to and including 123, we do not have to write out all the individual numbers to see that the last digit will cycle through the numbers 1-9 a total of 12 times, plus the numbers 1-3 once. The middle digit cycles through 1-9 once, showing each 10 times, and then another 10 times 1 and 4 times 2. And for the first digit, only the 1 appears 24 times. Thus, the total is 45*12 + 1+2+3 + 45*10 + 10 + 8 + 24 = 1038.
You can put this into a recursive formula using "a bit" of modulo magic.
def dsum(n, f=1, p=1):
if n:
d, r = divmod(n, 10)
k = (45*d + sum(range(r)))*f + r*p
return dsum(d, f*10, p + f*r) + k
return 0
This yields the same results as the "naive" approach, but with a running time of O(log n) instead of O(n) it can be used to calculate the digit sum of ridiculously large ranges of numbers.
>>> n = 1234567
>>> sum(int(c) for i in range(1, n+1) for c in str(i))
32556016
>>> dsum(n)
32556016
>>> dsum(12345678901234567890)
1047782339654778234045
1) This is assuming your list is always a range of numbers starting at 1 up to some upper bound, although this would also work for a range not starting at 1 by calculating the digit sum for the upper bound and then subtracting the digit sum for the lower bound. If the list is not a range, then there's no way around calculating the digit sum for all the individual numbers, though.
Try this:
sum(int(i) for j in range(1,10000) for i in str(j))
It is the same, but works slowly:
lst = []
for j in range(10000):
for i in str(j):
lst.append(int(i))
print(sum(lst))

loop over a list for the occurence of a list element

I want to define a majority function that would give back the list element if that element occurs at least (n/2)+1 times in the list, else prints out None(where n is the length of the list)
def majority(list):
a = len(list)
for i in list:
if list.count(i) >= (a/2) +1:
print(int(i))
else:
print("None")
However, the result of majority([3,1,2,1,1,3,1]) is 7 times None. I'm not sure why this is the case
Python has a built in class for this called Counter.
from collections import Counter
a = [3,1,2,1,1,3,1]
cc = Counter(a)
x = cc.most_common(1)
if x and x[0][1] > len(a) // 2:
print(x[0][0])
Output:
1
The reason is due to the differences between floating point division and integer division
/ represent floating point division in python, so a/2 + 1 is 3.5 + 1 = 4.5
// represent integer division, so a//2 + 1 is 3 + 1 = 4
In this case we want to round towards zero with our length calculation so we want to use //
Alternatively you could also get by using floating point division and not adding 1
ie) if list.count(i) >= a/2:

Finding if n! + 1 is a perfect square

I'm trying to write a program to look for a number, n, between 0 and 100 such that n! + 1 is a perfect square. I'm trying to do this because I know there are only three so it was meant as a test of my Python ability.
Refer to Brocard's problem.
math.sqrt always returns a float, even if that float happens to be, say, 4.0. As the docs say, "Except when explicitly noted otherwise, all return values are floats."
So, your test for type(math.sqrt(x)) == int will never be true.
You could try to work around that by checking whether the float represents an integer, like this:
sx = math.sqrt(x)
if round(sx) == sx:
There's even a built-in method that does this as well as possible:
if sx.is_integer():
But keep in mind that float values are not a perfect representation of real numbers, and there are always rounding issues. For example, for a too-large number, the sqrt might round to an integer, even though it really wasn't a perfect square. For example, if math.sqrt(10000000000**2 + 1).is_integer() is True, even though obviously the number is not a perfect square.
I could tell you whether this is safe within your range of values, but can you convince yourself? If not, you shouldn't just assume that it is.
So, is there a way we can check that isn't affected by float roading issues? Sure, we can use integer arithmetic to check:
sx = int(round(math.sqrt(x)))
if sx*sx == x:
But, as Stefan Pochmann points out, even if this check is safe, does that mean the whole algorithm is? No; sqrt itself could have already been rounded to the point where you've lost integer precision.
So, you need an exact sqrt. You could do this by using decimal.Decimal with a huge configured precision. This will take a bit of work, and a lot of memory, but it's doable. Like this:
decimal.getcontext().prec = ENOUGH_DIGITS
sx = decimal.Decimal(x).sqrt()
But how many digits is ENOUGH_DIGITS? Well, how many digits do you need to represent 100!+1 exactly?
So:
decimal.getcontext().prec = 156
while n <= 100:
x = math.factorial(n) + 1
sx = decimal.Decimal(x).sqrt()
if int(sx) ** 2 == x:
print(sx)
n = n + 1
If you think about it, there's a way to reduce the needed precision to 79 digits, but I'll leave that as an exercise for the reader.
The way you're presumably supposed to solve this is by using purely integer math. For example, you can find out whether an integer is a square in logarithmic time just by using Newton's method until your approximation error is small enough to just check the two bordering integers.
For very large numbers it's better to avoid using floating point square roots altogether because you will run into too many precision issues and you can't even guarantee that you will be within 1 integer value of the correct answer. Fortunately Python natively supports integers of arbitrary size, so you can write an integer square root checking function, like this:
def isSquare(x):
if x == 1:
return True
low = 0
high = x // 2
root = high
while root * root != x:
root = (low + high) // 2
if low + 1 >= high:
return False
if root * root > x:
high = root
else:
low = root
return True
Then you can run through the integers from 0 to 100 like this:
n = 0
while n <= 100:
x = math.factorial(n) + 1
if isSquare(x):
print n
n = n + 1
Here's another version working only with integers, computing the square root by adding decreasing powers of 2, for example intsqrt(24680) will be computed as 128+16+8+4+1.
def intsqrt(n):
pow2 = 1
while pow2 < n:
pow2 *= 2
sqrt = 0
while pow2:
if (sqrt + pow2) ** 2 <= n:
sqrt += pow2
pow2 //= 2
return sqrt
factorial = 1
for n in range(1, 101):
factorial *= n
if intsqrt(factorial + 1) ** 2 == factorial + 1:
print(n)
The number math.sqrt returns is never an int, even if it's an integer.How to check if a float value is a whole number

Project Euler #25: Keep getting Overflow error (result to large) - is it to do with calculating fibonacci number?

I'm working on solving the Project Euler problem 25:
What is the first term in the Fibonacci sequence to contain 1000
digits?
My piece of code works for smaller digits, but when I try a 1000 digits, i get the error:
OverflowError: (34, 'Result too large')
I'm thinking it may be on how I compute the fibonacci numbers, but i've tried several different methods, yet i get the same error.
Here's my code:
'''
What is the first term in the Fibonacci sequence to contain 1000 digits
'''
def fibonacci(n):
phi = (1 + pow(5, 0.5))/2 #Golden Ratio
return int((pow(phi, n) - pow(-phi, -n))/pow(5, 0.5)) #Formula: http://bit.ly/qDumIg
n = 0
while len(str(fibonacci(n))) < 1000:
n += 1
print n
Do you know what may the cause of this problem and how i could alter my code avoid this problem?
Thanks in advance.
The problem here is that only integers in Python have unlimited length, floating point values are still calculated using normal IEEE types which has a maximum precision.
As such, since you're using an approximation, using floating point calculations, you will get that problem eventually.
Instead, try calculating the Fibonacci sequence the normal way, one number (of the sequence) at a time, until you get to 1000 digits.
ie. calculate 1, 1, 2, 3, 5, 8, 13, 21, 34, etc.
By "normal way" I mean this:
/ 1 , n < 3
Fib(n) = |
\ Fib(n-2) + Fib(n-1) , n >= 3
Note that the "obvious" approach given the above formulas is wrong for this particular problem, so I'll post the code for the wrong approach just to make sure you don't waste time on that:
def fib(n):
if n <= 3:
return 1
else:
return fib(n-2) + fib(n-1)
n = 1
while True:
f = fib(n)
if len(str(f)) >= 1000:
print("#%d: %d" % (n, f))
exit()
n += 1
On my machine, the above code starts going really slow at around the 30th fibonacci number, which is still only 6 digits long.
I modified the above recursive approach to output the number of calls to the fib function for each number, and here are some values:
#1: 1
#10: 67
#20: 8361
#30: 1028457
#40: 126491971
I can reveal that the first Fibonacci number with 1000 digits or more is the 4782th number in the sequence (unless I miscalculated), and so the number of calls to the fib function in a recursive approach will be this number:
1322674645678488041058897524122997677251644370815418243017081997189365809170617080397240798694660940801306561333081985620826547131665853835988797427277436460008943552826302292637818371178869541946923675172160637882073812751617637975578859252434733232523159781720738111111789465039097802080315208597093485915332193691618926042255999185137115272769380924184682248184802491822233335279409301171526953109189313629293841597087510083986945111011402314286581478579689377521790151499066261906574161869200410684653808796432685809284286820053164879192557959922333112075826828349513158137604336674826721837135875890203904247933489561158950800113876836884059588285713810502973052057892127879455668391150708346800909439629659013173202984026200937561704281672042219641720514989818775239313026728787980474579564685426847905299010548673623281580547481750413205269166454195584292461766536845931986460985315260676689935535552432994592033224633385680958613360375475217820675316245314150525244440638913595353267694721961
And that is just for the 4782th number. The actual value is the sum of all those values for all the fibonacci numbers from 1 up to 4782. There is no way this will ever complete.
In fact, if we would give the code 1 year of running time (simplified as 365 days), and assuming that the machine could make 10.000.000.000 calls every second, the algorithm would get as far as to the 83rd number, which is still only 18 digits long.
Actually, althought the advice given above to avoid floating-point numbers is generally good advice for Project Euler problems, in this case it is incorrect. Fibonacci numbers can be computed by the formula F_n = phi^n / sqrt(5), so that the first fibonacci number greater than a thousand digits can be computed as 10^999 < phi^n / sqrt(5). Taking the logarithm to base ten of both sides -- recall that sqrt(5) is the same as 5^(1/2) -- gives 999 < n log_10(phi) - 1/2 log_10(5), and solving for n gives (999 + 1/2 log_10(5)) / log_10(phi) < n. The left-hand side of that equation evaluates to 4781.85927, so the smallest n that gives a thousand digits is 4782.
You can use the sliding window trick to compute the terms of the Fibonacci sequence iteratively, rather than using the closed form (or doing it recursively as it's normally defined).
The Python version for finding fib(n) is as follows:
def fib(n):
a = 1
b = 1
for i in range(2, n):
b = a + b
a = b - a
return b
This works when F(1) is defined as 1, as it is in Project Euler 25.
I won't give the exact solution to the problem here, but the code above can be reworked so it keeps track of n until a sentry value (10**999) is reached.
An iterative solution such as this one has no trouble executing. I get the answer in less than a second.
def fibonacci():
current = 0
previous = 1
while True:
temp = current
current = current + previous
previous = temp
yield current
def main():
for index, element in enumerate(fibonacci()):
if len(str(element)) >= 1000:
answer = index + 1 #starts from 0
break
print(answer)
import math as m
import time
start = time.time()
fib0 = 0
fib1 = 1
n = 0
k = 0
count = 1
while k<1000 :
n = fib0 + fib1
k = int(m.log10(n))+1
fib0 = fib1
fib1 = n
count += 1
print n
print count
print time.time()-start
takes 0.005388 s on my pc. did nothing fancy just followed simple code.
Iteration will always be better. Recursion was taking to long for me as well.
Also used a math function for calculating the number of digits in a number instead of taking the number in a list and iterating through it. Saves a lot of time
Here is my very simple solution
list = [1,1,2]
for i in range(2,5000):
if len(str(list[i]+list[i-1])) == 1000:
print (i + 2)
break
else:
list.append(list[i]+list[i-1])
This is sort of a "rogue" way of doing it, but if you change the 1000 to any number except one, it gets it right.
You can use the datatype Decimal. This is a little bit slower but you will be able to have arbitrary precision.
So your code:
'''
What is the first term in the Fibonacci sequence to contain 1000 digits
'''
from Decimal import *
def fibonacci(n):
phi = (Decimal(1) + pow(Decimal(5), Decimal(0.5))) / 2 #Golden Ratio
return int((pow(phi, Decimal(n))) - pow(-phi, Decimal(-n)))/pow(Decimal(5), Decimal(0.5)))
n = 0
while len(str(fibonacci(n))) < 1000:
n += 1
print n

Categories