def generate(num):
res = 0
for i in str(num):
res = res + int(i)
return res + num
I was looking at someone's code for generating numbers and I don't understand why he used 'str(num)' to iterate i to num. Then he translates the str(i) again to int(i).
What is the intention behind this? Why can we not create
def generate(num):
res = 0
for i in num:
res = res + i
return res + num
from the beginning?
Thanks
By turning a number into a string, you are allowed to iterate over the various digits.
Try this:
for c in str(123):
print(c)
Of course, c is then a string, so if you want to use the digit as a number, you need to cast it back to an int.
In other words, the first function you shared computes the sum of the digits of a number. The second function you shared would compute the sum of numbers in an iterable, like a list of integers.
A single int is not an iterable, while a single str is - so for x in y: will work if y is a str, but it will cause an error if y is an int.
This code snippet,
def generate(num):
res = 0
for i in str(num):
res = res + int(i)
return res + num
assumes num is a numerical value and adds the individual digits of a given number to produce res and then it adds that res to the the number itself,
So, when you call generate(123) it adds (1 + 2 + 3) + 123 which produces 129
The second code snippet raises and error when num is a numerical value.
Hope it helps!
Related
While practicing recursion I came across a question to reverse an integer using recursion. I tried to do the question without converting the integer into a string.
I was able to solve the question partially but the output would always come without any of the zeroes from the original input. Below is the code I came up with:
def reverseNumber(n):
if (n//10) == 0:
return n
lastDigit = n%10
ans = reverseNumber(n//10)
nod = 0
for i in str(ans):
nod += 1
return (10**nod)*lastDigit + ans
Upon inspection I could see that this was happening because when lastDigit is 0 it only returned the reversed integer from the recursive call i.e input 4230 will give 324.
But this also meant that all zeroes between the original input would also get removed as we went deeper in the recursive calls.
So please tell me how to modify this code so that zeroes in the original input are not removed while reversing.
You probably need just this:
def rev(n):
if n>0:
return str(n%10)+rev(n//10)
else:
return ''
reverseNumber should return an int and accept positive and negative numbers.
The simplest way to fix your code, without handling negative numbers, is:
def reverseNumber(n):
if n == 0:
return 0
lastDigit = n%10
n //= 10
return int(str(lastDigit) + str(reverseNumber(n))) if n else lastDigit
for test in (0, 123, 120):
print(test, reverseNumber(test))
Prints:
0 0
123 321
120 21
Yes! The reverse of 120 is 21 when you are dealing with int types as opposed to str types.
Another implementation that does handle negative numbers takes a whole different approach:
I have broken this out into two functions. Function rev is a generator function that assumes that it is being called with a positive, non-negative number and will recursively yield successive digits of the number in reverse. reverseNumber will join these numbers, convert to an int, adjust the sign and return the final result.
def reverseNumber(n):
def rev(n):
assert n >= 0
yield str(n % 10)
n //= 10
if n != 0:
yield from rev(n)
if n == 0: return 0 # special case
x = int(''.join(rev(abs(n))))
return x if n >= 0 else -x
tests = [0, 132, -132, 120]
for test in tests:
print(test, reverseNumber(test))
Prints:
0 0
132 231
-132 -231
120 21
For all non-negative n, when n < 10 it is a single digit and already the same as its reverse -
def reverse(n = 0):
if n < 10:
return str(n)
else
return str(n%10) + rev(n//10)
you can also try the following Python3 code. It will cover positive and negative integers to be reversed as integers - not as strings ...
x = int(input("What integer shall be reversed? "))
n = abs(x) # ... to handle negative integers
r = 0 # ... will hold the reversed int.
while n > 0: # Recursion part reversing int.
r = (r * 10) + (n % 10) # using '%' modulo
n = int(n / 10) # and a 'dirty way' to floor
if x < 0: # Turn result neg. if x was neg.
return (r * -1)
else:
return r # Keep result pos. if x was pos.
This approach will leave your zeros in the middle of the integer intact, though it will make any zero at the end of the initial number vanish - rightfully so as integers do not start with a zero. ;))
There was this question in a university assignment related to Hofstadter sequence. It basically say that it is a integer sequence blah blah and there are two values for a given index n. A male value [M(n)] and a female value [F(n)].
They are defined as:
M(0)=0
F(0)=1
M(n)=n-F(M(n-1))
F(n)=n-M(F(n-1))
And we were asked to write a program in python to find the Male and Female values of the sequence of a given integer.
So the code I wrote was:
def female(num):
if num == 0:
return 1
elif num >0:
return num - male(female(num-1))
def male(num):
if num==0:
return 0
elif num >0:
return num - female(male(num-1))
And when executed with a command like
print male(5)
It works without a fuss. But when I try to find the value of n = 300, the program gives no output.
So I added a print method inside one of the functions to find out what happens to the num value
[ elif num>0:
print num ...]
And it shows that the num value is decreasing until 1 and keeps hopping between 1 and 2 at times reaching values like 6.
I can’t figure out why it happens. Any insight would be nice. Also what should I do to find the values relevant to bigger integers. Thanks in advance. Cheers.
The code is theoretically fine. What you underestimate is the complexity of the computation. Formula
M(n)=n-F(M(n-1))
actually means
tmp = M(n-1)
M(n) = n - F(tmp)
So if you represent this calculation as a tree of necessary calls, you might see that its a binary tree and you should go through all its nodes to calculate the results. Given that M(n) and F(n) are about n/2 I'd estimate the total number of the nodes to be of order 2^(n/2) which becomes a huge number once you put n = 300 there. So the code works but it just will take a very very long time to finish.
The one way to work this around is to employ caching in a form of memoization. A code like this:
femCache = dict()
def female(num):
#print("female " + str(num))
global femCache
if num in femCache:
return femCache[num];
else:
res = 1 # value for num = 0
if num > 0:
res = num - male(female(num-1))
femCache[num] = res
return res
maleCache = dict()
def male(num):
#print("male " + str(num))
global maleCache
if num in maleCache:
return maleCache[num];
else:
res = 0 # value for num = 0
if num > 0:
res = num - female(male(num-1))
maleCache[num] = res
return res
print(male(300))
should be able to compute male(300) in no time.
Trying to write a piece of code that will sum the digits of a number. Also I should add that I want the program to keep summing the digits until the sum is only 1 digit.
For example, if you start with 1969, it should first add 1+9+6+9 to get 25. Since the value 25 has more than a single digit, it should repeat the operation to obtain 7 as a final answer.
Was just wondering how I could pull this off and possibly make it recursive as well. This is what I have so far
def sum_digits3(n):
r = 0
while n:
r, n = r + n % 10, n // 10
return r
Convert back and forth between strings and ints, make use of sum().
>>> def foo(n):
n = str(n)
if len(n) == 1:
return int(n)
return foo(sum(int(c) for c in n))
>>> foo(1969)
7
>>>
def foo(n):
n = str(n)
if len(n) == 1:
return int(n)
return foo(sum(int(c) for c in n))
It is as simple as involving explicitly the recursion.
def sum_digits3(n):
r = 0
while n:
r, n = r + n % 10, n // 10
if len(str(r))>1:
return sum_digits3(r)
return r
But i must admit that i am going to read the links given by suspicious dog. And the answer of wwii is smarter than mine.
We can implement decimal to binary conversion for positive integers in Python by using the following algorithm that takes an integer n as input and returns a string of 1's and 0's holding the binary representation of n .
Write a function int_to_bin_string(n) (in int_to_bin_string.py) which takes a non-negative integer n and returns the a string of 1's and 0's.
We are not allowed to use any built in python functions that converts numbers to strings or vice versa.
def int_to_bin_string(n):
if n == 0:
return "0"
s = ''
while n > 0:
if n % 2 == 0:
ch = "0"
else:
ch = "1"
s = s + ch
n = n/2
return s
That's what I have tried. When I try int_to_bin_string(255) I get '1', instead of '11111111'
It works now!
for the second to last line, you need to have
n = n/2
You have a premature return in return s. It needs to be outside your while loop, which is why you are getting only one character. Also it should be n = n/2.
Additionally, look at your first return statement, it returns an integer instead of a string.
You could use a key function of python
bin( value ) # returns a string like '0b110110'
simply get a slice if you only want the numbers...
bin( value )[2:]
This could be an alternative for you... it's almost the same though
def int_to_bin_string(n):
s = ''
while n:
s = ((n & 1) and "1" or "0") + s
n >>= 1
return s or "0"
I hope this does help ;)
I'll bet a nickel, from the symptoms, that this is a Python 3 vs. Python 2 issue. Your code as modified works fine on Python 2.
You can make this code bilingual with a simple modification. Replace the division by 2 with a shift. In place of n = n/2, use n = n>>1. That works on both P2 and P3.
def int_to_bin_string(num):
n = int(num) # added to force int type on entry
if n == 0:
return "0"
s = ''
while n > 0:
if n % 2 == 0:
ch = "0"
else:
ch = "1"
s = s + ch
n = n >> 1 # was n/2
return s
I also added a small filter to coerce the argument to int on entry, with a rename to avoid losing the original argument value. When debugging, I like see the original value--hence the rename. I just do that as reflex; it's not related to your question.
import sys
def keepsumming(number):
numberlist = []
for digit in str(number):
numberlist.append(int(digit))
total = reduce(add, numberlist)
if total > 9:
keepsumming(total)
if total <= 9:
return total
def add(x,y):
return x+y
keepsumming(sys.argv[1])
I want to create a function that adds the individual digits of any number, and to keep summing digits until the result is only one digit. (e.g. 1048576 = 1+0+4+8+5+7+6 = 31 = 3+1 = 4). The function seems to work in some laces but not in others. for example:
$python csp39.py 29
returns None, but:
$python csp39.py 30
returns 3, as it should...
Any help would be appreciated!
As others have mentioned, the problem seems to be with the part
if total > 9:
keepsumming(total) # you need return here!
Just for completeness, I want to present you some examples how this task could be solved a bit more elegantly (if you are interested). The first also uses strings:
while number >= 10:
number = sum(int(c) for c in str(number))
The second uses modulo so that no string operations are needed at all (which should be quite a lot faster):
while number >= 10:
total = 0
while number:
number, digit = divmod(number, 10)
total += digit
number = total
You can also use an iterator if you want to do different things with the digits:
def digits(number, base = 10):
while number:
yield number % base
number //= base
number = 12345
# sum digits
print sum(digits(number))
# multiply digits
from operator import mul
print reduce(mul, digits(number), 1)
This last one is very nice and idiomatic Python, IMHO. You can use it to implement your original function:
def keepsumming(number, base = 10):
if number < base:
return number
return keepsumming(sum(digits(number, base)), base)
Or iteratively:
def keepsumming(number, base = 10):
while number >= base:
number = sum(digits(number, base))
UPDATE: Thanks to Karl Knechtel for the hint that this actually is a very trivial problem. It can be solved in one line if the underlying mathematics are exploited properly:
def keepsumming(number, base = 10):
return 1 + (number - 1) % (b - 1)
There's a very simple solution:
while number >= 10:
number = sum(divmod(number, 10))
I am fairly sure you need to change
if total > 9:
keepsumming(total)
into
if total > 9:
return keepsumming(total)
As with most recursive algorithms, you need to pass results down through returning the next call.
and what about simply converting to string and summing?
res = 1234567
while len(str(res)) > 1 :
res = sum(int(val) for val in str(res))
return res
Thats's what I use to do :)
Here is a clean tail-recursive example with code that is designed to be easy to understand:
def keepsumming(n):
'Recursively sum digits until a single digit remains: 881 -> 17 -> 8'
return n if n < 10 else keepsumming(sum(map(int, str(n))))
Here you go:
>>> sumdig = (lambda recurse: (lambda fix: fix(lambda n: sum(int(c) for c in str(n)))) (recurse(lambda f, g: (lambda x: (lambda d, lg: d if d == lg else f(f,g)(d))(g(x),x)))))(lambda f: lambda x: f(f,x))
>>> sumdig(889977)
3
You are sure to get full, if not extra, credit for this solution.
Your code as currently written need to replace the recursive call to keepsumming(total) with return keepsumming(total); python does not automatically return the value of the last evaluated statement.
However, you should note that your code has redundancies.
for digit in str(number):
numberlist.append(int(digit))
total = reduce(add, numberlist)
should become
from operator import add
total = reduce(add, (int(digit) for digit in str(number)))
A code golf-able version that doesn't require a while loop, or recursion.
>>> import math
>>> (lambda x: sum(int((x * 10 ** -p) % 10) for p in range(math.ceil(math.log(x, 10)))))(1048576)
31
This is probably what I'd use though.
def sum_digits(number):
return sum(
int(number * (10 ** -place) % 10)
for place in range(math.ceil(math.log(number, 10)))
)
It's easier to see what's going on if you append to a list instead of summing the integer values.
def show_digits(number):
magnitude = int(math.log(number, 10))
forms = []
for digit_place in range(magnitude + 1):
form = number * (10 ** -digit_place) # force to one's place
forms.append(form)
return forms
>>> show_digits(1048576)
[1048576, 104857.6, 10485.76, 1048.576, 104.8576, 10.48576, 1.048576]
For keepsumming that takes a string:
def keepsumming(number):
return number if len(number) < 2 \
else keepsumming(str(sum(int(c) for c in number)))
For keepsumming that takes a number:
def keepsumming(number):
return number if number < 10 \
else keepsumming(sum(int(c) for c in str(number)))