问题描述
问题是创建一个函数来检查该列表中某些项目的线性组合加起来是否为一定总和。结果将是带有元组的列表(与列表的长度相同)。
例如:给定列表:[3,7,10]
,sum= 60
结果:[(0,6),(1,1,5),(2,2,4),(3,3,3),(4,4,2),(5,5,1),(6,6,0),(10,(11,(12,(13,(20,0)]
问题在于列表的长度各不相同。我尝试使用一堆if语句解决问题,而不是使用for循环来解决它,但是必须有一种更有效的方法来实现它。
这是我使用的一些代码。
def get_index(l,s):
res = []
if len(l)==3:
for i in range(s+1):
for j in range(s+1):
for k in range(s+1):
if l[0]*i + l[1]*j + l[2]*k==s:
res.append((i,j,k))
return res
已经感谢!
注意:
如果我确实将范围更改为(s//l[i]+1)
,就可以使用。
解决方法
我觉得有更好的方法,但这是数组的一种残酷方法:
A = np.array([3,7,10])
b = np.array([60])
from itertools import product
combin = [np.arange(i) for i in (b//A)+1]
d = np.stack(list(product(*combin)))
[tuple(i) for i in d[d@A==b]]
或等效地没有itertools:
d = np.rollaxis(np.indices((b//A)+1),4).reshape(-1,3)
[tuple(i) for i in d[d@A==b]]
输出:
[(0,6),(1,1,5),(2,2,4),(3,3,3),(4,4,2),(5,5,1),(6,6,0),(10,(11,(12,(13,(20,0)]
比较:
#@Ehsan's solution 1
def m1(b):
combin = [np.arange(i) for i in (b//A)+1]
d = np.stack(list(product(*combin)))
return [tuple(i) for i in d[d@A==b]]
#@Ehsan's solution 2
def m2(b):
d = np.rollaxis(np.indices((b//A)+1),3)
return [tuple(i) for i in d[d@A==b]]
#@mathfux solution
def m3(b):
A,B,C = range(0,b+1,range(0,7),10)
triplets = list(product(A,C)) #all triplets
suitable_triplets = list(filter(lambda x: sum(x)==b,triplets)) #triplets that adds up to 60
return [(a//3,b//7,c//10) for a,b,c in suitable_triplets]
性能:
in_ = [np.array([n]) for n in [10,100,1000]]
这使得 m2 在其中最快。
,这成为一个完全数学上的问题。您需要找到所有线性正二元方程3a+7b+10c=60
的非负解三元组。
可以使用生成函数(多项式)说明为该方程式找到解的主要思想。让我们采用三个这样的多项式:
A=1+x^3+x^6+x^9+...+x^60
B=1+x^7+x^14+x^21+...+x^56
C=1+x^10+x^20+x^30+...+x^60
如果将它们相乘,您会发现每个术语x^n
可以表示为x^a
,x^b
和x^c
的乘积,这些术语中的每一个均取自分别为A
,B
和C
。
暴力法。您需要定义这些多项式的乘法,以跟踪被乘项,就像这样:
[0,6] * [0,14] * [0,10] = [(0,(0,10),10)]
列表在Python中没有*
运算符,但幸运的是,您可以改用itertools.product
方法。这是一个完整的解决方案:
from itertools import product
A,61,10)
triplets = list(product(A,C)) # all triplets
suitable_triplets = list(filter(lambda x: sum(x)==60,triplets)) #triplets that adds up to 60
print([[a//3,c//10] for a,c in suitable_triplets])
矢量化的蛮力。这是基于以前的脚本的,用numpy
操作替换了所有循环:
import numpy as np
l = np.array([3,10])
s = 60
unknowns = [range(0,s+1,n) for n in l]
triplets = np.stack(np.meshgrid(*unknowns),axis=-1).reshape(-1,len(unknowns))
suitable_triplets = triplets[np.sum(triplets,axis=1) == s]
solutions = suitable_triplets//l
数学方法。通常,求解线性二阶方程方程很难。浏览此SO answer。它说sympy
仅能找到参数化的解决方案,但不能识别域:
import sympy as sp
from sympy.solvers.diophantine.diophantine import diop_solve
x,y,z = sp.symbols('x y z')
solution = diop_solve(3*x + 7*y + 10*z-60)
解决方案的输出为(t_0,t_0 + 10*t_1 + 180,-t_0 - 7*t_1 - 120)
。
使用Sage可以优化解决方案,但是在这种情况下,您需要下载Linux操作系统:D