How to create multiple iterations - python

I wanna know how do I create an iteration which would iterate several or more than one parameters with different ranges
For example I wanna instantiate several object with iterations that all have different ranges.
Like there is a triangle function which takes three parameters, how do I use iterations to give one parameter one value from a range e.g. 50 to 100 and another parameter a different one altogether.
I know how to instantiate it over one parameter by:
for i in range(100):
But what do I do to instantiate it if it requires more than one parameter for a function.

Looks like you will want to use nested for loops. For example for your three parameter function:
# these are just example ranges, replace with what's meaningful for your problem
range_for_parameter_0 = range(100)
range_for_parameter_1 = range(150)
range_for_parameter_2 = range(75)
# start a nested for loop
for i in range_for_parameter_0:
for j in range_for_parameter_1:
for k in range_for_parameter_2:
# you can print something out to see exactly what's happening
# feel free to comment out the print statement
print('Calling triangle_function with parameters {},{},{}'.format(i,j,k))
# evaluate your triangle_function which takes 3 parameters
triangle_function(i,j,k)

You can try iterating over a Cartesian product.
Given
import itertools as it
def is_tri(a, b, c):
"""Return True if the sides make a triangle."""
a, b, c = sorted([a, b, c])
return (a + b) > c
ranges = range(1, 2), range(1, 3), range(1, 5)
Code
[sides for sides in it.product(*ranges) if is_tri(*sides)]
# [(1, 1, 1), (1, 2, 2)]
Details
If you are unfamiliar with list comprehensions, the latter is equivalent to the following code:
results = []
for x, y, z in it.product(*ranges):
if is_tri(x, y, z):
results.append((x, y, z))
results
# [(1, 1, 1), (1, 2, 2)]
Per your comment, is_tri() pre-sorts arguments, so you interchange them:
assert is_tri(13, 12, 5) == True
assert is_tri(12, 5, 13) == True
assert is_tri(5, 13, 12) == True
If your ranges are the same, you can simplify the input with the repeat parameter, e.g. it.product(range(1, 101), repeat=3).

You can't. You need three iterations.
for i in range(x):
...
for j in range(y):
...
for k in range(z):
...
See range() definition here

Related

Is there any way to optimise this function?

I was told to make a program that solves a simple python exercise which should run under 12000ms. I managed to get a working piece of code. However it only works for small numbers passed into the n parameter.
def function(n):
res = [(a, b) for a in range(1, n+1) for b in range(1, n+1) if a*b == sum([i for i in range(1, n+1) if i!=a and i!=b])]
return res
Is there any way to optimise the code so that it runs under 12000ms for large numbers of n (e.g. n=100000)?
Exercise:
A friend of mine takes the sequence of all numbers from 1 to n (where n > 0).
Within that sequence, he chooses two numbers, a and b.
He says that the product of a and b should be equal to the sum of all numbers in the sequence, excluding a and b.
Given a number n, could you tell me the numbers he excluded from the sequence?
The function takes the parameter: n (n is always strictly greater than 0) and returns an array or a string (depending on the language) of the form:
[(a, b), ...] with all (a, b) which are the possible removed numbers in the sequence 1 to n.
[(a, b), ...] will be sorted in increasing order of the "a".
It happens that there are several possible (a, b). The function returns an empty array (or an empty string) if no possible numbers are found which will prove that my friend has not told the truth! (Go: in this case return nil).
E.g. function(26) should return [(15, 21), (21, 15)]
sum([i for i in range(1, n+1) if i!=a and i!=b])
is pretty easily optimized out. Just put:
basesum = sum(range(1, n+1))
outside the listcomp, then change the test to:
if a*b == basesum - sum({a, b}) # Accounts for possibility of a == b by deduping
or if a==b is not supposed to be allowed, the even simpler:
if a*b == basesum - a - b
That instantly reduces the per element work from O(n) to O(1), which should cut overall work from O(n**3) to O(n**2).
There's other optimizations available, but that's an easy one with a huge impact on big-O runtime.
If I'm reading the prompt correctly, your a and b are order-insensitive. So if your results can just show (a, b) and not (b, a) as well, you can replace:
for a in range(1, n+1) for b in range(1, n+1)
with:
for a, b in itertools.combinations(range(1, n+1), 2)
or if a == b is allowed:
for a, b in itertools.combinations_with_replacement(range(1, n+1), 2)
which halves the amount of work to do "for free" (and does more of it at the C layer instead of the Python bytecode layer, which often speeds things up a little more). If you must get the results in both orders, you can post-process to produce the reverse of each non-duplicated pair as well (or be a lazy programmer and use for a, b in itertools.permutations(range(1, n+1), 2) or for a, b in itertools.product(range(1, n+1), repeat=2) instead of combinations or combinations_with_replacement respectively, doing most/all of the work of your original nested loop, but shoving more to the C layer so the same theoretical work runs a little faster in practice).
This is more of a math problem than anything else:
Isolate b:
a*b = sum - (a+b)
(a+1)*b = sum - a
b = (sum - a)/(a+1)
Now you can substitute b where needed. With b out of the way, you don't have to iterate over the list for each element in it. You can iterate over the list just once, applying the equation for each element.
In fact, you don't even have to go through the whole list. Verifying its first sqrt(sum) elements is enough, as anything bigger than that has to be multiplied by another smaller than that number.
Here is the code:
import math
n = 26
valid = []
sum_n = (n+1)*n/2
limit = int(math.sqrt(sum_n)-0.5)
for a in range(1, (limit+1)):
if (sum_n-a) % (a+1) == 0:
valid.append(( a, int((sum_n-a) / (a+1)) ))
if valid:
if valid[-1][0] == valid[-1][1]:
valid += [(x, y) for y, x in reversed(valid[:-1])]
else:
valid += [(x, y) for y, x in reversed(valid)]
print(valid)
And the output:
[(1, 175), (3, 87), (7, 43), (10, 31), (15, 21), (21, 15), (31, 10), (43, 7), (87, 3), (175, 1)]

