Converting a for loop to recursion in Python [duplicate] - python

This question already has answers here:
Generating permutations with repetitions
(6 answers)
Closed 3 months ago.
I am new to recursion and am trying to convert a for loop to recursion.
allProducts = []
for a in range(10):
for b in range(10):
for c in range(10):
for d in range(10):
if (a*b*c*d)%6==0:
allProducts.append(a*b*c*d)
I am not able to convert this to a recursive program, which means I am not able to scale up. The idea is this - define a recursive program in Python that takes input A (number of for loops) and B (number which is a divisor of the product).
Any help would be really helpful.

You can use itertools.product and its repeat argument:
from operator import mul
import itertools
def myprod(n, div, repeat=4):
# i is a list of factors
for i in itertools.product(range(n), repeat=repeat):
# calculate product of all elements of list
prod = reduce(mul, i, 1)
if prod % div == 0:
yield prod
print list(myprod(10, 6))
Changing the repeat argument of myprod will change the number of loops and factors you are calculating.
Also, since multiplication is commutative (a * b == b * a) you should eliminate repetitive computations using itertools.combinations_with_replacement:
from operator import mul
import itertools
def myprod_unique(n, div, repeat=4):
for i in itertools.combinations_with_replacement(range(n), r=repeat):
prod = reduce(mul, i, 1)
if prod % div == 0:
yield prod
print list(myprod_unique(10, 6))
If you remove duplicate results from myprod using set you will find that the two results are equal:
print set(myprod_unique(10, 6)) == set(myprod(10, 6))
but you have cut down the number of operations drastically from n ** r to (n+r-1)! / r! / (n-1)!. For example 92,378 instead of 10,000,000,000 for n=10, r=10.

You should have a look at the builtin package itertools:
for a,b,c,d in itertools.product(range(10),range(10),range(10),range(10)):
if (a*b*c*d)%6==0:
allProducts.append(a*b*c*d)

The other answers don't use recursion. For completeness, here is one that does.
Recursive functions usually have two parts.
Base case: Handled directly. Corresponds to the body of the innermost loop in your code.
Recursive case: Involves calling the function again, corresponds to one for loop in your code.
Sometimes extra parameters need to be introduced. In this example the variables a, b, etc. that have already been set need to be passed in. The natural way to do that is using a list.
allProducts = []
def product(vals):
res = 1
for val in vals:
res *= val
return res
# n is the number of for loops remaining
# mod is the divisor
# vals is a list where the values of a, b, c, etc. will be placed
# start this function off using run(4, 6, [])
def run(n, mod, vals):
if n == 0:
# base case
if product(vals) % mod == 0:
allProducts.append(product(vals))
else:
# recursive case
for i in range(10):
# i takes the role of a, b, c, etc.
# add i to the vals
vals.append(i)
# recursively iterate over remaining n-1 variables
run(n - 1, mod, vals)
# remove i
vals.pop()

Related

How to convert a collection of numbers into other base

