Skip to main content
Code Review

Return to Answer

replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

After this you want to look into a better way to generate these random numbers. At first I used an algorithm that generates \$n\$ random numbers that add to a number. As then we can use an algorithm documented by David Schwartz algorithm documented by David Schwartz. To prove that we can use this algorithm rather than the one that you are using, we have two of the numbers in the mean equation:

After this you want to look into a better way to generate these random numbers. At first I used an algorithm that generates \$n\$ random numbers that add to a number. As then we can use an algorithm documented by David Schwartz. To prove that we can use this algorithm rather than the one that you are using, we have two of the numbers in the mean equation:

After this you want to look into a better way to generate these random numbers. At first I used an algorithm that generates \$n\$ random numbers that add to a number. As then we can use an algorithm documented by David Schwartz. To prove that we can use this algorithm rather than the one that you are using, we have two of the numbers in the mean equation:

Pretty much re-write the answer.
Source Link
Peilonrayz
  • 44.4k
  • 7
  • 80
  • 157

Instead ifFirst off I'd recommend that you thinkcreate a helper function. This is as thinking of algorithms as \0ドル-\text{stop}\$ with a step of\1ドル\$ is easier than thinking of \$\text{start}-\text{stop}\$ with a step of \$\text{step}\$. To do this assignmentyou can perform a transform on the output of generatingyou function so that you multiply by the step, and add by the start. But you should take into account that some means are not possible with certain steps. Finally if you make your input work like a Python range, then it'll be familiar to other Python programmers. And so you could get a decorator like:

from functools import wraps
from math import copysign
def normalize_input(fn):
 @wraps(fn)
 def wrapper(mean, length, start, stop=None, step=None):
 if stop is None:
 stop = start
 start = 0
 if step is None:
 step = 1
 if not (start <= mean * copysign(1, step) < stop):
 raise ValueError('mean is not within distribution range.')
 if mean * length % step != 0:
 raise ValueError('unreachable mean.')
 stop -= start
 mean -= start
 if step != 1:
 stop /= step
 mean /= step
 numbers = fn(mean, length, stop)
 for i, num in enumerate(numbers):
 numbers[i] = num * step + start
 return numbers
 return wrapper

This allows you to define functions that only take a mean, length and stop. And so could make your code:

@normalize_input
def list_with_mean(mean, length, stop):
 HT = [x for x in range(stop+1)]
 target_mean = 0
 while target_mean != mean:
 outcomes = [random.choice(HT) for x in range(length)]
 target_mean = sum(outcomes)/len(outcomes)
 return outcomes
numbers = list_with_mean(50, 100, 25, 75)

Which will generate \100ドル\$ random numbers with a mean of \50ドル\$, and with numbers ranging from \25ドル-75\$.


After this you want to look into a better way to generate these random numbers. At first I used an algorithm that generates \$n\$ random numbers that add to a number,. then a quick searchAs then we can get you ause an brilliant algorithmalgorithm documented by David Schwartz. The reasonTo prove that we can add to a specific number is as that's a re-arrangementuse this algorithm rather than the one that you are using, we have two of the numbers in the mean equation:

from random import randrange
from itertools import tee
# Itertools Recipe 
def pairwise(iterable):
 "s -> (s0,s1), (s1,s2), (s2, s3), ..."
 a, b = tee(iterable)
 next(b, None)
 return zip(a, b)
def fixed_sum(total, length):
 for a, b in pairwise(sorted([0, total] + [randrange(total) for _ in range(length - 1)])):
 yield b - a
def bias_mean(lower=False):
 def bias_mean_inner(fn):
 @wraps(fn)
 def wrapper(mean, length, stop):
 flip_mean = (mean > stop / 2) is lower
 if flip_mean:
 mean = stop - mean
 numbers = fn(mean, length, stop)
 if flip_mean:
 for i, num in enumerate(numbers):
 numbers[i] = stop - num
 return numbers
 return wrapper
 return bias_mean_inner