Resolve TypeError "can only concatenate tuple (not 'int') to tuple" with function designed to return all summation compositions

I'm trying to write a function in Python that returns a set of k-sized tuples that sum to n, and I'm fairly certain that I have the function written correctly, but I keep getting the type error that I mentioned in the title.
For example, compositions(3,4) should return the following:
{(1, 1, 2), (1, 2, 1), (2, 1, 1)}
def compositions(k, n):
if k == 1:
return (n,)
comp = []
for SumIterator in range(1, n+1):
for FunctionRecall in compositions(k = k-1, n = n-SumIterator):
comp.append((SumIterator,) + FunctionRecall)
return set(comp)
Any idea on how to fix this function so that it runs correctly?
Thanks!
If your function is supposed to return a set of tuples, you'll need to revise your base case.
if k == 1:
return (n,)
This doesn't return a set of tuples. It returns one tuple. Try putting it in a set.
if k == 1:
return {(n,)}
Additionally, if you don't want any of the tuples to contain zero, I think you need to change the bounds of your first for loop to for SumIterator in range(1, n):.
for FunctionRecall in compositions(k = k-1, n = n-SumIterator):
comp.append((SumIterator,) + FunctionRecall)
SumIterator is 1; (SumIterator,) is the tuple (1,).
Your inner for loop runs FunctionRecall through the elements of the compositions return, which is either a tuple or a set or integers. Thus, when you get to this point, you have:
comp.append((1,) + 2)
You cannot "add" a tuple and a scalar. What did you want here ... a tuple composition? That would be (1,) + (2,)

How can I fix this Pythagorean Triplet program?

