product of variable number of range(n)'s - python

I am trying to understand how to write code that will output all the divisors of a number. The approach that I am most interested in taking begins with a function that returns a dictionary where the keys are the prime divisors and the values are the number of times divisible. I have already written this function like so:
def div_pair(num):
divPair = {}
for prime in prime_gen():
primeDegree = 0
while num % prime == 0:
num = int(num / prime)
primeDegree += 1
if primeDegree > 0:
divPair[prime] = primeDegree
if num == 1:
return divPair
As an example, the number 84,000 outputs the dictionary
{2: 5, 3: 1, 5: 3, 7: 1}
What I want to do from here is generate powersets(?) of any given values returned by the different numbers divPair would return, and then multiply these powersets by their matched primes. This is an example which uses the kind of code I am trying to use to generate the powersets:
from itertools import product
list(product(range(5+1), range(1+1), range(3+1), range(1+1)))
Outputs this:
[(0, 0, 0, 0),
(0, 0, 0, 1),
(0, 0, 1, 0),
(0, 0, 1, 1),
(0, 0, 2, 0),
(0, 0, 2, 1),
(0, 0, 3, 0),
(0, 0, 3, 1),
(0, 1, 0, 0),
(0, 1, 0, 1),
(0, 1, 1, 0),
(0, 1, 1, 1),
(0, 1, 2, 0),
(0, 1, 2, 1),
(0, 1, 3, 0),
(0, 1, 3, 1),
(1, 0, 0, 0),
(1, 0, 0, 1),
(1, 0, 1, 0),
(1, 0, 1, 1),
(1, 0, 2, 0),
(1, 0, 2, 1),
(1, 0, 3, 0),
(1, 0, 3, 1),
(1, 1, 0, 0),
(1, 1, 0, 1),
(1, 1, 1, 0),
(1, 1, 1, 1),
(1, 1, 2, 0),
(1, 1, 2, 1),
(1, 1, 3, 0),
(1, 1, 3, 1),
(2, 0, 0, 0),
(2, 0, 0, 1),
(2, 0, 1, 0),
(2, 0, 1, 1),
(2, 0, 2, 0),
(2, 0, 2, 1),
(2, 0, 3, 0),
(2, 0, 3, 1),
(2, 1, 0, 0),
(2, 1, 0, 1),
(2, 1, 1, 0),
(2, 1, 1, 1),
(2, 1, 2, 0),
(2, 1, 2, 1),
(2, 1, 3, 0),
(2, 1, 3, 1),
(3, 0, 0, 0),
(3, 0, 0, 1),
(3, 0, 1, 0),
(3, 0, 1, 1),
(3, 0, 2, 0),
(3, 0, 2, 1),
(3, 0, 3, 0),
(3, 0, 3, 1),
(3, 1, 0, 0),
(3, 1, 0, 1),
(3, 1, 1, 0),
(3, 1, 1, 1),
(3, 1, 2, 0),
(3, 1, 2, 1),
(3, 1, 3, 0),
(3, 1, 3, 1),
(4, 0, 0, 0),
(4, 0, 0, 1),
(4, 0, 1, 0),
(4, 0, 1, 1),
(4, 0, 2, 0),
(4, 0, 2, 1),
(4, 0, 3, 0),
(4, 0, 3, 1),
(4, 1, 0, 0),
(4, 1, 0, 1),
(4, 1, 1, 0),
(4, 1, 1, 1),
(4, 1, 2, 0),
(4, 1, 2, 1),
(4, 1, 3, 0),
(4, 1, 3, 1),
(5, 0, 0, 0),
(5, 0, 0, 1),
(5, 0, 1, 0),
(5, 0, 1, 1),
(5, 0, 2, 0),
(5, 0, 2, 1),
(5, 0, 3, 0),
(5, 0, 3, 1),
(5, 1, 0, 0),
(5, 1, 0, 1),
(5, 1, 1, 0),
(5, 1, 1, 1),
(5, 1, 2, 0),
(5, 1, 2, 1),
(5, 1, 3, 0),
(5, 1, 3, 1)]
which is really the output that I want. I just need to modify the code to accept divPair.values() in some way. So I write this:
from itertools import product
divPair = div_pair(84000)
list(product(range(i+1) for i in divPair.values()))
which seems to me as if it should be correct, but it outputs this mess:
[(range(0, 6),), (range(0, 2),), (range(0, 4),), (range(0, 2),)]
and I can't figure out how to fix it. There is a post here which offers fantastic solutions to what I am trying to do. I am just trying to work toward them with what I know.

