使用约束生成随机非整数

时间:2020-04-25 21:23:45

标签: python random

我一直在尝试为我的随机非整数生成添加约束。我正在研究投资组合优化过程,并希望限制随机Generetad投资组合的权重。

我设法以某种方式施加约束,但是我无法将权重之和限制为100%,而同时将每个权重保持在其自身的阈值之间。

您可能会意识到我删除了以下语句,因为它导致权重高于或低于之前给出的限制。

#weights =重量/总和(weights)

#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)


2 个答案:

答案 0 :(得分:2)

那呢:

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)

答案 1 :(得分:0)

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

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

这是我根据约翰·麦克莱恩(John McClane)的算法进行的修改,他将其发布为answer to another question。我describe the algorithm的另一个答案。

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]]
相关问题