I'm looking for a way to randomly sample a fixed length subset of all permutations.
import itertools
from random import shuffle
mylist = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T']
APPROACH A
Approach A below suffers from the problem that the permutations are too similar.
a_pre = itertools.permutations(mylist,20)
a = itertools.islice(a_pre,3)
list(a)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T']
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'T', 'S']
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'S', 'R', 'T']
APPROACH B
Approach B gets me closer to my desired outcome, but here there's always a risk of producing identical ordering between lists, so this approach is not feasible.
#repeat n=3 times
shuffle(mylist)
print(mylist)
['J', 'B', 'M', 'A', 'O', 'C', 'K', 'S', 'H', 'Q', 'N', 'T', 'R', 'D', 'G', 'P', 'I', 'E', 'F', 'L']
['R', 'O', 'C', 'I', 'G', 'E', 'Q', 'L', 'P', 'J', 'F', 'N', 'A', 'B', 'H', 'T', 'D', 'K', 'M', 'S']
['L', 'O', 'I', 'G', 'B', 'E', 'R', 'A', 'D', 'N', 'J', 'S', 'H', 'F', 'K', 'M', 'Q', 'T', 'C', 'P']
but here there's always a risk of producing identical ordering between lists, so this approach is not feasible.
You can use tuples (since lists aren't hashable) and sets (so that there are no duplicates/identical lists) to get around this:
from random import shuffle
mylist = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T']
myset = set()
while len(myset) < 5: #change 5 to however many you want
shuffle(mylist)
myset.add(tuple(mylist))
print([list(x) for x in myset])
Edit: As #tobias_k points out:
For the given list, there are 20! = 2432902008176640000 different permutations, so collisions are really very unlikely.
Consider the itertools random_permutation recipe:
From the docs:
def random_permutation(iterable, r=None):
"Random selection from itertools.permutations(iterable, r)"
pool = tuple(iterable)
r = len(pool) if r is None else r
return tuple(random.sample(pool, r))
Code
import string
import more_itertools as mit
iterable = string.ascii_uppercase[:-6]
[random_permutation(iterable) for _ in range(3)]
Output
[('M', 'K', 'Q', 'A', 'I', 'J', 'H', 'T', 'C', 'E', 'P', 'L', 'B', 'N', 'G', 'F', 'S', 'D', 'O', 'R'),
('A', 'G', 'I', 'S', 'E', 'T', 'B', 'Q', 'D', 'M', 'C', 'O', 'J', 'H', 'N', 'F', 'K', 'P', 'R', 'L'),
('C', 'S', 'O', 'H', 'I', 'K', 'A', 'G', 'D', 'B', 'R', 'E', 'L', 'T', 'M', 'N', 'F', 'P', 'Q', 'J')]
more_itertools is a third-party library that implements this recipe for you.
you could use this to generate the number-th lexicographic perutation of N elements:
def permutation_from_int(N, number):
'''
get the number-th lexicographic permutation of length N.
N: the length of the permutation
0 <= number <= factorial(N) -1: the number of the desired
permutation
'''
# assert 0 <= number < factorial(N)
ret = [None] * N
select = list(range(N))
for i in range(N - 1, -1, -1):
index, number = divmod(number, factorial(i))
element = select[index]
ret[N - 1 - i] = element
select.remove(element)
return ret
then you just have to generate (and keep a set of - if you want to avoid duplicates) random integers that represent the permutations:
N_TESTS = 17
strg = 'ABCD'
N = len(strg)
N_MAX = factorial(N)
seen = set()
for _ in range(N_TESTS):
number = randrange(N_MAX)
while number in seen:
number = randrange(N_MAX)
seen.add(number)
perm = permutation_from_int(N, number)
print(''.join(strg[i] for i in perm))
note that this may loop forever if the number of test is bigger that the space of all the permutations...
which prints (e.g.):
DACB
DBCA
BADC
BDCA
DCAB
DABC
CADB
DBAC
DCBA
...
but as mentioned in the other answers: if you have a permutation of 20 elements the chance of hitting a repeated permutation is very small!
I think your question is a special case of one I had, for k=N
Based on this, the two solutions stated there should apply. The first one is a tad slow :)
So the random sampling one (Which you also hint at your question, just discard duplicates...) seems to be the only answer for now.
It would be very interesting to see if there is a generative solution to either this question, or the more general one...
Here's the code based on that answer:
import itertools
from random import shuffle
mylist = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T']
n=len(mylist)
k = n
m = 5
samples = set()
tries = 0
while len(samples) < m:
samples.add(tuple(random.sample(mylist,k)))
print (len(samples))
print(samples)
print(tries)
Related
A group of lists of equal length (strings as elements). I want to find out their commonality/characteristics.
Let’s call them “good” lists - I want to find out what makes a “good” list.
A thinking is to output all 3-element combination from each list, then summarize the occurrences of the 3-element to rank them.
For example, “D” and “N” and “T” appeared 4 times. It may conclude that, when “D” and “N” and “T” appear in a list, it is a “good” list.
The same method can apply to 4-element combinations, 5-element combinations (when the many lists are very long).
What would be a better solution?
import itertools
from itertools import combinations
from collections import Counter
s = [
['O', 'V', 'R', 'M', 'Y'],
['I', 'Q', 'L', 'J', 'A'],
['M', 'I', 'Q', 'N', 'G'],
['Y', 'M', 'R', 'Q', 'Z'],
['D', 'X', 'C', 'Q', 'N'],
['B', 'O', 'Q', 'E', 'V'],
['V', 'M', 'J', 'G', 'R'],
['M', 'T', 'L', 'I', 'Z'],
['Y', 'H', 'A', 'V', 'L'],
['O', 'T', 'D', 'N', 'E'],
['D', 'N', 'T', 'I', 'G'],
['T', 'Q', 'H', 'I', 'P'],
['F', 'T', 'D', 'W', 'N'],
['F', 'Z', 'H', 'E', 'X'],
['E', 'Z', 'R', 'K', 'J'],
['P', 'C', 'U', 'D', 'F'],
['N', 'I', 'Y', 'U', 'E'],
['T', 'N', 'D', 'L', 'V'],
['D', 'Z', 'I', 'P', 'X'],
['H', 'L', 'C', 'P', 'Y']]
summary = []
for each in s:
all_combinations = [comb for comb in combinations(each, 3)] # unique combinations only
for a in all_combinations:
summary.append('-'.join(sorted(a)))
print (Counter(summary))
Output:
Counter({'D-N-T': 4, 'M-R-V': 2, 'M-R-Y': 2.....})
I think I would make use of an sql database to do this (sqlite in the examples below). You could create a table with two columns element and list (both primary keys). You would populate it with the elements from all lists like this:
element | list
-----------------
O 1
V 1
R 1
M 1
Y 1
I 2
Q 2
L 2
J 2
A 2
...
You could run a query like the following to figure out which elements appear how many times in the lists:
SELECT element, count(element) as cnt FROM table, (SELECT distinct(element) as dst from table) as table2
WHERE element = table2.dst
GROUP BY element
ORDER BY cnt DESC
You can also run a query to find all lists that contain your elements of choice. Here is the query to find all lists that contain D, N and T:
SELECT COUNT(element), list FROM table WHERE element IN ('D', 'N', 'T')
GROUP BY list
HAVING COUNT(element) = 3;
Hi I am a newbie in python and there is a variable that I would like to carry over outside a for loop, the code is below:
positive_slopes=[[rows[p - q][q]
for q in range(max(p-column_length+1,0), min(p+1, row_length))]
for p in range(column_length + row_length - 1)]
In this case I would like to get all values of [p-q] and [q] but I don't really know how to manipulate this code,it essentially breaks when I try to change something.
I tried using a normal for loop but does not give the same result as the code above.The for loop code I tried to use is:
for p in range(column_length + row_length - 1):
for q in range(max(p-column_length+1,0), min(p+1, row_length)):
positive_slopes.append(rows[p - q][q])
This does not quite give the same results with the list comprehension code. This gives an output of:
['e', 'a', 'a', 'g', 'l', 'h', 'r', 'o', 'l', 'a', 'i', 'h', 'h', 'e', 'e', 'i', 'a', 'e', 'l', 'h', 'h', 'h', 'i', 'y', 'l', 'o', 'o', 'h', 'l', 'a', 'o', 'l', 'a', 'h', 'h', 'e', 'y', 'o', 'u', 'a', 'l', 'i', 'y', 'l', 'a', 'u', 'l', 'h', 'o']
The list comprehension gives me this:
[['e'], ['a', 'a'], ['g', 'l', 'h'], ['r', 'o', 'l', 'a'], ['i', 'h', 'h', 'e', 'e'], ['i', 'a', 'e', 'l', 'h', 'h'], ['h', 'i', 'y', 'l', 'o', 'o', 'h'], ['l', 'a', 'o', 'l', 'a', 'h'], ['h', 'e', 'y', 'o', 'u'], ['a', 'l', 'i', 'y'], ['l', 'a', 'u'], ['l', 'h'], ['o']]
The elements of both outputs are the same however I would like the for loop to group the letters like the list comprehension did. What am I missing in my for loop to give me the same output. Any help would be gladly appreciated!
Your edited code should be something like:
for p in range(column_length + row_length - 1):
tempList = []
for q in range(max(p-column_length+1,0), min(p+1, row_length)):
tempList.append(rows[p - q][q])
positive_slopes.append(tempList)
I am trying to make a small program that will jumble up the letters of the alphabet(In simple terms)
I have tried to use things like list.pop() or list.remove(), But those did nothing
import random
def rand_let():
i = 26
alphabet = str('')
for a in range(1, 26):
key = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
print(len(key))
print(key)
letter = random.randint(1, i)
print(key[letter])
letters = key[letter]
alphabet += str(letters)
key.remove(letter)
i -= 1
rand_let()
I want it to jumble up the alphabet,
it is, but the way it is doing it will make letters repeat(I don't want it to repeat)
The shuffle function from random will save you many lines, and does what you're looking for:
import random
alphabet = ['A', 'B', 'C']
random.shuffle(alphabet)
print(alphabet)
#Ex: ['C', 'A', 'B']
The reason you are getting duplicates is that although you have code to remove the letter from the key list, the line that declares the key list is within the loop as well. Try moving the line
key = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
above the for loop. Or, as suggested in other answers, use a library to do this for you.
import random
import string
alphabet = [letter for letter in string.ascii_uppercase]
random.shuffle(alphabet)
print(alphabet)
This will just shuffle the list and then print the elements of the list one by one for each iteration of the for loop.
import random
key = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
random.shuffle(key)
def choice(x):
for letter in key:
print()
choice(key)
Here I have a word list as:
[['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
And I have to display all the palindromes in this list which are in rows as well as columns.
I have coded to find all the palindromes in the rows. But cannot implement a method to find the palindromes in the columns.
Here is my code so far:
result_1=""
if len(palindrome)==len_line_str:
for row in range(len(palindrome)):
for horizontal_line in range(len(palindrome[row])):
if ''.join(palindrome[row])==''.join(reversed(palindrome[row])):
result_1=''.join(palindrome[row])+" is a palindrome starting at ["+str(row)+"]["+str(row)+"] and is a row in the table"
print(result_1)
Which will display the output:
rotor is a palindrome starting at [0][0] and is a row in the table
Where "rotor" is a palindrome.
I need a method to get the palindromes in the columns which are:
"refer", "tenet", "radar"
Any help is much appreciated. Thanks in advance!
You can use zip to transpose your lists:
>>> t = [['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
[['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
>>> list(zip(*t))
[('r', 'e', 'f', 'e', 'r'), ('o', 'v', 'i', 'n', 'a'), ('t', 'e', 'n', 'e', 't'), ('o', 'i', 'e', 't', 'e'), ('r', 'a', 'd', 'a', 'r')]
Your columns are now rows, and you can apply the same method than before. If you just need the words, you can use list comprehensions:
>>> rows = [['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
>>> [''.join(row) for row in rows if row[::-1] == row ]
['rotor']
>>> [''.join(column) for column in zip(*rows) if column[::-1] == column ]
['refer', 'tenet', 'radar']
This will do the job:
palindrome=[['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
n=len(palindrome)
for col in range(len(palindrome[0])):
col_word=[palindrome[i][col] for i in range(n)]
if ''.join(col_word)==''.join(reversed(col_word)):
result=''.join(col_word)+" is a palindrome starting at ["+str(col)+"] and is a col in the table"
print(result)
This prints
refer is a palindrome starting at [0] and is a col in the table
tenet is a palindrome starting at [2] and is a col in the table
radar is a palindrome starting at [4] and is a col in the table
Basically, in order to access the words in the column, you can do
col_word=[palindrome[i][col] for i in range(n)]
This fixes the column and iterates over the rows. The rest of the code is structures similarly to yours.
I saw you did not want to use Zip (which I would recommend using):
Alternative answer:
list_ = [['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
You can get the palindromes (rows) by checking each list with the reversed list [::-1]:
[i==i[::-1] for i in list_]
# prints [True, False, False, False, False]
And get the palindromes (columns) by 1. create the column list (called list_2 below) with a list comprehension and 2. same principle as above:
list_2 = [[i[ind] for i in list_] for ind in range(len(list_))]
[i==i[::-1] for i in list_2]
# prints [True, False, True, False, True]
Update
If you want the answers directly you can do:
[i for i in list_ if i==i[::-1]]
# prints [['r', 'o', 't', 'o', 'r']]
# and list_2: [['r', 'e', 'f', 'e', 'r'],['t', 'e', 'n', 'e', 't'],['r', 'a', 'd', 'a', 'r']]
There are a lot of ways to do it. I will take as example your code because of your effort on it
Another alternative following your code, is creating the columns in another list and check wich of them are palindromes:
palindrome = [['r', 'o', 't', 'o', 'r'],
['e', 'v', 'e', 'i', 'a'],
['f', 'i', 'n', 'e', 'd'],
['e', 'n', 'e', 't', 'a'],
['r', 'a', 't', 'e', 'r']]
len_line_str = 5
result_1=""
def is_pal(string):
return string == reversed(string)
colums = []
if len(palindrome)==len_line_str:
for row in range(len(palindrome)):
vertical = []
if ''.join(palindrome[row])==''.join(reversed(palindrome[row])):
result_1+=''.join(palindrome[row])+" is a palindrome starting at ["+str(0)+"]["+str(row)+"] and is a row in the table. " + "\n"
for horizontal_line in range(len(palindrome[row])):
if(len_line_str-1 > horizontal_line):
vertical += [palindrome[horizontal_line][row]]
else:
vertical += [palindrome[horizontal_line][row]]
colums += [(vertical,row)]
for word in colums:
if ''.join(word[0])==''.join(reversed(word[0])):
result_1+=''.join(word[0])+" is a palindrome starting at ["+str(0)+"]["+str(word[1])+"] and is a column in the table" + "\n"
print(result_1)
This should work. First loop iterates through the list s and the second loop iterates through each list.
Assuming s is the name of the list- [['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
for i in xrange(0,len(s),1):
str = ""
for j in s:
str = str + j[i]
print str
if str == str[::-1]:
print str," is a pallindrome - column", i
else:
print str," is not a pallindrome - column", i
There is no column wise traversal in Python. One hacky way you can follow is to perform transpose operation on your input matrix. Below is a simple way to implement transpose using list comprehensions.
def transpose(matrix):
if not matrix:
return []
return [[row[i] for row in matrix] for i in range(len(matrix[0]))]
Your same logic should work once modify your input using transpose.
Hope this helps!!
I was trying a sample exercise on regexes. To find all the letters of the alphabets. Sort the array, and finally eliminate all repetitions.
>>> letterRegex = re.compile(r'[a-z]')
>>> alphabets = letterRegex.findall("The quick brown fox jumped over the lazy dog")
>>> alphabets.sort()
>>> alphabets
['a', 'b', 'c', 'd', 'd', 'e', 'e', 'e', 'e', 'f', 'g', 'h', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'o', 'o', 'o', 'p', 'q', 'r', 'r', 't', 'u', 'u', 'v', 'w', 'x', 'y', 'z']
After doing the sort I tried to make a loop that'll eliminate all repetitions in the array.
e.g [...'e', 'e'...]
So I did this
>>> i, j = -1,0
>>> for items in range(len(alphabets)):
if alphabets[i+1] == alphabets[j+1]:
alphabets.remove(alphabets[j])
However it didn't work. How can I remove repetitons?
Here's a much easier way of removing co-occurrences:
import itertools
L = ['a', 'b', 'c', 'd', 'd', 'e', 'e', 'e', 'e', 'f', 'g', 'h', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'o', 'o', 'o', 'p', 'q', 'r', 'r', 't', 'u', 'u', 'v', 'w', 'x', 'y', 'z']
answer = []
for k,_group in itertools.groupby(L):
answer.append(k)
Or simpler still:
answer = [k for k,_g in itertools.groupby(L)]
Both yield this:
In [42]: print(answer)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 't', 'u', 'v', 'w', 'x', 'y', 'z']