How to count possibilities in python lists - python

Given a list like this:
num = [1, 2, 3, 4, 5]
There are 10 three-element combinations:
[123, 124, 125, 134, 135, 145, 234, 235, 245, 345]
How can I generate this list?

Use itertools.combinations:
import itertools
num = [1, 2, 3, 4, 5]
combinations = []
for combination in itertools.combinations(num, 3):
combinations.append(int("".join(str(i) for i in combination)))
# => [123, 124, 125, 134, 135, 145, 234, 235, 245, 345]
print len(combinations)
# => 10
Edit
You can skip int(), join(), and str() if you are only interested in the number of combinations. itertools.combinations() gives you tuples that may be good enough.

You are talking about combinations. There are n!/(k! * (n - k)!) ways to take k elements from a list of n elements. So:
>>> num = [1, 2, 3, 4, 5]
>>> fac = lambda n: 1 if n < 2 else n * fac(n - 1)
>>> combos = lambda n, k: fac(n) / fac(k) / fac(n - k)
>>> combos(len(num), 3)
10
Use itertools.combinations only if you actually want to generate all combinations. Not if you just want to know the number of different combinations.
Also, there are more efficient ways to calculate the number of combinations than using the code shown above. For example,
>>> from operator import truediv, mul
>>> from itertools import starmap
>>> from functools import reduce
>>> combos = lambda n, k: reduce(mul, starmap(truediv, zip(range(n, n - k, -1), range(k, 0, -1))))
>>> combos(len(num), 3)
10.0
(Note that this code uses floating point division!)

I believe you are looking for the binomial coefficient:

itertools.combinations():
Return r length subsequences of elements from the input iterable.
Combinations are emitted in lexicographic sort order. So, if the input iterable is sorted, the combination tuples will be produced in sorted order.
Elements are treated as unique based on their position, not on their value. So if the input elements are unique, there will be no repeat values in each combination.
>>> num = [1, 2, 3, 4, 5]
>>> [i for i in itertools.combinations(num,3)]
[(1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5), (2, 3, 4), (2, 3, 5),
(2, 4, 5), (3, 4, 5)]
>>>

Related

Using min and max on sublists and creating a list without using "for loop" outside reduce function

from functools import reduce
for _ in range(int(input())):
N = int(input())
l1 = list(map(int,input().split()))
def powerset(lst):
return reduce(lambda result, x: result + [subset + [x] for subset in result],
lst, [[]])
#https://stackoverflow.com/questions/1482308/how-to-get-all-subsets-of-a-set-powerset
lst = (powerset(l1))
print(lst)
ivlst = []
for i in range(1, len(lst)):
ivlst.append((min(lst[i])*max(lst[i])))
print(min(ivlst), end=" ")
print(max(ivlst))
Sample input:
2
2
2 2
3
5 0 9
Sample output:
4 4
0 81
The above code does the following:
It takes the input as N, where N is the number of elements in the list.
Then it takes the input as the elements of the list.
Then it creates a function called powerset which takes a list as an argument and returns all the subsets of that list.
Then it calls the reduce function on the powerset function with the list as the first argument and the empty list as the second argument.
The reduce function will return a list of lists.
The ivlst variable is used to store the values of the minimum and maximum of the subsets.
Then it iterates over the range from 1 to the length of the list.
For each iteration, it appends the multiplication of minimum and maximum of the subset to the ivlst list.
Finally, it prints the minimum and maximum of the ivlst list.
The time complexity is O(2^n) where n is the number of elements in the given set.
I need a way to not use the for loop for getting the min and max values of all sublists, rather I need to get a list containing multiplication of min and max values of all sublists as output from the powerset function itself.
You could maximize the production of the powerset function, in fact,
the Python itertools page has exactly a powerset recipe:
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
or you can achieve by:
from more_itertools import powerset
The equivalent code of your powerset function is as follows:
from itertools import islice
def powerset(lst):
ret = [[]]
for elem in lst:
for subset in islice(ret, len(ret)):
ret.append(subset + [elem])
return ret
We no longer build and record the remaining subsets in the inner loop, but only record the minimum and maximum values of the subsets, so that our operating costs will be greatly reduced (from O(n * 2^n) to O(2^n), because the generation algorithm of power sets will not be faster than O(2^n), and the minimum and maximum values calculated for each subset are O(n)):
from itertools import islice
def powerset_extremum(lst):
ret = [(float('inf'), float('-inf'))]
for elem in lst:
for min_, max_ in islice(ret, len(ret)):
ret.append((min(min_, elem), max(max_, elem)))
return ret
Simple test:
>>> powerset([2, 1, 4, 3])
[[],
[2],
[1],
[2, 1],
[4],
[2, 4],
[1, 4],
[2, 1, 4],
[3],
[2, 3],
[1, 3],
[2, 1, 3],
[4, 3],
[2, 4, 3],
[1, 4, 3],
[2, 1, 4, 3]]
>>> powerset_extremum([2, 1, 4, 3])
[(inf, -inf),
(2, 2),
(1, 1),
(1, 2),
(4, 4),
(2, 4),
(1, 4),
(1, 4),
(3, 3),
(2, 3),
(1, 3),
(1, 3),
(3, 4),
(2, 4),
(1, 4),
(1, 4)]
Then you can easily get their multiplication:
>>> from itertools import starmap, islice
>>> from operator import mul
>>> list(starmap(mul, islice(powerset_extremum([2, 1, 4, 3]), 1, None)))
[4, 1, 2, 16, 8, 4, 4, 9, 6, 3, 3, 12, 8, 4, 4]
I know that this does not meet your requirement of not using the for loop, but it is obviously much faster than eliminating the explicit for loop through some built-in functions after getting the power set.

