I’ve now been asked by two different people if Picalo can add (or subtract) a group of numbers together to see if any combinations equal a goal value. This is useful when a person comes up with a missing value during an audit. For example, if $7.07 is left after verifying some numbers, it must be the result of one or more of the values in the table.
The problem is going through the hundreds of thousands of potential combinations by hand. Picalo itself doesn’t do this (yet), but here’s a script that makes quick work of it. Note that the algorithm is *exponential*. It takes about 1 minute for my laptop to go through 8 numbers (which results in almost 20,000 possible combinations). If you have hundreds of numbers, prepare to wait a while.
The script has dummy numbers in it and seeks for a value of $7.07. It comes up with 5 potential matches. Readers should easily be able to use this script with their own data. I’ll include it as a detectlet in future version of Picalo.
- # written by Conan Albrecht
-
- # to be included in a future version of Picalo
-
- # released under the LGPL
-
- # import the PIcalo libraries
-
- from picalo import *
-
- # import commonly-needed built-in libraries
-
- import string, sys, re, random, os, os.path, urllib
-
- import itertools
-
- # the goal we want to find
-
- goal_value = 7.07
-
- # create our table of numbers
-
- # this would normally be done by importing the data
-
- # in other words, delete this part here. i'm only creating
-
- # it to make the script self-sufficient.
-
- nums = Table([("Num", number, "#.00")])
-
- nums.append(15.50)
-
- nums.append(42.33)
-
- nums.append(-33.11)
-
- nums.append(5.67)
-
- nums.append(22.31)
-
- nums.append(-15.23)
-
- nums.append(15.55)
-
- nums.append(14.88)
-
- nums.append(-7.33)
-
- # create a table to store the results in
-
- results = Table([
-
- ('Formula', str),
-
- ('Answer', number),
-
- ('DiffFromGoal', number),
-
- ])
-
- try:
-
- comb = {} # use a dict for the has_key below (to remove duplicates efficiently)
-
- pos_neg = [ [ abs(n), -1*abs(n) ] for n in nums.column('Num') ]
-
- prod = list(itertools.product(*pos_neg))
-
- combmap = {}
-
- for pi, p in enumerate(prod):
-
- show_progress('Finding all possible combinations...', float(pi) / float(len(prod)))
-
- for i in range(1, len(p)+1):
-
- for c in itertools.combinations(p, i):
-
- if not comb.has_key(c):
-
- comb[c] = None
-
- # go through and fill out the results table
-
- for i, c in enumerate(comb.keys()):
-
- show_progress('Checking combinations...', float(i) / float(len(comb)))
-
- formula = ' + '.join([ '(%s)' % num for num in c])
-
- total = sum(c)
-
- results.append(
-
- formula,
-
- total,
-
- abs(goal_value - total)
-
- )
-
- # sort the results
-
- show_progress('Sorting...', .99)
-
- Simple.sort(results, True, 'DiffFromGoal')
-
- finally:
-
- clear_progress()