Related
I would like to know how to use the python random.sample() function within a for-loop to generate multiple sample lists that are not identical.
For example, right now I have:
for i in range(3):
sample = random.sample(range(10), k=2)
This will generate 3 sample lists containing two numbers each, but I would like to make sure none of those sample lists are identical. (It is okay if there are repeating values, i.e., (2,1), (3,2), (3,7) would be okay, but (2,1), (1,2), (5,4) would not.)
If you specifically need to "use random.sample() within a for-loop", then you could keep track of samples that you've seen, and check that new ones haven't been seen yet.
import random
seen = set()
for i in range(3):
while True:
sample = random.sample(range(10), k=2)
print(f'TESTING: {sample = }') # For demo
fr = frozenset(sample)
if fr not in seen:
seen.add(fr)
break
print(sample)
Example output:
TESTING: sample = [0, 7]
[0, 7]
TESTING: sample = [0, 7]
TESTING: sample = [1, 5]
[1, 5]
TESTING: sample = [7, 0]
TESTING: sample = [3, 5]
[3, 5]
Here I made seen a set to allow fast lookups, and I converted sample to a frozenset so that order doesn't matter in comparisons. It has to be frozen because a set can't contain another set.
However, this could be very slow with different inputs, especially a larger range of i or smaller range to draw samples from. In theory, its runtime is infinite, but in practice, random's number generator is finite.
Alternatives
There are other ways to do the same thing that could be much more performant. For example, you could take a big random sample, then chunk it into the desired size:
n = 3
k = 2
upper = 10
sample = random.sample(range(upper), k=k*n)
for chunk in chunks(sample, k):
print(chunk)
Example output:
[6, 5]
[3, 0]
[1, 8]
With this approach, you'll never get any duplicate numbers like [[2,1], [3,2], [3,7]] because the sample contains all unique numbers.
This approach was inspired by Sven Marnach's answer on "Non-repetitive random number in numpy", which I coincidentally just read today.
it looks like you are trying to make a nested list of certain list items without repetition from original list, you can try below code.
import random
mylist = list(range(50))
def randomlist(mylist,k):
length = lambda : len(mylist)
newlist = []
while length() >= k:
newlist.append([mylist.pop(random.randint(0, length() - 1)) for I in range(k)])
newlist.append(mylist)
return newlist
randomlist(mylist,6)
[[2, 20, 36, 46, 14, 30],
[4, 12, 13, 3, 28, 5],
[45, 37, 18, 9, 34, 24],
[31, 48, 11, 6, 19, 17],
[40, 38, 0, 7, 22, 42],
[23, 25, 47, 41, 16, 39],
[8, 33, 10, 43, 15, 26],
[1, 49, 35, 44, 27, 21],
[29, 32]]
This should do the trick.
import random
import math
# create set to store samples
a = set()
# number of distinct elements in the population
m = 10
# sample size
k = 2
# number of samples
n = 3
# this protects against an infinite loop (see Safety Note)
if n > math.comb(m, k):
print(
f"Error: {math.comb(m, k)} is the number of {k}-combinations "
f"from a set of {m} distinct elements."
)
exit()
# the meat
while len(a) < n:
a.add(tuple(sorted(random.sample(range(m), k = k))))
print(a)
With a set you are guaranteed to get a collection with no duplicate elements. In a set, you would be allowed to have (1, 2) and (2, 1) inside, which is why sorted is applied. So if [1, 2] is drawn, sorted([1, 2]) returns [1, 2]. And if [2, 1] is subsequently drawn, sorted([2, 1]) returns [1, 2], which won't be added to the set because (1, 2) is already in the set. We use tuple because objects in a set have to be hashable and list objects are not.
I hope this helps. Any questions, please let me know.
Safety Note
To avoid an infinite loop when you change 3 to some large number, you need to know the maximum number of possible samples of the type that you desire.
The relevant mathematical concept for this is a combination.
Suppose your first argument of random.sample() is range(m) where
m is some arbitrary positive integer. Note that this means that the
sample will be drawn from a population of m distinct members
without replacement.
Suppose that you wish to have n samples of length k in total.
The number of possible k-combinations from the set of m distinct elements is
m! / (k! * (m - k)!)
You can get this value via
from math import comb
num_comb = comb(m, k)
comb(m, k) gives the number of different ways to choose k elements from m elements without repetition and without order, which is exactly what we want.
So in the example above, m = 10, k = 2, n = 3.
With these m and k, the number of possible k-combinations from the set of m distinct elements is 45.
You need to ensure that n is less than 45 if you want to use those specific m and k and avoid an infinite loop.
I find myself in a unique situation in which I need to multiply single elements within a listed pair of numbers where each pair is nested within a parent list of elements. For example, I have my pre-defined variables as:
output = []
initial_list = [[1,2],[3,4],[5,6]]
I am trying to calculate an output such that each element is the product of a unique combination (always of length len(initial_list)) of a single element from each pair. Using my example of initial_list, I am looking to generate an output of length pow(2 * len(initial_list)) that is scable for any "n" number of pairs in initial_list (with a minimum of 2 pairs). So in this case each element of the output would be as follows:
output[0] = 1 * 3 * 5
output[1] = 1 * 3 * 6
output[2] = 1 * 4 * 5
output[3] = 1 * 4 * 6
output[4] = 2 * 3 * 5
output[5] = 2 * 3 * 6
output[6] = 2 * 4 * 5
output[7] = 2 * 4 * 6
In my specific case, the order of output assignments does not matter other than output[0], which I need to be equivalent to the product of the first element in each pair in initial_list. What is the best way to proceed to generate an output list such that each element is a unique combination of every element in each list?
...
My initial approach consisted of using;
from itertools import combinations
from itertools import permutations
from itertools import product
to somehow generate a list of every possible combination then multiply the products together and append each product to the output list, but I couldn't figure out a wait to implement the tools successfully. I have since tried to create a recursive function that combines for x in range(2): with nested recursion recalls, but once again I cannot figured out a solution.
Someone more experienced and smarter than me please help me out; Any and all help is appreciated! Thank you!
Without using any external library
def multi_comb(my_list):
"""
This returns the multiplication of
every possible combinationation of
the `my_list` of type [[a1, a2], [b1, b2], ...]
Arg: List
Return: List
"""
if not my_list: return [1]
a, b = my_list.pop(0)
result = multi_comb(my_list)
left = [a * i for i in result]
right = [b * i for i in result]
return (left + right)
print(multi_comb([[1, 2], [3, 4], [5, 6]]))
# Output
# [15, 18, 20, 24, 30, 36, 40, 48]
I am using reccursion to get the result. Here's the visual illustration of how this works.
Instead of taking a top-down approach, we can take bottom-up approach to better understand how this program works.
At the last step, a and b becomes 5 and 6 respectively. Calling multi_comb() with empty list returns [1] as a result. So left and right becomes [5] and [6]. Thus we return [5, 6] to our previous step.
At the second last step, a and b was 3 and 4 respectively. From the last step we got [5, 6] as a result. After multiplying each of the values inside the result with a and b (notice left and right), we return the result [15, 18, 20, 24] to our previous step.
At our first step, that is our starting step, we had a and b as 1 and 2 respectively. The value returned from our last step becomes our result, ie, [15, 18, 20, 24]. Now we multiply both a and b with this result and return our final output.
Note:
This program works only if list is in the form [ [a1, a2], [b1, b2], [c1, c2], ... ] as told by the OP in the comments. The problem of solving the list containing the sub-list of n items will be little different in code, but the concept is same as in this answer.
This problem can also be solved using dynamic programming
output = [1, ]
for arr in initial_list:
output = [a * b for a in arr for b in product]
This problem is easy to solve if you have just one subarray -- the output is the given subarray.
Suppose you solved the problem for the first n - 1 subarrays, and you got the output. The new subarray is appended. How the output should change? The new output is all pair-wise products of the previous output and the "new" subarray.
Look closely, there's an easy pattern. Let there be n sublists, and 2 elements in each: at index 0 and 1. Now, the indexes selected can be represented as a binary string of length n.
It'll start with 0000..000, then 0000...001, 0000...010 and so on. So all you need to do is:
n = len(lst)
for i in range(2**n):
binary = bin(i)[2:] #get binary representation
for j in range(n):
if binary[j]=="1":
#include jth list's 1st index in product
else:
#include jth list's 0th index in product
The problem would a scalable solution would be, since you're generating all possible pairs, the time complexity will be O(2^N)
Your idea to use itertools.product is great!
import itertools
initial_list = [[1,2],[3,4],[5,6]]
combinations = list(itertools.product(*initial_list))
# [(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]
Now, you can get the product of each tuple in combination using for-loops, or using functools.reduce, or you can use math.prod which was introduced in python 3.8:
import itertools
import math
initial_list = [[1,2],[3,4],[5,6]]
output = [math.prod(c) for c in itertools.product(*initial_list)]
# [15, 18, 20, 24, 30, 36, 40, 48]
import itertools
import functools
import operator
initial_list = [[1,2],[3,4],[5,6]]
output = [functools.reduce(operator.mul, c) for c in itertools.product(*initial_list)]
# [15, 18, 20, 24, 30, 36, 40, 48]
import itertools
output = []
for c in itertools.product(*initial_list):
p = 1
for x in c:
p *= x
output.append(p)
# output == [15, 18, 20, 24, 30, 36, 40, 48]
Note: if you are more familiar with lambdas, operator.mul is pretty much equivalent to lambda x,y: x*y.
itertools.product and math.prod are a nice fit -
from itertools import product
from math import prod
input = [[1,2],[3,4],[5,6]]
output = [prod(x) for x in product(*input)]
print(output)
[15, 18, 20, 24, 30, 36, 40, 48]
I'm trying to write a function that will take as input a string of intervals e.g "0-0,4-8,20-21,43-45" to produce all numbers within each of the ranges meaning: [0, 4, 5, 6, 7, 8, 20, 21, 43, 44, 45]
The exercise requires to do so using generators. I managed to parse the input through a generator but I can't do the same for populating the numbers. I'm trying to int() each number so I could leverage the range() to produce all numbers within the edges.
Here's my conceptual code - how can I produce the numbers within each interval?
def parse_ranges(arg):
arg = arg.split(",")
parsed= (line.split("-") for line in arg)
#Trying to parse each character to int to use range()
intervals= (int(i) for i in number for number in parsed)
# Even if I had the characters parsed to int, I still don't know how to produce the range
ranges = (range(interval[0],interval[1]) interval for interval in intervals)
return ranges
print(list(parse_ranges("0-0,4-8,20-21,43-45")))
def parse_ranges(arg):
arg = arg.split(",")
parsed = ((line.split("-")) for line in arg)
for pair in parsed:
yield from range(int(pair[0]), int(pair[1])+1)
print(list(parse_ranges("0-0,4-8,20-21,43-45")))
Out: [0, 4, 5, 6, 7, 8, 20, 21, 43, 44, 45]
If you want to pass values from a generator INSIDE another generator directly out to the consumer of the outer generator, you need to use the "yield from" expression. (Also, note that you need to extend the "to" end of each range by +1, since the range endpoint is not inclusive.)
Two pieces that you seem to missing are: the second argument of range() needs to be one beyond what you want; you can pass control from one generator to another via yield from:
def parse_ranges(arg):
for start, stop in (interval.split('-') for interval in arg.split(',')):
yield from range(int(start), int(stop) + 1)
print(*parse_ranges("0-0,4-8,20-21,43-45"))
OUTPUT
% python3 test.py
0 4 5 6 7 8 20 21 43 44 45
%
www.codingame.com
Task
Write a program which, using a given number of strengths,
identifies the two closest strengths and shows their difference with an integer
Info
n = Number of horses
pi = strength of each horse
d = difference
1 < n < 100000
0 < pi ≤ 10000000
My code currently
def get_dif(a, b):
return abs(a - b)
horse_str = [10, 5, 15, 17, 3, 8, 11, 28, 6, 55, 7]
n = len(horse_str)
d = 10000001
for x in range(len(horse_str)):
for y in range(x, len(horse_str) - 1):
d = min([get_dif(horse_str[x], horse_str[y + 1]), d])
print(d)
Test cases
[3,5,8, 9] outputs: 1
[10, 5, 15, 17, 3, 8, 11, 28, 6, 55, 7] outputs: 1
Problem
They both work but then the next test gives me a very long list of horse strengths and i get **Process has timed out. This may mean that your solution is not optimized enough to handle some cases.
How can i optimise it? Thank you!
EDIT ONE
Default code given
import sys
import math
# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.
n = int(input())
for i in range(n):
pi = int(input())
# Write an action using print
# To debug: print("Debug messages...", file=sys.stderr)
print("answer")
Since you can use sort method (which is optimized to avoid performing a costly bubble sort or double loop by hand which has O(n**2) complexity, and times out with a very big list), let me propose something:
sort the list
compute the minimum of absolute value of difference of the adjacent values, passing a generator comprehension to the min function
The minimum has to be the abs difference of adjacent values. Since the list is sorted using a fast algorithm, the heavy lifting is done for you.
like this:
horse_str = [10, 5, 15, 17, 3, 8, 11, 28, 6, 55, 7]
sh = sorted(horse_str)
print(min(abs(sh[i]-sh[i+1]) for i in range(len(sh)-1)))
I also get 1 as a result (I hope I didn't miss anything)
Say I have an array of positive whole integers; I'd like to manipulate the order so that the concatenation of the resultant array is the largest number possible. For example [97, 9, 13] results in 99713; [9,1,95,17,5] results in 9955171. I'm not sure of an answer.
sorted(x, cmp=lambda a, b: -1 if str(b)+str(a) < str(a)+str(b) else 1)
Intuitively, we can see that a reverse sort of single digit numbers would lead to the higest number:
>>> ''.join(sorted(['1', '5', '2', '9'], reverse=True))
'9521'
so reverse sorting should work. The problem arises when there are multi-digit snippets in the input. Here, intuition again lets us order 9 before 95 and 17 before 1, but why does that work? Again, if they had been the same length, it would have been clear how to sort them:
95 < 99
96 < 97
14 < 17
The trick then, is to 'extend' shorter numbers so they can be compared with the longer ones and can be sorted automatically, lexicographically. All you need to do, really, is to repeat the snippet to beyond the maximum length:
comparing 9 and 95: compare 999 and 9595 instead and thus 999 comes first.
comparing 1 and 17: compare 111 and 1717 instead and thus 1717 comes first.
comparing 132 and 13: compare 132132 and 1313 instead and thus 132132 comes first.
comparing 23 and 2341: compare 232323 and 23412341 instead and thus 2341 comes first.
This works because python only needs to compare the two snippets until they differ somewhere; and it's (repeating) matching prefixes that we need to skip when comparing two snippets to determine which order they need to be in to form a largest number.
You only need to repeat a snippet until it is longer than the longest snippet * 2 in the input to guarantee that you can find the first non-matching digit when comparing two snippets.
You can do this with a key argument to sorted(), but you need to determine the maximum length of the snippets first. Using that length, you can 'pad' all snippets in the sort key until they are longer than that maximum length:
def largestpossible(snippets):
snippets = [str(s) for s in snippets]
mlen = max(len(s) for s in snippets) * 2 # double the length of the longest snippet
return ''.join(sorted(snippets, reverse=True, key=lambda s: s*(mlen//len(s)+1)))
where s*(mlen//len(s)+1) pads the snippet with itself to be more than mlen in length.
This gives:
>>> combos = {
... '12012011': [1201, 120, 1],
... '87887': [87, 878],
... '99713': [97, 9, 13],
... '9955171': [9, 1, 95, 17, 5],
... '99799713': [97, 9, 13, 979],
... '10100': [100, 10],
... '13213': [13, 132],
... '8788717': [87, 17, 878],
... '93621221': [936, 21, 212],
... '11101110': [1, 1101, 110],
... }
>>> def test(f):
... for k,v in combos.items():
... print '{} -> {} ({})'.format(v, f(v), 'correct' if f(v) == k else 'incorrect, should be {}'.format(k))
...
>>> test(largestpossible)
[97, 9, 13] -> 99713 (correct)
[1, 1101, 110] -> 11101110 (correct)
[936, 21, 212] -> 93621221 (correct)
[13, 132] -> 13213 (correct)
[97, 9, 13, 979] -> 99799713 (correct)
[87, 878] -> 87887 (correct)
[1201, 120, 1] -> 12012011 (correct)
[100, 10] -> 10100 (correct)
[9, 1, 95, 17, 5] -> 9955171 (correct)
[87, 17, 878] -> 8788717 (correct)
Note that this solution is a) 3 lines short and b) works on Python 3 as well without having to resort to functools.cmp_to_key() and c) does not bruteforce the solution (which is what the itertools.permutations option does).
Hint one: you concatenate strings, not integers.
Hint two: itertools.permutations().
import itertools
nums = ["9", "97", "13"]
m = max(("".join(p) for p in itertools.permutations(nums)), key = int)
You can use itertools.permutations as hinted and use the key argument of the max function (which tells which function to apply to each element in order to decide the maximum) after you concat them with the join function.
It's easier to work with strings to begin with.
I don't like the brute force approach to this. It requires a massive amount of computation for large sets.
You can write your own comparison function for the sorted builtin method, which will return a sorting parameter for any pair, based on any logic you put in the function.
Sample code:
def compareInts(a,b):
# create string representations
sa = str(a)
sb = str(b)
# compare character by character, left to right
# up to first inequality
# if you hit the end of one str before the other,
# and all is equal up til then, continue to next step
for i in xrange(min(len(sa), len(sb))):
if sa[i] > sb[i]:
return 1
elif sa[i] < sb[i]:
return -1
# if we got here, they are both identical up to the length of the shorter
# one.
# this means we need to compare the shorter number again to the
# remainder of the longer
# at this point we need to know which is shorter
if len(sa) > len(sb): # sa is longer, so slice it
return compareInts(sa[len(sb):], sb)
elif len(sa) < len(sb): # sb is longer, slice it
return compareInts(sa, sb[len(sa):])
else:
# both are the same length, and therefore equal, return 0
return 0
def NumberFromList(numlist):
return int(''.join('{}'.format(n) for n in numlist))
nums = [97, 9, 13, 979]
sortednums = sorted(nums, cmp = compareInts, reverse = True)
print nums # [97, 9, 13, 979]
print sortednums # [9, 979, 97, 13]
print NumberFromList(sortednums) # 99799713
Well, there's always the brute force approach...
from itertools import permutations
lst = [9, 1, 95, 17, 5]
max(int(''.join(str(x) for x in y)) for y in permutations(lst))
=> 9955171
Or this, an adaptation of #Zah's answer that receives a list of integers and returns an integer, as specified in the question:
int(max((''.join(y) for y in permutations(str(x) for x in lst)), key=int))
=> 9955171
You can do this with some clever sorting.
If two strings are the same length, choose the larger of the two to come first. Easy.
If they're not the same length, figure out what would be the result if the best possible combination were appended to the shorter one. Since everything that follows the shorter one must be equal to or less than it, you can determine this by appending the short one to itself until it's the same size as the longer one. Once they're the same length you do a direct comparison as before.
If the second comparison is equal, you've proven that the shorter string can't possibly be better than the longer one. Depending on what it's paired with it could still come out worse, so the longer one should come first.
def compare(s1, s2):
if len(s1) == len(s2):
return -1 if s1 > s2 else int(s2 > s1)
s1x, s2x = s1, s2
m = max(len(s1), len(s2))
while len(s1x) < m:
s1x = s1x + s1
s1x = s1x[:m]
while len(s2x) < m:
s2x = s2x + s2
s2x = s2x[:m]
return -1 if s1x > s2x or (s1x == s2x and len(s1) > len(s2)) else 1
def solve_puzzle(seq):
return ''.join(sorted([str(x) for x in seq], cmp=compare))
>>> solve_puzzle([9, 1, 95, 17, 5])
'9955171'
>>> solve_puzzle([97, 9, 13])
'99713'
>>> solve_puzzle([936, 21, 212])
'93621221'
>>> solve_puzzle([87, 17, 878])
'8788717'
>>> solve_puzzle([97, 9, 13, 979])
'99799713'
This should be much more efficient than running through all the permutations.
import itertools
def largestInt(a):
b = list(itertools.permutations(a))
c = []
x = ""
for i in xrange(len(b)):
c.append(x.join(map(str, b[i])))
return max(c)