How to count elements inside list with specific condition

I am making combination of six number is seems easy but i need output of specific combination
i think i need to use count function and loop?????
from itertools import combinations
comb = combinations([1, 2, 3, 4, 5, 6], 3)
for n in list(comb):
print (n)
Actual result give me 20 combination, but i need solution of code gives me only combination n where n(n1,n2,n3) n1+n2=n3,
so in my case it will be
(1,2,3) (1,3,4) (1,4,5) (1,5,6) (2,3,5) (2,4,6)
i need solution of code gives me only combination n where n(n1,n2,n3) n1+n2=n3
Add that as an if statement inside the for loop:
for n in comb:
if n[0] + n[1] == n[2]:
print (n)
Try this oneliner:
from itertools import combinations as combs
print(list(filter(lambda c: c[0]+c[1]==c[2], combs(range(1,7), 3))))
Or if your want to print one combination at a time you can do:
from itertools import combinations as combs
for comb in filter(lambda c: c[0]+c[1]==c[2], combs(range(1,7), 3)):
print(comb)
Another solution:
result = [(x,y,z) for (x,y,z) in combinations([1, 2, 3, 4, 5, 6], 3) if x+y==z]
print(result)
[(1, 2, 3), (1, 3, 4), (1, 4, 5), (1, 5, 6), (2, 3, 5), (2, 4, 6)]

Find most common element

How can I print the most common element of a list without importing a library?
l=[1,2,3,4,4,4]
So I want the output to be 4.
You can get the unique values first:
l = [1, 2, 3, 4, 4, 4]
s = set(l)
then you can create list of (occurrences, value) tuples
freq = [(l.count(i), i) for i in s] # [(1, 1), (1, 2), (1, 3), (3, 4)]
get the "biggest" element (biggest number of occurrences, the biggest value if there are more than one with the same number of occurrences):
result = max(freq) # (3, 4)
and print the value:
print(result[1]) # 4
or as a "one-liner" way:
l = [1, 2, 3, 4, 4, 4]
print(max((l.count(i), i) for i in set(l))[1]) # 4
lst=[1,2,2,2,3,3,4,4,5,6]
from collections import Counter
Counter(lst).most_common(1)[0]
Counter(lst) returns a dict of element-occurence pairs. most_common(n) returns the n most common elements from the dict, along with the number of occurences.

group clusters of numbers in array

I have an array like:
A = [1,3,8,9,3,7,2,1,3,9,6,8,3,8,8,1,2]
And I want to count the number of "entry clusters" that are >5. In this case the result should be 4, because:
[1, 3, (8,9), 3, (7), 2, 1, 3, (9,6,8), 3, (8,8), 1, 2]
Given L length of the array, I can do:
A = [1,3,8,9,3,7,2,1,3,9,6,8,3,8,8,1,2]
A = np.array(A)
for k in range(0,L):
if A[k]>5:
print k, A[k]
and this gives me all entries greater than 5. But how could I group every cluster of numbers?
You could use the groupby function from itertools.
from itertools import groupby
A = [1,3,8,9,3,7,2,1,3,9,6,8,3,8,8,1,2]
result = [tuple(g) for k, g in groupby(A, lambda x: x > 5) if k]
print(result)
# [(8, 9), (7,), (9, 6, 8), (8, 8)]
print(len(result))
# 4

Efficient combinations with replacement for multiple iterables, or order-independent product

I'm trying to find a performant solution in Python that works like so:
>>> func([1,2,3], [1,2])
[(1,1), (1,2), (1,3), (2,2), (2,3)]
This is similar to itertools.combinations_with_replacement, except that it can take multiple iterables. It's also similar to itertools.product, except that it omits order-independent duplicate results.
All of the inputs will be prefixes of the same series (i.e. they all start with the same element and follow the same pattern, but might have different lengths).
The function must be able to take any number of iterables as input.
Given a set of lists A, B, C, ..., here is a sketch of an algorithm that generates those results.
assert len(A) <= len(B) <= len(C) <= ...
for i in 0..len(A)
for j in i..len(B)
for k in j..len(C)
.
.
.
yield A[i], B[j], C[k], ...
Things I can't do
Use itertools.product and filter the results. This has to be performant.
Use recursion. The function overhead would make it slower than using itertools.product and filtering for a reasonable number of iterables.
I suspect there's a way to do this with itertools, but I have no idea what it is.
EDIT: I'm looking for the solution that takes the least time.
EDIT 2: There seems to be some confusion about what I'm trying to optimize. I'll illustrate with an example.
>>> len(list(itertools.product( *[range(8)] * 5 )))
32768
>>> len(list(itertools.combinations_with_replacement(range(8), 5)))
792
The first line gives the number of order-dependent possibilities for rolling 5 8-sided dice. The second gives the number of order-independent possibilities. Regardless of how performant itertools.product is, it'll take 2 orders of magnitude more iterations to get a result than itertools.combinations_with_replacement. I'm trying to find a way to do something similar to itertools.combinations_with_replacement, but with multiple iterables that minimizes the number of iterations, or time performance. (product runs in whereas combinations_with_replacement runs in , where M is the number of sides on the die and N is the number of dice)
This solution hasn't recursion or filtering. It's trying to produce only ascending sequences of indices so it's usable only for prefixes of same collection. Also it's uses only indices for element identification so it's not enforces elements of series to be comparable or even hashable.
def prefixCombinations(coll,prefixes):
"produces combinations of elements of the same collection prefixes"
prefixes = sorted(prefixes) # does not impact result through it's unordered combinations
n = len(prefixes)
indices = [0]*n
while True:
yield tuple(coll[indices[i]] for i in range(n))
#searching backwards for non-maximum index
for i in range(n-1,-1,-1):
if indices[i] < prefixes[i] - 1 : break
# if all indices hits maximum - leave
else: break
level = indices[i] + 1
for i in range(i,n): indices[i] = level
examples are
>>> list(prefixCombinations([1,2,3,4,5], (3,2)))
[[1, 1], [1, 2], [1, 3], [2, 2], [2, 3]]
>>> list(prefixCombinations([1,2,3,4,5], (3,2,5)))
[[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4], [1, 1, 5], [1, 2, 2], [1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 3], [1, 3, 4], [1, 3, 5], [2, 2, 2], [2, 2, 3], [2, 2, 4], [2, 2, 5], [2, 3, 3], [2, 3, 4], [2, 3, 5]]
>>> from itertools import combinations_with_replacement
>>> tuple(prefixCombinations(range(10),[10]*4)) == tuple(combinations_with_replacement(range(10),4))
True
Since this is a generator it doesn't effectively change the performance (just wraps O(n) around itertools.product):
import itertools
def product(*args):
for a, b in itertools.product(*args):
if a >= b:
yield b, a
print list(product([1,2,3], [1,2]))
Output:
[(1, 1), (1, 2), (2, 2), (1, 3), (2, 3)]
Or even:
product = lambda a, b: ((y, x) for x in a for y in b if x >= y)
Here an implementation.
The idea is to use sorted containers to impose canonical order and avoid duplicates this way. So I'm not generating duplicates at one step and avoid need of filtering later.
It relies on "sortedcontainers" library that provides fast (as fast as C implementation) sorted containers. [I'm not affiliated to this library in any manner]
from sortedcontainers import SortedList as SList
#see at http://www.grantjenks.com/docs/sortedcontainers/
def order_independant_combination(*args):
filtered = 0
previous= set()
current = set()
for iterable in args:
if not previous:
for elem in iterable:
current.add(tuple([elem]))
else:
for elem in iterable:
for combination in previous:
newCombination = SList(combination)
newCombination.add(elem)
newCombination = tuple(newCombination)
if not newCombination in current:
current.add(newCombination)
else:
filtered += 1
previous = current
current = set()
if filtered != 0:
print("{0} duplicates have been filtered during geneeration process".format(filtered))
return list(SList(previous))
if __name__ == "__main__":
result = order_independant_combination(*[range(8)] * 5)
print("Generated a result of length {0} that is {1}".format(len(result), result))
Execution give:
[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3)]
You can test adding more iterables as parameters, it works.
Hope it can at least helps you if not solve your problem.
Vaisse Arthur.
EDIT : to answer the comment. This is not a good analysis. Filtering duplicates during generation is far most effectives than using itertools.product and then filters duplicates result. In fact, eliminating duplicates result at one step avoid to generate duplicates solution in all the following steps.
Executing this:
if __name__ == "__main__":
result = order_independant_combination([1,2,3],[1,2],[1,2],[1,2])
print("Generated a result of length {0} that is {1}".format(len(result), result))
I got the following result :
9 duplicates have been filtered during geneeration process
Generated a result of length 9 that is [(1, 1, 1, 1), (1, 1, 1, 2), (1, 1, 1, 3), (1, 1, 2, 2), (1, 1, 2, 3), (1, 2, 2, 2), (1, 2, 2, 3), (2, 2, 2, 2), (2, 2, 2, 3)]
While using itertools I got this :
>>> import itertools
>>> c = list(itertools.product([1,2,3],[1,2],[1,2],[1,2]))
>>> c
[(1, 1, 1, 1), (1, 1, 1, 2), (1, 1, 2, 1), (1, 1, 2, 2), (1, 2, 1, 1), (1, 2, 1, 2), (1, 2, 2, 1), (1, 2, 2, 2), (2, 1, 1, 1), (2, 1, 1, 2), (2, 1, 2, 1), (2, 1, 2, 2), (2, 2, 1, 1), (2, 2, 1, 2), (2, 2, 2, 1), (2, 2, 2, 2), (3, 1, 1, 1), (3, 1, 1, 2), (3, 1, 2, 1), (3, 1, 2, 2), (3, 2, 1, 1), (3, 2, 1, 2), (3, 2, 2, 1), (3, 2, 2, 2)]
>>> len(c)
24
Simple calcul give this:
pruned generation : 9 result + 9 element filtered -> 18 element generated.
itertools : 24 element generated.
And the more element you give it, the more they are long, the more the difference will be important.
Example :
result = order_independant_combination([1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5])
print("Generated a result of length {0} that is {1}".format(len(result), result))
Result :
155 duplicates have been filtered during geneeration process
Generated a result of length 70 ...
Itertools :
>>> len(list(itertools.product([1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5])))
625
Difference of 400 elements.
EDIT 2 : with *range(8) * 5 it gives 2674 duplicates have been filtered during geneeration process. Generated a result of length 792...

Categories

Resources