用Python约束生成随机非整数



我一直在尝试为随机非整数生成添加约束。我正在进行投资组合优化过程,并寻求随机广义投资组合的约束权重。

我设法以某种方式设置了约束,但我无法将权重的总和限制在100%,同时将每个权重保持在自己的阈值之间。

你可能会意识到,我删除了以下声明,因为它导致重量高于或低于之前给出的极限。

#weights=权重/np.sum(权重(

#Create Random Weighted Portfolios for 5 Assets
num_assets = 5
num_portfolios = 1000
p_weights = []
for portfolio in range(num_portfolios):
w1 = random.uniform(0.5, 0.7)
w2 = random.uniform(0.05, 0.3)
w3 = random.uniform(0.05, 0.3)
w4 = random.uniform(0.05, 0.5)
w5 = random.uniform(0.03, 0.15)
k = w1 + w2 + w3 + w4 + w5 
#Sum of Asset weights is 100%

weights = (w1, w2, w3, w4, w5)

#weights = weights/np.sum(weights)
p_weights.append(weights)

w_data_matrix =  np.asmatrix(p_weights)
print(w_data_matrix)

这个怎么样:

for portfolio in range(num_portfolios):
weights = [ random.uniform(0.5, 0.7),
random.uniform(0.05, 0.3),
random.uniform(0.05, 0.3),
random.uniform(0.05, 0.5),
random.uniform(0.03, 0.15) ]
k = sum(weights)
weights = [ w/k for w in weights ]
p_weights.append(weights)

以下是您的问题的Python解决方案。这种具有以下优点:

  • 它不使用拒绝采样
  • 它从所有符合要求的组合中随机统一选择

这是我根据约翰·麦克莱恩的算法进行的修改,他将其作为另一个问题的答案发布。我在另一个答案中描述了算法。

import random # Or secrets
def _getSolTableForRanges(ranges, adjsum):
n = len(ranges)
t = [[0 for i in range(adjsum + 1)] for j in range(n + 1)]
t[0][0] = 1
for i in range(1, n + 1):
for j in range(0, adjsum + 1):
krange = ranges[i-1][1] - ranges[i-1][0]
jm = max(j - krange, 0)
v = 0
for k in range(jm, j + 1):
v += t[i - 1][k]
t[i][j] = v
return t
def intsInRangesWithSum(numSamples, ranges, total):
""" Generates one or more combinations of
'len(ranges)' numbers each, where each
combination's numbers sum to 'total', and each number
has its own valid range.  'ranges' is a list of valid ranges
for each number; the first item in each range is the minimum
value and the second is the maximum value.  For example,
'ranges' can be [[1,4],[3,5],[2,6]], which says that the first
number must be in the interval [1, 4], the second in [3, 5],
and the third in [2, 6].
The combinations are chosen uniformly at random.
Neither the integers in the 'ranges' list nor
'total' may be negative.  Returns an empty
list if 'numSamples' is zero.
This is a modification I made to an algorithm that
was contributed in a _Stack Overflow_
answer (`questions/61393463`) by John McClane.
Raises an error if there is no solution for the given
parameters.  """
mintotal = sum([x[0] for x in ranges])
maxtotal = sum([x[1] for x in ranges])
adjsum = total - mintotal
print([total,adjsum])
# Min, max, sum negative
if total<0: raise ValueError
for r in ranges:
if r[0]<0 or r[1]<0: raise ValueError
# No solution
if mintotal > total or maxtotal < total:
raise ValueError
if numSamples == 0:
return []
# One solution
if maxtotal == total:
return [[x[1] for x in ranges] for i in range(numSamples)]
if mintotal == total:
return [[x[0] for x in ranges] for i in range(numSamples)]
samples = [None for i in range(numSamples)]
numPerSample = len(ranges)
table = _getSolTableForRanges(ranges, adjsum)
for sample in range(numSamples):
s = adjsum
ret = [0 for i in range(numPerSample)]
for ib in range(numPerSample):
i = numPerSample - 1 - ib
# Or secrets.randbelow(table[i + 1][s])
v = random.randint(0, table[i + 1][s] - 1)
r = ranges[i][0]
v -= table[i][s]
while v >= 0:
s -= 1
r += 1
v -= table[i][s]
ret[i] = r
samples[sample] = ret
return samples

示例:

weights=intsInRangesWithSum(
# One sample
1,
# Ranges for each random number
[[50, 70], [5, 30], [5, 30], [5, 50], [3, 15]],
# Sum of the numbers
100)
# Divide by 100 to get weights that sum to 1
weights=[x/100.0 for x in weights[0]]

最新更新