How do I implement restricted combinations iterator? - python

I want something equivalent to the following code.
The following code generate poker possible hand patterns.
from itertools import combinations
m = 13
n = 4
x = range(m) * n
y = 5
pattern = set()
for i in combinations(x, y):
pattern.add(tuple(sorted(i)))
I tried to use itertools.combinations_with_replacement. Not surprisingly there are combinations like (0, 0, 0, 0, 0) or (1, 1, 1, 1, 1). But there are no 5 Queens and 5 Kings in a cards. So I don't want to take 5 same things. How do I implement restricted combinations iterator.
from itertools import combinations_with_replacement
m = 13
n = 4
x = range(m)
y = 5
pattern = []
for i in combinations_with_replacement(x, y):
pattern.append(i)
I want like the following code.
Pseudo code:
m = 13
n = 4
x = range(m)
y = 5
pattern = []
for i in combinations_with_replacement_restricted(x, y, max=n):
pattern.append(i)
P.S. Because I'm English learner, please modify my grammar mistakes.

After reading the docs: https://docs.python.org/2/library/itertools.html?highlight=combinations#itertools.combinations_with_replacement
I found that a built-in solution for your target don't exists.
So if you want to make this, I think the two solution may be suitable:
[1]. Make all your cards distinct:
from itertools import combinations
m = 13
n = 4
x = range(m * n)
y = 5
pattern = set()
# combinations from range(0, 52)
for i in combinations(x, y):
# so p/n maps to range(0, 13)
pattern.add((p / n for p in sorted(i)))
[2]. Exclude all invalid results:
from itertools import combinations
m = 13
n = 4
x = range(m) * n
y = 5
pattern = set()
for i in combinations(x, y):
if min(i) == max(i):
continue
pattern.add(tuple(sorted(i)))

You could do this :
import itertools
# each card is a pair (value, color)
cards = [ (value, color) for value in xrange(13) for color in xrange(4) ]
# all 5 cards combinations within 52 cards
for hand in itertools.combinations( cards, 5 ) :
print hand

Related

Generate a percent of all possible combinations from a list of numbers

I know that itertools.combinations(iterable, r) returns combinations of a list/tuple of elements (as an iterable). How can I build a function around it that returns only x% of all combinations? (i just need a list of tuples, so it does not have to be an iterator). I want that if there are very few combinations, for example nCn, it should return all (in this case one) of them (so minimum 1).
With itertools.islice you can produce an iterator with an upper bound in the number of elements:
import itertools
MAX_COMBS = 2
combs = itertools.combinations(range(3), 2)
combs_slice = itertools.islice(combs, MAX_COMBS)
print(*combs_slice, sep='\n')
# (0, 1)
# (0, 2)
If the size of the iterable has a len, then you can make the upper limit dependant on the total number of combinations:
import itertools
import math
# Percentage of combinations to draw
COMB_RATIO = 0.2
# Lower bound for number of combinations
MIN_COMBS = 2
iterable = range(5)
k = 3
combs = itertools.combinations(iterable, k)
max_combs = max(round(COMB_RATIO * math.comb(len(iterable), k)), MIN_COMBS)
combs_slice = itertools.islice(combs, max_combs)
print(*combs_slice, sep='\n')
# (0, 1, 2)
# (0, 1, 3)
# (0, 1, 4)
iterable = range(3)
k = 2
combs = itertools.combinations(iterable, k)
max_combs = max(round(COMB_RATIO * math.comb(len(iterable), k)), MIN_COMBS)
combs_slice = itertools.islice(combs, max_combs)
print(*combs_slice, sep='\n')
# (0, 1)
# (0, 2)
Note: math.comb was introduced in Python 3.8, if you are in a previous version you may need to roll your own implementation, or take it e.g. from SciPy.
Because iterators don't carry the information of how long their collections are, you cannot obtain the length from it.
In your case, you could determine the size of the combination using the formula n!/(k! (n-k)!) and iterate until your percentage.
For instance:
from math import factorial, ceil
def my_combinations():
ratio = .2 # 20 percent
a = range(10)
n = len(a)
k = 5
it = itertools.combinations(a, k)
total_combinations = factorial(n) / factorial(k) / factorial(n-k)
for _ in range(ceil(total_combinations * ratio)):
yield it.next()

