Generating list of iterations on own output in Python - python

Sorry for what seems like a basic question, but I could not find it anywhere. In Python 2, I would like to apply a 1-variable function to its own output storing the list of all steps, i.e. if f(x) returns x*x then iterating from 2, i need to get
[2, 4, 16, 256, 65536, ...]
Ideally, I would need to pass in my function f, the first input 1, and the number of iterations I would like to keep.
I guess this is, in some sense, the opposite of reduce and somewhat similar to unfold from functional programming.
A naive way to do this is to write
out = [2]
for x in xrange(5):
out.append(f(out[-1]))
What is a good Pythonic way to do this?
Thank you very much.

What you need is a "Generator". For example,
def f(x, n):
for _ in range(n):
yield x
x = x * x
l = list(f(2, 5))
print(l) # [2, 4, 16, 256, 65536]
Or
def f(x):
while True:
yield x
x = x * x
for v in f(2):
if v > 100000:
break
print(v), # 2 4 16 256 65536

Ideally, I would need to pass in my function f, the first input 1, and
the number of iterations I would like to keep.
Here is an unfold function that accepts a function, a starting value, and an iteration count.
def unfold(function, start, iterations):
results = []
for _ in range(iterations):
results.append(start)
start = function(start)
return results
Which you can use as expected:
>>> print unfold(lambda x: x*x, 2, 5)
[2, 4, 16, 256, 65536]

Related

How to create a list out of results from a simple maths equation?

