So in python, I have a list like so
['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']
I was wondering if it is possible for me to randomize it so that there's no way for the values that share the same number (i.e. 1a and 1b) to be beside each other after I randomize them.
So for example the final list would come out like something like this:
['1a', '3b', '4b', '2a', '3a', '6a', '5a', '1b', '5b', '4a', '6b', '2b']
Thank you.
I don't know if this is the best approach but it does what you want:
from random import shuffle
unsorted_ls = ['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']
while True:
shuffle(unsorted_ls)
checker = False
for i in range(1, len(unsorted_ls)):
if unsorted_ls[i - 1][0] == unsorted_ls[i][0]:
checker = True
break
if checker == False:
break
Yet whenever you run this code you will get different results.
One way about this would be to construct a new table-like dictionary that excludes the 'similar candidate' for each item:
{'1a': ['2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b'],
'1b': ['2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b'],
'2a': ['1a', '1b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b'],
'2b': ['1a', '1b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b'],
'3a': ['1a', '1b', '2a', '2b', '4a', '4b', '5a', '5b', '6a', '6b'],
'3b': ['1a', '1b', '2a', '2b', '4a', '4b', '5a', '5b', '6a', '6b'],
'4a': ['1a', '1b', '2a', '2b', '3a', '3b', '5a', '5b', '6a', '6b'],
'4b': ['1a', '1b', '2a', '2b', '3a', '3b', '5a', '5b', '6a', '6b'],
'5a': ['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '6a', '6b'],
'5b': ['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '6a', '6b'],
'6a': ['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b'],
'6b': ['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b']}
Call this object y. You can construct it like so:
x = ['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']
y = {}
for a in x:
y[a] = [i for i in x if not i.startswith(a[0])]
You can then pick from the values of each element based on the last-seen element:
import random
len_new = 10 # Desired length of new list
new = []
last_val = random.choice(list(y)) # Initial pick
for _ in range(len_new):
last_val = random.choice(y[last_val])
new.append(last_val)
Result:
>>> print(new)
['6b', '3b', '2a', '3a', '4a', '2a', '6b', '5a', '4b', '1b']
Downsides:
Memory inefficiency. For a very large x, your y "table" becomes large very quickly. But for small inputs such as yours, this is not an issue. You could cut down on this by not constructing the full y up front, but rather creating just the needed table row at each iteration.
Try this:
from random import shuffle
def randomize(list_):
shuffle(list_)
for n, n_plus_1 in zip(list_, list_[1:]):
if n[0] == n_plus_1[0]:
return randomize(list_)
return list_
list_ = ['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']
print(randomize(list_))
output:
['2b', '1a', '4b', '3b', '5a', '3a', '6a', '2a', '6b', '4a', '1b', '5b']
The output will obviously be different every time, but adjacent items will never have the same leading number.
If there is a high percentage of items that have the same prefix, it would be preferable to use a backtracking approach. Note that a recursive approach is not recommended for this as it would quickly hit recursion depth limits for lists that have a few hundred items
from random import choice
def shuffle(A):
result = []
remaining = A.copy()
while len(result)<len(A):
eligible = [v for v in remaining if not result or v[0]!=result[-1][0]]
if eligible: # pick from eligibles
selected = choice(eligible)
remaining.remove(selected)
result.append(selected)
else:
remaining.append(result.pop(-1)) # backtrack
return result
A = ['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']
print(shuffle(A))
# ['5b', '6b', '3b', '6a', '2b', '1a', '2a', '4b', '3a', '4a', '5a', '1b']
On the other hand, if there are few common prefixes or the list is small, then a simpler trial-and-error approach on the whole list may be sufficient:
from random import sample
def shuffle(A):
while True:
result = sample(A,len(A))
if all(a[0]!=b[0] for a,b in zip(result,result[1:])):
return result
print(shuffle(A))
# ['6b', '2b', '6a', '3a', '4b', '1b', '5a', '1a', '2a', '3b', '4a', '5b']
In both cases, if it is not possible to obtain a permutation that meets the condition, the functions will never return (e.g. A = ['1a','1b']) so you may want to add a validation for that.
from collections import Counter
def shuffle(A):
maxFreq = max(Counter(a[0] for a in A).values())
if maxFreq*2-1>len(A): return
...
Related
import itertools
deck = ['AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', '10D', 'JD', 'QD', 'KD',
'AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', '10C', 'JC', 'QC', 'KC',
'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', '10H', 'JH', 'QH', 'KH',
'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', '10S', 'JS', 'QS', 'KS']
combinations = list(itertools.combinations(deck, 9))
i try to find all this combinations then i will load this combinations in a csv file but kaggle gives me this error message:
Your notebook tried to allocate more memory than is available. It has restarted.
Don't use a list, just write line after line.
itertools.combinations create an iterator that allows you to iterate over each value without having to create a list and store each value in memory.
You can use the csv module to write each combination as a line.
If you don't want an empty line between each combination, don't forget to use the newline='' in open: https://stackoverflow.com/a/3348664/6251742.
import csv
import itertools
deck = ['AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', '10D', 'JD', 'QD', 'KD',
'AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', '10C', 'JC', 'QC', 'KC',
'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', '10H', 'JH', 'QH', 'KH',
'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', '10S', 'JS', 'QS', 'KS']
combinations = itertools.combinations(deck, 9)
with open('combinations.csv', 'w', newline='') as file:
writer = csv.writer(file, delimiter=',')
for combination in combinations:
writer.writerow(combination)
Result after some time:
AD,2D,3D,4D,5D,6D,7D,8D,9D
AD,2D,3D,4D,5D,6D,7D,8D,10D
AD,2D,3D,4D,5D,6D,7D,8D,JD
AD,2D,3D,4D,5D,6D,7D,8D,QD
AD,2D,3D,4D,5D,6D,7D,8D,KD
... # 3679075395 more lines, 98.3 GB
I recently had a live coding interview, and was asked to solve a relatively simple problem:
Given a list of two-character strings in an arbitrary order, write a function that returns a list of the same strings, sorted first by strings containing both alpha and numeric characters in ascending order, followed by numerical-only strings in ascending order.
I was able to solve this fairly quickly with the below:
polelist = ['13', '2', '20', '3', '30', '1a', '1b', '1', '3c', '2a', 'a1', '2b', '10', 'aa']
def sortpoles(poles):
alphapoles = []
numpoles = []
for item in poles:
if item.isnumeric():
numpoles.append(item)
else:
alphapoles.append(item)
numpoles = [int(x) for x in numpoles]
numpoles.sort()
numpoles = [str(x) for x in numpoles]
alphapoles.sort()
alphapoles.extend(numpoles)
return alphapoles
This returns: ['1a', '1b', '2a', '2b', '3c', 'a1', 'aa', '1', '2', '3', '10', '13', '20', '30'] which is the correct answer.
With the remaining time, they asked me if I could find a more efficient way to do this. I know that both sort() and sorted() can accept a "key" argument with a function for custom sort criteria, but I wasn't able to figure out the logic to accomplish this. I've been trying to solve this for my own edification for the last couple hours but I'm stumped. Is this even the right approach for improving the efficiency of my solution? Is there a better way I'm not thinking of?
Not the cleanest solution, but does the job.
>>> polelist.sort(key = lambda x : (x.isnumeric(), len(x)))
['1a', '1b', '2a', '2b', '3c', 'a1', 'aa', '1', '2', '3', '10', '13', '20', '30']
The logic is to sort first by bool (is numeric or not), and the by the length of the string as larger numbers --> larger length and numbers of the same length are intrinsically sorted.
This is a clean solution:
polelist = ['13', '2', '20', '3', '30', '1a', '1b', '1', '3c', '2a', 'a1', '2b', '10', 'aa']
result = sorted(polelist, key=lambda x: (x.isnumeric(), int(x) if x.isnumeric() else x))
print(result)
It's similar to what I just noticed #GIOVANNIQUINONESVALDEZ posted, but since your description didn't mention negative numbers being excluded, I wouldn't want to rely on length.
Output:
['1a', '1b', '2a', '2b', '3c', 'a1', 'aa', '1', '2', '3', '10', '13', '20', '30']
This is more efficient not because of its brevity (although it's also shorter and , in my opinion, more readable), but because it avoids needlessly copying the lists and later recombining them.
Also, sorted ensures each key is only computed once, when needed, so there's no losses there.
Your code is already NlogN and there is technically no more efficient way, but coding can be shorten
print(list(sorted(polelist,key=lambda x:(x.isnumeric(),int(x) if x.isnumeric() else x))))
so im using rstrip to get rid of new line identifier at end of end of dictionary entries
Here's dictionary I am working with:
{'100': ['Smith', 'James', '66', '150\n'], '101': ['Jones', 'Linda', '62', '124\n'], '102': ['Davis', 'David', '68', '180\n'], '103': ['Miller', 'Sandra', '65', '90\n'], '104': ['Taylor', 'Paul', '72', '150\n']}
notice \n at the end of each
So to get rid of this I create a list of only the keys. then use of for loop to ammend each entry.
keys = [k for k in dictionary]
for key in keys:
dictionary[key][3] = dictionary[key][3].rstrip('\n')
However when I do this the output gets rid of the first \n only for key 100, but the rest stay. Here's what the new dictionary looks like:
{'100': ['Smith', 'James', '66', '150'], '101': ['Jones', 'Linda', '62', '124\n'], '102': ['Davis', 'David', '68', '180\n'], '103': ['Miller', 'Sandra', '65', '90\n'], '104': ['Taylor', 'Paul', '72', '150\n']}
Process finished with exit code 0
the first entry is fixed perfectly but the rest stay the same. I cant figure out why the for loop doesnt run through all the keys, but only the first one :(
Using dict.items() will make your life easy while iterating through it. But here since you only need to update values, you can use dict.values() to iterate over values
Also use index -1 to get the last element in the list
dictionary = {'100': ['Smith', 'James', '66', '150\n'], '101': ['Jones', 'Linda', '62', '124\n'], '102': ['Davis', 'David', '68', '180\n'], '103': ['Miller', 'Sandra', '65', '90\n'], '104': ['Taylor', 'Paul', '72', '150\n']}
for value in dictionary.values():
value[-1] = value[-1].rstrip('\n')
print(dictionary)
#{'100': ['Smith', 'James', '66', '150'],
#'101': ['Jones', 'Linda', '62', '124'],
#'102': ['Davis', 'David', '68', '180'],
#'103': ['Miller', 'Sandra', '65', '90'],
#'104': ['Taylor', 'Paul', '72', '150']}
While we're all suggesting different ways to do this, note that you don't actually need to use the keys at all. The values are lists, so they're mutable. Thus, here's another approach:
dictionary = {'100': ['Smith', 'James', '66', '150\n'], '101': ['Jones', 'Linda', '62', '124\n'], '102': ['Davis', 'David', '68', '180\n'], '103': ['Miller', 'Sandra', '65', '90\n'], '104': ['Taylor', 'Paul', '72', '150\n']}
for value in dictionary.values():
value[-1] = value[-1].rstrip("\n")
dictionary
# {'100': ['Smith', 'James', '66', '150'],
# '101': ['Jones', 'Linda', '62', '124'],
# '102': ['Davis', 'David', '68', '180'],
# '103': ['Miller', 'Sandra', '65', '90'],
# '104': ['Taylor', 'Paul', '72', '150']}
I have a list:
['8C', '2C', 'QC', '5C', '7C', '3C', '6D', 'TD', 'TH', 'AS',
'QS', 'TS', 'JS', 'KS']
I need to get a dictionary something like this: (sorting is not important)
{'C': ['QC', '8C', '7C', '5C', '3C', '2C'],
'S': ['AS', 'KS', 'QS', 'JS', 'TS']
}
code:
def parse_flush(cards):
cards = sort_by_color(cards)
flush_dic = {}
print str(cards)
count = 0
pos = 0
last_index = 0
for color in colors:
for i, card in enumerate(cards):
if card[1] == color:
count += 1
last_index = i+1
if count == 1:
pos = i
if count >= 5:
flush_dic[color] = sort_high_to_low(cards[pos:last_index])
count = 0
return flush_dic
my code now looks like, it works but I do not like its length it is possible to make it shorter using python tricks?
You can use simple collections.defaultdict to get the results you wanted
from collections import defaultdict
result = defaultdict(list)
for item in data:
result[item[1]].append(item)
print result
Output
{'S': ['AS', 'QS', 'TS', 'JS', 'KS'],
'H': ['TH'],
'C': ['8C', '2C', 'QC', '5C', '7C', '3C'],
'D': ['6D', 'TD']}
You can solve this, using itertools.groupby as well
data = ['8C', '2C', 'QC', '5C', '7C', '3C', '6D', 'TD', 'TH', 'AS', 'QS',
'TS', 'JS', 'KS']
from itertools import groupby
from operator import itemgetter
keyFn = itemgetter(1)
print {k:list(grp) for k, grp in groupby(sorted(data, key = keyFn), keyFn)}
Explanation
sorted returns a sorted list of items, and it uses keyFn for sorting the data.
groupby accepts a sorted list and it groups the items based on the keyFn, in this case keyFn returns the second elements for each and every items and the result is as seen in the output.
Use a very simple for loop:
>>> l = ['8C', '2C', 'QC', '5C', '7C', '3C', '6D', 'TD', 'TH', 'AS',
... 'QS', 'TS', 'JS', 'KS']
>>> my_dict = {}
>>> for x in l:
... my_dict.setdefault(x[-1],[]).append(x)
...
>>> my_dict
{'S': ['AS', 'QS', 'TS', 'JS', 'KS'], 'H': ['TH'], 'C': ['8C', '2C', 'QC', '5C', '7C', '3C'], 'D': ['6D', 'TD']}
data = ['8C', '2C', 'QC', '5C', '7C', '3C', '6D', 'TD', 'TH', 'AS', 'QS',
'TS', 'JS', 'KS']
dic = {}
for i in data:
try:
dic[i[1]].append(i)
except:
dic[i[1]] = []
dic[i[1]].append(i)
print dic
Output
{'S': ['AS', 'QS', 'TS', 'JS', 'KS'],
'H': ['TH'],
'C': ['8C', '2C', 'QC', '5C', '7C', '3C'],
'D': ['6D', 'TD']}
first=[1,2,3,4,5]
second=['a','b','c','d','e']
final=['1a','2a','3a','1b','2b',3b','1c','2c','3c']
I want to combine two lists in python but I don't care about order. Aka I don't want '1a' and 'a1'.
>>> import itertools
>>> first=[1,2,3,4,5]
>>> second=['a','b','c','d','e']
>>> final = [''.join(str(i) for i in s) for s in itertools.product(first, second)]
>>> final
['1a', '1b', '1c', '1d', '1e', '2a', '2b', '2c', '2d', '2e', '3a', '3b', '3c', '3d', '3e', '4a', '4b', '4c', '4d', '4e', '5a', '5b', '5c', '5d', '5e']
A simple list comprehension will work.
print([str(first[i])+second[i] for i in range(len(first))])
final = list()
for i in first:
for j in second:
final.append(str(i)+j)
If you've only got two sequences to "multiply" like this, and your iteration is dead-simple, a nested loop in a comprehension is perfectly readable:
['{}{}'.format(a, b) for a in first for b in second]
If you have a longer, or dynamic, list, you want itertools.product, as in inspectorG4dget's answer.
If you have anything more complicated than just iterating over the product, you probably want explicit loop statements rather than a comprehension (or maybe factor part of it out into a generator function and use that with the nested comp or product call).
One way without using itertools, map or zip is:
first = [1, 2, 3, 4, 5]
second = ['a', 'b', 'c', 'd', 'e']
print [str(i) + j for i in first for j in second]
Output:
['1a', '1b', '1c', '1d', '1e', '2a', '2b', '2c', '2d', '2e', '3a', '3b', '3c', '3d', '3e', '4a', '4b', '4c', '4d', '4e', '5a', '5b', '5c', '5d', '5e']