Visual Basic Code Issue - visual-studio-2010

Can somebody explain why this doesn't work? (meaning the variable colorChange stays the same throughout function)
Each array (leftRing and rightRing have integer values of numbers ranging from 1 through 4)
Private Sub Solve_Click(sender As System.Object, e As System.EventArgs) Handles Solve.Click
countColorChangesInRings(colorChanges)
RotateLeftRingClockwise()
countColorChangesInRings(colorChanges)
End Sub
Sub countColorChangesInRings(ByRef var As Integer)
Dim colorChangesLeft As Integer = 0
Dim colorChangesRight As Integer = 0
Dim totalChanges As Integer = 0
' Count Color Changes in Left Ring
For i = 1 To 20
If (i = 20) Then
If (leftRing(i) <> leftRing(1)) Then
colorChangesLeft += 1
End If
Else
If (leftRing(i) <> leftRing(i + 1)) Then
colorChangesLeft += 1
End If
End If
Next
' Count Color Changes in Right Ring
For i = 1 To 20
If (i = 20) Then
If (rightRing(i) <> rightRing(1)) Then
colorChangesRight += 1
End If
Else
If (rightRing(i) <> rightRing(i + 1)) Then
colorChangesRight += 1
End If
End If
Next
totalChanges = colorChangesLeft + colorChangesRight
var = totalChanges
End Sub
Sub RotateLeftRingClockwise()
Dim temp As Integer = leftRing(1)
' Rotates Rings clockwise direction
For i = 1 To 20
If (i = 20) Then
leftRing(i) = temp
Else
leftRing(i) = leftRing(i + 1)
End If
Next
End Sub

From what I can see, it will stay the same.
You basically have a circular buffer of values (due to your special handle when i = 20) and you count the number of times the value changes from index to index.
That number is not going to change if you simply rotate the values through the circular buffer.
Take for example, the array {1, 2, 2, 2, 3, 4}. There are four transitions there, 1-2, 2-3, 3-4 and 4-1.
If you then rotate the list clockwise, you get {4, 1, 2, 2, 2, 3} and the transitions are 4-1, 1-2, 2-3 and 3-4.
In other words, the order of the transitions may change, but the quantity does not.
Based on your addition of the RotateLeftRingClockwise code, this is where your problem lies.
Because it simply rotates the left ring, you're not handling the intersection points of the Hungarian rings correctly. Rotating the left ring would, as well as moving the values around the left ring, change two of the array elements in the right ring (and vice versa), as per the diagram below:
array element 1 (in a 32-element array)
|
+---+---+
| |
V V
LLL RRR
LL LL RR RR
L * R <- shared cell is left(29)/right(5)
L R L R
L R L R
L R L R
L R L R
L R L R
L R L R
L R L R
L * R <- shared cell is left(21)/right(13)
LL LL RR RR
\ LLL RRR /
\ /
\----->>>-----/
Direction of
increasing
array index
Because your rotate code does not do this, the transition count of both rings will remain identical and hence the sum of them will not change.
For example, say the top-middle cells are array element 1 (as shown) and it increases in a anti-clockwise direction (as your code seems to indicate from the fact that a clockwise rotation shifts from ring[i+1] to ring[i]).
What your code needs to do is first rotate the left ring and then force the new values for the * cells into the right ring, something like:
Sub RotateLeftRingClockwise()
Dim temp As Integer = leftRing(1)
For i = 1 To 19
leftRing(i) = leftRing(i + 1)
Next
leftRing(20) = temp
rightRing( 5) = leftRing(29)
rightRing(13) = leftRing(21)
End Sub
Those array indexes in the last two lines are specific to my diagram above (32 cells rather than 20), you'll need to adjust them for your own puzzle.
If you make those changes (and similar ones to your other three rotation functions), you should find that the function starts returning different values. Specifically, rotating the left ring will leave the left value unchanged but should change the right value as different balls come into the intersection point.

Related

Find second minimum for each row of a matrix

