Python Recursive calls not updating global variable - python-3.x

I'm trying to implement a partial brute force approach in python for Leetcode Beautiful Arrangements. I'm struggling to update my "counter" variable during the recursive calls.
I've tried multiple approaches with global variables and passing it in as a function parameter, no matter what I've done, I'm not getting the correct return value even though the if L == len(numbers) condition is met.
class Solution:
def countArrangement(self, n: int) -> int:
def count_beautiful_arranges(N: int, counter):
numbers = [0]*N
# start index at 1
# loop creates the array of 1 to N for initial list
for i in range(1, N+1):
numbers[(i-1)] = i
# call recursive permutation function
permutation(numbers, 0, counter)
return counter
def permutation(numbers: list, L: int, counter):
# check each recursive call
#print(L)
#print(len(numbers))
if L == len(numbers):
counter = counter + 1
for j in range(L, len(numbers)):
swap(numbers, j, L)
if numbers[L] % (L+1) == 0 or (L+1) % numbers[L] == 0:
permutation(numbers, L+1, counter)
swap(numbers, j, L)
def swap(numbers: list, x: int, y: int):
#pythonic code
numbers[x], numbers[y] = numbers[y], numbers[x]
count_beautiful_arranges(n, 0)

I fixed it by using Python's ability to attach a variable to a function. with
<Function_Name>. method I was able to attach a counter and increment it in the recursive stack.
I declared permutation.countser before calling the recursive functions then returned the value after the calls were completed.
This StackOverflow answer helped me.
def count_beautiful_arranges(N: int, counter):
numbers = [0]*N
# start index at 1
# loop creates the array of 1 to N for initial list
for i in range(1, N+1):
numbers[(i-1)] = i
permutation(numbers, 0)
def permutation(numbers: list, L: int):
if L == len(numbers):
permutation.countser += 1
for j in range(L, len(numbers)):
swap(numbers, j, L)
if numbers[L] % (L+1) == 0 or (L+1) % numbers[L] == 0:
permutation(numbers, L+1)
swap(numbers, j, L)
def swap(numbers: list, x: int, y: int):
#pythonic code
numbers[x], numbers[y] = numbers[y], numbers[x]
permutation.countser = 0
count_beautiful_arranges(n, 0)
return permutation.countser

Related

How to sort a list into even then odd numbers using recursion when the function can only call itself and use basic list manipulations

The function takes in a list of integers and returns a list of the same elements sorted into the order of even integers then odd ones. Im struggling with implementing a recursive function to complete it even though I am able to solve this using a for loop.
def sort(lst: list[int]) -> list[int]:
l1 = []
l2 = []
for i in lst:
if i % 2 == 0:
l1.append(i)
else:
l2.append(i)
x = l1 + l2
return x
def eto(lst: list[int]) -> list[int]:
if len(lst) == 1
return lst
else:
y = lst[0]
x = lst[1:]
return(eto(x))
I'm not sure how to proceed from here.
I suppose we are allowed to reorder the odd numbers, as long as they are in the second part of the list. I completed your proposition:
def eto(lst: list[int]) -> list[int]:
if len(lst) <= 1:
return lst
head = lst[0]
tail = lst[1:]
if head % 2 == 0:
return [head] + eto(tail)
return eto(tail) + [head]

Is it possible to merge these two functions as one calls on another

