June 24, 2005 at 2:50 pm
This line just initialize the Array to have 1 elem inside at index 0
Vasc
June 24, 2005 at 3:00 pm
Thanks Vasc. It works now. Let me see whether I can reproduce it in SQL.
Recursive is way too fast than non recursive. Check 1200. Not found is the answer.
Regards,
gova
June 25, 2005 at 11:56 am
My test results match govinn's. Test results for both programs set to not start timer until after value is loaded into the array. (in both cases it requires about 0.0156 seconds to load the array: a great place to save a lot of time)
For values that can be found I have not been able to locate one that requires measurable amounts of time using either method. Both methods are fast when the value can be found
Sample data for cases where value is not found.
value 1200 recursive method = 0.0000 non-recursive method =11.328 seconds
value 789 recursive method =1.234 seconds non-recursive = 15.81 seconds
value 889 recursive method = 0.59375 seconds non_recursive method =19.0488 seconds
a wide variance in the time for both methods but the recursive method seems to be beating the pants off the non-recursive method.
Mike
June 26, 2005 at 3:13 am
Noticed the large number of posts on this, and got curious.
Here's an iterative (non-recursive) search. It does it breadth-first, so it finds the solution that uses the smallest number of values.
I'm doing a nasty cursor at the end just to print the the results.
Of course, you can use a table variable instead of my table.
It's not particularly optimised... because a breadth-first search isn't typically the best way of doing this type of thing. But I'm sure someone could work out ways of optimising it, and working out what values give a better result this way than the depth-first search that the recursive method uses. For instance... it works fast for 103, but not so fast on 512.
RobF
/*
create table vals
(valueid int identity(1,1), value numeric(10,2))
go
INSERT INTO vals (Value) VALUES (15)
INSERT INTO vals (Value) VALUES (10)
INSERT INTO vals (Value) VALUES (21)
INSERT INTO vals (Value) VALUES (25)
INSERT INTO vals (Value) VALUES (26)
INSERT INTO vals (Value) VALUES (30)
INSERT INTO vals (Value) VALUES (30)
INSERT INTO vals (Value) VALUES (40)
INSERT INTO vals (Value) VALUES (41)
INSERT INTO vals (Value) VALUES (45)
INSERT INTO vals (Value) VALUES (50)
INSERT INTO vals (Value) VALUES (55)
INSERT INTO vals (Value) VALUES (60)
INSERT INTO vals (Value) VALUES (60)
INSERT INTO vals (Value) VALUES (65)
INSERT INTO vals (Value) VALUES (70)
INSERT INTO vals (Value) VALUES (75)
INSERT INTO vals (Value) VALUES (80)
INSERT INTO vals (Value) VALUES (85)
INSERT INTO vals (Value) VALUES (90)
INSERT INTO vals (Value) VALUES (95)
INSERT INTO vals (Value) VALUES (100)
INSERT INTO vals (Value) VALUES (105)
go
create table match
( id int identity(1,1),
value numeric(10,2),
valueid int,
parent int,
remainder numeric(10,2),
checked int
)
*/
declare @goal numeric(10,2)
set @goal = 627
truncate table match
insert into match
(value, valueid, parent, remainder, checked)
select value, valueid, 0, @goal-value, -1 from vals where value < @goal
declare @stillgoing int
set @stillgoing = 1
while (@stillgoing = 1)
begin
update match set checked = checked + 1
where checked < 1
insert into match
(value, valueid, parent, remainder, checked)
select v.value, v.valueid, m.id, m.remainder - v.value, -1
from match m
join
vals v
on v.valueid <= m.valueid -- Or '<' if you want distinct values
and v.value 0)
begin
select @res = parent, @val = value
from match
where id = @res
print convert(varchar,@val)
end
fetch csrResult into @val, @res
end
close csrResult
deallocate csrResult
Rob Farley
LobsterPot Solutions & Adelaide SQL Server User Group
Company: http://www.lobsterpot.com.au
Blog: http://blogs.lobsterpot.com.au
June 27, 2005 at 9:09 am
There is a RULE: an iterative version is ALWAYS faster than the RECURSIVE version. (off course if it is well written : )
So based on your Observation (good one : ) ) and govvin's too I looked on the nonrecursive code and I found that the recursive function is faster because is "hidding" an extra optimization that reduce the number of unwanted cicles by starting with the biggest value, decrease value and stop when it doesn't make sense to go on that path anymore ... because SUM is smaller than MyNumber this part was missing from the nonrecursive code : when I was generating the Stack I was putting on the stack ALL values that are smaller than my values EVEN if by treating them you won't reach a solution (because their SUM is smaller that MyNumber so they won't have any change to produce a solution)
I added this to the VB code:
==========================================================
Private Sub Command1_Click()
Dim val As Double
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim High As Double
Dim Low As Double
Dim Found As Boolean
Dim count As Integer
Dim AllSum As Double
Dim TempSum As Double
AllSum = 0
count = 0
Found = False
cn.CursorLocation = adUseClient
cn.Provider = "SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=test1;Data Source=ITWSVK01"
cn.Open
val = CDbl(Text1.Text)
rs.Open "select value From MyTable WHERE Value<=" + Str(val) + " order by Value", cn
While Not rs.EOF
count = count + 1
ReDim Preserve ArrayValues(count)
ArrayValues(count) = rs!Value
AllSum = AllSum + ArrayValues(count)
rs.MoveNext
Wend
Set rs = Nothing
cn.Close
Set cn = Nothing
If count = 0 Then
MsgBox "The value is smaller than the lowest value in the table" _
, vbOKOnly, "Error"
Exit Sub
End If
Dim MyElem As Double
Dim MySol As String
Dim cntSOl As Integer
Dim cntStack As Integer
cntStack = 0
mm = UBound(ArrayValues)
TempSum = 0
For ii = 1 To mm
TempSum = TempSum + ArrayValues(ii)
If TempSum >= val Then
cntStack = cntStack + 1
ReDim Preserve MyStack(cntStack)
MyStack(cntStack) = ii
End If
Next ii
If cntStack = 0 Then
MsgBox "NotFound"
Exit Sub
End If
ReDim Preserve Sol(0)
While UBound(MyStack) > 0
MyElem = MyStack(UBound(MyStack))
If MyElem = Sol(UBound(Sol)) Then
ReDim Preserve MyStack(UBound(MyStack) - 1)
ReDim Preserve Sol(UBound(Sol) - 1)
val = val + ArrayValues(MyElem)
AllSum = AllSum + ArrayValues(MyElem)
If UBound(Sol) = 1 Then AllSum = AllSum - ArrayValues(MyElem)
Else
cntSOl = UBound(Sol)
cntSOl = cntSOl + 1
ReDim Preserve Sol(cntSOl)
Sol(cntSOl) = MyElem
val = val - ArrayValues(MyElem)
AllSum = AllSum - ArrayValues(MyElem)
If val = 0 Then
For jj = 1 To UBound(Sol)
MySol = MySol + Str(ArrayValues(Sol(jj)))
Next jj
MsgBox MySol
Exit Sub
Else
If AllSum >= val Then
'check to see if the value is in the remaining values
For rr = MyElem - 1 To 1 Step -1
If val = ArrayValues(rr) Then
For jj1 = 1 To UBound(Sol)
MySol = MySol + Str(ArrayValues(Sol(jj1)))
Next jj1
MsgBox MySol + " " + Str(val)
Exit Sub
End If
Next rr
TempSum = AllSum
While ArrayValues(MyElem) > val
MyElem = MyElem - 1
TempSum = TempSum - ArrayValues(MyElem)
Wend
If MyElem > 1 And TempSum >= val Then
TempSum = ArrayValues(1)
jj1 = 1
While TempSum < val
jj1 = jj1 + 1
TempSum = TempSum + ArrayValues(jj1)
Wend
For kk = jj1 To MyElem - 1
ReDim Preserve MyStack(UBound(MyStack) + 1)
MyStack(UBound(MyStack)) = kk
Next kk
End If
End If
End If
End If
Wend
MsgBox "notfound"
End Sub
=========================================================
If there are still cases in which the Recursive version is FASTER than the NonRecursive than this version is NOT DONE YET : )
Vasc
June 27, 2005 at 9:13 am
Do you guys think this thread will hit 100 messages?? I think it would be a first .
June 27, 2005 at 9:14 am
breadth-first or deph-first they have the same cost. They produce different result times based on the distribution of the Initial Set of Numbers which you can't control
Vasc
June 27, 2005 at 9:40 am
breadth-first or deph-first they have the same cost. They produce different result times based on the distribution of the Initial Set of Numbers which you can't control
I tend to differ on that. In databases you must know your data and usually you can find skews on it
so if you know where it will most probaly go, just use the most probable way.
I had to research a problem that one client had once and they were assuming any number of order detail items could happen in an Order. After we analized the DATA we arrived to the conclusion that with 60 line Items 97% of the cases could be covered accross their customer base. Speed improvement based on that finding was outstanding!
Just my $0.02
* Noel
June 27, 2005 at 9:43 am
Can you elaborate on that one??
June 27, 2005 at 9:48 am
Actually it seems that you agree with me (or my english was poor )
They produce different result times based on the distribution of the Initial Set of Numbers which you can't control
They have the same cost regarding the processing time as a metod to travers searching tree.
If you have knoledge like in your case you can optimize your solution based on the metod choosed I really agree with that : )
Vasc
June 27, 2005 at 9:52 am
Yes I agree with this part:
They produce different result times based on the distribution of the Initial Set of Numbers
The part I was differing with was:
which you can't control
Which in my opinion in Databases you can tell if there is a skew in the data and I was proposing not to stop at one or the other just Like you said use the most appropriate for your data
* Noel
June 27, 2005 at 7:08 pm
As an exercise, it's fun to write the code to make a database do this kind of work. In reality though, you wouldn't use a database for it, because a database is just that, a BASE for the data - not really designed for the kind of computations that are required here. Pulling the data into something that is better at playing with numbers and different data structures is definitely the way to go.
Rob Farley
LobsterPot Solutions & Adelaide SQL Server User Group
Company: http://www.lobsterpot.com.au
Blog: http://blogs.lobsterpot.com.au
Viewing 12 posts - 91 through 101 (of 101 total)
You must be logged in to reply to this topic. Login to reply