How to find the shortest square sum of a number - algorithm

Find and output the given number's shortest square sum.
Example: 12 = 2^2 + 2^2 + 2^2 (not 3^2 + 1^2 + 1^2 + 1^2)
Output: {2 2 2}

This is min-coin-change problem, where the coins are [1,4,9,...,(ceil(sqrt(n)))^2].
It can be solved using Dynamic Programming (DP) by following the recurrence formula:
D(i,0) = 0
D(i,x) = infinity x<0
D(0,x) = infinity x>0
D(i,x) = min { D(i,x-i^2) + 1, D(i-1,x) }
When building your matrix (assuming bottom-up DP), the element denoted in the matrix in D(ceil(sqrt(n)),n) is the minimal number of "coins" (squared numbers) needed to build your input number.
Getting the actual elements is done by tracking back your choices in the matrix after it is built, and at each point checking if you added a summand or not.
This is explained in more details for similar problems in this thread and this thread.

Here is a visual basic solution to the problem.
An edit would need to be done to cache intermediate answers so that the algorithm would be significantly faster. Currently ... only a value of about 40 to 50 is quickly computed. This is fully working and tested. It only returns the shortest answer and one where the higher value are to the left of other values.
Private Function ShortestSquareSum(x As Integer) As Integer()()
If x < 0 Then
Throw New ArgumentException("Parameter cannot be negative.", "x")
ElseIf x = 0 Then
Return New Integer()() {New Integer() {}}
Else
Dim answers As New List(Of Integer())
Dim shortest As Integer? = Nothing
For y As Integer = Math.Floor(Math.Sqrt(x)) To 1 Step -1
Dim remaining As Integer = x - y * y
Dim tempAnswers As Integer()() = ShortestSquareSum(x - y * y)
For Each answer As Integer() In tempAnswers
Dim currentAnswer As New List(Of Integer)
currentAnswer.Add(y)
currentAnswer.AddRange(answer)
If Not shortest.HasValue OrElse currentAnswer.Count < shortest Then
shortest = currentAnswer.Count
answers.Clear()
answers.Add(currentAnswer.ToArray())
ElseIf currentAnswer.Count = shortest AndAlso (answer.Count = 0 OrElse y > answer(0)) Then
answers.Add(currentAnswer.ToArray())
End If
Next
Next
Return answers.ToArray()
End If
End Function

public static void shortest_square(int n)
{
int i=2;
List<Integer> list= new ArrayList<Integer>();
while(i<=n) || n!=0)
{
if(n%(i*i)==0)
{
n=n-(i*i);
list.add(i);
}
else
{
i++;
}
}
System.out.println(list);
}

Related

Output smallest number k palindrome

With a dynamic programming problem, I am trying to come up with an english algorithm, memo table, best case, and recursive calls to fill the table for the following problem:
Given a string s of length n, design an algorithm that outputs the smallest number k such that s = w1w2 . . . wk where each wi is a palindrome. In other words, find the smallest k such that s can be written as a concatenation of k palindromes. For the definition of a palindrome see practice problems. For example if s = "add" then the algorithm should output k = 2 since we can take w1 ="a" and w2 ="dd". On the other hand, if s = "ada", then the algorithm should output k = 1.
I came up with the following algorithm:
Start
Declare variables s as string, n as integer, k as integer, i as integer.
Initialize k to 1
Read s
n<-length of string s
while i is less then n-1
if s[i]==s[i+1] then
k++
end if
display k
End
However, I am unsure how to come up with the memoization table, best case, and the recursive steps needed to fill the table.
In dynamic programming, memo table saves the answer for the sub problem of the actual problem. Combining these sub problem answer we can calculate the answer for the actual problem.
For this problem, actual problem is to find minimum k palindrome for string s. So, sub problem could be like what is the minimum k for part/substring's of string s. To simplify it, if we know the answer for substring s[0:i] and s[i+1:length(s)-1], then we can calculate the answer for s[0:length(s)-1] = s[0:i] + s[i+1:length(s)-1].
From this we can easily form our recursive relation:
minKPalindrome(start, end) = minKPalindrome(start, i) + minKPalindrome(i+1, end), (start < i < end)
here, minKPalindrome(start, end) is function that return minimum k plaindrome for s[start:end] substring
Possible base cases:
- if start > end , return answer 0
- s[start:end] is palindrome, so return answer is 1
Recursive algorithm:
memo[start][end] = contains answer for s[start:end]
minKPalindrome(start, end) {
if s[start:end] is palindrome {
return 1
}
if ans is already saved in memo[start][end] {
return memo[start][end]
}
memo[start][end] = end - start + 1 // set maximum possible answer
for i = start+1 to end-1 {
memo[start][end] = min( memo[start][end], minKPalindrome(start, i) + minKPalindrome(i+1, end))
}
return memo[start][end]
}
Optimized algorithm:
Generalize previous recursive relation:
minKPalindrome(s[start: end]) = minKPalindrome(s[start, i]) + minKPalindrome(s[i+1, end])
or,
minKPalindrome(s[start: end]) = minKPalindrome(prefix of s) + minKPalindrome(suffix of s)
Instead checking for every prefix of string s, we can just check for only the prefix that are palindrome.
if start[start:i] is palindrome {
minKPalindrome(s[start: end]) = 1 + minKPalindrome(s[i+1:end])
}
base case:
- if start > end, return 0
recursive algorithm:
memo[start] = contains answer for s[start:end]
end = lenght(s) - 1
minKPalindrome(start, end) {
if start > end {
return 0
}
if ans is already saved in memo[start] {
return memo[start]
}
memo[start] = end - start + 1 // set maximum possible answer
for i = start to end {
if s[start:i] is palindrome {
memo[start] = min( memo[start], 1 + minKPalindrome(i+1, end))
}
}
return memo[start]
}
iterative algorithm:
for iterative algorithm we will save answer for for prefix of string s.
memo[i] = contains answer for s[0:i]
for i = 0 to length(s)-1 {
for j= 0 to i {
if s[j:i] is palindrome {
memo[i] = min( memo[i], 1 + memo[j-1])
}
}
}

