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()
[codesyntax lang="python"]
# 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()
[/codesyntax]