@normalize_input
@bias_mean(True)
def by_sum(mean, length, stop):
 numbers = list(fixed_sum(int(mean * length), length))
 add = 0
 while True:
 for i in range(0, length):
 num = numbers[i]
 if num >= stop:
 add += (num - stop)
 numbers[i] = stop
 else:
 if add + num > stop:
 n = stop
 add -= stop - num
 else:
 n = num + add
 add = 0
 numbers[i] = n
 if add == 0:
 break
 return numbers

And then you just need to wrap fixed_sumAs pointed out by @Graipher this algorithm isn't very good to outputgenerate the random numbers between certain ranges. If you change you function to take; start, stop, and stepAnd so instead of using the above algorithm, weyou can implementuse one that adds a range like selectionrandom amount to each number, as long as it doesn't exceed the maximum size. AfterTo do this you can create an amount variable that you subtract from, when you generate a random number. You then want to check ifloop through you list and pick a random number to add to the inputcurrent item. The domain of this random item is actually valid\0ドル-\min(\text{amount}, \text{stop} - \text{item})\$. And This is so asking for a mean outside the range should error, along with a mean that is unreachableit doesn't exceed the maximum number size, withinand all the rangeitems add to the total we want. ToThis can get you the code:

@normalize_input
@bias_mean(False)
def list_with_meanby_sum_distribution(mean, length, start, stop=None, step=Nonestop):
 if stopnumbers is= None:
[0] * length
 amount = stoplength =* startmean
 while amount:
 start = 0
 iffor stepi isin Nonerange(length):
 step = 1
  ifn not= randrange(start <= mean * copysignmin(1amount, stepstop - numbers[i]) <+ stop1):
 raise ValueError('mean is not withinnumbers[i] distrobution+= range.')n
 if mean * length % step != 0:
  amount -= n
 raise ValueError('unreachablereturn mean.')numbers

After this you need to actually implement the extra options. If the start is not zero then you want to add start to each of your output numbers. And, if the step is not one then you want to multiply the output by the step.

After this you want to reduce the numbers output by fixed_sum so they don't go above the maximum, but you can't just throw away the numbers. And so at the cost of a uniform distribution I add the extra to the next number. But as this makes long streaks of 100's at high numbers, you can invert the mean, and then subtract the numbers from stop if the mean goes above '(a + b) /2'.

This got me:has a uniform output when the \$\text{mean} = \frac{\text{stop}}{2}\$, but otherwise the beta function is much better.

def numbers_with_mean(mean, length, start, stop=None, step=None):
 if stop is None:
 stop = start
 start = 0
 if step is None:
 step = 1
 if not (start <= mean * copysign(1, step) < stop):
 raise ValueError('mean is not within distribution range.')
 if mean * length % step != 0:
 raise ValueError('unreachable mean.')
 # shift domain from start-stop with a step of step, to 0-(start-stop)/step
 k = 0
 if start != 0:
 k = start
 start = 0
 stop -= k
 mean -=k
 if step != 1:
 start //= step
 stop //= step
 mean /= step
 # Get better distribution on larger means
 if mean > (stop / 2):
 numbers = list(fixed_sum(int((stop - mean) * length), length))
 else:
 numbers = list(fixed_sum(int(mean * length), length))
 # Reassign numbers with overflow to next number.
 add = 0
 while True:
 for i in range(0, length):
 num = numbers[i]
 if num >= stop:
 add += (num - stop)
 numbers[i] = stop
 else:
 if add + num > stop:
 n = stop
 add -= stop - num
 else:
 n = num + add
 add = 0
 numbers[i] = n
 if add == 0:
 break
 if mean > (stop / 2):
 for i, num in enumerate(numbers):
 numbers[i] = stop - numbers[i]
 # shift data to the from 0-(start-stop)/step to start-stop with a step of step.
 for i, num in enumerate(numbers):
 numbers[i] = num * step + k
 return numbers