How to find the following type of set with computation time less than O(n)?

Here 5 different sets are shown. S1 contains 1. Next set S2 is calculated from S1 considering the following logic:
Suppose Sn contains {a1,a2,a3,a4.....,an} and middle element of Sn is b.
Then the set Sn+1 contains elements {b,b+a1,b+a2,......,b+an}. Total (n+1) elements. If a set contains even number of elements then middle element is (n/2 +1) .
Now, if n is given as input then we have to display all the elements of set Sn.
Clearly it is possible to solve the problem in O(n) time.
we can compute all the middle element as (2^(n-1) - middle element of the previous set + 1) where s1 ={1} is base case. In this way O(n) time we will get the all middle elements till (n-1)th set. So, middle element of (n-1)th set is the first element of the nth set set. (middle element of (n-1)th set + middle element of (n-2)th set) is the middle second element of the nth set. In this way we will get all the elements of nth set.
So it needs O(n) time.
Here id the complete java code I have written:
public class SpecialSubset {
private static Scanner inp;
public static void main(String[] args) {
// TODO Auto-generated method stub
int N,fst,mid,con=0;
inp = new Scanner(System.in);
N=inp.nextInt();
int[] setarr=new int[N];
int[] midarr=new int[N];
fst=1;
mid=1;
midarr[0]=1;
for(int i=1;i<N;i++)
{
midarr[i]=(int) (Math.pow(2, i)-midarr[i-1]+1);
}
setarr[0]=midarr[N-2];
System.out.print(setarr[0]);
System.out.print(" ");
for(int i=1,j=N-3;i<N-1;i++,j--)
{
setarr[i]=setarr[i-1]+midarr[j];
System.out.print(setarr[i]);
System.out.print(" ");
}
setarr[N-1]=setarr[N-2]+1;
System.out.print(setarr[N-1]);
}
}
Here is the link of the Question:
https://www.hackerrank.com/contests/projecteuler/challenges/euler103
IS it possible to solve the problem with less than O(n) time?
#Paul Boddington has given an answer that relies on the sequence of first numbers of these sets being the Narayana-Zidek-Capell numbers and has checked it for some small-ish values. However, there was no proof of the conjecture given. This answer is in addition to the above, to make it complete. I'm no HTML/CSS/Markdown guru, so you'll have to excuse the bad positioning of subscripts (If anyone can improve those - be my guest.
Notation:
Let aij be the i-th number in the j-th set.
I'll also define bj as the first number of the j-2-th set. This is the sequence the proof is about. The -2 is to account for the first and second 1 in the Narayana-Zidek-Capell sequence.
Generating rules:
The problem statement didn't clarify what "center number" is for a even-length set (a list really, but whatever), but it seems they meant the "center right" in that case. I'll denote the rules numbers in bold when I use them below.
a11 = 1
a1n = aceil(n+1⁄2)n-1
ain = a1n + ai-1n-1
bn = a1n-2
Proof:
First step is to make a slightly more involved formula for ain by unwinding the recursion a bit more and substituting b:
ain = Σ a1n-j = Σ bn-j+2 for j in [0 ... i-1]
Next, we consider two cases for bn - one where n is odd, one where n is even.
Even case:
b2n+2 = a12n =
2 = aceil(2n+1⁄2)2n-1 = an+12n-1 =
3 = a12n-1 + an2n-2 =
2, 4 = b2n+1 + a12n-1 =
5 = 2 * b2n+1
Odd case:
b2n+1 = a12n-1 =
2 = aceil(2n⁄2)2n-2 = an2n-2 =
3 = a12n-2 + an-12n-3 =
4 = 2 * b2n + (an-12n-3 - a12n-2) =
2 = 2 * b2n + (an-12n-3 - an2n-3) =
5 = 2 * b2n - bn
These rules are the exact sequence definition, and provide a way to generate the nth set in linear time (as opposed to quadratic when generating each set in turn)
The smallest numbers in the sets appear to be the Narayana-Zidek-Capell numbers
1, 1, 2, 3, 6, 11, 22, ...
The other numbers are obtained from the first number by repeatedly adding these numbers in reverse.
For example,
S6 = {11, 17, 20, 22, 23, 24}
+6 +3 +2 +1 +1
Using a recurrence for the Narayana-Zidek-Capell sequence found in that link, I have managed to produce a solution for this problem that runs in O(n) time. Here is a solution in Java. It only works for n <= 32 due to int overflow, but it could be written using BigInteger to work for higher values.
static Set<Integer> set(int n) {
int[] a = new int[n + 2];
for (int i = 1; i < n + 2; i++) {
if (i <= 2)
a[i] = 1;
else if (i % 2 == 0)
a[i] = 2 * a[i - 1];
else
a[i] = 2 * a[i - 1] - a[i / 2];
}
Set<Integer> set = new HashSet<>();
int sum = 0;
for (int i = n + 1; i >= 2; i--) {
sum += a[i];
set.add(sum);
}
return set;
}
I'm not able to justify right now why this is the same as the set in the question, but I'm working on it. However I have checked for all n <= 32 that this algorithm gives the same set as the "obvious" algorithm, so I'm reasonably sure it's correct.

