Looking to understand the logical breakdown of this code block - python

Just playing with some basic code (factorials) but cannot quite get my head around how this is achieving the correct result. The result on each loop does not seem to be stored anywhere - so how does the code remember the iterated value? (I know there are modules - this is just a logic exercise)
def factoral2(num):
if num == 0:
return 1
return num * factoral2(num - 1)
Above is the method that I'm not quite sure how works
def factoral(num):
number = []
for i in range(0, num):
number.append(num)
num -= 1
print(number)
product = 1
for x in number:
product *= x
return product
This was my interpretation of its logic, which is obviously a bit more verbose than what's ideal
Both work - just trying to understand the logic of the optimised version

ok, let's run through an example lets say the user inputs 2 -> num=2.
First, we have to build our way down (essentially we need num to equal 0)
first, we call factorial(2), and check the if statement which turns out to be false.
therefor factorial(2) = return num * factorial(num-1) = return 2 * factorial(1)
the above statement needs to compute the value for factorial(1) before it can return a value, so let's go ahead and do that
first, we call factorial(1), and check the if statement which == false
therefor factorial(1) = return num * factorial(num-1) = return 1* factorial(0)
the above statement needs to compute the value for factorial(0) before it can return a value, so let's go ahead and do that also
first, we call factorial(0), and check the if statement which == true
therefor factorial(0) = 1
Now we can start building up
factorial(0) = 1
now just start plugging in values
we said factorial(1) = return 1* factorial(0) = return 1* 1 = return 1
factorial(1) = 1
now we can plug in some more values
we said factorial(2) = return 2* factorial(1) = return 2 * 1 = return 2
factorial(2) = 2