Instead if you think of this assignment of generating \$n\$ numbers that add to a number, then a quick search can get you a brilliant algorithm documented by David Schwartz. The reason that we can add to a specific number is as that's a re-arrangement of the mean equation:

from random import randrange
from itertools import tee
# Itertools Recipe 
def pairwise(iterable):
 "s -> (s0,s1), (s1,s2), (s2, s3), ..."
 a, b = tee(iterable)
 next(b, None)
 return zip(a, b)
def fixed_sum(total, length):
 for a, b in pairwise(sorted([0, total] + [randrange(total) for _ in range(length - 1)])):
 yield b - a

And then you just need to wrap fixed_sum to output numbers between certain ranges. If you change you function to take; start, stop, and step, we can implement a range like selection. After this you want to check if the input is actually valid. And so asking for a mean outside the range should error, along with a mean that is unreachable, within the range. To get:

def list_with_mean(mean, length, start, stop=None, step=None):
 if stop is None:
 stop = start
 start = 0
 if step is None:
 step = 1
  if not (start <= mean * copysign(1, step) < stop):
 raise ValueError('mean is not within distrobution range.')
 if mean * length % step != 0:
  raise ValueError('unreachable mean.')

After this you need to actually implement the extra options. If the start is not zero then you want to add start to each of your output numbers. And, if the step is not one then you want to multiply the output by the step.

After this you want to reduce the numbers output by fixed_sum so they don't go above the maximum, but you can't just throw away the numbers. And so at the cost of a uniform distribution I add the extra to the next number. But as this makes long streaks of 100's at high numbers, you can invert the mean, and then subtract the numbers from stop if the mean goes above '(a + b) /2'.

This got me:

def numbers_with_mean(mean, length, start, stop=None, step=None):
 if stop is None:
 stop = start
 start = 0
 if step is None:
 step = 1
 if not (start <= mean * copysign(1, step) < stop):
 raise ValueError('mean is not within distribution range.')
 if mean * length % step != 0:
 raise ValueError('unreachable mean.')
 # shift domain from start-stop with a step of step, to 0-(start-stop)/step
 k = 0
 if start != 0:
 k = start
 start = 0
 stop -= k
 mean -=k
 if step != 1:
 start //= step
 stop //= step
 mean /= step
 # Get better distribution on larger means
 if mean > (stop / 2):
 numbers = list(fixed_sum(int((stop - mean) * length), length))
 else:
 numbers = list(fixed_sum(int(mean * length), length))
 # Reassign numbers with overflow to next number.
 add = 0
 while True:
 for i in range(0, length):
 num = numbers[i]
 if num >= stop:
 add += (num - stop)
 numbers[i] = stop
 else:
 if add + num > stop:
 n = stop
 add -= stop - num
 else:
 n = num + add
 add = 0
 numbers[i] = n
 if add == 0:
 break
 if mean > (stop / 2):
 for i, num in enumerate(numbers):
 numbers[i] = stop - numbers[i]
 # shift data to the from 0-(start-stop)/step to start-stop with a step of step.
 for i, num in enumerate(numbers):
 numbers[i] = num * step + k
 return numbers

First off I'd recommend that you create a helper function. This is as thinking of algorithms as \0ドル-\text{stop}\$ with a step of\1ドル\$ is easier than thinking of \$\text{start}-\text{stop}\$ with a step of \$\text{step}\$. To do this you can perform a transform on the output of you function so that you multiply by the step, and add by the start. But you should take into account that some means are not possible with certain steps. Finally if you make your input work like a Python range, then it'll be familiar to other Python programmers. And so you could get a decorator like:

from functools import wraps
from math import copysign
def normalize_input(fn):
 @wraps(fn)
 def wrapper(mean, length, start, stop=None, step=None):
 if stop is None:
 stop = start
 start = 0
 if step is None:
 step = 1
 if not (start <= mean * copysign(1, step) < stop):
 raise ValueError('mean is not within distribution range.')
 if mean * length % step != 0:
 raise ValueError('unreachable mean.')
 stop -= start
 mean -= start
 if step != 1:
 stop /= step
 mean /= step
 numbers = fn(mean, length, stop)
 for i, num in enumerate(numbers):
 numbers[i] = num * step + start
 return numbers
 return wrapper

This allows you to define functions that only take a mean, length and stop. And so could make your code:

@normalize_input
def list_with_mean(mean, length, stop):
 HT = [x for x in range(stop+1)]
 target_mean = 0
 while target_mean != mean:
 outcomes = [random.choice(HT) for x in range(length)]
 target_mean = sum(outcomes)/len(outcomes)
 return outcomes
numbers = list_with_mean(50, 100, 25, 75)

Which will generate \100ドル\$ random numbers with a mean of \50ドル\$, and with numbers ranging from \25ドル-75\$.


After this you want to look into a better way to generate these random numbers. At first I used an algorithm that generates \$n\$ random numbers that add to a number. As then we can use an algorithm documented by David Schwartz. To prove that we can use this algorithm rather than the one that you are using, we have two of the numbers in the mean equation:

from random import randrange
from itertools import tee
# Itertools Recipe 
def pairwise(iterable):
 "s -> (s0,s1), (s1,s2), (s2, s3), ..."
 a, b = tee(iterable)
 next(b, None)
 return zip(a, b)
def fixed_sum(total, length):
 for a, b in pairwise(sorted([0, total] + [randrange(total) for _ in range(length - 1)])):
 yield b - a
def bias_mean(lower=False):
 def bias_mean_inner(fn):
 @wraps(fn)
 def wrapper(mean, length, stop):
 flip_mean = (mean > stop / 2) is lower
 if flip_mean:
 mean = stop - mean
 numbers = fn(mean, length, stop)
 if flip_mean:
 for i, num in enumerate(numbers):
 numbers[i] = stop - num
 return numbers
 return wrapper
 return bias_mean_inner
@normalize_input
@bias_mean(True)
def by_sum(mean, length, stop):
 numbers = list(fixed_sum(int(mean * length), length))
 add = 0
 while True:
 for i in range(0, length):
 num = numbers[i]
 if num >= stop:
 add += (num - stop)
 numbers[i] = stop
 else:
 if add + num > stop:
 n = stop
 add -= stop - num
 else:
 n = num + add
 add = 0
 numbers[i] = n
 if add == 0:
 break
 return numbers

As pointed out by @Graipher this algorithm isn't very good to generate the random numbers. And so instead of using the above algorithm, you can use one that adds a random amount to each number, as long as it doesn't exceed the maximum size. To do this you can create an amount variable that you subtract from, when you generate a random number. You then want to loop through you list and pick a random number to add to the current item. The domain of this random item is \0ドル-\min(\text{amount}, \text{stop} - \text{item})\$. This is so that it doesn't exceed the maximum number size, and all the items add to the total we want. This can get you the code:

@normalize_input
@bias_mean(False)
def by_sum_distribution(mean, length, stop):
 numbers = [0] * length
 amount = length * mean
 while amount:
 for i in range(length):
 n = randrange(min(amount, stop - numbers[i]) + 1)
 numbers[i] += n
 amount -= n
 return numbers

This has a uniform output when the \$\text{mean} = \frac{\text{stop}}{2}\$, but otherwise the beta function is much better.

Fix code, unshifting whilst changing is _bad_.
Source Link
Peilonrayz
  • 44.4k
  • 7
  • 80
  • 157
