python 求组合数最快方法_Python-生成符合条件的大集合组合的最有效方法?

我正在尝试根据边界条件生成投资组合中金融工具的所有可能组合.

例如,假设我有一个列表集合,这些列表代表对投资组合的分配,但要以每种工具在投资组合总规模中的最小和最大百分比为准:

"US Bonds" = {0.10,0.15,0.20,0.25,0.30}

"US Equities" = {0.25, 0.30, 0.35, 0.40, 0.45, 0.50}

"European Bonds" = {0.10, 0.15, 0.20}

"European Equities = {0.20,0.25,0.30,0.35,0.40,0.45,0.50}

...

"Cash" = {0.0, 0.05, 0.10, 0.15,...0.95}

我的清单资产因此如下所示:

[In]

Asset

[Out]

[[0.1, 0.15, 0.2, 0.25, 0.30],

[0.25, 0.30,0.35, 0.40, 0.45, 0.50],

[0.1, 0.15, 0.2],

[0.20, 0.25, 0.30,0.35, 0.40, 0.45, 0.50]

...

[0.0, 0.05, 0.1, 0.15, 0.2, 0.25,...0.95]]

在所有工具组合的总和必须等于1的标准下,产生所有可能的投资组合的最有效方法是什么?

现在,我将创建一个列表“投资组合”,如下所示:

portfolios = [item for item in itertools.product(*asset) if np.isclose(sum(item),1)]

(nb,“ np.isclose”用于处理时髦的fp算法).

我已经将资产类别和可能的分配表示为列表的集合,但想知道是否存在其他更快的数据表示形式(例如NumPY数组).

关于各种组合的最佳执行存在一些问题,但是我没有看到任何具有任何边界条件的情况.

解决方法:

首先,我将百分比表示为整数值,以避免浮点舍入错误.

其次,最有效的方法将使用边界来避免查看可能无法满足== 1约束的投资组合.

您要编写的循环将这样运行:

def portfolios():

for us_bonds in [ 10, 15, 20, 25, 30 ]:

if us_bonds > 100: break

for us_equaties in [ 25, 30, 35, 40, 45, 50 ]:

if us_bonds + us_equaties > 100: break

for euro_bonds in [ 10, 15, 20 ]:

if us_bonds + us_equaties + euro_bonds > 100: break

for euro_equaties in [ 20, 25, 30, 35, 40, 45, 50 ]:

if us_bonds + us_equaties + euro_bonds + euro_equaties > 100: break

cash = 100 - (us_bonds + us_equaties + euro_bonds + euro_equaties)

yield [us_bonds, us_equaties, euro_bonds, euro_equaties, cash]

这定义了一个生成器,您可以在for循环中使用它,如下所示:

for x in portfolios(): print x

这种方法之所以有效,是因为它避免构造超过== 100约束的投资组合.

还要注意,我们利用了“现金”百分比基本上可以是任何东西这一事实,因此它只占用了100%与其他投资类别的总和之间的差额.

以下函数针对任意数量的投资类别概括了此循环:

def gen_portfolio(categories):

n = len(categories)

tarr = [0] * (n+1)

parr = [0] * (n+1)

karr = [0] * (n+1)

marr = [ len(c) for c in categories ]

i = 0

while True:

while True:

if i < n:

p = categories[i][ karr[i] ]

t = tarr[i] + p

if t <= 100:

parr[i] = p

tarr[i+1] = t

i += 1

karr[i] = 0

continue

else:

break # backup

else:

parr[n] = 100 - tarr[n] # set the Cash percentage

yield parr[:] # yield a copy of the array parr

break

# backup

while True:

if i > 0:

i -= 1

karr[i] += 1

if karr[i] < marr[i]: break

else:

return # done!

def portfolios2():

cats = [ [ 10, 15, 20, 25, 30 ], [ 25, 30, 35, 40, 45, 50 ], [ 10, 15, 20 ], [ 20, 25, 30, 35, 40, 45, 50 ] ]

return gen_portfolio(cats)

这是一个测试,表明它们产生了相同的结果:

def compareTest():

ports1 = [ x for x in portfolios() ]

ports2 = [ x for x in portfolios2() ]

print "ports1 length:", len(ports1)

print "ports2 length:", len(ports2)

for x in ports1:

if x not in ports2: print "not in ports2:", x

for x in ports2:

if x not in ports1: print "not in ports1:", x

更新资料

这是一个示例,演示了此方法与itertools.product之间的区别.

假设有10个投资类别,每个类别的百分比为[90,91,..,99].带break语句的嵌套循环将按以下步骤进行:

start the loop: for p1 in [90,91,..,99]

set p1 = 90

p1 < 100 so continue

start the loop: for p2 in [90,91,..,99]

set p2 = 90

p1 + p2 > 100, so break out of the p2 loop

set p1 = 91

p1 < 100 so continue

start the loop: for p2 in [90,91,..,99]

set p2 = 90

p1 + p2 > 100, so break out of the p2 loop

set p1 = 92

...

因此带有break语句的嵌套循环仅查看10种情况-p1 = 90、91,..,99和p2 =90.p2的值永远不会大于90,并且永远不会尝试将任何内容分配给p3,p4,… ,第10页.

另一方面,itertools.product将生成所有100个案例,然后您必须过滤出总和大于的那些组合. 100

对于某些输入,itertools.product可能会更快,因为它是用C编写的,但它不会根据当前选择的总和对大小写进行任何修剪.

标签:combinations,combinatorics,python,numpy

来源: https://codeday.me/bug/20191119/2036168.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值