微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

检查列表中元素的线性组合是否等于某个总和的函数

如何解决检查列表中元素的线性组合是否等于某个总和的函数

问题是创建一个函数来检查该列表中某些项目的线性组合加起来是否为一定总和。结果将是带有元组的列表(与列表的长度相同)。 例如:给定列表:[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 在其中最快。

enter image description here

,

这成为一个完全数学上的问题。您需要找到所有线性正二元方程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^ax^bx^c的乘积,这些术语中的每一个均取自分别为ABC

暴力法。您需要定义这些多项式的乘法,以跟踪被乘项,就像这样:

[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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。