Finding all the Combination to sum set of coins to a certain number

I have given an array and I have to find the targeted sum.
For Example:
A[] ={1,2,3};
S = 5;
Total Combination = {1,1,1,1,1} , {2,3} ,{3,2} . {1,1,3} , {1,3,1} , {3,1,1} and other possible pair
I know it sounds like coin change problem, But the problem is how to find the Combination i.e {2,3} and {3,2} are 2 different solutions.
In the original coin change problem, you "choose" an arbitrary coin - and "guess" if it is or is not in the solution, this is done because the order is not important.
Here, you will have to iterate all possibilities for "which coin is first", until you are done:
D(0) = 1
D(x) = 0 | x < 0
D(x) = sum { D(x-coins[0]) , D(x-coins[1]), ..., D(x-coins[n-1] }
Note that for each step, you are giving all possibilities for the choosing the next coin, and moving on. At the end, you sum up all the solutions, for all possibilities to place each coin at the head of the solution.
Complexity of this solution using DP is O(n*S), where n is the number of coins and S is the desired sum.
Matlab code (wrote it in imperative style, this is my current open IDE, sorry it's matlab and not more common language like java or C)
function [ n ] = make_change( coins, x )
D = zeros(x,1);
for k = 1:x
for t = 1:length(coins)
curr = k-coins(t);
if curr>0
D(k) = D(k) + D(curr);
elseif curr == 0
D(k) = D(k) + 1;
end
end
end
n = D(x);
end
Invoking will yield:
>> make_change([1,2,3],5)
ans =
13
Which is correct, since all possibilities are [1,1,1,1,1],[1,1,1,2]*4, [1,1,3]*3,[1,2,2]*3,[2,3]*2 = 13

Calculate Nth root with integer arithmetic

There are a couple of ways to find integer square roots using only integer arithmetic. For example this one. It makes for interesting reading and also a very interesting theory, particularly for my generation where such techniques aren't so useful any more.
The main thing is that it can't use floating point arithmetic, so that rules out newtons method and it's derivations. The only other way I know of to find roots is binomial expansion, but that also requires floating point arithmetic.
What techniques/algorithms are there for computing integral nth roots using only integer arithmetic?
Edit: Thanks for all the answers so far. They all seem to be slightly more intelligent trial and improvement. Is there no better way?
Edit2: Ok, so it would seem there is no smart way to do this without trial/improvement and either newtons method or a binary search. Can anyone provide a comparison of the two in theory? I have run a number of benchmarks between the two and found them quite similar.
You can use Newton's method using only integer arithmetic, the step is the same as for floating point arithmetic, except you have to replace floating point operators with the corresponding integer operators in languages which have different operators for these.
Let's say you want to find the integer-k-th root of a > 0, which should be the largest integer r such that r^k <= a. You start with any positive integer (of course a good starting point helps).
int_type step(int_type k, int_type a, int_type x) {
return ((k-1)*x + a/x^(k-1))/k;
}
int_type root(int_type k, int_type a) {
int_type x = 1, y = step(k,a,x);
do {
x = y;
y = step(k,a,x);
}while(y < x);
return x;
}
Except for the very first step, you have x == r <==> step(k,a,x) >= x.
One obvious way would be to use binary search together with exponentiation by squaring. This will allow you to find nthRoot(x, n) in O(log (x + n)): binary search in [0, x] for the largest integer k such that k^n <= x. For some k, if k^n <= x, reduce the search to [k + 1, x], otherwise reduce it to [0, k].
Do you require something smarter or faster?
One easy solution is to use the binary search.
Assume we are finding nth root of x.
Function GetRange(x,n):
y=1
While y^n < x:
y*2
return (y/2,y)
Function BinSearch(a,b,x,):
if a == b+1:
if x-a^n < b^n - x:
return a
else:
return b
c = (a+b)/2
if n< c^n:
return BinSearch(a,c,x,n)
else:
return BinSearch(c,b,x,n)
a,b = GetRange(x,n)
print BinSearch(a,b,x,n)
===Faster Version===
Function BinSearch(a,b,x,):
w1 = x-a^n
w2 = b^n - x
if a <= b+1:
if w1 < w2:
return a
else:
return b
c = (w2*a+w1*b)/(w1+w2)
if n< c^n:
return BinSearch(a,c,x,n)
else:
return BinSearch(c,b,x,n)
It seems to me that the Shifting nth root algorithm provides exactly what you want:
The shifting nth root algorithm is an algorithm for extracting the nth root of a positive real number which proceeds iteratively by shifting in n digits of the radicand, starting with the most significant, and produces one digit of the root on each iteration, in a manner similar to long division.
There are worked examples on the linked wikipedia page.
I made the algorithm in VBA in Excel. For now it only calculates roots of integers. It is easy to implement the decimals as well.
Just copy and paste the code into an EXCEL module and type the name of the function into some cell, passing the parameters.
Public Function RootShift(ByVal radicand As Double, degree As Long, Optional ByRef remainder As Double = 0) As Double
Dim fullRadicand As String, partialRadicand As String, missingZeroes As Long, digit As Long
Dim minimalPotency As Double, minimalRemainder As Double, potency As Double
radicand = Int(radicand)
degree = Abs(degree)
fullRadicand = CStr(radicand)
missingZeroes = degree - Len(fullRadicand) Mod degree
If missingZeroes < degree Then
fullRadicand = String(missingZeroes, "0") + fullRadicand
End If
remainder = 0
RootShift = 0
Do While fullRadicand <> ""
partialRadicand = Left(fullRadicand, degree)
fullRadicand = Mid(fullRadicand, degree + 1)
minimalPotency = (RootShift * 10) ^ degree
minimalRemainder = remainder * 10 ^ degree + Val(partialRadicand)
For digit = 9 To 0 Step -1
potency = (RootShift * 10 + digit) ^ degree - minimalPotency
If potency <= minimalRemainder Then
Exit For
End If
Next
RootShift = RootShift * 10 + digit
remainder = minimalRemainder - potency
Loop
End Function
Algorithm more simple in VBA.
Public Function RootNth(radicand As Double, degree As Long) As Double
Dim countDigits As Long, digit As Long, potency As Double
Dim minDigit As Long, maxDigit As Long, partialRadicand As String
Dim totalRadicand As String, remainder As Double
radicand = Int(radicand)
degree = Abs(degree)
RootNth = 0
partialRadicand = ""
totalRadicand = CStr(radicand)
countDigits = Len(totalRadicand) Mod degree
countDigits = IIf(countDigits = 0, degree, countDigits)
Do While totalRadicand <> ""
partialRadicand = partialRadicand + Left(totalRadicand, countDigits)
totalRadicand = Mid(totalRadicand, countDigits + 1)
countDigits = degree
minDigit = 0
maxDigit = 9
Do While minDigit <= maxDigit
digit = Int((minDigit + maxDigit) / 2)
potency = (RootNth * 10 + digit) ^ degree
If potency = Val(partialRadicand) Then
maxDigit = digit
Exit Do
End If
If potency < Val(partialRadicand) Then
minDigit = digit + 1
Else
maxDigit = digit - 1
End If
Loop
RootNth = RootNth * 10 + maxDigit
Loop
End Function

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