import sys
def pythTrue(a,b,c):
(A,B,C) = (a*a,b*b,c*c)
if A + B == C or B + C == A or A + C == B:
return True
def smallestTrip(a,b,c):
if pythTrue(a,b,c) == True:
if (a+b+c)%12 == 0:
return True
else:
return False
def tuplePyth(n):
list_=[]
for x in range(1, n):
for y in range(1, n):
for z in range (1, n):
if x+y+z<=n:
if smallestTrip(x, y, z)==False:
list_.append([x,y,z])
print (list_)
tuplePyth(int(sys.argv[1]))
Pythagorean triplets are sets of 3 positive integers a, b, c
satisfying the relationship a2 + b2 =
c2. The smallest and best-known Pythagorean triple is
(a, b, c) = (3, 4, 5). Write a program that reads a command line
argument n and prints to the screen all Pythagorean triplets whose sum
is less than n (i.e., a+b+c < n) and that are not multiple of the (3,
4, 5) triplet. Your program will represent triplets as 3-tuples, and
should consist of three functions:
a function that takes in a tuple
and returns a boolean indicating whether the Pythagorean relationship holds or not.
a function that takes in a tuple and returns
a boolean indicating whether a triplet is a multiple of the smallest
triplet or not.
a function that takes in an integer n and generates
the Pythagorean triplets as specified above. The function should
return a list of tuples.
The main portion of your program pythagore.py will read in the command
line input, call the last function described above, and print the
results one triplet per line.
My problem is that I am getting the same combination in different
orders for example: (5,12,13),(13,12,5)...etc
You're short on logic in your main routine. There is nothing to enforce that the triple comes in only one order: your x and y are interchangeable, and you guarantee that you'll check both.
Instead, force x < y with your loop limits, and then make sure you stop when the value of y or z gets too large to be viable. Note that this gets rid of your check for the sum of the three.
def tuplePyth(n):
list_=[]
for x in range(1, n):
for y in range(1, n):
for z in range (1, n):
if x+y+z<=n:
if smallestTrip(x, y, z)==False:
list_.append([x,y,z])
print (list_)
Instead:
def tuplePyth(n):
list_=[]
for x in range(1, n):
for y in range(x + 1, (n - x) // 2):
for z in range (y + 1, n - x - y):
if smallestTrip(x, y, z)==False:
list_.append([x,y,z])
print (list_)
Output with n=100:
[[5, 12, 13], [7, 24, 25], [8, 15, 17], [9, 40, 41], [15, 36, 39], [16, 30, 34], [20, 21, 29]]
Note that you still have a problem with smallestTrip: your check is not logically equivalent to "smallest triple". Instead, check that the three numbers are relatively prime. Since Stack Overflow allows only one question per posting, and the problem is readily researched on line, I'll leave that as an exercise for the student. :-)
An easy solution would be to keep track of the ones aleady found and add checks to avoid repeating them. The following uses a set to store the ones already produced and sorts the the elements in each triple so that their order doesn't matter.
def tuplePyth(n):
list_=[]
seen = set()
for x in range(1, n):
for y in range(1, n):
for z in range (1, n):
if tuple(sorted((x,y,z))) not in seen:
if x+y+z <= n:
if smallestTrip(x, y, z) == False:
list_.append([x,y,z])
seen.add((x,y,z))
print (list_)
You can use itertools:
import itertools.combinations_with_replacement as cwr
list_ = [triple for triple in cwr(range(n),3) if sum(triple)<n and not smallestTrip(triple)]
You can also force the numbers to be in order with the limits. Also, you can simplify finding a,b, c by realizing that if we define a to be the smallest number, then it must be smaller than n/3 (b and c will both be at least as large as a, so if a were larger than n/3, then the sum of a, b, and c would be more than n). Similarly, b must be smaller than n/2. Once you've found all the combinations of a and b, you can find all the c that are larger than b and smaller than n-a-b.
list_=[]
for x in range(1, n//3):
for y in range(x+1, n//2):
for z in range (x+y+1, n-x-y):
if not smallestTrip(x, y, z):
list_.append([x,y,z])
Because the three numbers are never the same you can just change the second and the third range from (1,n) to (x+1,n) and (y+1,n) correspondingly.

Mapping a specific iteration number to an n-dimensional list/array

I'm aware that there's itertools.product for loops, but I wanted to write something that would return an arbitrary coordinate in n-space given the iteration number that would yield it in a loop. I've already written something that's similar, viz.
def clock(iteration_number, axis_lengths):
dimension=len(axis_lengths)
coordinate = []
for i in range(dimension):
s = axis_lengths[dimension-i-1:dimension-i][0]
g = iteration_number % s
iteration_number /= s
coordinate += [g]
return tuple(reversed(coordinate))
but I'm hoping that, with the help of the built-in function divmod (or another) it may be compressed to a list comprehension; I've been trying to use lambda functions and map as well, but to no avail, so I'm stuck. For example, running the above function on an array A with axes lengths [6, 14, 9, 13, 17] (i.e. a 5-dimensional array) for iteration number 98000 results in the coordinate (3, 7, 2, 5, 12). How can I do this, i.e. map a specific iteration number to its location in an n-dimensional array? And again, my goal is not to write another function like that above.
I'm not sure about the example you give (your code yields a different result from the one you quote). But the built-in one-liner to do the same operation is np.unravel_index:
import numpy as np
import operator
def product(iterable):
return reduce(operator.mul, iterable)
def clock_1(iteration_number, axis_lengths):
dimension=len(axis_lengths)
coordinate = []
for i in range(dimension):
s = axis_lengths[dimension-i-1:dimension-i][0]
g = iteration_number % s
iteration_number //= s
coordinate += [g]
return tuple(reversed(coordinate))
def clock_2(iteration_number, axis_lengths):
return np.unravel_index(iteration_number % product(axis_lengths), axis_lengths)
print "clock_1:", clock_1(98000, (6,14,9,3,17))
print "clock_2:", clock_2(98000, (6,14,9,3,17))
clock_1: (3, 3, 4, 1, 12)
clock_2: (3, 3, 4, 1, 12)

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