I have a set i of customers and a set j of facilities. I have two binary variables: y ij which is 1 if client i is served by a primary facility, 0 otherwise; b ij is 1 if client i is served by a backup facility, 0 otherwise.
Given the starting matrix d:
-I must set y[i,j] = 1 based on the minimum distance of each row in the matrix (and this I have done);
I have to fix b[i,j] = 1 according to the second minimum distance of each row in the matrix (I don't know how to do this. I wrote max, but I don't have to do that). I've tried removing the first minimum from each row with the various pop, deleteat, splice, etc, but the solver gives me an error.
using JuMP
using Gurobi
using DelimitedFiles
import Random
import LinearAlgebra
import Plots
n = 3
m = 5
model = Model(Gurobi.Optimizer);
#variable(model, y[1:m,1:n] >= 0, Bin);
#variable(model, b[1:m,1:n] >= 0, Bin);
d = [
[80 20 40]
[71 55 24]
[56 47 81]
[10 20 30]
[31 41 21]
];
#PRIMARY ASSIGNMENTS
# 1) For each customer find the minimum d i-j and its position in matrix and create a vector V composed by all d i-j just founded
V = [];
for i = 1:m;
c = findmin(d[i,j] for j = 1:n);
push!(V,[c[1] ,c[2], i]);
end
println(V)
# 2) Sort vector's evelements from the smallest to the largest
S = sort(V)
println(S)
for i = 1:m
println(S[i][2])
println(S[i][3])
end
# 3) Fix primary assingnments for the first 50% of customers
for i = 1:3
fix(y[S[i][3], S[i][2]], 1.0, force = true);
end
# SECONDARY ASSIGNMENTS
# 1) For each customer find the second minimum d i-j and its position in matrix and create a vector W composed by all d i-j just founded
W = [];
for i = 1:m;
f = findmax(d[i,j] for j = 1:n);
push!(W,[f[1] ,f[2], i]);
end
println(W)
# 2) Sort vector's elements from the smallest to the largest
T = sort(W)
println(T)
for i = 1:3
println(T[i][2])
println(T[i][3])
end
# 3) Fix secondary assingnments for the first 50% of customers
for i = 1:3
fix(b[T[i][3], T[i][2]], 1.0, force = true);
end
optimize!(model)
I tried to find for each line the second minimum, but I could not.

an algorithm for uniquely random patterns of colors for Christmas lights written in AppleScript

