Related
What would be the cleanest way to generate random numbers from 0 to 50, of size 1000, with the condition that no number should have the same number of occurrence as any other number using python and numpy.
Example for size 10: [0, 0, 0, 1, 1, 3, 3, 3, 3, 2] --> no number occurs same number of times
Drawing from a rng.dirichlet distribution and rejecting samples guarantees to obey the requirements, but with low entropy for the number of unique elements. You have to adjust the range of unique elements yourself with np.ones(rng.integers(min,max)). If max approaches the maximum number of unique elements (here 50) rejection might take long or has no solution, causing an infinite loop. The code is for a resulting array of size of 100.
import numpy as np
times = np.array([])
rng = np.random.default_rng()
#rejection sampling
while times.sum() != 100 or len(times) != len(np.unique(times)):
times = np.around(rng.dirichlet(np.ones(rng.integers(5,10)))*100)
nr = rng.permutation(np.arange(51))[:len(times)]
np.repeat(nr, times.astype(int))
Random output
array([ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 33, 33, 33,
33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 25, 5, 5, 5])
Here's a recursive and possibly very slow implementation that produces the output desired.
import numpy as np
def get_sequence_lengths(values, total):
if total == 0:
return [[]], True
if total < 0:
return [], False
if len(values) == 0:
return [], False
sequences = []
result = False
for i in range(len(values)):
ls, suc = get_sequence_lengths(values[:i] + values[i + 1:], total - values[i])
result |= suc
if suc:
sequences.extend([[values[i]] + s for s in ls])
return sequences, result
def gen_numbers(rand_min, rand_max, count):
values = list(range(rand_min, rand_max + 1))
sequences, success = get_sequence_lengths(list(range(1, count+1)), count)
sequences = list(filter(lambda x: len(x) <= 1 + rand_max - rand_min, sequences))
if not success or not len(sequences):
raise ValueError('Cannot generate with given parameters.')
sequence = sequences[np.random.randint(len(sequences))]
values = np.random.choice(values, len(sequence), replace=False)
result = []
for v, s in zip(values, sequence):
result.extend([v] * s)
return result
get_sequence_length will generate all permutations of unique positive integers that sum up to the given total. The sequence will then be further filtered by the number available values. Finally the generation of paired value and counts from the sequence produces the output.
As mentioned above get_sequence_length is recursive and is going to be quite slow for larger input values.
To avoid the variability of generating random combinations in a potentially long trial/error loop, you could use a function that directly produces a random partition of a number where all parts are distinct (increasing). from that you simply need to map shuffled numbers over the chunks provided by the partition function:
def randPart(N,size=0): # O(√N)
if not size:
maxSize = int((N*2+0.25)**0.5-0.5) # ∑1..maxSize <= N
size = random.randrange(1,maxSize) # select random size
if size == 1: return (N,) # one part --> all of N
s = size*(size-1)//2 # min sum of deltas for rest
a = random.randrange(1,(N-s)//size) # base value
p = randPart(N-a*size,size-1) # deltas on other parts
return (a,*(n+a for n in p)) # combine to distinct parts
usage:
size = 30
n = 10
chunks = randPart(size)
numbers = random.sample(range(n),len(chunks))
result = [n for count,n in zip(chunks,numbers) for _ in range(count)]
print(result)
[9, 9, 9, 0, 0, 0, 0, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6]
# resulting frequency counts
from collections import Counter
print(sorted(Counter(result).values()))
[3, 4, 5, 6, 12]
note that, if your range of random numbers is smaller than the maximum number of distinct partitions (for example fewer than 44 numbers for an output of 1000 values), you would need to modify the randPart function to take the limit into account in its calculation of maxSize:
def randPart(N,sizeLimit=0,size=0):
if not size:
maxSize = int((N*2+0.25)**0.5-0.5) # ∑1..maxSize <= N
maxSize = min(maxSize,sizeLimit or maxSize)
...
You could also change it to force a minimum number of partitions
This solves your problem in the way #MYousefi suggested.
import random
seq = list(range(50))
random.shuffle(seq)
values = []
for n,v in enumerate(seq):
values.extend( [v]*(n+1) )
if len(values) > 1000:
break
print(values)
Note that you can't get exactly 1,000 numbers. At first, I generated the entire sequence and then took the first 1,000, but that means whichever sequence gets truncated will be the same length as one of the earlier ones. You end up with 1,035.
I am able to generate the desired output but i need 10 of them and each list has to be unicue. The best solution i thought of was to create a 2nd function, generate a emty list and populate each element with list from 1st function. The output i got so far is x amount of lists but they are not unique and python gives me error when i try to call on the first function inside the 2nd one.
import random
numbers = list(range(1, 35))
out = []
final = []
print(numbers) # to see all numbers
# returns 7 unique pop'd numbers from list 'numbers' and appends them to list 'out'
def do():
for x in range(7):
out.append(numbers.pop(random.randrange(len(numbers))))
print(sorted(out))
# In other words i want to print output from function do() 10 times but each item in list has to be unique, not the lists themself
def do_ten():
for x in range(10):
final.append(out)
# do() python doesnt like this call
print(sorted(final))
do_ten()
This generates a specific amount of lists, in a list, which contain random numbers from 1 to 100, you can use l and n to control the amount of lists and numbers respectively.
import random
l, n = 3, 5 # The amount of lists and numbers respectively.
lis = [[i for i in random.sample(range(1, 35), n)] for group in range(l)]
print(lis)
Random Output:
[[16, 11, 17, 13, 9], [26, 6, 16, 29, 24], [24, 2, 4, 1, 20]]
You are popping 10 times 7 numbers from a list containing 34 elements (from 1 to 34). This is not possible. You need to have at least 70 elements in your list numbers(for example, from 0 to 69).
This is a solution that should work, based on the code you've already written:
import random
numbers = list(range(0, 70))
final = []
print(numbers) # to see all numbers
# returns a list of 7 unique popped numbers from list 'numbers'
def do():
out = []
for x in range(7):
l = len(numbers)
r = random.randrange(l)
t = numbers.pop(r)
out.append(t)
return out
# Call 10 times do() and add the returned list to 'final'
def do_ten():
for x in range(10):
out = do() # Get result from do()
final.append(out) # Add it to 'final'
do_ten()
print(final)
Does it help:
num_lists = 10
len_list = 10
[list(np.random.randint(1,11,len_list)) for _ in range(num_lists)]
As some people may have different definitin of "uniqueness", you may try:
source_list = range(0, num_lists*len_list,1)
[list(np.random.choice(source_list, len_list, replace=False)) for _ in range(num_lists)]
Pulling 7 of 34 numbers from your numberrange without repeats can be done using random.sample - to ensure you do not get duplicate lists, you can add a tuple of the list to a set and your final result and only add to final if this tuple is not yet in the set:
import random
numbers = range(1, 35) # 1...34
final = []
chosen = set()
while len(final) < 10:
# replace popping numbers with random.sample
one_list = random.sample(numbers, k=7) # 7 numbers out of 34 - no repeats
# create a tuple of this list and only add to final if not yet added
t = tuple(one_list)
if t not in chosen:
chosen.add(t)
final.append(one_list)
print (final)
Output:
[[1, 5, 10, 26, 14, 33, 6],
[3, 11, 1, 30, 7, 21, 18],
[24, 23, 28, 2, 13, 18, 1],
[4, 25, 32, 15, 22, 8, 27],
[32, 9, 10, 16, 17, 26, 12],
[34, 32, 10, 26, 16, 21, 20],
[6, 34, 22, 11, 26, 12, 5],
[29, 17, 25, 15, 3, 6, 5],
[24, 8, 31, 28, 17, 12, 15],
[6, 19, 11, 22, 30, 33, 15]]
If you dont need unique resulting lists, you can simplify this to a one-liner but it might have dupes inside:
final = [random.sample(range(1,11),k=7) for _ in range(10)]
I have two lists note = [6,8,10,13,14,17] Effective = [3,5,6,7,5,1] ,the first one represents grades, the second one the students in the class that got that grade. so 3 kids got a 6 and 1 got a 17. I want to calculate the mean and the median. for the mean I got:
note = [6,8,10,13,14,17]
Effective = [3,5,6,7,5,1]
products = [] for num1, num2 in zip(note, Effective):
products.append(num1 * num2)
print(sum(products)/(sum(Effective)))
My first question is, how do I turn both lists into a 3rd list:
(6,6,6,8,8,8,8,8,10,10,10,10,10,10,13,13,13,13,13,13,13,14,14,14,14,14,17)
in order to get the median.
Thanks,
Donka
Here's one approach iterating over Effective on an inner level to replicate each number as many times as specified in Effective, and taking the median using statistics.median:
from statistics import median
out = []
for i in range(len(note)):
for _ in range(Effective[i]):
out.append(note[i])
print(median(out))
# 10
To get your list you could do something like
total = []
for grade, freq in zip(note, Effective):
total += freq*[grade]
You can use np.repeat to get a list with the new values.
note = [6,8,10,13,14,17]
Effective = [3,5,6,7,5,1]
import numpy as np
new_list = np.repeat(note,Effective)
np.median(new_list),np.mean(new_list)
To achieve output like the third list that you expect you have to do something like that:
from statistics import median
note = [6,8,10,13,14,17]
Effective = [3,5,6,7,5,1]
newList = []
for index,value in enumerate(Effective):
for j in range(value):
newList.append(note[index])
print(newList)
print("Median is {}".format(median(newList)))
Output:
[6, 6, 6, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 17]
Median is 10
For computing the median I suggest you use statistics.median:
from statistics import median
note = [6, 8, 10, 13, 14, 17]
effective = [3, 5, 6, 7, 5, 1]
total = [n for n, e in zip(note, effective) for _ in range(e)]
result = median(total)
print(result)
Output
10
If you look at total (in the code above), you have:
[6, 6, 6, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 17]
A functional alternative, using repeat:
from statistics import median
from itertools import repeat
note = [6, 8, 10, 13, 14, 17]
effective = [3, 5, 6, 7, 5, 1]
total = [v for vs in map(repeat, note, effective) for v in vs]
result = median(total)
print(result)
note = [6,8,10,13,14,17]
effective = [3,5,6,7,5,1]
newlist=[]
for i in range(0,len(note)):
for j in range(effective[i]):
newlist.append(note[i])
print(newlist)
I have a list of hours starting from (0 is midnight).
hour = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
I want to generate a sequence of 3 consecutive hours randomly. Example:
[3,6]
or
[15, 18]
or
[23,2]
and so on. random.sample does not achieve what I want!
import random
hourSequence = sorted(random.sample(range(1,24), 2))
Any suggestions?
Doesn't exactly sure what you want, but probably
import random
s = random.randint(0, 23)
r = [s, (s+3)%24]
r
Out[14]: [16, 19]
Note: None of the other answers take in to consideration the possible sequence [23,0,1]
Please notice the following using itertools from python lib:
from itertools import islice, cycle
from random import choice
hours = list(range(24)) # List w/ 24h
hours_cycle = cycle(hours) # Transform the list in to a cycle
select_init = islice(hours_cycle, choice(hours), None) # Select a iterator on a random position
# Get the next 3 values for the iterator
select_range = []
for i in range(3):
select_range.append(next(select_init))
print(select_range)
This will print sequences of three values on your hours list in a circular way, which will also include on your results for example the [23,0,1].
You can try this:
import random
hour = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
index = random.randint(0,len(hour)-2)
l = [hour[index],hour[index+3]]
print(l)
You can get a random number from the array you already created hour and take the element that is 3 places afterward:
import random
def random_sequence_endpoints(l, span):
i = random.choice(range(len(l)))
return [hour[i], hour[(i+span) % len(l)]]
hour = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
result = random_sequence_endpoints(hour, 3)
This will work not only for the above hours list example but for any other list contain any other elements.
I am trying to get possible numbers from 1 to n, given 4 numbers. by adding or subbtracting 2 or more of the 4 numbers.
e.g. it goes into loop for numlist(1,2,3,16). Below is the code:
def numlist(a,b,c,d):
#user input of 4 numbers a,b,c,d
# assigning variables value of -1. This will be used to provide -1 or +1 or 0
p=-1
q=-1
r=-1
s=-1
count=0
myarray=[]
mysum=a+b+c+d #sum of given 4 numbers
for x in range(mysum):
while count<mysum:
if p<=1:
if q<=1:
if r <=1:
if s<=1:
n1=p*a+q*b+r*c+s*d #number to be generated by adding/subtracting
s=s+1
#print(n1)
if n1>0 and (n1 in myarray)==False:
#print(n1)
myarray.append(n1) #add to myarray if number is positive and not already present
myarray.sort() #sort myarray
count=count+1
if count==mysum:
break
else:
s=-1
r=r+1
else:
r=-1
q=q+1
else:
q=-1
p=p+1
else:
p=-1
print(len(myarray),'total')
print(myarray)
numlist(1,3,4,14)
outputs
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
but if numlist(1,3,4,19)
it keeps running without ending in output of array. only shows total.
where am I going wrong ?
I think you should rethink your algorithm. Consider this:
from itertools import combinations
def numlist(lst):
lst = lst + [-i for i in lst]
result = set()
for i in range(2, 5):
result.update(sum(k) for k in combinations(lst, i))
return sorted(i for i in result if i > 0)
numlist([1, 3, 4, 19])
# [1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]
I did a little patch-up work, and found the high-level problem with your logic.
Your code goes into an infinite loop when the current value of count cannot be formed with the input values. Your logic fails to increment count until it finds a way to create that value. You spin through one combination after another of coefficients.
for x in range(mysum):
print ("top of loop; x =", x)
while count<mysum:
print("count", count, "\tmysum", mysum, "\tcoeffs", p, q, r, s)
if p<=1:
...