Python:获得投资组合的所有权重组合

时间:2016-07-27 11:17:24

标签: python list itertools cartesian-product

我认为这个问题可以使用itertools或者笛卡尔来解决,但我对Python很陌生并且正在努力使用它们:

我有5个股票的投资组合,每个股票的权重可以是-0.4,-0.2,0,0.2或0.4,权重加起来为0.如何创建一个产生每个股票列表的函数重量的可能组合。例如[-0.4,0.2,0,0.2,0] ......等

理想情况下,该功能适用​​于n种股票,因为我最终希望对50种股票进行相同的处理。

编辑:为了澄清,我正在寻找长度为n的所有组合(在这种情况下为5),求和为0. 值可以重复:例如:[0.2,0.2,-0.4 ,0,0],[0.4,0,-0.2,-0.2,0.4],[0,0,0,0.2,-0.2],[0,0.4,-0.4,0.2,-0.2]等。[ 0,0,0,0,0]将是一种可能的组合。有5个可能的权重和5个股票的事实是巧合(我应该避免!),同样的问题可能是5个可能的权重和3个股票或7个股票。感谢。

6 个答案:

答案 0 :(得分:3)

像这样的东西,虽然效率不高。

from decimal import Decimal
import itertools

# possible optimization: use integers rather than Decimal
weights = [Decimal("-0.4"), Decimal("-0.2"), Decimal(0), Decimal("0.2"), Decimal("0.4")]

def possible_weightings(n = 5, target = 0):
    for all_bar_one in itertools.product(weights, repeat = n - 1):
        final = target - sum(all_bar_one)
        if final in weights:
            yield all_bar_one + (final,)

我从评论中重复一遍,你不能n = 50执行此操作。代码产生正确的值,但宇宙中没有时间迭代所有可能的权重。

这段代码并不精彩。它做了一些不必要的工作来检查案例,例如,除了前两个之外的所有事件的总和已经大于0.8,因此没有必要单独检查这两个中的第一个的所有可能性。

所以,这几乎没有时间n = 5,但是n有一些值,这段代码变得不可能慢,你可以用更好的代码进一步。你仍然不会达到50.我懒得写出更好的代码,但基本上代替all_bar_one,您可以使用possible_weightings的连续较小的值对n进行递归调用并且target的值等于你给出的目标,减去你到目前为止的总和。然后,在target过大(正面或负面)只能使用n值的情况下,尽早挽救所有不需要采取的分支。

答案 1 :(得分:1)

我理解值可以重复,但都必须总和为零,因此解决方案可能是:

>>> from itertools import permutations
>>> weights = [-0.4, -0.2, 0, 0.2, 0.4]
>>> result = (com for com in permutations(weights) if sum(com)==0)
>>> for i in result: print(i)

编辑: 您可以使用product作为@Steve Jassop建议。

combi = (i for i in itertools.product(weights, repeat= len(weights)) if not sum(i))
for c in combi:
    print(c)

答案 2 :(得分:0)

我喜欢使用filter功能:

from itertools import permutations
w = [-0.4, -0.2, 0, 0.2, 0.4] 

def foo(w):
    perms = list(permutations(w))
    sum0 = filter(lambda x: sum(x)==0, perms)
    return sum0

print foo(w)

答案 3 :(得分:0)

不同的方法。

1按顺​​序计算出加起来为零的所有权重序列。

例如,这些是一些可能性(使用整数来减少输入):
[0,0,0,0,0]
[-4,0,0,+ 2,+ 2]
[-4,0,0,0,+ 4]

[ - 4,+ 4,0,0,0]不正确,因为未按顺序挑选权重。

2置换上面的内容,因为排列也会加起来为零。

这是你获得[-4,0,0,0,+ 4] [-4,+ 4,0,0,0]

好的,懒惰。我将伪代码/注释代码用于我的解决方案。在递归时不那么强大,这些东西太难以快速编码而且我怀疑这种类型的解决方案可以扩展到50个。

即。我不认为我是对的,但它可能会给别人一个想法。

def find_solution(weights, length, last_pick, target_sum):

    # returns a list of solutions, in growing order, of weights adding up to the target_sum

    # weights are the sequence of possible weights - IN ORDER, NO REPEATS
    # length is how many weights we are adding up
    # last_pick - the weight picked by the caller 
    # target_sum is what we are aiming for, which will always be >=0

    solutions = []

    if length > 1:

        #since we are picking in order, having picked 0 "disqualifies" -4 and -2.
        if last_pick > weights[0]:
            weights = [w for w in weights if w >= last_pick]

        #all remaining weights are possible
        for weight in weights:
            child_target_sum = target_sum + weight

            #basic idea, we are picking in growing order
            #if we start out picking +2 in a [-4,-2,0,+2,+4] list in order, then we are constrained to finding -2
            #with just 2 and 4 as choices.  won't work.
            if child_target_sum <= 0:
                break

            child_solutions = find_solution(weights, length=length-1, last_pick=weight, target_sum=child_target_sum)

            [solutions.append([weight] + child ) for child in child_solutions if child_solution]

    else:
        #only 1 item to pick left, so it has be the target_sum
        if target_sum in weights:
            return [[target_sum]]

    return solutions

weights = list(set(weights))
weights.sort()

#those are not permutated yet
solutions = find_solutions(weights, len(solution), -999999999, 0)

permutated = []
for solution in solutions:
   permutated.extend(itertools.permutations(solution))

答案 4 :(得分:-1)

如果您只想要所有组合的列表,请使用itertools.combinations

w = [-0.4, -0.2, 0, 0.2, 0.4]
l = len(w)

if __name__ == '__main__':
    for i in xrange(1, l+1):
        for p in itertools.combinations(w, i):
            print p

如果您想计算使用这些组合可以创建的不同权重,它会更复杂。

首先,使用1,2,3,...元素生成排列。然后你拿它们的总和。然后将总和添加到集合中(如果数字已经存在则不会执行任何操作,非常快速的操作)。最后,您转换为列表并对其进行排序。

from itertools import combinations

def round_it(n, p):
    """rounds n, to have maximum p figures to the right of the comma"""
    return int((10**p)*n)/float(10**p)

w = [-0.4, -0.2, 0, 0.2, 0.4]
l = len(w)
res = set()

if __name__ == '__main__':
    for i in xrange(1, l+1):
        for p in combinations(w, i):
            res.add(round_it(sum(p), 10))  # rounding necessary to avoid artifacts

    print sorted(list(res))

答案 5 :(得分:-3)

这是你在找什么: 如果L = [-0.4, 0.2, 0, 0.2, 0]

AllCombi = itertools.permutations(L)

for each in AllCombi:
    print each