I know how to do this using a loop, but interested what's a one liner pythonic way to do it.
For example, how would I get first 10 values of y for y = x^2 or y = log(x) for integers of x ?
eg for x^2 [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
List expression
ys = [x ** 2 for x in range(10)]
I guess you need the "map" function? for example:
# Python program to demonstrate working
# of map.
# Return double of n
def addition(n):
return n + n
# We double all numbers using map()
numbers = (1, 2, 3, 4)
result = map(addition, numbers)
print(list(result))
the output is:
[2, 4, 6, 8]
Above code from https://www.geeksforgeeks.org/python-map-function/

Most Pythonic way to iterate by multiples

I was reviewing some algorithms and they had a for loop that increases by a constant multiple. What would be the most Pythonic way of solving this?
This is not an issue of how to solve the problem, but more of a discussion on what the best solution would be?
This is the Java snip:
for (int i = 1; i <=n; i *= c) {
// some stuff
}
Here is an actual solution in python. I don't think it is the most Pythonic method:
i = 1
while i < limit:
# some stuff, remember to use i - 1 as array index
i *= constant
Pythonic way I could see (That does not exist):
for i in mrange(1, limit, c):
# some stuff
First post here. Hope I tagged and all correctly...
You still can do this :
def mrange(start, stop, step):
i = start
while i < stop:
yield i
i *= step
And then :
for i in mrange(1, 100, 4):
print(i)
Prints :
1
4
16
64
Python cannot provide default range functions to fit every needs, but it is pythonic to create your own generators.
If you don't like this solution, the while alternative looks ok too.
You can use itertools.accumulate; start with a range 1 to n, then apply a function which multiplies its first argument by your constant and ignores its second argument.
>>> from itertools import accumulate
>>> [x for x in accumulate(range(1,10), lambda x,_: 4*x)]
[1, 4, 16, 64, 256, 1024, 4096, 16384, 65536]
Having missed that you want to take the values less than n, start with the infinite sequence [c, c**2, c**3, c**4, ...] and use takewhile to "filter" it. (Also, I just realized you only need map, not accumulate, although accumulate may be more efficient. Note the difference in starting points when using map vs accumulate, too.):
>>> from itertools import count, takewhile
>>> n = 100
>>> [x for x in takewhile(lambda x: x < n, map(lambda x: 4**x, count(0)))]
[1, 4, 16, 64]
>>> [x for x in takewhile(lambda x: x < n, accumulate(count(1), lambda x,_: x*4))]
[1, 4, 16, 64]
Using math module:-
for i in range(math.ceil(math.log(limit, const))):
# code goes here
Ex:-
>>> for i in range(math.ceil(math.log(20, 2))):
... print("runs")
...
runs
runs
runs
runs
runs
which is similar to:-
i = 1
while i< 20:
print('runs')
i*=2
Finally; in easy seeming way:-
>>> import math
>>> mrange = lambda i, limit, const: range(i, math.ceil(math.log(limit, const)))
>>> for i in mrange(0, 20, 2):
print('whoa')
..
whoa
whoa
whoa
whoa
whoa

Can you for loop completely through a range, but starting from the nth element?

I would like to know if there exists a base solution to do something like this:
for n in range(length=8, start_position= 3, direction= forward)
The problem I'm encountering is I would like the loop to continue past the final index, and pick up again at idx =0, then idx=1, etc. and stop at idx= 3, the start_position.
To give context, I seek all possible complete solutions to the n-queen problem.
Based on your latest edit, you need a "normal" range and the modulo operator:
for i in range(START, START + LEN):
do_something_with(i % LEN)
from itertools import chain
for n in chain(range(3,8), range(3)):
...
The chain() returns an iterator with 3, 4, ..., 7, 0, 1, 2
Another option for solving this is to use modular arithmetic. You could do something like this, for example:
for i in range(8)
idx = (i + 3) % 8
# use idx
This easily can be generalized to work with different lengths and offsets.
def loop_around_range(length, start_position, direction='forward'):
looped_range = [k % length for k in range(start_position, start_position+length)]
if direction == 'forward':
return looped_range
else:
return looped_range[::-1]
You could implement this for an arbitrary iterable by using itertools.cycle.
from itertools import cycle
def circular_iterator(iterable, skip=0, length=None, reverse=False):
"""Produces a full cycle of #iterable#, skipping the first #skip# elements
then tacking them on to the end.
if #iterable# does not implement #__len__#, you must provide #length#
"""
if reverse:
iterable = reversed(iterable)
cyc_iter = cycle(iterable)
for _ in range(skip):
next(cyc_iter, None)
if length:
total_length = length
else:
total_length = len(iterable)
for _ in range(total_length):
yield next(cyc_iter, None)
>>> lst = [x for x in range(1, 9)]
# [1, 2, 3, 4, 5, 6, 7, 8]
>>> list(circular_iterator(lst, skip=3))
[4, 5, 6, 7, 8, 1, 2, 3]

Algorithm to find minimal number of sub sets where each set's sum doesn't exceed a max value

Given an array of positive integers, find the minimum number of subsets where:
The sum of each element in the subset does not exceed a value, k.
Each element from the array is only used once in any of the subsets
All values in the array must present in any of the subsets.
Basically, a 'filling' algorithm but need to minimize the containers and need to ensure everything gets filled. My current idea is to sort in descending order and start creating sets when the sum exceeds k, start the next one but not sure what is the better way.
EDIT:
Ex:
Inputs: arr = [1,2,3,4,5], k= 10
Output: [[1,4,5], [2,3]]
# Other solutions such as [[2,3,4],[1,5]] are also acceptable
# But the important thing is the number of sets returned is 2
In the output sets, all 1-5 are used and used only once in the sets. Hope this clears it up.
There may be a smarter way to just find the minimal number of sets, but here's some code which uses Knuth's Algorithm X to do the Exact Cover operation, and a function I wrote last year to generate subsets whose sums are less than a given value. My test code first finds a solution for the data given in the question, and then it finds a solution for a larger random list. It finds the solution for [1, 2, 3, 4, 5] with maximum sum 10 almost instantly, but it takes almost 20 seconds on my old 32 bit 2GHz machine to solve the larger problem.
This code just prints a single solution that is of the minimum size, but it wouldn't be hard to modify it to print all solutions that are of the minimum size.
""" Find the minimal number of subsets of a set of integers
which conform to these constraints:
The sum of each subset does not exceed a value, k.
Each element from the full set is only used once in any of the subsets.
All values from the full set must be present in some subset.
See https://stackoverflow.com/q/50066757/4014959
Uses Knuth's Algorithm X for the exact cover problem,
using dicts instead of doubly linked circular lists.
Written by Ali Assaf
From http://www.cs.mcgill.ca/~aassaf9/python/algorithm_x.html
and http://www.cs.mcgill.ca/~aassaf9/python/sudoku.txt
Written by PM 2Ring 2018.04.28
"""
from itertools import product
from random import seed, sample
from operator import itemgetter
#Algorithm X functions
def solve(X, Y, solution):
if X:
c = min(X, key=lambda c: len(X[c]))
for r in list(X[c]):
solution.append(r)
cols = select(X, Y, r)
yield from solve(X, Y, solution)
deselect(X, Y, r, cols)
solution.pop()
else:
yield list(solution)
def select(X, Y, r):
cols = []
for j in Y[r]:
for i in X[j]:
for k in Y[i]:
if k != j:
X[k].remove(i)
cols.append(X.pop(j))
return cols
def deselect(X, Y, r, cols):
for j in reversed(Y[r]):
X[j] = cols.pop()
for i in X[j]:
for k in Y[i]:
if k != j:
X[k].add(i)
#Invert subset collection
def exact_cover(X, Y):
newX = {j: set() for j in X}
for i, row in Y.items():
for j in row:
newX[j].add(i)
return newX
#----------------------------------------------------------------------
def subset_sums(seq, goal):
totkey = itemgetter(1)
# Store each subset as a (sequence, sum) tuple
subsets = [([], 0)]
for x in seq:
subgoal = goal - x
temp = []
for subseq, subtot in subsets:
if subtot <= subgoal:
temp.append((subseq + [x], subtot + x))
else:
break
subsets.extend(temp)
subsets.sort(key=totkey)
for subseq, _ in subsets:
yield tuple(subseq)
#----------------------------------------------------------------------
# Tests
nums = [1, 2, 3, 4, 5]
k = 10
print("Numbers:", nums, "k:", k)
Y = {u: u for u in subset_sums(nums, k)}
X = exact_cover(nums, Y)
minset = min(solve(X, Y, []), key=len)
print("Minimal:", minset, len(minset))
# Now test with a larger list of random data
seed(42)
hi = 20
k = 2 * hi
size = 10
nums = sorted(sample(range(1, hi+1), size))
print("\nNumbers:", nums, "k:", k)
Y = {u: u for u in subset_sums(nums, k)}
X = exact_cover(nums, Y)
minset = min(solve(X, Y, []), key=len)
print("Minimal:", minset, len(minset))
output
Numbers: [1, 2, 3, 4, 5] k: 10
Minimal: [(2, 3, 5), (1, 4)] 2
Numbers: [1, 2, 3, 4, 8, 9, 11, 12, 17, 18] k: 40
Minimal: [(1, 8, 9, 18), (4, 11, 17), (2, 3, 12)] 3

Multiply element of list with other elements in the same list

I want to multiply an element of a list with all other elements.
For example:
def product(a,b,c):
return (a*b, a*c, a*b*c)
I have done this
def product(*args):
list = []
for index,element in enumerate(args):
for i in args:
if (args[index]*i) not in list:
list.append(args[index]*i)
return list
but this gives me [a*a, a*b,a*c, b*b] etc. I don't want the a*a, b*b, c*c bit in there.
you could check for equality
if (args[index]*i) not in list and args[index] != i:
itertools is your friend here:
from itertools import combinations
from functools import reduce, partial
from operator import mul
# Make a sum-like function for multiplication; I'd call it product,
# but that overlaps a name in itertools and our own function
multiplyall = partial(reduce, mul)
def product(*args):
# Loop so you get all two elements combinations, then all three element, etc.
for n in range(2, len(args) + 1):
# Get the combinations for the current combo count
for comb in combinations(args, n):
# Compute product and yield it
# yielding comb as well just for illustration
yield comb, multiplyall(comb)
I made it a generator function, because frankly, almost any function that's just slowly building a list element by element and returning it should really be a generator function (if the caller wants a list, they just do mylist = list(generatorfunc(...))), making it easier to use iteratively without blowing main memory when many arguments are passed.
Example usage:
>>> for pieces, prod in product(2, 3, 4):
print ' * '.join(map(str, pieces)), '=', prod
Which outputs:
2 * 3 = 6
2 * 4 = 8
3 * 4 = 12
2 * 3 * 4 = 24
So if the values are 2, 3, 4, 5 you want all and only these products:
2*3=6, 2*4=8, 2*5=10, 2*3*4=24, 2*3*5=30, 2*4*5=40, 2*3*4*5=120
This means taking all combinations of 3, 4, 5 and then multiplying them togther with 2. The itertools module has a combinations function, and reduce can be used in conjunction with operator.mul to do the calculation:
def product(first, *other):
for n in range(1, len(other) + 1):
for m in combinations(other, n):
yield reduce(mul, m, first)
list(product(2, 3, 4, 5))
Output:
[6, 8, 10, 24, 30, 40, 120]
Does your list have duplicate elements, like [2, 3, 4, 2]?
If it does not, here is a one liner:
First, with tags to illustrate the pattern:
a = ['a1','a2','a3']
lsta = [[x+y for y in [z for z in a if z != x]] for x in a]
lsta
[['a1a2', 'a1a3'], ['a2a1', 'a2a3'], ['a3a1', 'a3a2']]
And here, with numbers:
a =[2,3,4,5]
print [[x*y for y in [z for z in a if z != x]] for x in a]
[[6, 8, 10], [6, 12, 15], [8, 12, 20], [10, 15, 20]]
or the sum of the products, if you wish:
a =[2,3,4,5]
print [sum([x*y for y in [z for z in a if z != x]]) for x in a]
[24, 33, 40, 45]
If the list has duplicates, it gets more complicated. Do you want the first occurrence and the second occurrence of 2 in [2,3,4,2] to be separately calculated (you might need that for some purposes even though you will get the same value for both)?

Categories