product returns the product of its arguments, and you have passed it a single one, the (range(i+1) for i in divPair.values()) generator. The generator yielded a list of range objects. That's like doing this:
>>> list(product(['range', 'range', 'range']))
[('range',), ('range',), ('range',)]
You have to pass your ranges as individual arguments.
Do this:
list(product(*[range(i+1) for i in divPair.values()]))
(or this)
list(product(*(range(i+1) for i in divPair.values())))

Related

how to make pythonic Nested List

I'd like to make nested list
given_list = [[0, 1, 2], [0, 1, 2], [0, 1, 2]] # each element : range(0, n), num of element : m
new_list = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], ..., [2, 2, 2]] # total num : n^m
How do I make it?
I tried to overlap the for statement m times, but I don't think it's pythonic.
Looks like you are trying to compute the product of the lists in given_list:
> from itertools import product
> list(product(*given_list))
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 2, 0), (2, 2, 1), (2, 2, 2)]
If you really need a list of lists, rather than a list of tuples, you'll have to call list on each element.
[list(t) for t in product(*given_list)]

Reconstructing missing information from game results

I have a dataset of results from games of n players, where each game has n-1 players playing. The results of a game may look like this:
1 2 _
_ 1 2
2 _ 1
where each column represents the results of 1 player. However, the dataset has been corrupted and columns where players have a bye (_) have been collapsed so that results turn out like this:
1 2 2
2 1 1
I currently have python code to take in the results from a file and add them to an numpy array, which includes a function to insert a bye into a column. Printing the array gives this output:
[['1' '2' '2']
['1' '1' '2']
['0' '0' '0']]
I am struggling to figure out how to find the corrected results, especially if some collapsed results may have multiple solutions. I know I need to use a recursive solve () function, but I'm not sure how to go about it. Here is my current source code:
import numpy as np
collapsed_results = []
p = 0
def insert_bye(grid, row, column):
for i in reversed(range(row, p)):
if i == row:
grid[i][column] = "_"
else:
grid[i][column] = grid[i - 1][column]
return grid
def solve(collapsed_results):
pass
if __name__ == "__main__":
while True:
try:
line = input()
except EOFError:
break
line = line.split(" ")
collapsed_results.append(line)
# Number of players
p = len(collapsed_results[0])
collapsed_results.append([0] * p)
collapsed_results = np.array(collapsed_results)
You can use a recursive generator function:
from collections import deque
def pad_col(d, l, c=[]):
if len(c) == l:
yield c
else:
yield from ([] if not d else pad_col(d[1:], l, c+[d[0]]))
if l - len(c) > len(d):
yield from pad_col(d, l, c+[0])
def solve(collapsed, l = 3):
def combos(d, c = []):
if not d:
yield list(zip(*c))
else:
for i in pad_col(d[0], l):
yield from combos(d[1:], c+[i])
return list(combos([*zip(*collapsed)]))
print(solve([[1, 2, 2], [2, 1, 1]]))
Output:
[[(1, 2, 2), (2, 1, 1), (0, 0, 0)], [(1, 2, 2), (2, 1, 0), (0, 0, 1)], [(1, 2, 0), (2, 1, 2), (0, 0, 1)], [(1, 2, 2), (2, 0, 1), (0, 1, 0)], [(1, 2, 2), (2, 0, 0), (0, 1, 1)], [(1, 2, 0), (2, 0, 2), (0, 1, 1)], [(1, 0, 2), (2, 2, 1), (0, 1, 0)], [(1, 0, 2), (2, 2, 0), (0, 1, 1)], [(1, 0, 0), (2, 2, 2), (0, 1, 1)], [(1, 2, 2), (0, 1, 1), (2, 0, 0)], [(1, 2, 2), (0, 1, 0), (2, 0, 1)], [(1, 2, 0), (0, 1, 2), (2, 0, 1)], [(1, 2, 2), (0, 0, 1), (2, 1, 0)], [(1, 2, 2), (0, 0, 0), (2, 1, 1)], [(1, 2, 0), (0, 0, 2), (2, 1, 1)], [(1, 0, 2), (0, 2, 1), (2, 1, 0)], [(1, 0, 2), (0, 2, 0), (2, 1, 1)], [(1, 0, 0), (0, 2, 2), (2, 1, 1)], [(0, 2, 2), (1, 1, 1), (2, 0, 0)], [(0, 2, 2), (1, 1, 0), (2, 0, 1)], [(0, 2, 0), (1, 1, 2), (2, 0, 1)], [(0, 2, 2), (1, 0, 1), (2, 1, 0)], [(0, 2, 2), (1, 0, 0), (2, 1, 1)], [(0, 2, 0), (1, 0, 2), (2, 1, 1)], [(0, 0, 2), (1, 2, 1), (2, 1, 0)], [(0, 0, 2), (1, 2, 0), (2, 1, 1)], [(0, 0, 0), (1, 2, 2), (2, 1, 1)]]