itertools combinations in tandem with looping

I have the following Python code. Because random is being used, it generates a new answer every time:
import random
import numpy as np
N = 64 # Given
T = 5 # Given
FinalLengths = []
for i in range(T):
c = range(1, N)
x = random.sample(c, 2) # Choose 2 random numbers between 1 and N-1
LrgstNode = max(x)
SmlstNode = min(x)
RopeLengths = [SmlstNode, LrgstNode - SmlstNode, N - LrgstNode]
S = max(RopeLengths)
N = S
FinalLengths.append(S)
avgS = np.mean(FinalLengths) # Find average
print("The mean of S is {}".format(avgS))
My research has led me to possibly using itertools combinations in order to produce all possible combinations within the range and get the avg to converge. If so, how?
Thank you.
It sounds like you're after something like this:
import random
import numpy as np
from itertools import combinations
N = 64 # Given
T = 5 # Given
FinalLengths = []
for i in range(T):
c = list(range(1, N))
for x in combinations(c, 2):
S = max([min(x), max(x) - min(x), N - max(x)])
N = S
FinalLengths.append(S)
avgS = np.mean(FinalLengths) # Find average
print("The mean of S is {}".format(avgS))
To use combinations(l, size) we can pass in a list l and the size of each combination tuple, and int size. That's all there is to it!

How do I output a list of inputs and outputs of a function?

