繁体   English   中英

Subset-AVG - 查找与已知有理数匹配的List的子集

[英]Subset-AVG - Finding a subset of List Which Matches Known Rational Number

我已经在数学上溢出问了这个,并用评论来澄清/夸大我的问题。 我希望它具有预期的效果,并且不会让人感到不安。

我试图找到哪个数字子集达到已知平均值。

我有一个已知值列表,负数和可能的小数。 它们看起来像这样{-.32, - 。64, - 。12,.08, - 。54, - .43,...}

在某些情况下,它大约有50个数字,但这个问题也会在其他情况下进行测试。

该集合主要包含负十进制数,而在极少数情况下,有一些正小数 - 它从来没有整数。

我也有一个已知值,我知道它是上面列表的某个子集的平均值。

已知值类似于-.03。

我不确定所使用的分组机制,但似乎达不到堆栈溢出,试图在不分组时解决这个问题。

我尝试了几种方法来解决这个问题。 我正在使用Python 3.6并将numpy导入为np。

我想知道我是否已经从另一个子集总和的解决方案改编的“子集平均”代码(当我能再次找到该问题时,我会给予应有的信用)不是最有效的方式/如果有任何巨大的我甚至试图解决这个我没见过的错误。

提前感谢任何想法。

def subset_avg(numbers, target, partial=[],depth=1):
    # create AVG function

    # set average of partial
    a = np.mean(partial)

    # check if the partial sum is equals to target


    if a != target:
        print("Currently Testing the Following Subset's " " " + "Average(%s)  =  %s\n\n" % (partial, round(a,2)))
    print(depth)

    if a == target or round(a,2) == target:

            print('\n\n')
            print("************")
            print("************")
            print('\n\n')
            print("Found Subset AVG " + "Average(%s)  =  %s" % (partial, target))
            print('\n\n')
            print("************")
            print("************")
            print('\n\n')
    print(depth)        
    # for each number in range of list
    for i in range(len(numbers)):
        # set n = current iteration in list
        n = numbers[i]
        # remaining values is current iteration + 1 through end of list
        remaining = numbers[i+1:]
        # calculate mean of partial, set partial = partial plus n 
        subset_avg(remaining, target, partial + [n],depth+1)

# Example of use
x = [-.32,-.64,-.12,.08,-.54,-.43]

subset_avg(x,-.03)

这是我从另一个问题( 这里 )发布的subSet sum算法改编的解决方案。 由于算法循环使用潜在的解决方案大小,因此很容易使其适应搜索平均值。

iSubSum()函数有3个参数:目标平均值,值列表和可选的舍入精度参数。 它是一个发生器,因此当在循环中使用时它将产生所有可能的解决方案。 您还可以使用next()函数快速获得第一个解决方案。 这应该比蛮力方法更快地产生结果,特别是对于大型列表。

该函数基于子集和算法的修改版本,该算法将解决方案作为索引列表返回。 这旨在区分具有来自原始列表中的不同索引的重复值的组合。

from bisect import bisect_right
from itertools import accumulate
def iSubAverage(M,A,P=0):
    smallSize     = 20
    smallSums     = set()
    def subSumForSize(S,A,size,failedSums=None):
        nextSum = A[size-2][2] if size>1 else 0
        index   = bisect_right([a for a,_,_ in A],S-nextSum) # max element for target
        A       = A[:index]
        if len(A) < size:    return                  # not enough elements for size
        if A[size-1][2]  > S: return                 # minimum sum > target
        maxSum = A[-1][2]
        if len(A) > size: maxSum -= A[-size-1][2]
        if maxSum < S:  return                       # maximum sum < target
        if len(A) <= smallSize and S not in smallSums: return

        if failedSums is None: failedSums = set()

        while index >= size: 
            index -= 1
            a,i,ca = A[index]
            if size == 1:
                if a == S: yield [i]
                continue
            c0 = A[index-size][2] if index>size else 0
            if ca-c0 < S: break
            subS = S-a
            if subS in failedSums: continue # known unreachable sum
            failed = True
            for result in subSumForSize(subS,A[:index],size-1,failedSums):
                yield result+[i]
                failed = False
            if failed: failedSums.add(subS)

    if not A: return
    if M < 0: M,A = -M,[-a for a in A] # must have positive target
    offset = max(0,-min(A)) # circumvent negatives (requires loop on sizes)
    A      = sorted([(round(a+offset,P),i) for i,a in enumerate(A)])
    cumA   = accumulate(a for a,i in A)
    A      = [(a,i,ca) for (a,i),ca in zip(A,cumA)]

    for a,_,_ in A[:smallSize]:
        newSums = [a+s for s in smallSums] + [a]
        smallSums.update(newSums)

    for size in range(1,len(A)+1):
        subS  = round(M*size,P)
        if subS != round(M*size,P*2): continue # fractional numerator
        subS += round(offset*size,P)
        for result in subSumForSize(subS,A,size):
            yield result

为了获得实际值, iSubAvg()函数将索引映射到列表中的相应值:

def iSubAvg(M,A,P=0):
    for iA in iSubAverage(M,A,P):
        yield sorted([A[i] for i in iA])

L       = [-.32,-.64,-.12,.08,-.54,-.43]
targetL = -0.02
for solution in iSubAvg(targetL,L,2):
    print(solution)

# [-0.12, 0.08]   (there isn't a solution for -0.03)

K = [0.72, 0.69, 0.81, -0.28, 0.6, 0.59, 0.77, 0.46, 0.36, 0.66, 0.88, 0.88, 0.9, -0.24, 0.5, -0.5, 0.46, 0.96, -0.22, -0.8, -0.13, 0.87, 0.78, 0.2]    
targetK = -0.02
for solution in iSubAvg(targetK,K,2):
    print(solution)

# [-0.5, 0.46]
# [-0.5, 0.46]
# [-0.8, -0.22, 0.96]
# [-0.5, -0.28, 0.72]
# [-0.28, -0.24, 0.46]
# [-0.28, -0.24, 0.46]
# [-0.5, -0.24, 0.2, 0.46]
# [-0.5, -0.24, 0.2, 0.46]
# [-0.8, -0.28, -0.24, -0.22, 0.46, 0.96]
# [-0.8, -0.28, -0.24, -0.22, 0.46, 0.96]


next(iSubAvg(0.165,K,2))

# [-0.8, -0.28, -0.24, 0.66, 0.69, 0.96]

请注意,该函数返回所有组合,包括源列表中重复值的重复。 如果您不需要,可以过滤掉这些重复项

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM