简体   繁体   中英

dynamic programming - find numbers that add up to a sum

EDIT - based on some feedback, i have decided to mention the problem statement. If some of you want to check what i have tried, please feel free to scroll below the "--------------" line

we need to be able to pick line items from an invoice and one of the ways we are trying to make sure we get the total amount correct is the following ..suppose i manage to extract an array of numbers [ 235, 120, 340, 600, 150000, 1175 ] i would find out the largest subset that adds up to one of the numbers in the array , which in this case is going to be 1175 = 235 + 340 + 600 .. i have tried a partial DP approach to it ( details below ) , but if this info is good enough i would like a complete DP solution, minus the hacks i had to use ..appreciate it.

"-----------------------------------------------------------"

im new to DP and wanted to implement a solution (at scale) for our product. Basically we need to find all line items in a table that add up to the total and since we dont use "keywords" one of the solutions was to find all possible numbers and then add up subsets to see which subset resulted in the biggest number. Theoretically this always gave us the right answer but my implementation in DP looks a little hackey .. pfb the code and i will have the queries at the end ..thanks for your patience

import numpy as np

#main_arr = [ 3, 4, 1, 8, 10, 16 ]
main_arr = [ 3, 4, 1, 7, 15, 23, 25, 36 ]

sorted_arr_ = sorted( main_arr )

matrix_ = dict()

def calcMatrix():
        for outer_ in sorted_arr_:
                for inner_ in sorted_arr_:
                        if outer_ == inner_: continue
                        matrix_[ str(outer_)+'-'+str(inner_) ] = inner_ + outer_
calcMatrix()

print( matrix_ )

mem_dict_ = dict()
for ctr in range(1, max(main_arr)):
        if ctr in sorted_arr_:
                mem_dict_[ ctr ] = str(ctr)
        else:
                mem_dict_[ ctr ] = '0'

def search_matrix( balance ):
        for key, val in matrix_.items():
                if val == balance: return key

        return None
def search_mem( balance ):
        for key, val in mem_dict_.items():
                if balance == key and val != '0': return val

        return None

def findAlt( bal, avoid ):
        for idx in range( bal ):
                major, minor = idx, bal-idx
                maj, mino = None, None
                print('In findAlt->', major, minor)
                if major != avoid and minor != avoid:
                        matrix_res_major = search_matrix( major )
                        matrix_res_minor = search_matrix( minor )
                        mem_res_major = search_mem( major )
                        mem_res_minor = search_mem( minor )
                        maj = matrix_res_major if mem_res_major is None else mem_res_major
                        mino = matrix_res_minor if mem_res_minor is None else mem_res_minor
                        if maj is not None and mino is not None: return maj+mino

def recur( updated_idx, main_idx ):
        if updated_idx <= 0 : return None

        bal_ = sorted_arr_[ main_idx ] - sorted_arr_[ updated_idx-1 ]
        print( 'updated_idx, main_idx, bal_, sorted_arr_[ updated_idx ], sorted_arr_[ updated_idx-1 ] = ',\
                updated_idx, main_idx, bal_, sorted_arr_[ updated_idx ], sorted_arr_[ updated_idx-1 ] )

        pos_key_ = search_matrix( bal_ )
        pos_mem_key_bal_ = search_mem( bal_ )
        pos_mem_key_ = search_matrix( sorted_arr_[ main_idx ] )
        print( pos_key_, pos_mem_key_bal_, pos_mem_key_ )

        if pos_key_ is not None :
                if str(sorted_arr_[ updated_idx-1 ]) not in str(pos_key_):
                        mem_dict_[ sorted_arr_[ main_idx ] ] = str(pos_key_)+'-'+str(sorted_arr_[ updated_idx-1 ])
                        print('Updated mem with key (a) = ', sorted_arr_[ main_idx ], ' with str = ', \
                                str(pos_key_)+str(sorted_arr_[ updated_idx-1 ]) )
                else:
                        posKey = findAlt( bal_, sorted_arr_[ updated_idx-1 ] )
                        if posKey is not None:
                                mem_dict_[ sorted_arr_[ main_idx ] ] = str(posKey)+'-'+\
                                                                        str(sorted_arr_[ updated_idx-1 ])
                                print('Updated mem with key (a.1) = ', sorted_arr_[ main_idx ], ' with str = ', \
                                str(posKey)+str(sorted_arr_[ updated_idx-1 ]) )

        elif pos_key_ is None and pos_mem_key_bal_ is not None and str(sorted_arr_[ updated_idx-1 ]) \
                        not in str( pos_mem_key_bal_ ):
                mem_dict_[ sorted_arr_[ main_idx ] ] = str(pos_mem_key_bal_)+'-'+str(sorted_arr_[ updated_idx-1 ])
                print('Updated mem with key (b) = ', sorted_arr_[ main_idx ], ' with str = ', \
                        str(pos_mem_key_bal_)+str(sorted_arr_[ updated_idx-1 ]) )
        else:
                recur( updated_idx-1, main_idx )
for i in range( 2, len(sorted_arr_) ):
        recur( i, i )

so first i create a matrix that calculates the pair wise sum of all elements and use it as a base to find numbers that could be potential sums of the elements. Then while iterating through the sorted input array, i try and create a memoized array that holds sums of members of the array. This works in most cases but in case the subset contains more than 3 elements ( meaning its a sum of more than 3 numbers ) it can fail and hence i had to add that hackey method findAlt ..bottomline is that though this works, im not happy with the code. I am quite sure this can be done in 10-15 lines of code but i have tried multiple iterations and now making the same mistakes. Any pointers will be greatly appreciated.

Have a look here: Algorithm to find which numbers from a list of size n sum to another number

This is my solution

(I'm assuming : the numbers in the solution are unique, all the numbers positive and the target greater than zero)

recursive approach

def find_longest_sum(array: list, target: int):
   solution = []
   array = sorted(array)
   def rec_finder(sublst: list, s: int):
      nonlocal solution
      if s == target and len(sublst) > len(solution):
         solution = sublst
      else:
         for n in array:
            if s+n > target:
               break
            if n not in sublst:
               rec_finder(sublst + [n], s+n)
   rec_finder([],0)
   return solution

not recursive approach

def find_longest_sum_not_rec(array: list, target: int):
   array = sorted(array)
   solutions = [[n] for n in array]
   solution = []

   while solutions:
      new_solutions = []
      for sol in solutions:
         for n in array:
            if n not in sol:
               # Note here I'm calling sum for simplicity but
               # you'd want to save the sum and reuse it 
               # like I did for the recursive solution,
               # probably a NamedTuple would be a fine solution
               if sum(sol)+n < target:
                  new_solutions.append(sol+[n])
               elif sum(sol)+n == target and len(solution) < len(sol)+1:
                  solution = sol+[n]
                  break
               else:
                  break
      solutions = new_solutions
   return solution
print(find_longest_sum([1,2,3,5], 8)) #[1, 2, 5]
print(find_longest_sum_not_rec([1,2,3,5], 8)) #[1, 2, 5]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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