This is the current python code I am using to evaluate y = (exp^(-mu) * mu**N) / (n!) for 0 <= n <= N:
N = 10
mu = 2
n = np.arange(0, N+1)
numerator = (np.exp(-mu)) * mu**n
denominator = factorial(n)
function = numerator / denominator
print('N =', str(N), 'mu =', str(mu))
print('n = {:}'.format(n))
print('function = {:}'.format(function))
The current output I am getting is:
N = 10 mu = 2
n = [ 0 1 2 3 4 5 6 7 8 9 10]
function = [ 1.35335283e-01 2.70670566e-01 2.70670566e-01 1.80447044e-
01
9.02235222e-02 3.60894089e-02 1.20298030e-02 3.43708656e-03
8.59271640e-04 1.90949253e-04 3.81898506e-05]
What I want as the output is a list, not an array. For each value of n, I want the corresponding value of the function. I.e:
N = 10 mu = 2
n = 0, function = 1.35335283e-01
n = 1, function = 2.70670566e-01
...
n = 10, function = 3.81898506e-05
I have tried other methods, most of which have resulted in the following TypeError:
print('n = {:.}'.format(n))
TypeError: non-empty format string passed to object.__format__
I have managed to get this to work using a 'for' loop, but I would like a way to do this aside from a loop as I'm trying to do an exercise for college without using any loops.
Does anyone know how to do this?
You can zip the outputs together, but you will need to populate n and function with a loop. Why strings?
n = [0,1,2,3,4,5,6,7,8,9,10]
function = [1.35335283e-01,2.70670566e-01,2.70670566e-01,
1.80447044e-01,9.02235222e-02,3.60894089e-02,
1.20298030e-02,3.43708656e-03,8.59271640e-04,
1.90949253e-04,3.81898506e-05]
zipped = list(zip(n,function))
zipped
Out[ ]:
[(0, 0.135335283),
(1, 0.270670566),
(2, 0.270670566),
(3, 0.180447044),
(4, 0.0902235222),
(5, 0.0360894089),
(6, 0.012029803),
(7, 0.00343708656),
(8, 0.00085927164),
(9, 0.000190949253),
(10, 3.81898506e-05)]
And I agree that not allowing you to print with a loop is a pointless requirement.
for a,b in zipped:
print('n =',a,', function =',b)
Out[ ]:
n = 0 , function = 0.135335283
n = 1 , function = 0.270670566
n = 2 , function = 0.270670566
n = 3 , function = 0.180447044
n = 4 , function = 0.0902235222
n = 5 , function = 0.0360894089
n = 6 , function = 0.012029803
n = 7 , function = 0.00343708656
n = 8 , function = 0.00085927164
n = 9 , function = 0.000190949253
n = 10 , function = 3.81898506e-05
You can use numpy.vectorize in the following way
import numpy as np
def custom_print(n, f):
print("n = {}, function = {}".format(n, f))
vprint = np.vectorize(custom_print)
n = np.array([1, 2, 3])
f = np.array([1, 4, 9])
vprint(n, f)
Output
n = 1, function = 1
n = 2, function = 4
n = 3, function = 9
Note:
As FHTMitchell stated in the comments
The vectorize function is provided primarily for convenience, not for performance. The implementation is essentially a for loop.
(from the docpage: https://docs.scipy.org/doc/numpy/reference/generated/numpy.vectorize.html)

Code to maximize the sum of squares modulo m

Inputs:
k-> number of lists
m->modulo
Constraints
1<=k<=7
1<=M<=1000
1<=Magnitude of elements in list<=10*9
1<=Elements in each list<=7
`
This snippet of code is responsible for maximizing (x1^2 + x2^2 + ...) % m where x1, x2, ... are chosen from lists X1, X2, ...
k,m=map(int,input().split())
Sum=0
s=[]
for _ in range(k):
s.append(max(map(int,input().split())))
Sum+=int(s[_])**2
print(Sum%m)
So for instance if inputs are :
3 1000
2 5 4
3 7 8 9
5 5 7 8 9 10
The output would be 206, owing to selecting highest element in each list, square that element, take the sum and perform modulus operation using m
So, it would be (5^2+9^2+10^2)%1000=206
If I provide input like,
3 998
6 67828645 425092764 242723908 669696211 501122842 438815206
4 625649397 295060482 262686951 815352670
3 100876777 196900030 523615865
The expected output is 974, but I am getting 624
I would like to know how you would approach this problem or how to correct existing code.
You have to find max((sum of squares) modulo m). That's not the same as max(sum of squares) modulo m.
It may be that you find a sum of squares that's not in absolute terms as large as possible, but is maximum when you take it modulo m.
For example:
m=100
[10, 9],
[10, 5]
Here, the maximum sum of squares is 100 + 100 = 200, which is 0 modulo 100. The maximum (sum of squares modulo 100) is (81 + 100) = 182, which is 82 modulo 100.
Given that m is forced to be small, there's an fast dynamic programming solution that runs in O(m * N) time, where N is the total number of items in all the lists.
def solve(m, xxs):
r = [1] + [0] * (m - 1)
for xs in xxs:
s = [0] * m
for i in xrange(m):
for x in xs:
xx = (x * x) % m
s[i] += r[(i - xx) % m]
r = s
return max(i for i in xrange(m) if r[i])
m = 998
xxs = [
[67828645, 425092764, 242723908, 669696211, 501122842, 438815206],
[625649397, 295060482, 262686951, 815352670],
[100876777, 196900030, 523615865]]
print solve(m, xxs)
This outputs 974 as required.
One important logical problem here is you have to skip the number of items in each list while find the max element in your for loop. That is, instead of
Example,
6 67828645 425092764 242723908 669696211 501122842 438815206
and your data is
67828645 425092764 242723908 669696211 501122842 438815206
That is,
input().split()
You have to use,
input().split()[1:]
As pointed by Paul Hankin, you basically need to find max(sum of powers % m)
You have to find the combination from three lists whose sum%m is max.
So, this is basically,
You scan the input, split with space, leaving the first element which is the number of values in each line,you map them to integers. And then, you find the squares and append them to a list s. Having that you find the product(itertools module) Example - product([1,2],[3,4,5]) will give, [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3)]. Now, you can find the sum of each such result % m and find the max value!
That is,
k,m=map(int,input().split())
from itertools import product
s=[]
for _ in range(k):
s.append(map(lambda x:x**2,map(int,input().split()[1:])))
print(max([sum(i)%m for i in product(*s)]))
Try it online!
This will give you the desired output!
Hope it helps!
Your question is not very clear. However, if I understand it correctly, you have lists of possible values for f(X1), ..., f(Xn) (probably obtained by applying f to all possible values for X1, ..., Xn), and you want to maximize f(X1)^2 + ... + f(Xn)^2 ?
If so, your code seems good, I get the same result:
lists = [[6, 67828645, 425092764, 242723908, 669696211, 501122842, 438815206],
[4, 625649397, 295060482, 262686951, 815352670],
[3, 100876777, 196900030, 523615865]]
sum = 0
for l in lists:
sum += max(l)**2
print(sum%998)
This print 624, just like your code. Where are you getting the 974 from ?
Not going to win any codegolf with this but here was my solution:
from functools import reduce
def get_input():
"""
gets input from stdin.
input format:
3 1000
2 5 4
3 7 8 9
5 5 7 8 9 10
"""
k, m = [int(i) for i in input().split()]
lists = []
for _ in range(k):
lists.append([int(i) for i in input().split()[1:]])
return m, k, lists
def maximise(m, k, lists):
"""
m is the number by which the sum of squares is modulo'd
k is the number of lists in the list of lists
lists is the list of lists containing vals to be sum of squared
maximise aims to maximise S for:
S = (f(x1) + f(x2)...+ f(xk)) % m
where:
f(x) = x**2
"""
max_value = reduce(lambda x,y: x+y**2, [max(l) for l in lists], 0)
# check whether the max sum of squares is greater than m
# if it is the answer has to be the max
if max_value < m:
print(max_value)
return
results = []
for product in cartesian_product(lists):
S = reduce(lambda x, y: x + y**2, product, 0) % m
if S == m-1:
print(S)
return
results.append(S)
print(max(results))
def cartesian_product(ll, accum=None):
"""
all combinations of lists made by combining one element from
each list in a list of lists (cartesian product)
"""
if not accum:
accum = []
for i in range(len(ll[0])):
if len(ll) == 1:
yield accum + [ll[0][i]]
else:
yield from cartesian_product(ll[1:], accum + [ll[0][i]])
if __name__ == "__main__":
maximise(*get_input())

Creating a N sets of combinations of variables from the given range of each variable

This may be a very vague question -- I apologize in advance.
y is a function of a,b,c,d,e.
a can go from 1 to 130; b from 0.5 to 1; c from 3 to 10; d from 0 to 1; and e is 1-d.
Is there a way in python I can create N (say, 10,000) sets of combinations of a,b,c,d and e from the given range?
itertools is a great library for creating iterators like that. In your case, it is product that we need. However, you cannot set the number of point you want. You are going to have to derive that mathematically and translate it into steps (the 1s at the end of the range() function).
a = [i for i in range(1, 131, 1)]
b = [i/10 for i in range(5, 11,1)]
c = [i for i in range(3, 11, 1)]
d = [i/10 for i in range(0, 11, 1)]
from itertools import product
combs = product(a, b, c, d)
for i, x in enumerate(combs, 1):
x = list(x)
x.append(1-x[-1])
print(i, x) # example print: 52076 [99, 0.8, 9, 0.1, 0.9]
The example above produces: 130 * 6 * 8 * 11 = 68640 combinations
These are more than you requested but you get the point. You can also decide to have a variable more ro less finely discretised.
I am also assuming a & c are integer variables..
I assume you want floating point numbers for all of these. If you want ints use random.randint
from random import uniform
inputs = [[uniform(1,130), uniform(.5, 1), uniform(3,10), uniform(0,1)] for _ in range(N)]
for input_list in inputs:
input_list.append(1-input_list[-1])
a(*input_list)

Categories