I have the numbers [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 15, 16].
I want to find all permutations of numbers of exactly 9 characters. For example, if I have 8 single digit numbers, the last one can't be a double digit, because it would exceed the character limit.
Clarifications:
The same number can be reused, so for example the number 1 nine times is valid.
There can be any number of double digit numbers, as long as the total character length of the digits is exactly 9. For example, 1, 10, 12, 15, 16 is valid.
I tried itertools.permutations, but I couldn't get it to work with two-digit numbers.
I would use a mixture of combination and permutations.
First find all of the combinations of the data which add up to the desired length. Then for each unique combination, find their permutations. There's probably some work to be done here which can limit the amount of incorrect combinations checked:
import itertools
def perm_of_length(data, length):
for i in range(len(data)):
for comb in itertools.combinations(data, i + 1):
if sum(map(len, (map(str, comb)))) == length:
for perm in itertools.permutations(comb):
yield perm
for perm in perm_of_length([0, 1, 2, 3, 10, 12], 4):
print(perm)
Outputs:
(10, 12)
(12, 10)
(0, 1, 10)
(0, 10, 1)
(1, 0, 10)
(1, 10, 0)
(10, 0, 1)
(10, 1, 0)
(0, 1, 12)
(0, 12, 1)
(1, 0, 12)
(1, 12, 0)
(12, 0, 1)
(12, 1, 0)
(0, 2, 10)
(0, 10, 2)
(2, 0, 10)
(2, 10, 0)
(10, 0, 2)
(10, 2, 0)
(0, 2, 12)
(0, 12, 2)
(2, 0, 12)
(2, 12, 0)
(12, 0, 2)
(12, 2, 0)
(0, 3, 10)
(0, 10, 3)
(3, 0, 10)
(3, 10, 0)
(10, 0, 3)
(10, 3, 0)
(0, 3, 12)
(0, 12, 3)
(3, 0, 12)
(3, 12, 0)
(12, 0, 3)
(12, 3, 0)
(1, 2, 10)
(1, 10, 2)
(2, 1, 10)
(2, 10, 1)
(10, 1, 2)
(10, 2, 1)
(1, 2, 12)
(1, 12, 2)
(2, 1, 12)
(2, 12, 1)
(12, 1, 2)
(12, 2, 1)
(1, 3, 10)
(1, 10, 3)
(3, 1, 10)
(3, 10, 1)
(10, 1, 3)
(10, 3, 1)
(1, 3, 12)
(1, 12, 3)
(3, 1, 12)
(3, 12, 1)
(12, 1, 3)
(12, 3, 1)
(2, 3, 10)
(2, 10, 3)
(3, 2, 10)
(3, 10, 2)
(10, 2, 3)
(10, 3, 2)
(2, 3, 12)
(2, 12, 3)
(3, 2, 12)
(3, 12, 2)
(12, 2, 3)
(12, 3, 2)
(0, 1, 2, 3)
(0, 1, 3, 2)
(0, 2, 1, 3)
(0, 2, 3, 1)
(0, 3, 1, 2)
(0, 3, 2, 1)
(1, 0, 2, 3)
(1, 0, 3, 2)
(1, 2, 0, 3)
(1, 2, 3, 0)
(1, 3, 0, 2)
(1, 3, 2, 0)
(2, 0, 1, 3)
(2, 0, 3, 1)
(2, 1, 0, 3)
(2, 1, 3, 0)
(2, 3, 0, 1)
(2, 3, 1, 0)
(3, 0, 1, 2)
(3, 0, 2, 1)
(3, 1, 0, 2)
(3, 1, 2, 0)
(3, 2, 0, 1)
(3, 2, 1, 0)
Proof this works:
for perm in perm_of_length([0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 15, 16], 9):
assert sum(map(len, map(str, perm))) == 9
NOTE: this is much slower than it needs to be.
A simple, non-performant, brute-force approach with filtering:
symbols = [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 15, 16]
symbols = [str(x) for x in symbols]
perms = itertools.chain.from_iterable(
itertools.permutations(symbols, i) for i in range(10)
)
perms = ("".join(x) for x in perms)
perms = [x for x in perms if len(x) <= 9]
>>> len(perms)
13600046
>>> perms[:4]
['', '0', '1', '2']
>>> perms[-4:]
['987643102', '987643120', '987643201', '987643210']
One can drop the empty string by simply skipping the first item in perms.
A similar approach which does not use strings:
symbols = [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 15, 16]
lengths = {k: int(math.log10(k)) + 1 if k != 0 else 1 for k in symbols}
perms = itertools.chain.from_iterable(
itertools.permutations(symbols, i) for i in range(10)
)
perms = [x for x in perms if sum(lengths[k] for k in x) <= 9]
>>> len(perms)
13600046
If numbers can be reused (as mentioned in the comments), I would consider itertools.product instead of itertools.permutations:
symbols = [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 15, 16]
lengths = {k: int(math.log10(k)) + 1 if k != 0 else 1 for k in symbols}
perms = itertools.product(symbols, repeat=9)
perms = [x for x in perms if sum(lengths[k] for k in x) <= 9]
Related
I am trying to modify the code provided as answer by Hilberts in below question to allow it for multiple values and not only binary.
All binary combinations in a 2d Numpy
So, rather than only allow 0 and 1 like in the previous example, i would like find all combinations without rotations for data like 0, 1, 2 and 3 .
I modified it in this way, mainly replacing the "2" in the original code by a variable and tried to replace it by the variable "m" to follow the initial formula. But I think the issue here is related to the numpy type of data "bitwise_and" . Does anyone have any advices about what would be problem or any hint?
def get_binary_mats(n,m):
# all possible n by n binary matrices up to rotation:
bin_mats = (np.bitwise_and(np.arange(m**(n*n))[:,None], m ** np.arange(n*n)) > 0)\
.reshape(-1, n, n)
# define a score for each matrix based on position of ones
score = m ** np.arange(n*n).reshape(n,n)
# array([[ 1, 2, 4],
# [ 8, 16, 32],
# [ 64, 128, 256]])
score_arr = np.stack([np.rot90(score, k=k) for k in range(4)])
# array([[[ 1, 2, 4],
# [ 8, 16, 32],
# [ 64, 128, 256]],
# [[ 4, 32, 256],
# [ 2, 16, 128],
# [ 1, 8, 64]],
# [[256, 128, 64],
# [ 32, 16, 8],
# [ 4, 2, 1]],
# [[ 64, 8, 1],
# [128, 16, 2],
# [256, 32, 4]]])
scores = np.einsum("ijk,ljk->il", bin_mats, score_arr)
_, idx = np.unique(scores.min(1), return_index=True)
return bin_mats[idx,...]
Thank you
from itertools import combinations_with_replacement as combn
list(combn([0,1,2,3], 4))
[(0, 0, 0, 0),
(0, 0, 0, 1),
(0, 0, 0, 2),
(0, 0, 0, 3),
(0, 0, 1, 1),
(0, 0, 1, 2),
(0, 0, 1, 3),
(0, 0, 2, 2),
(0, 0, 2, 3),
(0, 0, 3, 3),
(0, 1, 1, 1),
(0, 1, 1, 2),
(0, 1, 1, 3),
(0, 1, 2, 2),
(0, 1, 2, 3),
(0, 1, 3, 3),
(0, 2, 2, 2),
(0, 2, 2, 3),
(0, 2, 3, 3),
(0, 3, 3, 3),
(1, 1, 1, 1),
(1, 1, 1, 2),
(1, 1, 1, 3),
(1, 1, 2, 2),
(1, 1, 2, 3),
(1, 1, 3, 3),
(1, 2, 2, 2),
(1, 2, 2, 3),
(1, 2, 3, 3),
(1, 3, 3, 3),
(2, 2, 2, 2),
(2, 2, 2, 3),
(2, 2, 3, 3),
(2, 3, 3, 3),
(3, 3, 3, 3)]
Let's say I have a multidimensional array, foobar:
foobar = [[[0, 1, 2],
[3, 4, 5, 6],
[7, 8]],
[[9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18],
[19, 20, 21, 22]],
[[23, 24, 25],
[26, 27]]]
Note that foobar is jagged.
The thing I need to do is replace each number in foobar with a tuple containing that number and its exact position in foobar. I also need to be able to do this when the number of dimensions and whether or not foobar is jagged is unknown.
Here is something similar, except it only works for 2 dimensions:
def enum_multidim(data):
for i, a in enumerate(data):
for j, b in enumerate(a):
yield (i, j, b)
Is there a user-defined function that can do what I said above?
Recursive generator for an arbitrary number of dimensions
Code
def enum_multidim(data, t = None):
if t is None:
t = ()
if not isinstance(data, list):
yield t + (data,)
else:
for i, v in enumerate(data):
yield from enum_multidim(v, t + (i,))
Test
for t in enum_multidim(foobar):
print(t)
# Out:
(0, 0, 0, 0)
(0, 0, 1, 1)
(0, 0, 2, 2)
(0, 1, 0, 3)
(0, 1, 1, 4)
(0, 1, 2, 5)
(0, 1, 3, 6)
(0, 2, 0, 7)
(0, 2, 1, 8)
(1, 0, 0, 9)
(1, 0, 1, 10)
(1, 1, 0, 11)
(1, 1, 1, 12)
(1, 1, 2, 13)
(1, 1, 3, 14)
(1, 1, 4, 15)
(1, 2, 0, 16)
(1, 2, 1, 17)
(1, 2, 2, 18)
(1, 3, 0, 19)
(1, 3, 1, 20)
(1, 3, 2, 21)
(1, 3, 3, 22)
(2, 0, 0, 23)
(2, 0, 1, 24)
(2, 0, 2, 25)
(2, 1, 0, 26)
(2, 1, 1, 27)
So, I saw DarryIG's answer and modified it to fit my style:
def get_dims(data): # some other function I use
if not isinstance(data, itertypes):
return 0
return get_dims(data[0]) + 1
def enum_multidim(data, index = []):
if get_dims(data) == 0:
return index + [data]
return [enum_multidim(x, index + [i]) for i, x in enumerate(data)]
Credit will go to him. Thanks so much!
I've been struggling with defining a function that returns the permutations of a given array.
I have written the code below:
def gPermutations(array):
result = []
idx = 0
for element in array:
for i in range(len(array)):
if i != idx:
z = array[:array.index(element)]
z.append(array[i])
s = z[:] + array[array.index(element)+1:]
s[i] = element
result.append(s)
idx += 1
return result
This code seems to work by returning some of the permutations of an array, but doesn't return all of them, and sometimes duplicates a certain permutation. May someone please explain what is the issue with my code? Thanks!
Use the permutations method from itertools
In [1]: from itertools import permutations
In [2]: arr = [1,2,3,4]
In [3]: for perm in permutations(arr, len(arr)):
...: print(perm)
...:
(1, 2, 3, 4)
(1, 2, 4, 3)
(1, 3, 2, 4)
(1, 3, 4, 2)
(1, 4, 2, 3)
(1, 4, 3, 2)
(2, 1, 3, 4)
(2, 1, 4, 3)
(2, 3, 1, 4)
(2, 3, 4, 1)
(2, 4, 1, 3)
(2, 4, 3, 1)
(3, 1, 2, 4)
(3, 1, 4, 2)
(3, 2, 1, 4)
(3, 2, 4, 1)
(3, 4, 1, 2)
(3, 4, 2, 1)
(4, 1, 2, 3)
(4, 1, 3, 2)
(4, 2, 1, 3)
(4, 2, 3, 1)
(4, 3, 1, 2)
(4, 3, 2, 1)
If you truly want all the permutations (re-orderings?) of your list, I'd use the itertools package:
>>> from itertools import permutations
>>> array = [1, 2, 3]
>>> print(list(permutations(array)))
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
I have two arrays as follows:
a = [1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5]
b = [1, 2, 5, 1, 3, 2, 3, 4, 3, 5, 1, 4]
All the possible pairs these two arrays can form are (a[0],b[0]),(a[1],b[1]),(a[2],b[2]),(a[3],b[3]),...,(a[11],b[11]). The two arrays have same length and a pair is formed between same indices.
The problem is to check that how many pairs are there who have a reverse pair present for them.
For example, in the arrays I provided above the pairs (a[1],b[1]) and (a[3],b[3]) form one reverse pair because (1,2) and (2,1) are reverse of each other. Similarly (a[2],b[2]) forms a reverse pair with (a[10],b[10]). Can someone guide me how to count the total number of reverse pairs that exist between two arrays?
Thanks a lot for your time.
A = [1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5]
B = [1, 2, 5, 1, 3, 2, 3, 4, 3, 5, 1, 4]
for pair in zip(A,B):
rpair = pair[::-1]
print(pair,rpair)
Result:
(1, 1) (1, 1)
(1, 2) (2, 1)
(1, 5) (5, 1)
(2, 1) (1, 2)
(2, 3) (3, 2)
(3, 2) (2, 3)
(3, 3) (3, 3)
(3, 4) (4, 3)
(4, 3) (3, 4)
(4, 5) (5, 4)
(5, 1) (1, 5)
(5, 4) (4, 5)
A = [1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5]
B = [1, 2, 5, 1, 3, 2, 3, 4, 3, 5, 1, 4]
for pair in zip(A,B):
rpair = pair[::-1]
if pair[0] == rpair[1] and pair[1] == rpair[0]:
print(pair,rev_pair)
Result
(1, 1) (4, 5)
(1, 2) (4, 5)
(1, 5) (4, 5)
(2, 1) (4, 5)
(2, 3) (4, 5)
(3, 2) (4, 5)
(3, 3) (4, 5)
(3, 4) (4, 5)
(4, 3) (4, 5)
(4, 5) (4, 5)
(5, 1) (4, 5)
(5, 4) (4, 5)
Final
A = [1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5]
B = [1, 2, 5, 1, 3, 2, 3, 4, 3, 5, 1, 4]
n = 0
for pair in zip(A,B):
rpair = pair[::-1]
if pair[0] == rpair[1] and pair[1] == rpair[0]:
n = n + 1
print('total pairs : {}'.format(n))
Result
total pairs : 12
This is a pretty vague question.
Do you want to count +1 if a single reverse pair is present, or +n if n reverse of (a[i], b[i]) is found in the arrays?
How about only counting reverse pairs a single time?
Should (1, 1) be counted as a reverse pair, or not?
For the future, perhaps show the answer you expect for your simple test-system.
I would expect that your test-system should give n=12 reverse pairs, assuming you only count reverse pair a single time in the calculation.
Just a simple implementation that is easy to read:
A = [1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5]
B = [1, 2, 5, 1, 3, 2, 3, 4, 3, 5, 1, 4]
n = 0
pairs = [pair for pair in zip(A, B)]
for rev_pair in zip(B, A):
if rev_pair in pairs:
n += 1
I'm sure a much faster implementation could be made..
At first, I apologize because I am a newbie...
I have one 3d list of tuple:
list=[(0, 1, 6), (5,1,4), (1, 6, 0), (3, 2,1),(4,5,1)]
I want to find duplicates and display like this, no matter the position of numbers ,only to have the same numbers :
{ (0,1,6): 2,
(4,5,1): 2,
(3,2,1): 1 }
I want to count the similar tuples.
Any suggestions?
You say you are initializing your list with:
mylist = [[[(x,y,z) for x in range(7)] for y in range(7)] for z in range(7)]
At this point, you can change how you initialize it:
from collections import Counter
# take off some square brackets
mylist = [(x,y,z) for x in range(7) for y in range(7) for z in range(7)]
dict(Counter(tuple(sorted(tup)) for tup in mylist))
Output:
{(0, 0, 0): 1, (0, 0, 1): 3, (0, 0, 2): 3, (0, 0, 3): 3, (0, 0, 4): 3, (0, 0, 5): 3, (0, 0, 6): 3, (0, 1, 1): 3, (0, 1, 2): 6, (0, 1, 3): 6, (0, 1, 4): 6, (0, 1, 5): 6, (0, 1, 6): 6, (0, 2, 2): 3, (0, 2, 3): 6, (0, 2, 4): 6, (0, 2, 5): 6, (0, 2, 6): 6, (0, 3, 3): 3, (0, 3, 4): 6, (0, 3, 5): 6, (0, 3, 6): 6, (0, 4, 4): 3, (0, 4, 5): 6, (0, 4, 6): 6, (0, 5, 5): 3, (0, 5, 6): 6, (0, 6, 6): 3, (1, 1, 1): 1, (1, 1, 2): 3, (1, 1, 3): 3, (1, 1, 4): 3, (1, 1, 5): 3, (1, 1, 6): 3, (1, 2, 2): 3, (1, 2, 3): 6, (1, 2, 4): 6, (1, 2, 5): 6, (1, 2, 6): 6, (1, 3, 3): 3, (1, 3, 4): 6, (1, 3, 5): 6, (1, 3, 6): 6, (1, 4, 4): 3, (1, 4, 5): 6, (1, 4, 6): 6, (1, 5, 5): 3, (1, 5, 6): 6, (1, 6, 6): 3, (2, 2, 2): 1, (2, 2, 3): 3, (2, 2, 4): 3, (2, 2, 5): 3, (2, 2, 6): 3, (2, 3, 3): 3, (2, 3, 4): 6, (2, 3, 5): 6, (2, 3, 6): 6, (2, 4, 4): 3, (2, 4, 5): 6, (2, 4, 6): 6, (2, 5, 5): 3, (2, 5, 6): 6, (2, 6, 6): 3, (3, 3, 3): 1, (3, 3, 4): 3, (3, 3, 5): 3, (3, 3, 6): 3, (3, 4, 4): 3, (3, 4, 5): 6, (3, 4, 6): 6, (3, 5, 5): 3, (3, 5, 6): 6, (3, 6, 6): 3, (4, 4, 4): 1, (4, 4, 5): 3, (4, 4, 6): 3, (4, 5, 5): 3, (4, 5, 6): 6, (4, 6, 6): 3, (5, 5, 5): 1, (5, 5, 6): 3, (5, 6, 6): 3, (6, 6, 6): 1}
If you don't want to change how you initialized it:
def flatten(alist):
for item in alist:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
dict(Counter(tuple(sorted(tup)) for tup in flatten(mylist)))
list=[(0, 1, 6), (5,1,4), (1, 6, 0), (3, 2,1),(4,5,1)]
#first please make a list of sorted tuples to ignore the sequence of numbers
sorted_list = [sorted(x) for x in list]
#then count the tuple in sorted list and ignore count for duplicated items.
list = [ { x: sorted_list.count(sorted(x)) } for i, x in enumerate(list) if sorted(x) not in sorted_list[:i]]
print(list);
import pandas as pd
import numpy as np
# make a dataframe out of the list
df = pd.DataFrame(data=ll)
# sort columns and find duplicates
df_dup = df[pd.DataFrame(np.sort(df.values), columns=df.columns, index=df.index)
.duplicated(keep=False)]
# return duplicates as dictionary of tuples
result = df_dup.T.apply(tuple).to_dict()
# return number of duplicates found
n = len(result)
Use Counter from collections:
>>> from collections import Counter
>>> list=[(0, 1, 6), (5,1,4), (1, 6, 0), (3, 2,1),(4,5,1)]
>>> dict(Counter(map(lambda x: tuple(sorted(x)),list)))
{(0, 1, 6): 2, (1, 4, 5): 2, (1, 2, 3): 1}
>>>
Or a-style-of list comprehension:
>>> from collections import Counter
>>> list=[(0, 1, 6), (5,1,4), (1, 6, 0), (3, 2,1),(4,5,1)]
>>> dict(Counter([tuple(sorted(i)) for i in list]))
{(0, 1, 6): 2, (1, 4, 5): 2, (1, 2, 3): 1}
>>>
Also, list is a replica of a python keyword, after making that a variable, you would't have any access to the keyword anymore, so it's better to rename that variable, and so rename it in my code.
Edit:
Use:
dict(Counter([tuple(sorted(x)) for i in list for x in i]))
as suggestion, not to use keyword in python as a variable, here is the keywords in python:
import keyword
keyword_list = keyword.kwlist
for your question, here is my answer :
from collections import Counter
result = dict(Counter([tuple(sorted(item)) for item in your_list ]))