This post is ultimately about creating files of random patterns for smart Christmas lights.
I have 7 colors (Red, Green, Blue, Orange, Purple, White, Dark) that I have chosen that I want to semi-randomize.
I have 4 "frames" that is a 5 wide x 4 tall grid that I want to fill with "random" colors, with no color being side my side, or directly up and down from one another and no color repeating between frames in each grid location.
Here are examples of what the totally random frames might look like. Completely random is easy, it is the "no duplicates" random that I am looking for. Again, these frames are NOT what I am ultimately looking for, but they are close. Also the following is purely for visual reference, in AppleScript (or probably any other programming language), the following would be a list of lists.
P G O R D D O G D O P W D O G W O G B W
W B P D O P G R G B R G B O P D R B P G
R G B D O R O G O W P O W D R O G R W D
B G W D B D R G P R R W D O B B O G D W
So here is some code that gets me to the first column of the first frame:
set colorList to {"R", "G", "B", "D", "O", "W", "P"}
set newColumn to {{}, {}, {}, {}, {}}
set previousColor to {}
set previousColumn to {}
repeat with i from 1 to 4 -- for the 4 frames that I need
repeat with j from 1 to 5 -- for the 5 columns that each frame needs
repeat with k from 1 to 4 -- for the 4 values I need in each column
set isRandom to false
set theColor to some item of colorList
if i is not greater than 1 then -- if i is 1 then it is the first frame of the series
if j is not greater than 1 then -- if J is 1 then it is the first column
if k is not greater than 1 then -- if K is 1 then its the first value of the column
set end of (item j of newColumn) to theColor
else
if theColor is not previousColor then -- if k is greater than 1 then ¬
--check the color against the previous color, to make sure they are not the same
set end of (item j of newColumn) to theColor
set previousColor to theColor
else
repeat until isRandom is true
set theColor to some item of colorList
if theColor is not previousColor then
set end of (item j of newColumn) to theColor
set isRandom to true
end if
end repeat
end if
end if
end if
end if
end repeat
end repeat
end repeat
I have tried to go further than, but every time I do, it becomes an incoherent mess of if-then-else statements that I get lost in.
So the script needs to do 3 things before it adds a color to the column:
Make sure that the previous color of the column is not the same
Make sure (if the column we are dealing with is not column 1) that the color is not the same as the previous columns item,
Make sure (if the frame we are dealing with is beyond the first frame) that the color is not the same as it was in the same position as the frame before.
So the question that I am asking is, is their a handler or algorithm here that I am missing (I am no CS Major) that would make this task more straightforward?
That is all you probably NEED to know about my issue, but for reference sake, I am trying to create the patterns for Christmas lights. The colorList in my script actually looks like this:
set colorList to {{255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {255, 255, 255}, {255, 128, 0}, {255, 255, 0},{0, 0, 0}}
So in the end I need a text file that is formatted exactly like this:
255, 0, 0
0, 0, 255
0, 0, 0
255, 255, 0
(there is a space character in front of each line).
I take the text file, convert it over to binary, and then use some Python to send the file to the REST API that the lights are using.
The lights are from a company called Twinkly.
I am just trying to make the process of creating some scenes easier for me (and learn something in the process, hopefully).
Ok, I went ahead and did this without objC. The approach I used was to first build an empty array of an appropriate size and shape, and then run through the array adding colors, blocking the colors from being added to farther-on adjacent cells. It has some tricky aspects — recursions to drill down through the nested lists, using references for speed optimization and list processing, mocking up a version of an indexPath to locate list elements — but you ought to get the idea of it fairly quickly.
I've left the finalization of the script to you; this produces a list of text elements in the property fullList that you can convert into the form you need. If you have any questions, ask in the comments.
property colorList : {"Red", "Green", "Blue", "Orange", "Purple", "White", "Dark"}
property fullList : missing value
property columns : 5
property rows : 4
property blocks : 4
my buildEmptyNestedList()
my setColorsRecursivelyInNestedList:(a reference to fullList) withIndexList:{}
return get fullList
on buildEmptyNestedList()
set fullList to {}
set fullListRef to a reference to fullList
repeat blocks times
set blocksList to {}
repeat rows times
set rowsList to {}
repeat columns times
set end of rowsList to {}
end repeat
copy rowsList to end of blocksList
end repeat
copy blocksList to end of fullListRef
end repeat
end buildEmptyNestedList
on setColorsRecursivelyInNestedList:nestedListElement withIndexList:idxList
local idx
if lists of nestedListElement is equal to {} then -- bottom level: empty list or list of strings
set localColors to my filterColorsByList:nestedListElement
set chosenColor to some item of localColors
set excludePathsList to my processPathList:idxList
repeat with aPathList in excludePathsList
set target to (my getSublistByIndexes:aPathList)
copy chosenColor to end of target
end repeat
set contents of nestedListElement to chosenColor
else
set idx to 1
repeat with aSublist in nestedListElement
copy idxList to nextIdxList
set nextIdxList to nextIdxList & idx
(my setColorsRecursivelyInNestedList:(a reference to (item idx of nestedListElement)) withIndexList:nextIdxList)
set idx to idx + 1
end repeat
end if
end setColorsRecursivelyInNestedList:withIndexList:
on getSublistByIndexes:idxList
set foundList to item (item 1 of idxList) of fullList
repeat with idx in (rest of idxList)
set foundList to item idx of foundList
end repeat
return foundList
end getSublistByIndexes:
on getElementFromList:aNestedList byIndexes:idxArray
set currIdx to item 1 of idxArray
if (count of idxArray) = 1 then
return item currIdx of aNestedList
else
return my getElementFromList:(item currIdx of aNestedList) byIndexes:(rest of idxArray)
end if
end getElementFromList:byIndexes:
on processPathList:aPathList
set pathCheckList to {}
repeat with i from 1 to count of aPathList
copy aPathList to tempList
set item i of tempList to (item i of tempList) + 1
if item i of tempList ≤ item i of {blocks, rows, columns} then
copy tempList to end of pathCheckList
end if
end repeat
return pathCheckList
end processPathList:
on filterColorsByList:aList
set filteredList to {}
set colorListRef to a reference to colorList
repeat with aColor in colorListRef
if aColor is not in aList then
copy aColor as text to end of filteredList
end if
end repeat
return filteredList
end filterColorsByList:

Calculating particle equilibrium using Monte Carlo

I am trying to write a function that calculates the number of iterations it takes for two chambers to have equally as many particles.The evolution of the system is considered as a series of time-steps, beginning at t = 1. At each time-step exactly
one particle will pass through the hole, and we assume that the particles do not interact. The probability that
a particle will move from the left to the right chamber is pLR = NL/N, and the probability of a particle will
move from the right to the left chamber is pRL = 1 − pLR = (N − NL)/N.
The simulation will iteratively proceed as follows:
Get a random number r from the interval 0 ≤ r ≤ 1.
If r ≤ pLR, move one particle from the left to the right chamber. Otherwise move one particle from the
right to the left chamber.
Repeat step 1 and 2 until NL = NR. Report back, how many time-steps it took to reach this equilibrium
This is my code thus far.
function t = thermoEquilibrium(N, r) %N = number of particles, r = random numbers from 0-1
h = []; %right side of the chamber
v = []; %left side of the chamber
rr = r;
k = false
NL=N-length(h) %This is especially where i suspect i make a mistake.
%How can the probability change with every iteration?
pLR = NL/N;
pRL = 1 - pLR;
count = 1
while k==false
for i = r
if i<=pLR
h(end+1)=i
rr = rr(rr~=i)
end
end
for l = h
if pRL>l
v(end+1) = l
h = h(h~=l)
end
end
if length(h)==N/2 && length(v)==N/2
k=true
end
count = count + 1
end
t = count
Can someone point me in a direction, so i can get a bit closer to something that works?

Combining two lists with minimum distance between elements

I have to lists like these:
a = ["1a","2a","3a","4a","5a","6a","7a","8a","9a","10a","11a","12a","13a","14a"]
b = ["1b","2b","3b","4b","5b","6b","7b","8b","9b","10b","11b","12b","13b","14b"]
And what I want is to combine them, so that there is at least a difference of n elements between an element from a and it's corresponding element in b.
As an example, if my n is 10, and "3a" is in position 3 and "3b" is in position 5, that isn't a solution since there's only a distance of 2 between these corresponding elements.
I have already solved this for the purpose I want through a brute force method: shuffle the union of the two arrays and see if the constraint is met; if not, shuffle again and so on... Needless to say, that for 14 elements array, sometimes there is 5 to 10 second computation to yield a solution with a minimum distance of 10. Even though that's kind of ok for what I am looking for, I am curious about how I could solve this in a more optimized way.
I am currently using Python, but code in any language (or pseudo-code) is more than welcomed.
EDIT: The context of this problem is something like a questionnarie, in which around 100 participants are expected to join in. Therefore, I am not necessarily interested in all the solutions, but rather something like the first 100.
Thanks.
For your specific scenario, you could use a randomized approach -- though not as random as what you've already tried. Something like this:
start with a random permutation of the items in both lists
create a new permutation by creating a copy of the other and randomly swapping two items
measure the quality of the permutations, e.g., the sum of the distances of each pair of related items, or the minimum of such distances
if the quality of the new permutation is better than that of the original permutation, keep the new one, otherwise discard the new one and continue with the original permutation
repeat from 2. until each distance is at least 10 or until quality does not improve over a number of iterations
The difference to shuffling the whole list in each iteration (as in your approach) is that in each iteration the permutation can only get better, until a satisfying solution is found.
Each time you run this algorithm, the result will be slightly different, so you can run it 100 times for 100 different solutions. Of course, this algorithm does not guarantee to find a solution (much less all such solutions), but it should be fast enough so that you could just restart it in case it fails.
In Python, this could look somewhat like this (slightly simplified, but still working):
def shuffle(A, B):
# original positions, i.e. types of questions
kind = dict([(item, i) for i, item in list(enumerate(A)) + list(enumerate(B))])
# get positions of elements of kinds, and return sum of their distances
def quality(perm):
pos = dict([(kind[item], i) for i, item in enumerate(perm)])
return sum(abs(pos[kind[item]] - i) for i, item in enumerate(perm))
# initial permutation and quality
current = A + B
random.shuffle(current)
best = quality(current)
# improve upon initial permutation by randomly swapping items
for g in range(1000):
i = random.randint(0, len(current)-1)
j = random.randint(0, len(current)-1)
copy = current[:]
copy[i], copy[j] = copy[j], copy[i]
q = quality(copy)
if q > best:
current, best = copy, q
return current
Example output for print shuffle(a, b):
['14b', '2a', '13b', '3a', '9b', '4a', '6a', '1a', '8a', '5b', '12b', '11a', '10b', '7b', '4b', '11b', '5a', '7a', '8b', '12a', '13a', '14a', '1b', '2b', '3b', '6b', '10a', '9a']
As I understand from your question, it is possible to perform all the ordering by relying exclusively on the indices of the arrays (i.e., on pure integers) and thus the problem can be reduced to create (valid) ranges instead of analysing each element.
for each a <= total_items-n , valid b = if(a + n == total_items){total_items} else{[a + n, total_items]}
For example:
n = 10;
total_items = 15;
for a = 1 -> valid b = [11, 15]
for a = 2 -> valid b = [12, 15]
, etc.
This would be perfomed 4 times: forwards and backwards for a respect to b and the same for b respect to a.
In this way you would reduce the number of iterations to its minimum expression and would get, as an output, a set of "solutions" for each position, rather than a one-to-one binding (that is what you have right now, isn't it?).
If there are equivalents in Python to .NET's Lists and LINQ, then you may be able to directly convert the following code. It generates up to 100 lists really quickly: I press "debug" to run it and up pops a windows with the results in much less than a second.
' VS2012
Option Infer On
Module Module1
Dim minDistance As Integer = 10
Dim rand As New Random ' a random number generator
Function OkToAppend(current As List(Of Integer), x As Integer) As Boolean
' see if the previous minDistance values contain the number x
Return Not (current.Skip(current.Count - minDistance).Take(minDistance).Contains(x))
End Function
Function GenerateList() As List(Of String)
' We don't need to start with strings: integers will make it faster.
' The "a" and "b" suffixes can be sprinkled on at random once the
' list is created.
Dim numbersToUse() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
Dim pool As New List(Of Integer)
' we need all the numbers twice
pool.AddRange(numbersToUse)
pool.AddRange(numbersToUse)
Dim newList As New List(Of Integer)
Dim pos As Integer
For i = 0 To pool.Count - 1
' limit the effort it puts in
Dim sanity As Integer = pool.Count * 10
Do
pos = rand.Next(0, pool.Count)
sanity -= 1
Loop Until OkToAppend(newList, pool(pos)) OrElse sanity = 0
If sanity > 0 Then ' it worked
' append the value to the list
newList.Add(pool(pos))
' remove the value which has been used
pool.RemoveAt(pos)
Else ' give up on this arrangement
Return Nothing
End If
Next
' Create the final list with "a" and "b" stuck on each value.
Dim stringList As New List(Of String)
Dim usedA(numbersToUse.Length) As Boolean
Dim usedB(numbersToUse.Length) As Boolean
For i = 0 To newList.Count - 1
Dim z = newList(i)
Dim suffix As String = ""
If usedA(z) Then
suffix = "b"
ElseIf usedB(z) Then
suffix = "a"
End If
' rand.Next(2) generates an integer in the range [0,2)
If suffix.Length = 0 Then suffix = If(rand.Next(2) = 1, "a", "b")
If suffix = "a" Then
usedA(z) = True
Else
usedB(z) = True
End If
stringList.Add(z.ToString & suffix)
Next
Return stringList
End Function
Sub Main()
Dim arrangements As New List(Of List(Of String))
For i = 1 To 100
Dim thisArrangement = GenerateList()
If thisArrangement IsNot Nothing Then
arrangements.Add(thisArrangement)
End If
Next
'TODO: remove duplicate entries and generate more to make it up to
' the required quantity.
For Each a In arrangements
' outputs the elements of a with ", " as a separator
Console.WriteLine(String.Join(", ", a))
Next
' wait for user to press enter
Console.ReadLine()
End Sub
End Module

Adding values in various combinations

Not sure how best to explain it, other than using an example...
Imagine having a client with 10 outstanding invoices, and one day they provide you with a cheque, but do not tell you which invoices it's for.
What would be the best way to return all the possible combination of values which can produce the required total?
My current thinking is a kind of brute force method, which involves using a self-calling function that runs though all the possibilities (see current version).
For example, with 3 numbers, there are 15 ways to add them together:
A
A + B
A + B + C
A + C
A + C + B
B
B + A
B + A + C
B + C
B + C + A
C
C + A
C + A + B
C + B
C + B + A
Which, if you remove the duplicates, give you 7 unique ways to add them together:
A
A + B
A + B + C
A + C
B
B + C
C
However, this kind of falls apart after you have:
15 numbers (32,767 possibilities / ~2 seconds to calculate)
16 numbers (65,535 possibilities / ~6 seconds to calculate)
17 numbers (131,071 possibilities / ~9 seconds to calculate)
18 numbers (262,143 possibilities / ~20 seconds to calculate)
Where, I would like this function to handle at least 100 numbers.
So, any ideas on how to improve it? (in any language)
This is a pretty common variation of the subset sum problem, and it is indeed quite hard. The section on the Pseudo-polynomial time dynamic programming solution on the page linked is what you're after.
This is strictly for the number of possibilities and does not consider overlap. I am unsure what you want.
Consider the states that any single value could be at one time - it could either be included or excluded. That is two different states so the number of different states for all n items will be 2^n. However there is one state that is not wanted; that state is when none of the numbers are included.
And thus, for any n numbers, the number of combinations is equal to 2^n-1.
def setNumbers(n): return 2**n-1
print(setNumbers(15))
These findings are very closely related to combinations and permutations.
Instead, though, I think you may be after telling whether given a set of values any combination of them sum to a value k. For this Bill the Lizard pointed you in the right direction.
Following from that, and bearing in mind I haven't read the whole Wikipedia article, I propose this algorithm in Python:
def combs(arr):
r = set()
for i in range(len(arr)):
v = arr[i]
new = set()
new.add(v)
for a in r: new.add(a+v)
r |= new
return r
def subsetSum(arr, val):
middle = len(arr)//2
seta = combs(arr[:middle])
setb = combs(arr[middle:])
for a in seta:
if (val-a) in setb:
return True
return False
print(subsetSum([2, 3, 5, 8, 9], 8))
Basically the algorithm works as this:
Splits the list into 2 lists of approximately half the length. [O(n)]
Finds the set of subset sums. [O(2n/2 n)]
Loops through the first set of up to 2floor(n/2)-1 values seeing if the another value in the second set would total to k. [O(2n/2 n)]
So I think overall it runs in O(2n/2 n) - still pretty slow but much better.
Sounds like a bin packing problem. Those are NP-complete, i.e. it's nearly impossible to find a perfect solution for large problem sets. But you can get pretty close using heuristics, which are probably applicable to your problem even if it's not strictly a bin packing problem.
This is a variant on a similar problem.
But you can solve this by creating a counter with n bits. Where n is the amount of numbers. Then you count from 000 to 111 (n 1's) and for each number a 1 is equivalent to an available number:
001 = A
010 = B
011 = A+B
100 = C
101 = A+C
110 = B+C
111 = A+B+C
(But that was not the question, ah well I leave it as a target).
It's not strictly a bin packing problem. It's a what combination of values could have produced another value.
It's more like the change making problem, which has a bunch of papers detailing how to solve it. Google pointed me here: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.57.3243
I don't know how often it would work in practice as there are many exceptions to this oversimplified case, but here's a thought:
In a perfect world, the invoices are going to be paid up to a certain point. People will pay A, or A+B, or A+B+C, but not A+C - if they've received invoice C then they've received invoice B already. In the perfect world the problem is not to find to a combination, it's to find a point along a line.
Rather than brute forcing every combination of invoice totals, you could iterate through the outstanding invoices in order of date issued, and simply add each invoice amount to a running total which you compare with the target figure.
Back in the real world, it's a trivially quick check you can do before launching into the heavy number-crunching, or chasing them up. Any hits it gets are a bonus :)
Here is an optimized Object-Oriented version of the exact integer solution to the Subset Sums problem(Horowitz, Sahni 1974). On my laptop (which is nothing special) this vb.net Class solves 1900 subset sums a second (for 20 items):
Option Explicit On
Public Class SubsetSum
'Class to solve exact integer Subset Sum problems'
''
' 06-sep-09 RBarryYoung Created.'
Dim Power2() As Integer = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32764}
Public ForceMatch As Boolean
Public watch As New Stopwatch
Public w0 As Integer, w1 As Integer, w1a As Integer, w2 As Integer, w3 As Integer, w4 As Integer
Public Function SolveMany(ByVal ItemCount As Integer, ByVal Range As Integer, ByVal Iterations As Integer) As Integer
' Solve many subset sum problems in sequence.'
''
' 06-sep-09 RBarryYoung Created.'
Dim TotalFound As Integer
Dim Items() As Integer
ReDim Items(ItemCount - 1)
'First create our list of selectable items:'
Randomize()
For item As Integer = 0 To Items.GetUpperBound(0)
Items(item) = Rnd() * Range
Next
For iteration As Integer = 1 To Iterations
Dim TargetSum As Integer
If ForceMatch Then
'Use a random value but make sure that it can be matched:'
' First, make a random bitmask to use:'
Dim bits As Integer = Rnd() * (2 ^ (Items.GetUpperBound(0) + 1) - 1)
' Now enumerate the bits and match them to the Items:'
Dim sum As Integer = 0
For b As Integer = 0 To Items.GetUpperBound(0)
'build the sum from the corresponding items:'
If b < 16 Then
If Power2(b) = (bits And Power2(b)) Then
sum = sum + Items(b)
End If
Else
If Power2(b - 15) * Power2(15) = (bits And (Power2(b - 15) * Power2(15))) Then
sum = sum + Items(b)
End If
End If
Next
TargetSum = sum
Else
'Use a completely random Target Sum (low chance of matching): (Range / 2^ItemCount)'
TargetSum = ((Rnd() * Range / 4) + Range * (3.0 / 8.0)) * ItemCount
End If
'Now see if there is a match'
If SolveOne(TargetSum, ItemCount, Range, Items) Then TotalFound += 1
Next
Return TotalFound
End Function
Public Function SolveOne(ByVal TargetSum As Integer, ByVal ItemCount As Integer _
, ByVal Range As Integer, ByRef Items() As Integer) As Boolean
' Solve a single Subset Sum problem: determine if the TargetSum can be made from'
'the integer items.'
'first split the items into two half-lists: [O(n)]'
Dim H1() As Integer, H2() As Integer
Dim hu1 As Integer, hu2 As Integer
If ItemCount Mod 2 = 0 Then
'even is easy:'
hu1 = (ItemCount / 2) - 1 : hu2 = (ItemCount / 2) - 1
ReDim H1((ItemCount / 2) - 1), H2((ItemCount / 2) - 1)
Else
'odd is a little harder, give the first half the extra item:'
hu1 = ((ItemCount + 1) / 2) - 1 : hu2 = ((ItemCount - 1) / 2) - 1
ReDim H1(hu1), H2(hu2)
End If
For i As Integer = 0 To ItemCount - 1 Step 2
H1(i / 2) = Items(i)
'make sure that H2 doesnt run over on the last item of an odd-numbered list:'
If (i + 1) <= ItemCount - 1 Then
H2(i / 2) = Items(i + 1)
End If
Next
'Now generate all of the sums for each half-list: [O( 2^(n/2) * n )] **(this is the slowest step)'
Dim S1() As Integer, S2() As Integer
Dim sum1 As Integer, sum2 As Integer
Dim su1 As Integer = 2 ^ (hu1 + 1) - 1, su2 As Integer = 2 ^ (hu2 + 1) - 1
ReDim S1(su1), S2(su2)
For i As Integer = 0 To su1
' Use the binary bitmask of our enumerator(i) to select items to use in our candidate sums:'
sum1 = 0 : sum2 = 0
For b As Integer = 0 To hu1
If 0 < (i And Power2(b)) Then
sum1 += H1(b)
If i <= su2 Then sum2 += H2(b)
End If
Next
S1(i) = sum1
If i <= su2 Then S2(i) = sum2
Next
'Sort both lists: [O( 2^(n/2) * n )] **(this is the 2nd slowest step)'
Array.Sort(S1)
Array.Sort(S2)
' Start the first half-sums from lowest to highest,'
'and the second half sums from highest to lowest.'
Dim i1 As Integer = 0, i2 As Integer = su2
' Now do a merge-match on the lists (but reversing S2) and looking to '
'match their sum to the target sum: [O( 2^(n/2) )]'
Dim sum As Integer
Do While i1 <= su1 And i2 >= 0
sum = S1(i1) + S2(i2)
If sum < TargetSum Then
'if the Sum is too low, then we need to increase the ascending side (S1):'
i1 += 1
ElseIf sum > TargetSum Then
'if the Sum is too high, then we need to decrease the descending side (S2):'
i2 -= 1
Else
'Sums match:'
Return True
End If
Loop
'if we got here, then there are no matches to the TargetSum'
Return False
End Function
End Class
Here is the Forms code to go along with it:
Public Class frmSubsetSum
Dim ssm As New SubsetSum
Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGo.Click
Dim Total As Integer
Dim datStart As Date, datEnd As Date
Dim Iterations As Integer, Range As Integer, NumberCount As Integer
Iterations = CInt(txtIterations.Text)
Range = CInt(txtRange.Text)
NumberCount = CInt(txtNumberCount.Text)
ssm.ForceMatch = chkForceMatch.Checked
datStart = Now
Total = ssm.SolveMany(NumberCount, Range, Iterations)
datEnd = Now()
lblStart.Text = datStart.TimeOfDay.ToString
lblEnd.Text = datEnd.TimeOfDay.ToString
lblRate.Text = Format(Iterations / (datEnd - datStart).TotalMilliseconds * 1000, "####0.0")
ListBox1.Items.Insert(0, "Found " & Total.ToString & " Matches out of " & Iterations.ToString & " tries.")
ListBox1.Items.Insert(1, "Tics 0:" & ssm.w0 _
& " 1:" & Format(ssm.w1 - ssm.w0, "###,###,##0") _
& " 1a:" & Format(ssm.w1a - ssm.w1, "###,###,##0") _
& " 2:" & Format(ssm.w2 - ssm.w1a, "###,###,##0") _
& " 3:" & Format(ssm.w3 - ssm.w2, "###,###,##0") _
& " 4:" & Format(ssm.w4 - ssm.w3, "###,###,##0") _
& ", tics/sec = " & Stopwatch.Frequency)
End Sub
End Class
Let me know if you have any questions.
For the record, here is some fairly simple Java code that uses recursion to solve this problem. It is optimised for simplicity rather than performance, although with 100 elements it seems to be quite fast. With 1000 elements it takes dramatically longer, so if you are processing larger amounts of data you could better use a more sophisticated algorithm.
public static List<Double> getMatchingAmounts(Double goal, List<Double> amounts) {
List<Double> remaining = new ArrayList<Double>(amounts);
for (final Double amount : amounts) {
if (amount > goal) {
continue;
} else if (amount.equals(goal)) {
return new ArrayList<Double>(){{ add(amount); }};
}
remaining.remove(amount);
List<Double> matchingAmounts = getMatchingAmounts(goal - amount, remaining);
if (matchingAmounts != null) {
matchingAmounts.add(amount);
return matchingAmounts;
}
}
return null;
}

Resources