How do I pop item off a list of tuples, based on the value of the first number in each tuple?

I am trying to create a list of tuples that consists of all the variations of a set of numbers. However I want to remove any variants from the list that are the same sequence but offset by a position or two. For example:
(-1,1,2), (1,2,-1) & (2,-1,1) I would only want the first one.
Here's where I'm up to:
import itertools as it
list = [-1, 0, 1, 2]
cycles = []
list_cycle_3 = it.permutations(cycles, 3)
list_cycle_4 = it.permutations(cycles, 4)
for item in list_cycle_3:
cycles.append(item)
for item in list_cycle_4:
cycles.append(item)
print(cycles)
This results in:
[(-1, 0, 1), (-1, 0, 2), (-1, 1, 0), (-1, 1, 2), (-1, 2, 0), (-1, 2, 1),
(0, -1, 1), (0, -1, 2), (0, 1, -1), (0, 1, 2), (0, 2, -1), (0, 2, 1),
(1, -1, 0), (1, -1, 2), (1, 0, -1), (1, 0, 2), (1, 2, -1), (1, 2, 0),
(2, -1, 0), (2, -1, 1), (2, 0, -1), (2, 0, 1), (2, 1, -1), (2, 1, 0),
(-1, 0, 1, 2), (-1, 0, 2, 1), (-1, 1, 0, 2), (-1, 1, 2, 0), (-1, 2, 0, 1), (-1, 2, 1, 0), (0, -1, 1, 2), (0, -1, 2, 1), (0, 1, -1, 2), (0, 1, 2, -1),
(0, 2, -1, 1), (0, 2, 1, -1), (1, -1, 0, 2), (1, -1, 2, 0), (1, 0, -1, 2),
(1, 0, 2, -1), (1, 2, -1, 0), (1, 2, 0, -1), (2, -1, 0, 1), (2, -1, 1, 0),
(2, 0, -1, 1), (2, 0, 1, -1), (2, 1, -1, 0), (2, 1, 0, -1)]
So what do I do next to filter the results so I only have the results I want, which are:
[(-1, 0, 1), (-1, 0, 2), (-1, 1, 0), (-1, 1, 2), (-1, 2, 0), (-1, 2, 1),
(0, 1, 2), (0, 2, 1), (-1, 0, 1, 2), (-1, 0, 2, 1), (-1, 1, 0, 2),
(-1, 1, 2, 0), (-1, 2, 0, 1), (-1, 2, 1, 0)]
If it helps a simple difference between the lists are that the list I want is all the tuples starting with -1, and the tuples where there is no -1 starting with 0
Start with a list that does not contain the number that you want to filter against:
For example, you'll only need the ones that starts with 0. Then your list is
l = [-1, 1, 2]
Find all the two element permutatations and filter them as you like.
Ant then add 0 as the first element by mapping your result set.
Example:
In [2]: from itertools import permutations
In [3]: l = [-1, 1, 2]
In [4]: p = permutations(l, 2)
In [5]: [(0, *t) for t in p]
Out[5]: [(0, -1, 1), (0, -1, 2), (0, 1, -1), (0, 1, 2), (0, 2, -1), (0, 2, 1)]
You could do a similar trick for, say, the ones that does not have -1 in them and start with 0 which is omit -1 and 0 from your list and then add 0 as the first element into your result sets items.
And do not override reserved keywords in your code:
list = [...] # do not do that
l=[(-1, 0, 1), (-1, 0, 2), (-1, 1, 0), (-1, 1, 2), (-1, 2, 0), (-1, 2, 1),
(0, -1, 1), (0, -1, 2), (0, 1, -1), (0, 1, 2), (0, 2, -1), (0, 2, 1),
(1, -1, 0), (1, -1, 2), (1, 0, -1), (1, 0, 2), (1, 2, -1), (1, 2, 0),
(2, -1, 0), (2, -1, 1), (2, 0, -1), (2, 0, 1), (2, 1, -1), (2, 1, 0),
(-1, 0, 1, 2), (-1, 0, 2, 1), (-1, 1, 0, 2), (-1, 1, 2, 0), (-1, 2, 0, 1), (-1, 2, 1, 0), (0, -1, 1, 2), (0, -1, 2, 1), (0, 1, -1, 2), (0, 1, 2, -1),
(0, 2, -1, 1), (0, 2, 1, -1), (1, -1, 0, 2), (1, -1, 2, 0), (1, 0, -1, 2),
(1, 0, 2, -1), (1, 2, -1, 0), (1, 2, 0, -1), (2, -1, 0, 1), (2, -1, 1, 0),
(2, 0, -1, 1), (2, 0, 1, -1), (2, 1, -1, 0), (2, 1, 0, -1)]
l2=[]
for i in l:
if i[0] == -1 :
l2.append(i)
print(l2)
this code works
output
[(-1, 0, 1), (-1, 0, 2), (-1, 1, 0), (-1, 1, 2), (-1, 2, 0), (-1, 2, 1), (-1, 0, 1, 2), (-1, 0, 2, 1), (-1, 1, 0, 2), (-1, 1, 2, 0), (-1, 2, 0, 1), (-1, 2, 1, 0)]