def numbers_with_mean(mean, length, start, stop=None, step=None):
 if stop is None:
 stop = start
 start = 0
 if step is None:
 step = 1
 if not (start <= mean * copysign(1, step) < stop):
 raise ValueError('mean is not within distribution range.')
 if mean * length % step != 0:
 raise ValueError('unreachable mean.')
 # shift domain from start-stop with a step of step, to 0-(start-stop)/step
 k = 0
 if start != 0:
 k = start
 start = 0
 stop -= k
 mean -=k
 if step != 1:
 start //= step
 stop //= step
 mean /= step
 # Get better distribution on larger means
 if mean > (stop / 2):
 numbers = list(fixed_sum(int((stop - mean) * length), length))
 else:
 numbers = list(fixed_sum(int(mean * length), length))
 # Reassign numbers with overflow to next number.
 add = 0
 while True:
 for i in range(0, length):
 num = numbers[i]
 if num >= stop:
 add += (num - stop)
 numbers[i] = stop * step + k
 else:
 if add + num > stop:
 n = stop
 add -= stop - num
 else:
 n = num + add
 add = 0
 numbers[i] = n * step + k
 if add == 0:
 break
 if mean > (stop / 2):
 for i, num in enumerate(numbers):
 numbers[i] = stop - numbers[i]
 # shift data to the from 0-(start-stop)/step to start-stop with a step of step.
 for i, num in enumerate(numbers):
 numbers[i] = num * step + k
 return numbers
def numbers_with_mean(mean, length, start, stop=None, step=None):
 if stop is None:
 stop = start
 start = 0
 if step is None:
 step = 1
 if not (start <= mean * copysign(1, step) < stop):
 raise ValueError('mean is not within distribution range.')
 if mean * length % step != 0:
 raise ValueError('unreachable mean.')
 k = 0
 if start != 0:
 k = start
 start = 0
 stop -= k
 mean -=k
 if step != 1:
 start //= step
 stop //= step
 mean /= step
 if mean > (stop / 2):
 numbers = list(fixed_sum(int((stop - mean) * length), length))
 else:
 numbers = list(fixed_sum(int(mean * length), length))
 add = 0
 while True:
 for i in range(0, length):
 num = numbers[i]
 if num >= stop:
 add += (num - stop)
 numbers[i] = stop * step + k
 else:
 if add + num > stop:
 n = stop
 add -= stop - num
 else:
 n = num + add
 add = 0
 numbers[i] = n * step + k
 if add == 0:
 break
 if mean > (stop / 2):
 for i, num in enumerate(numbers):
 numbers[i] = stop - numbers[i]
 return numbers
def numbers_with_mean(mean, length, start, stop=None, step=None):
 if stop is None:
 stop = start
 start = 0
 if step is None:
 step = 1
 if not (start <= mean * copysign(1, step) < stop):
 raise ValueError('mean is not within distribution range.')
 if mean * length % step != 0:
 raise ValueError('unreachable mean.')
 # shift domain from start-stop with a step of step, to 0-(start-stop)/step
 k = 0
 if start != 0:
 k = start
 start = 0
 stop -= k
 mean -=k
 if step != 1:
 start //= step
 stop //= step
 mean /= step
 # Get better distribution on larger means
 if mean > (stop / 2):
 numbers = list(fixed_sum(int((stop - mean) * length), length))
 else:
 numbers = list(fixed_sum(int(mean * length), length))
 # Reassign numbers with overflow to next number.
 add = 0
 while True:
 for i in range(0, length):
 num = numbers[i]
 if num >= stop:
 add += (num - stop)
 numbers[i] = stop
 else:
 if add + num > stop:
 n = stop
 add -= stop - num
 else:
 n = num + add
 add = 0
 numbers[i] = n
 if add == 0:
 break
 if mean > (stop / 2):
 for i, num in enumerate(numbers):
 numbers[i] = stop - numbers[i]
 # shift data to the from 0-(start-stop)/step to start-stop with a step of step.
 for i, num in enumerate(numbers):
 numbers[i] = num * step + k
 return numbers
Source Link
Peilonrayz
  • 44.4k
  • 7
  • 80
  • 157
Loading
lang-py

AltStyle によって変換されたページ (->オリジナル) /