Would like to know if it is possible to merge these two functions together it seems like it could be a singular function through recursion but i'm not sure. I don't really do a lot of programming but this works as intended i just thought it would be nice to have it as a single function. Just not sure how to program recursion any advice would be great thanks
def sieve(A, n):
for i in A:
if i % n == 0 and i > n:
A.remove(i)
return A
def primelist(N):
A = [i for i in range(2, N + 1)]
for i in A:
A = (sieve(A, i))
print(A)
Decided on a new approach and solved:
def primelist(N):
k = 0
A = [i for i in range(2, N + 1)]
while k < len(A):
for i in A:
if i % A[k] == 0 and i > A[k]:
A.remove(i)
k += 1
return(A)
Decided on a new approach and solved:
We can do better -- your solution, and that of #ikuamike, have the same issue. These lines in particular are inefficient:
for i in A:
if i % A[k] == 0 and i > A[k]:
First, when possible, we should do an easier test before a harder test, so the if should really be:
for i in A:
if i > A[k] and i % A[k] == 0:
to do the comparison (subtraction) test ahead of the modulus (division) test. (Why do all those divisions when you don't need too?)
The next issue is that all the numbers from A[0] to A[k] don't need to be tested as they're eliminated by the comparison, so why not leave them out in the first place:
for i in A[k + 1:]:
if i % A[k] == 0:
Revised code:
def primelist(N):
k = 0
A = [i for i in range(2, N + 1)]
while k < len(A):
for i in A[k + 1:]:
if i % A[k] == 0:
A.remove(i)
k += 1
return A
With N set to 10,000, you can measure the time improvement.
def primelist(N):
A = [i for i in range(2, N + 1)]
new_a = A
for i in A:
for n in new_a:
if n % i == 0 and n > i:
new_a.remove(n)
print(new_a)
This should work well for you, I just replaced the for loop in seive onto the primelist function.

Using Recursive Functions in Python to find Factors of a Given Number

Have tried searching for this, but can't find exactly what I'm looking for.
I want to make a function that will recursively find the factors of a number; for example, the factors of 12 are 1, 2, 3, 4, 6 & 12.
I can write this fairly simply using a for loop with an if statement:
#a function to find the factors of a given number
def print_factors(x):
print ("The factors of %s are:" % number)
for i in range(1, x + 1):
if number % i == 0: #if the number divided by i is zero, then i is a factor of that number
print (i)
number = int(input("Enter a number: "))
print (print_factors(number))
However, when I try to change it to a recursive function, I am getting just a loop of the "The factors of x are:" statement. This is what I currently have:
#uses recursive function to print all the letters of an integer
def print_factors(x): #function to print factors of the number with the argument n
print ("The factors of %s are:" % number)
while print_factors(x) != 0: #to break the recursion loop
for i in range(1,x + 1):
if x % i == 0:
print (i)
number = int(input("Enter a number: "))
print_factors(number)
The error must be coming in either when I am calling the function again, or to do with the while loop (as far as I understand, you need a while loop in a recursive function, in order to break it?)
There are quite many problems with your recursive approach. In fact its not recursive at all.
1) Your function doesn't return anything but your while loop has a comparision while print_factors(x) != 0:
2) Even if your function was returning a value, it would never get to the point of evaluating it and comparing due to the way you have coded.
You are constantly calling your function with the same parameter over and over which is why you are getting a loop of print statements.
In a recursive approach, you define a problem in terms of a simpler version of itself.
And you need a base case to break out of recursive function, not a while loop.
Here is a very naive recursive approach.
def factors(x,i):
if i==0:
return
if x%i == 0:
print(i)
return factors (x,i-1) #simpler version of the problem
factors(12,12)
I think we do using below method:
def findfactor(n):
factorizeDict
def factorize(acc, x):
if(n%x == 0 and n/x >= x):
if(n/x > x):
acc += [x, n//x]
return factorize(acc, x+1)
else:
acc += [x]
return acc
elif(n%x != 0):
return factorize(acc, x+1)
else:
return acc
return factorize(list(), 1)
def factors(x,i=None) :
if i is None :
print('the factors of %s are : ' %x)
print(x,end=' ')
i = int(x/2)
if i == 0 :
return
if x % i == 0 :
print(i,end=' ')
return factors(x,i-1)
num1 = int(input('enter number : '))
print(factors(num1))
Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutations, variable reassignments, and other side effects. That said, here's how I'd write factors -
def factors(n, m = 2):
if m >= n:
return
if n % m == 0:
yield m
yield from factors(n, m + 1)
print(list(factors(10))) # [2,5]
print(list(factors(24))) # [2,3,4,6,8,12]
print(list(factors(99))) # [3,9,11,33]
And here's prime_factors -
def prime_factors(n, m = 2):
if m > n:
return
elif n % m == 0:
yield m
yield from prime_factors(n // m, m)
else:
yield from prime_factors(n, m + 1)
print(list(prime_factors(10))) # [2,5]
print(list(prime_factors(24))) # [2,2,2,3]
print(list(prime_factors(99))) # [3,3,11]
def fact (n , a = 2):
if n <= a :
return n
elif n % a != 0 :
return fact(n , a + 1 )
elif n % a == 0:
return str(a) + f" * {str(fact(n / a , a ))}"
Here is another way. The 'x' is the number you want to find the factors of. The 'c = 1' is used as a counter, using it we'll divide your number by 1, then by 2, all the way up to and including your nubmer, and if the modular returns a 0, then we know that number is a factor, so we print it out.
def factors (x,c=1):
if c == x: return x
else:
if x%c == 0: print(c)
return factors(x,c+1)

Quick sort counting

Python questions again.
I want to count the number of comparison operations performed by quick sort. Because I use a recursive function, I do not think that assigning count = 0 to the beginning of the function body is inappropriate, so I made it as follows.
def QuickSort(lst, count = 0):
if len(lst) > 1:
pivot_idx = len(lst) // 2
smaller_nums, larger_nums = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
if num < lst[pivot_idx]:
smaller_nums.append(num)
else:
larger_nums.append(num)
count = QuickSort(smaller_nums, count + 1)[1]
count = QuickSort(larger_nums, count + 1)[1]
lst[:] = smaller_nums + [lst[pivot_idx]] + larger_nums
return lst, count
However, after counting, I confirmed the count which is much lower than my expectation. According to big o, the quick sort would have to show the calculation of n * log (n), but it showed a much lower count. For example, when sorting a list with 1000 random elements, we expected to see a count of 1000 * log (1000) = 6907, but actually only 1164 counts. I am wondering if I am misusing the count in the function or misunderstanding it.
Thank you.
Your post is mistaken on several points:
Big-O is allows arbitrary constant factors and also ignoring the values for "small" values of n, where "small" can be arbitrarily large for any given analysis. So your computations are meaningless.
Your counts are wrong. There's one comparison per loop iteration. You're counting something else.
This is a strange way to code the count. Just use a global variable.
Try this. Note really you're using twice as many comparisons as this reports. The check that the loop index isn't the pivot could be eliminated with a smarter implementation.
c = 0
def QuickSort(lst):
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
global c
c += 1
(larger, smaller)[num < lst[pivot_idx]].append(num)
return QuickSort(smaller) + [lst[pivot_idx]] + QuickSort(larger)
def Run(n):
lst = [random.randint(0,1000) for r in xrange(n)]
QuickSort(lst)
print c
Run(1000)
If you're aghast at the prospect of using a global variable, then you can just wrap the sort in a class:
import random
class QuickSort:
def __init__(self):
self.comparisons = 0
def sort(self, lst):
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
self.comparisons += 1
(larger, smaller)[num < lst[pivot_idx]].append(num)
return self.sort(smaller) + [lst[pivot_idx]] + self.sort(larger)
def Run(n):
lst = [random.randint(0,1000) for r in xrange(n)]
quicksort = QuickSort()
print quicksort.sort(lst)
print quicksort.comparisons
Run(100)
Building on the answer provided by Gene by adding print statements and a sort "error" range, his example was very helpful to my understanding of quicksort and an error term on the big O impact of operations performance comparison.
class QuickSort:
def __init__(self):
self.comparisons = 0
def sort(self, lst):
k_err = 0 # k << n, the value the sort array can be in error
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != (pivot_idx) :
self.comparisons += 1
try:
(larger, smaller)[(num - k_err) < lst[pivot_idx]].append(num)
except:
(larger, smaller)[(num + k_err) < lst[pivot_idx]].append(num)
print(pivot_idx,"larger", self.comparisons, larger)
print(pivot_idx, "smaller", self.comparisons, smaller, )
return self.sort(smaller) + [lst[pivot_idx]] + self.sort(larger)
def Run(n):
random.seed(100)
lst = [random.randint(0,round(100,0)) for r in range(n)]
quicksort = QuickSort()
print(len(lst), lst)
print(quicksort.sort(lst))
print(quicksort.comparisons, quicksort.comparisons/n, ((quicksort.comparisons/n)/math.log(n,10)), math.log(n,10) )

permutation of a list of numbers done recursively in python

So basically I'm trying to take a list of numbers and write a recursive function that outputs all possible outputs in a list of lists.
My code:
def permutations(lst):
if len(lst) <= 1:
return lst
l = []
for i in range(len(lst)):
m = lst[i]
remlst = lst[:i] + lst[i+1:]
for p in permutations(remlst):
l.append([m] + p)
return l
I'm getting a few errors about not being able to append int.
Simple output:
>>>permutations([1,2])
[[1,2],[2,1]]
There is an implementation for that in itertools:
import itertools
for p in itertools.permutations(list):
# do stuff
Also, to fix your own function, notice that in your "base case" len(lst) <= 1 your returning a list, instead of a list-of-lists. Also, the second return statement should be moved out of the loop;
def permutations(lst):
if len(lst) <= 1:
return [lst]
l = []
for i in range(len(lst)):
m = lst[i]
remlst = lst[:i] + lst[i+1:]
for p in permutations(remlst):
l.append([m] + p)
return l
Because you iterate through result of permutations
for p in permutations(remlst):
your base case needs to return a list of lists like your recursive case does, otherwise you get the error TypeError: can only concatenate list (not "int") to list
You also need to return after the outer for loop.
def permutations(lst):
if len(lst) <= 1:
return [lst] # [[X]]
l = []
for i in range(len(lst)):
m = lst[i]
remlst = lst[:i] + lst[i+1:]
for p in permutations(remlst):
l.append([m] + p)
return l # return at end of outer for loop

Resources