python all possible combinations of 0,1 of length k

I need all possible combinations of 0,1 of length k.
Suppose k=2 I want (0,0), (0,1), (1,0), (1,1)
I have tried different function in itertools but I did not find what I want.
>>> list(itertools.combinations_with_replacement([0,1], 2))
[(0, 0), (0, 1), (1, 1)]
>>> list(itertools.product([0,1], [0,1])) #does not work if k>2
[(0, 0), (0, 1), (1, 0), (1, 1)]
itertools.product() takes a repeat keyword argument; set it to k:
product(range(2), repeat=k)
Demo:
>>> from itertools import product
>>> for k in range(2, 5):
... print list(product(range(2), repeat=k))
...
[(0, 0), (0, 1), (1, 0), (1, 1)]
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
[(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0), (0, 0, 1, 1), (0, 1, 0, 0), (0, 1, 0, 1), (0, 1, 1, 0), (0, 1, 1, 1), (1, 0, 0, 0), (1, 0, 0, 1), (1, 0, 1, 0), (1, 0, 1, 1), (1, 1, 0, 0), (1, 1, 0, 1), (1, 1, 1, 0), (1, 1, 1, 1)]

python: perform a generic multi dimensional loop

Python:
How to efficiency execute a multidimensional loop, when the number of indexes to loop is dynamic.
Assume an array var_size containing the size of each variable
var_size = [ 3, 4, 5 ]
and a function 'loop' which will call 'f(current_state)' for each point.
def f(state): print state
loop(var_size, f)
This call would call f in the following order:
f( [ 0, 0, 0])
f( [ 0, 0, 1])
f( [ 0, 0, 2])
f( [ 0, 1, 0])
etc....
You can do this with itertools.product:
>>> print list(itertools.product(*(range(x) for x in reversed([3,4,5]))))
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (0, 3, 0), (0, 3, 1), (0, 3, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (1, 3, 0), (1, 3, 1), (1, 3, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 2, 0), (2, 2, 1), (2, 2, 2), (2, 3, 0), (2, 3, 1), (2, 3, 2), (3, 0, 0), (3, 0, 1), (3, 0, 2), (3, 1, 0), (3, 1, 1), (3, 1, 2), (3, 2, 0), (3, 2, 1), (3, 2, 2), (3, 3, 0), (3, 3, 1), (3, 3, 2), (4, 0, 0), (4, 0, 1), (4, 0, 2), (4, 1, 0), (4, 1, 1), (4, 1, 2), (4, 2, 0), (4, 2, 1), (4, 2, 2), (4, 3, 0), (4, 3, 1), (4, 3, 2)]
Note that I'm generating tuples instead of lists, but that's easy to fix if you really need to.
So, to me it looks like you want:
map(f,itertools.product(*map(range,reversed(var_size))))
Make a list initialized to 0s, as many entries as are in var_size. We treat this list as a list of 'tumblers' - we increment the last one in the list until it overflows its limit (aka var_size at the same point into the list). If so, we set it to 0, go one left and repeat the increment/overflow check until we either do not overflow (reset the 'which tumbler are we looking at' variable back to the last and continue) or overflow all entries of the list (we're done, we looped all the way around), then perform the next call.
I don't know if this is optimal or pythonic, but it is O(n).
This code does the job - And it has the advantage of not creating the list. However, it not that elegant....
Any ideas on how to get this better?
def loop(var_size, f):
nb = len(var_size)
state = [0]*nb
ok = True
while ok:
f(state)
for i in range(nb-1, -1, -1):
state[i] = state[i]+1
if state[i] < var_size[i]:
break
else:
if i == 0:
ok = False
break
else:
state[i] = 0
var_size = [3,4,5]
def f(state):
print state
loop(var_size, f)

Categories

Resources