Thanks to Andreas and below_avg_st for your help.
Makes alot more sense now.
As I see it:
Recursive functions generate an infinite loop if a condition to break is not allocated.
(I've re-written the below to state that logic abit more). If X does not equal 0, keep looping through the function while decrementing X.
When the loop does break - (because X = 0) assign a value of 1 - to exit the loop.
def test(x):
if x != 0:
return x * test(x-1)
else:
return 1
test(4)
Thanks to all for your help

It's a recursive function that calls itself, giving num - 1 as argument.
The function supplied:
def factoral2(num):
if num == 0:
return 1
return num * factoral2(num-1)
"gets expanded" into this:
def factoral2(num):
result = 1
while (num != 0):
result *= num
num -= 1
return result
value = factoral2(4) can be illustrated as such:
num = 4
num = 3
num = 2
num = 1
num = 0; return 1
result = 1
result = 2 * 1
result = 3 * 2
result = 4 * 6
value = 24
I'd also like to note out a better non-recursive version of the factorial function, compared to the one you crafted, that unnecessarily creates and fills a list.
def factorial(n):
if n < 0:
raise ValueError("The factorial value may only be calculated on positive integers")
product = 1
for i in range(2, n + 1):
product *= i
return product
– or just a simplified recursive one, using the "ternary operator":
def factorial(n):
if n < 0:
raise ValueError("The factorial value may only be calculated on positive integers")
return 1 if n == 0 else n * factorial(n - 1)

Related

The incrementing here continues to return a 0 value

I was writing the solution to this codewars problem however I've ran into a bit of an issue.
Problem statement:
Write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit, e.g.:
persistence(39) # returns 3, because 39=27, 27=14, 1*4=4 and 4 has only one digit
def persistence(n, t=1, x=0):
if len(str(n)) > 1:
number = [int(i) for i in str(n)]
for i in number:
t = t * i
if len(str(t)) > 1:
x += 1
return(persistence(t,x))
else:
return(x)
else:
return 0
I can't quite figure out what the error is in this code. My hunch is that it's either a parameter error or the way the return() value is placed.
In essence, the code for distilling an integer to it's multiples is correct, so I just added an extra parameter to persistence; setting x = 0 and making it so that each time the if condition was fulfilled it would increment that exact x value. Once the number was distilled, simply output x. Yet it continues to simply output 0 as the final answer. What's the problem here?
Edit: Solution was in the comments, didn't realise how the parameters were passing. Correct version is:
return(persistence(t,1,x))
Also had to set x = 1 for the logic to work on codewars.
There are 2 flaws in Your code:
return(persistence(t,x))
should be
return(persistence(t,1,x))
otherwise the value of x will be assigned to t and x will be defaulted to 0.
Then you must increment x directly after the first test, otherwise You will miss one iteration.
Another way to calculate this is not to switch over to strings, but to do it numerically:
def persistence(n):
iterations = 0; # no iterations yet
while n > 9: # while n has more than 1 digit:
product = 1 # neutrum for result product
while n > 0: # while there a digit to process:
digit = n % 10 # retrieve the right most digit
product *= digit # do the multiplication
n = n // 10 # cut off the processed digit
iterations += 1 # increment iterations
n = product # let n be the newly calculated product
return iterations # return the result
I think you your function's parameters work not as you expect them to do.
When you call function persistence(t, x), the first argument n should become t, and second argument x, should become new x. But in your function, x becomes new t because of their position.
It is quite useful to have bunch of print statements to reveal the bug.
def persistence(n, x=1, t=1):
print('x:', x)
if len(str(n)) > 1:
number = [int(i) for i in str(n)]
for i in number:
t = t * i
print('t:', t)
if len(str(t)) > 1:
x += 1
print('x has changed:', x)
return persistence(t, x)
else:
return x
else:
return 0
print(persistence(39))
print('---------------')
print(persistence(999))
print('---------------')
print(persistence(4))
Passes all test cases with two changes:
You were not updating your n with the new t everytime
Your x was being set to 0 every time. That should be set to 1 in the beginning (default value)
def persistence(n, t=1, x=1):
if len(str(n)) > 1:
number = [int(i) for i in str(n)]
for i in number:
t = t * i
if len(str(t)) > 1:
x += 1
return (persistence(n=t, x=x))
else:
return (x)
else:
return 0
Actually, you can write it without needing both parameters t and n. Just one n is fine as shown below:
def persistence(n, x=1):
if len(str(n)) > 1:
number = [int(i) for i in str(n)]
t = 1
for i in number:
t = t * i
if len(str(t)) > 1:
return x + (persistence(n=t, x=x))
else:
return (x)
else:
return 0

How to add number in each base case of recursion?(Python3)?

I want to add 1 to ans once a recursion branch reaches to where n==0 but it fails.
I tried to assign ans at first and add global in front of it but they don't work.
It's all known that we can do it in the list. That is, the base case can process ans.append(1) for ans=[]. I'm confused about what I should do here.
# Type: int n
def func(n)
def climb(n):
if n == 0:
ans += 1
elif n < 0:
ans += 0
else:
climb(n - 1)
climb(n - 2)
ans = 0
climb(n)
return ans
Edit: In case you think whether I miss the return inside. No, the mechanism I want to copy is something like this, which works:
# Type: string digits
def func(digits)
dict = {...}
def dfs(comb, i):
if i == len(digits):
ans.append(comb)
else:
for letter in dict[digits[i]]:
dfs(comb + letter, i + 1)
ans = []
if digits:
dfs("", 0)
return ans
Your code cannot parse correctly:
In your original question version, the return appeared outside of any function
ans is not defined inside climb, and you cannot modify a variable outside of climb without using a declaration with nonlocal or similar
Not a problem, but ans += 0 is a no-op.
Here is how the code can be made to parse and run:
def func(n):
def climb(n):
nonlocal ans
if n == 0:
ans += 1
elif n > 0:
climb(n - 1)
climb(n - 2)
ans = 0
climb(n)
return ans
print(func(5)) # 8
However, this way of working makes climb a function with side-effect, i.e. it is not pure. This is not best practice. You could make climb pure by returning the value that has to be added. This also makes it unnecessary to wrap climb into another function:
def climb(n):
if n == 0:
return 1
if n < 0:
return 0
return climb(n - 1) + climb(n - 2)
print(climb(5)) # 8
Note that neither represents an efficient solution. You could improve by using memoization.

Best way to store the bigger out of 2 numbers

I am trying to make my python program as optimised as possible, and there is this portion I am unsure about. I need to store the largest value obtained so far, and I have 2 candidate codes to do so. Which one is the most time-optimised? Or is there a faster way to do so?
Code 1:
if value > biggest_value:
biggest_value = value
Code 2:
biggest_value = max(biggest_value, value)
Context:
def check_palindrome(num):
num = str(num)
if len(num) < 1:
return True
else:
if num[0] == num[-1]:
return check_palindrome(num[1:-1])
else:
return False
def main():
biggest_product = 0
for a in range(100, 1000):
for b in range(100, 1000):
product = a * b
if check_palindrome(product):
# store the biggest_product here
return biggest_product
main()
Code 2 is the better option because in Code 1 you have to compare the numbers with 2 commands ( < = ) and in Code 2 you only have one command.
Good Luck with your Programm and have fun!
Jonas

recursive digit sum of number , Is there a better way to write this functions?

[![enter image description here][1]][1]
First function returns the recursive digit sum of that number. The second function return dictionary where key is reg_dig_sum and value is count of that number occurring. when I tested it it failed giving me this
elf.assertEqual(sum_dict[0], 1)
AssertionError: 0 != 1
How can I solve this?
def reg_dig_sum(n):
x = sum(int(digit) for digit in str(n))
if x < 10:
return x
else:
return reg_dig_sum(x)
def distr_of_rec_digit_sums(low=0, high=1500):
distr = {}
for x in range(low, high):
if reg_dig_sum(x) not in distr:
distr[reg_dig_sum(x)] = 0
else:
distr[reg_dig_sum(x)] += 1
return distr
The problem I can think is your count for each reg_dig_sum will be one less than what it should be. That's the reason assertion condition is failing.
You can fix this logical error by initializing distr to either 1 or by removing else condition.
I would also suggest to use other alternatives like defaultdict to take care of initialization for you.
def distr_of_rec_digit_sums(low=0, high=1500):
distr = {}
for x in range(low, high):
if reg_dig_sum(x) not in distr:
distr[reg_dig_sum(x)] = 1 # this should be initialized to 1 for first occurance.
else:
distr[reg_dig_sum(x)] += 1
return distr
Using defaultdict
from collections import defaultdict
def distr_of_rec_digit_sums(low=0, high=1500):
distr = defaultdict(int)
for x in range(low, high):
distr[reg_dig_sum(x)] += 1
return distr
See previous comments and answers regarding changing 0 to 1
Also try to use much simpler code for reg_dig_sum with the same result:
def reg_dig_sum(n):
return (n - 1) % 9 + 1
I've used a simple function to do this for a long time:
def sum_digits(num):
if num <= 9:
return num
num = int(num / 10) + num - int(num / 10) * 10
''' takes the last digit of num off, and adds it to num. '''
return sum_digits(num)
There are a lot of things you can find with this function. Try this on the power series, and you'll see what I mean. Really any mathematical function you could use on a number line will return interesting patterns; this function helps you see part of that.

Why is this code not running fully? It doesn't run line 53

I made myself an exercise with python since I am new. I wanted to make a rever LMC calculator ( Least common multiple ) but for some reason, something as simple as a print in a loop doesn't seem o work for me. I would appreciate some help since I am stuck on this weird issue for 20 minutes now. Here is the code:
import random
import sys
def print_list():
count_4_print = 0
while count_4_print < len(values):
print(values[count_4_print])
count_4_print += 1
def lcm(x, y):
if x > y:
greater = x
else:
greater = y
while True:
if (greater % x == 0) and (greater % y == 0):
lcm1 = greater
break
greater += 1
return lcm1
def guess(index, first_guess, second_guess):
num = 1
while lcm(first_guess, second_guess) != values[num - 1]:
first_guess = random.randrange(1, 1000000)
second_guess = random.randrange(1, 1000000)
num += 1
num = 1
if lcm(first_guess, second_guess) == values[num - 1]:
return first_guess, second_guess
num += 1
lineN = int(input())
values = []
count_4_add = 0
count_4_guess = 0
for x in range(lineN):
values.append(int(input()))
count_4_add += 1
if count_4_add >= lineN:
break
print_list()
for x in range(lineN + 1):
first, second = guess(count_4_guess, 1, 1)
count_4_guess += 1
print(first + second)
# this ^^^ doesn't work for some reason
Line 57 is in the while loop with count_4_guess. Right above this text, it says print(first_guess + second_guess)
Edit: The code is supposed to take in an int x and then prompt for x values. The outputs are the inputs without x and LMC(output1, output2) where the "LMC" is one of the values. This is done for each of the values, x times. What it actually does is just the first part. It takes the x and prompts for x outputs and then prints them but doesn't process the data (or it just doesn't print it)
Note: From looking at your comments and edits it seems that you are lacking some basic knowledge and/or understanding of things. I strongly encourage you to study more programming, computer science and python before attempting to create entire programs like this.
It is tough to answer your question properly since many aspects are unclear, so I will update my answer to reflect any relevant changes in your post.
Now, onto my answer. First, I will go over some of your code and attempt to give feedback on what could improved. Then, I will present two ways to compute the least common multiple (LCM) in python.
Code review
Code:
def print_list():
count_4_print = 0
while count_4_print < len(values):
print(values[count_4_print])
count_4_print += 1
Notes:
Where are the parameters? It was already mentioned in a few comments, but the importance of this cannot be stressed enough! (see the note at the beginning of my comment)
It appears that you are trying to print each element of a list on a new line. You can do that with print(*my_list, sep='\n').
That while loop is not how you should iterate over the elements of a list. Instead, use a for loop: for element in (my_list):.
Code:
def lcm(x, y):
if x > y:
greater = x
else:
greater = y
while True:
if (greater % x == 0) and (greater % y == 0):
lcm1 = greater
break
greater += 1
return lcm1
Notes:
This is not a correct algorithm for the LCM, since it crashes when both numbers are 0.
The comparison of a and b can be replaced with greater = max(x, y).
See the solution I posted below for a different way of writing this same algorithm.
Code:
def guess(index, first_guess, second_guess):
num = 1
while lcm(first_guess, second_guess) != values[num - 1]:
first_guess = random.randrange(1, 1000000)
second_guess = random.randrange(1, 1000000)
num += 1
num = 1
if lcm(first_guess, second_guess) == values[num - 1]:
return first_guess, second_guess
num += 1
Notes:
The line num += 1 comes immediately after return first_guess, second_guess, which means it is never executed. Somehow the mistakes cancel each other out since, as far as I can tell, it wouldn't do anything anyway if it were executed.
if lcm(first_guess, second_guess) == values[num - 1]: is completely redundant, since the while loop above checks the exact same condition.
In fact, not only is it redundant it is also fundamentally broken, as mentioned in this comment by user b_c.
Unfortunately I cannot say much more on this function since it is too difficult for me to understand its purpose.
Code:
lineN = int(input())
values = []
count_4_add = 0
count_4_guess = 0
for x in range(lineN):
values.append(int(input()))
count_4_add += 1
if count_4_add >= lineN:
break
print_list()
Notes:
As explained previously, print_list() should not be a thing.
lineN should be changed to line_n, or even better, something like num_in_vals.
count_4_add will always be equal to lineN at the end of your for loop.
Building on the previous point, the check if count_4_add >= lineN is useless.
In conclusion, count_4_add and count_4_guess are completely unnecessary and detrimental to the program.
The for loop produces values in the variable x which is never used. You can replace an unused variable with _: for _ in range(10):.
Since your input code is simple you could probably get away with something like in_vals = [int(input(f'Enter value number {i}: ')) for i in range(1, num_in_vals+1)]. Again, this depends on what it is you're actually trying to do.
LCM Implementations
According to the Wikipedia article referenced earlier, the best way to calculate the LCM is using the greatest common denominator.
import math
def lcm(a: int, b: int) -> int:
if a == b:
res = a
else:
res = abs(a * b) // math.gcd(a, b)
return res
This second method is one possible brute force solution, which is similar to how the one you are currently using should be written.
def lcm(a, b):
if a == b:
res = a
else:
max_mult = a * b
res = max_mult
great = max(a, b)
small = min(a, b)
for i in range(great, max_mult, great):
if i % small == 0:
res = i
break
return res
This final method works for any number of inputs.
import math
import functools
def lcm_simp(a: int, b: int) -> int:
if a == b:
res = a
else:
res = abs(a * b) // math.gcd(a, b)
return res
def lcm(*args: int) -> int:
return functools.reduce(lcm_simp, args)
Oof, that ended up being way longer than I expected. Anyway, let me know if anything is unclear, if I've made a mistake, or if you have any further questions! :)

Categories