Again firstly I am sorry because keep asking the same question.I am new to python programming. I am now trying to build a program which convert every element in a list of numbers of base 10 into its base 4. For example here, say i have numbers from 0 to 20 or more, I want to convert every number starting from 0 to numbers of base 4. The result should be like this
[[0,0],[0,1],...[1,1,0]
for numbers starting from 0 to 20. For the code,here is what I wrote so far
for n in range(21):
def base(n,b):
result = []
while n > 0:
result.insert(0, n % b)
n = n // b
return result
print(base(n, 4))
For the result I got only for number 20 which is
[1,1,0]
Did I missed something here or there is another option to make it work?
Thank you for the answer
The issue is that you are defining the function inside the loop body, and only call it once. The structure you actually need is
<define the function>
for n in range(21):
print(base(n, 4))
The results will print out, but each one on a separate line. To create a list of the results you should replace the two lines of the loop with a single line that prints a list of all results, like
print([base(n, 4) for n in range(21)])
The problem is that you're defining the function inside the loop. What you want to do is define the function first, then construct your desired list:
def base(n,b):
result = []
while n > 0:
result.insert(0, n % b)
n = n // b
return result
print [base(n, 4) for n in range(21)]
(See this article if you're new to list comprehensions.)
If you want all the numbers, then indent your print statement, so that it's part of the loop.
If you want these as strings, then convert the digits to characters and concatenate them.
I've done both here:
def base(n, b):
result = []
while n > 0:
result.insert(0, n % b)
n = n / b
return result
for decimal in range(21):
print(''.join(str(digit) for digit in base(decimal, 4)))
You can manually compute the base of a number by doing something like this:
from math import log
from math import floor
def n_to_base(n, b):
num = n
lead_idx = floor(log(num, b))
b_rep = int(10**lead_idx)
b_rep = str(b_rep)
rep = 0
for i in range(len(b_rep)-1, -1, -1):
coeff = floor(num/(b**i))
rep += int(coeff*(10**i))
num -= int(coeff*(b**i))
return rep

How to check if sum of 3 integers in list matches another integer? (python)

Here's my issue:
I have a large integer (anywhere between 0 and 2^32-1). Let's call this number X.
I also have a list of integers, unsorted currently. They are all unique numbers, greater than 0 and less than X. Assume that there is a large amount of items in this list, let's say over 100,000 items.
I need to find up to 3 numbers in this list (let's call them A, B and C) that add up to X.
A, B and C all need to be inside of the list, and they can be repeated (for example, if X is 4, I can have A=1, B=1 and C=2 even though 1 would only appear once in the list).
There can be multiple solutions for A, B and C but I just need to find one possible solution for each the quickest way possible.
I've tried creating a for loop structure like this:
For A in itemlist:
For B in itemlist:
For C in itemlist:
if A + B + C == X:
exit("Done")
But since my list of integers contains over 100,000 items, this uses too much memory and would take far too long.
Is there any way to find a solution for A, B and C without using an insane amount of memory or taking an insane amount of time? Thanks in advance.
you can reduce the running time from n^3 to n^2 by using set something like that
s = set(itemlist)
for A in itemlist:
for B in itemlist:
if X-(A+B) in s:
print A,B,X-(A+B)
break
you can also sort the list and use binary search if you want to save memory
import itertools
nums = collections.Counter(itemlist)
target = t # the target sum
for i in range(len(itemlist)):
if itemlist[i] > target: continue
for j in range(i+1, len(itemlist)):
if itemlist[i]+itemlist[j] > target: continue
if target - (itemlist[i]+itemlist[j]) in nums - collections.Counter([itemlist[i], itemlist[j]]):
print("Found", itemlist[i], itemlist[j], target - (itemlist[i]+itemlist[j]))
Borrowing from #inspectorG4dget's code, this has two modifications:
If C < B then we can short-circuit the loop.
Use bisect_left() instead of collections.Counter().
This seems to run more quickly.
from random import randint
from bisect import bisect_left
X = randint(0, 2**32 - 1)
itemset = set(randint(0,X) for _ in range(100000))
itemlist = sorted(list(itemset)) # sort the list for binary search
l = len(itemlist)
for i,A in enumerate(itemlist):
for j in range(i+1, l): # use numbers above A
B = itemlist[j]
C = X - A - B # calculate C
if C <= B: continue
# see https://docs.python.org/2/library/bisect.html#searching-sorted-lists
i = bisect_left(itemlist, C)
if i != l and itemlist[i] == C:
print("Found", A, B, C)
To reduce the number of comparisons, we enforce A < B < C.

My way of finding the sum of all the multiples of 3 or 5 below 1000

I want to find multiples of 3 or 5 below 1000 using the code below:
a=[]
b=[]
def multiples_of_3():
i=1
for i in range(330):
m=i*3
if(m<1000):
a.append(m)
def multiples_of_5():
j=1
for j in range(330):
k=j*5
if(k<1000):
b.append(k)
if __name__ == "__main__":
multiples_of_3()
multiples_of_5()
print sum(a) + sum(b)
Result- 262355
Result is not right. It should be 233168 . How am I going wrong with the logic here?
You are looping over the wrong ranges and adding multiples of 15 twice.
I believe that this is the smallest change to your program that makes it work:
a=[]
b=[]
def multiples_of_3():
i=1
for i in range(334): # stops at 1 less than the value passed to `range`
m=i*3
if(m<1000):
a.append(m)
def multiples_of_5():
j=1
for j in range(330): # could change to 201 and still work
k=j*5
if(k<1000):
b.append(k)
if __name__ == "__main__":
multiples_of_3()
multiples_of_5()
print sum(set(a+b))
But you should probably rethink your approach. Using global variables is generally a bad idea - looking at the call multiples_of_3(), there is no way to know what the subroutine is doing with those multiples; the variable a is not referenced anywhere, yet before the line and after the line it has two different values. So for starters, I would turn the subroutines into pure functions - have them return the arrays instead of modifying globals.
As minor stylistic points, you also don't need to use different variable names inside the two functions, since they're all local. You don't need to assign anything to i before the loops (the loop will create the variable for you), and you don't need parentheses around the condition in Python's if statement (the colon takes care of delimiting it):
def multiples_of_3():
a = []
for i in range(334):
m = i * 3
if m < 1000:
a.append(m)
return a
def multiples_of_5():
a = []
for i in range(201):
m = i * 5
if m < 1000:
a.append(m)
return a
if __name__ == "__main__":
a = multiples_of_3()
b = multiples_of_5()
print sum(set(a+b))
You could also combine the two multiples_of functions into a single generic one that takes a parameter telling it what to return multiples of:
def multiples_of(k):
result = []
for i in range(1000/k+1):
multiple = i * k
if multiple < 1000:
result.append(multiple)
return result
You could even turn the maximum value into an optional parameter:
def multiples_of(k, under=1000):
result = []
for i in range(under/k+1):
multiple = i * k
if multiple < under:
result.append(multiple)
return result
Either way, your main part becomes this:
a = multiples_of(3)
b = multiples_of(5)
print sum(set(a+b))
Finally, just as a point of reference, it is possible to do the whole thing as a one-liner. Here I've switched from building up the list of multiples by actually doing the multiplication, to just looping over all the numbers under 1000 and testing them for divisibility by either 3 or 5:
print sum([n for n in range(1000) if n%3==0 or n%5==0])
Shouldn't for j in range(330): be for j in range(200): since you're using multiples of 5?

Subsequent Application of Python First Class Functions: Any Way Cleaner Than Nesting?

I'm working an example to help me learn how to use first-class functions in Python. In general, I'm satisfied with the solution I came up with, except for one line of code that screams "un-Pythonic" to me.
So the problem I'm working with is defined here. The puzzle seeks the single permutation (out of 720 possible) of six simple functions involving "2" that ultimately returns -3.
Here's my solution, which simply dumps every possible six-function permutation and its result.
def perform (fun, arg):
return fun(arg)
def a(n):
return n + 2
def d(n):
return n / 2.
def m(n):
return n * 2
def p(n):
return n ** 2
def r(n):
return n ** 0.5
def s(n):
return n - 2
if __name__ == "__main__":
from itertools import permutations
for i, perm in enumerate(permutations([a, d, m, p, r, s])):
try:
k = perform(perm[5], perform(perm[4], perform(perm[3], perform(perm[2], perform(perm[1], perform(perm[0], 0))))))
except ValueError:
k = float('nan')
print "%03d. %s: %8.8f" % (i + 1, ''.join([x.__name__ for x in perm]), k)
The line that doesn't seem right to me is the one with nested perform calls: k = perform(...perform(...(. What I need to do is apply the first function in the permutation tuple to 0, and then that function's result to the second function in the tuple, and so on through the permutation tuple until I come up with the ultimate result of applying the component functions.
Is there a cleaner way to successively apply the functions in perm to the corresponding results, starting with 0 as an argument? I've toyed with map and recursion, but I haven't been able to hit upon a solution any more elegant than the one above.
Why not simply:
x = init value
for f in funclist:
x = f(x)
or in a bit fancier way:
value = reduce(lambda x, f: f(x), funclist, init_value)

How does reduce function work?

As far as I understand, the reduce function takes a list l and a function f. Then, it calls the function f on first two elements of the list and then repeatedly calls the function f with the next list element and the previous result.
So, I define the following functions:
The following function computes the factorial.
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return fact(x) * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
Now, shouldn't this give me ((1! * 3!) * 1!) = 6? But, instead it gives 720. Why 720? It seems to take the factorial of 6 too. But, I need to understand why.
Can someone explains why this happens and a work-around?
I basically want to compute the product of factorials of all the entries in the list.
The backup plan is to run a loop and compute it. But, I would prefer using reduce.
The other answers are great. I'll simply add an illustrated example that I find pretty good to understand reduce():
>>> reduce(lambda x,y: x+y, [47,11,42,13])
113
will be computed as follows:
(Source) (mirror)
The easiest way to understand reduce() is to look at its pure Python equivalent code:
def myreduce(func, iterable, start=None):
it = iter(iterable)
if start is None:
try:
start = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = start
for x in iterable:
accum_value = func(accum_value, x)
return accum_value
You can see that it only makes sense for your reduce_func() to apply the factorial to the rightmost argument:
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return x * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
With that small revision, the code produces 6 as you expected :-)
Your function calls fact() on both arguments. You are calculating ((1! * 3!)! * 1!). The workaround is to only call it on only the second argument, and pass reduce() an initial value of 1.
From the Python reduce documentation,
reduce(function, sequence) returns a single value constructed by calling the (binary) function on the first two items of the sequence, then on the result and the next item, and so on.
So, stepping through. It computes reduce_func of the first two elements, reduce_func(1, 3) = 1! * 3! = 6. Then, it computes reduce_func of the result and the next item: reduce_func(6, 1) = 6! * 1! = 720.
You missed that, when the result of the first reduce_func call is passed as input to the second, it's factorialized before the multiplication.
Ok, got it:
I need to map the numbers to their factorials first and then call reduce with multiply operator.
So, this would work:
lst_fact = map(fact, lst)
reduce(operator.mul, lst_fact)
You could also implement factorial using reduce.
def factorial(n):
return(reduce(lambda x,y:x*y,range(n+1)[1:]))
Beyond the trivial examples, here is one where I find reduce to be actually quite useful:
Imagine an iterable of ordered int values, often with some runs of contiguous values, and that we'd like to "summarize" it as a list of tuples representing ranges. (Note also that this iterable could be a generator of a very long sequence --another reason to use reduce and not some operation on an in-memory collection).
from functools import reduce
def rle(a, b):
if a and a[-1][1] == b:
return a[:-1] + [(a[-1][0], b + 1)]
return a + [(b, b + 1)]
reduce(rle, [0, 1, 2, 5, 8, 9], [])
# [(0, 3), (5, 6), (8, 10)]
Notice the use of a proper initial value ([] here) for reduce.
Corner cases handled as well:
reduce(rle, [], [])
# []
reduce(rle, [0], [])
# [(0, 1)]
Well, first of all, your reduce_func doesn't have the structure of a fold; it doesn't match your description of a fold (which is correct).
The structure of a fold is: def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)
Now, your fact function doesn't operate on two elements - it just calculates factorial.
So, in sum, you're not using a fold, and with that definition of factorial, you don't need to.
If you do want to play around with factorial, check out the y-combinator: http://mvanier.livejournal.com/2897.html
If you want to learn about folds, look at my answer to this question, which demonstrates its use to calculate cumulative fractions: creating cumulative percentage from a dictionary of data
Reduce executes the function in parameter#1 successively through the values provided by the iterator in parameter#2
print '-------------- Example: Reduce(x + y) --------------'
def add(x,y): return x+y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 0
for i in range(a,b):
tot = tot+i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)
print '-------------- Example: Reduce(x * y) --------------'
def add(x,y): return x*y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 1
for i in range(a,b):
tot = tot * i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)

Categories

Resources