Author |
Topic |
notmyrealname
98 Posts |
Posted - 2010-09-14 : 13:11:22
|
Hi,This is not exactly a SQL question but i wanted to post it here because this is the smartest forum i've used. I know it can be accomplished in SQL so i would be happy to see what ideas anyone has.I need to list all possible sums of any combination of values in a given list.For example...Let's say i have the following numbers...1 2 3 4 5... the resulting list would include everything between 1 (1 by itself) through 15 (1+2+3+4+5).I am looking for a list of sums for everything from each individual number to any combination of the numbers all the way to the sum of every number.1=11+2=31+3=41+4=51+5=61+2+3=61+2+4=71+4+5=102+3+5=10and so on...I am trying to come up with a batching algorithm. Given a certain length of material and a list of n number of part lengths, i want to batch the parts on the material in the most efficient way.Say we have a piece of material that is 20' long and we have the following parts...10'7'7'6'4'2'In this case there are three combinations that would fit perfectly onto the material...10+6+4=207+7+6+207+7+4+2=20Once i have a list of all possible combinations i can then select the combination that is the closest to the desired length.I hope this makes enough sense to everybody.Any suggestion would be greatly appreciated.Thanks,Joel |
|
rohitkumar
Constraint Violating Yak Guru
472 Posts |
Posted - 2010-09-14 : 13:40:00
|
I would build a dynamic SQL to get something like this?SELECT *FROM (SELECT * FROM (SELECT 0 AS col1 UNION ALL SELECT 10 AS col1) a CROSS JOIN (SELECT 0 AS col2 UNION ALL SELECT 7 AS col2) b CROSS JOIN (SELECT 0 AS col3 UNION ALL SELECT 7 AS col3) c CROSS JOIN (SELECT 0 AS col4 UNION ALL SELECT 6 AS col4) d CROSS JOIN (SELECT 0 AS col5 UNION ALL SELECT 4 AS col5) e CROSS JOIN (SELECT 0 AS col6 UNION ALL SELECT 2 AS col6) f) xWHERE col1 + col2 + col3 + col4 + col5 + col6 = 20 |
 |
|
kunal.mehta
Yak Posting Veteran
83 Posts |
Posted - 2010-09-15 : 05:24:32
|
perfect solution. but need to make it dynamic to fulfil the reqt |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-09-15 : 08:56:07
|
Dynamic SQL? Why?DECLARE @Sample TABLE ( PieceID INT IDENTITY(1, 1) PRIMARY KEY CLUSTERED, PieceLength INT NOT NULL )INSERT @Sample ( PieceLength )SELECT 10 UNION ALLSELECT 7 UNION ALLSELECT 7 UNION ALLSELECT 6 UNION ALLSELECT 4 UNION ALLSELECT 2DECLARE @WantedLength INTSET @WantedLength = 20;WITH ctePermutations(PieceGroup, PieceLength, PiecePath, PieceTotal)AS ( SELECT PieceID AS PieceGroup, PieceLength, '<p>' + CAST(PieceID AS VARCHAR(MAX)) + '</p>' AS PiecePath, PieceLength AS PieceTotal FROM @Sample UNION ALL SELECT p.PieceGroup, s.PieceLength, p.PiecePath + '<p>' + CAST(s.PieceID AS VARCHAR(MAX)) + '</p>' AS PiecePath, p.PieceTotal + s.PieceLength AS PieceTotal FROM @Sample AS s INNER JOIN ctePermutations AS p ON p.PiecePath NOT LIKE '%<p>' + CAST(s.PieceID AS VARCHAR(MAX)) + '</p>%' WHERE s.PieceLength >= p.PieceLength), cteResult(PieceGroup, PieceID)AS ( SELECT DISTINCT x.PieceGroup, f.a.value('.', 'INT') AS PieceID FROM ( SELECT PieceGroup, CAST(PiecePath AS XML) AS w FROM ctePermutations WHERE PieceTotal = @WantedLength ) AS x CROSS APPLY x.w.nodes('p') AS f(a))SELECT DENSE_RANK() OVER (ORDER BY r.PieceGroup) AS Alternative, s.PieceLengthFROM cteResult AS rINNER JOIN @Sample AS s ON s.PieceID = r.PieceIDORDER BY r.PieceGroup, s.PieceLength N 56°04'39.26"E 12°55'05.63" |
 |
|
notmyrealname
98 Posts |
Posted - 2010-09-15 : 09:10:23
|
Thanks guys. I like the ideas. Not a huge fan of dynamic SQL so i think Peso takes this one. I'm on the fence between handling this in SQL versus handling it in VB. Your solution, Peso, is so simple that i might just stick with SQL.Thanks again everyone. I knew i could count on SQL Team. |
 |
|
rohitkumar
Constraint Violating Yak Guru
472 Posts |
Posted - 2010-09-15 : 10:20:05
|
quote: Originally posted by Peso Dynamic SQL? Why?
good solution Peter, needs some tweaking, try 19, 21... etc. instead of 20. |
 |
|
notmyrealname
98 Posts |
Posted - 2010-09-15 : 10:24:44
|
Hey Peso,I forgot to mention one thing. The pieces may not always add up to the @WantedLength. You might have a @WantedLength of 20 but no combination actually adds up to an even 20. In this case i need to know what pieces will add up closest to 20 without exceeding 20.What i would really love to see would be a complete list of all poosible combinations with their corresponding Alternative ID. The list that i provided would yield 63 possible combinations. This is what's in my head. Imagine each length representing a digit in a binary number as shown below.10: 7: 7: 6: 4: 2-- -- -- -- -- -- 1 0 1 0 1 1We could have any combination from 000000 (total length = 0) to 111111 (total length = 10+7+7+6+4+2 = 36). The above example 101011 would add up to 10+7+4+2=23. Because there are six lengths we are working with, the corresponding binary number would be 6 digits long meaning we could have 63 possible combinations. Clearly the number of possibilities could become quite large depending on how many lengths we are working with. I can add some rules later that will restrict certain combinations from ever being included.At the end of the day i am looking for a list that i can compare to the actual material in our shop and determine the most efficient placement of the pieces. I am trying to put something together in VB but am also interested in seeing if this could be done in SQL.Thanks again for your help.Joel |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-09-15 : 10:29:51
|
21 givesAlternative PieceLength1 41 71 71 10 N 56°04'39.26"E 12°55'05.63" |
 |
|
rohitkumar
Constraint Violating Yak Guru
472 Posts |
Posted - 2010-09-15 : 10:37:20
|
19 givesAlternative PieceLength1 21 41 61 71 71 10 |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-09-15 : 10:56:29
|
Here is a slightly new algorithmDECLARE @Sample TABLE ( PieceID INT IDENTITY(1, 1) PRIMARY KEY CLUSTERED, PieceLength INT NOT NULL )INSERT @Sample ( PieceLength )SELECT 10 UNION ALLSELECT 7 UNION ALLSELECT 7 UNION ALLSELECT 6 UNION ALLSELECT 4 UNION ALLSELECT 2DECLARE @WantedLength INTSET @WantedLength = 20;WITH ctePermutations(PieceLength, PiecePath, PieceTotal)AS ( SELECT PieceLength, '<p>' + CAST(PieceID AS VARCHAR(MAX)) + '</p>' AS PiecePath, PieceLength AS PieceTotal FROM @Sample UNION ALL SELECT s.PieceLength, p.PiecePath + '<p>' + CAST(s.PieceID AS VARCHAR(MAX)) + '</p>' AS PiecePath, p.PieceTotal + s.PieceLength AS PieceTotal FROM @Sample AS s INNER JOIN ctePermutations AS p ON p.PiecePath NOT LIKE '%<p>' + CAST(s.PieceID AS VARCHAR(MAX)) + '</p>%' WHERE s.PieceLength >= p.PieceLength), cteIntermediate(PiecePath, PieceLength, RecID)AS ( SELECT x.PiecePath, s.PieceLength, RANK() OVER (ORDER BY x.PieceTotal DESC) AS RecID FROM ( SELECT PiecePath, CAST(PiecePath AS XML) AS w, PieceTotal FROM ctePermutations WHERE PieceTotal <= @WantedLength ) AS x CROSS APPLY x.w.nodes('p') AS f(a) INNER JOIN @Sample AS s ON s.PieceID = f.a.value('.', 'INT')), cteResult(RecID, t)AS ( SELECT ROW_NUMBER() OVER (PARTITION BY f.t ORDER BY i.PiecePath) AS RecID, f.t FROM cteIntermediate AS i CROSS APPLY ( SELECT CAST('<i>' + CAST(e.PieceLength AS VARCHAR(MAX)) + '</i>' AS XML) FROM cteIntermediate AS e WHERE e.PiecePath = i.PiecePath AND e.RecID = 1 ORDER BY e.PieceLength FOR XML PATH('') ) AS f(t) WHERE i.RecID = 1)SELECT r.Alternative, f.a.value('.', 'INT') AS PieceLength, SUM(f.a.value('.', 'INT')) OVER (PARTITION BY r.Alternative) AS TotalFROM ( SELECT ROW_NUMBER() OVER (ORDER BY t) AS Alternative, CAST(t AS XML) AS w FROM cteResult WHERE RecID = 1 ) AS rCROSS APPLY r.w.nodes('i') AS f(a) N 56°04'39.26"E 12°55'05.63" |
 |
|
rohitkumar
Constraint Violating Yak Guru
472 Posts |
Posted - 2010-09-15 : 10:59:56
|
check 5 |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-09-15 : 11:02:29
|
Yes? It gives you 4 as requested.quote: Originally posted by notmyrealname Once i have a list of all possible combinations i can then select the combination that is the closest to the desired length.
N 56°04'39.26"E 12°55'05.63" |
 |
|
rohitkumar
Constraint Violating Yak Guru
472 Posts |
Posted - 2010-09-15 : 11:17:32
|
quote: Originally posted by Peso Yes? It gives you 4 as requested.
shouldn't it give 6? |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-09-15 : 11:49:45
|
No. See op last response with "without exceeding" part. N 56°04'39.26"E 12°55'05.63" |
 |
|
notmyrealname
98 Posts |
Posted - 2010-09-15 : 11:50:09
|
Try adding some more pieces. Add about 20 random lengths between 1 and 10 and then try it. It looks like i is going to slow down too much |
 |
|
notmyrealname
98 Posts |
Posted - 2010-09-15 : 12:00:41
|
I'm not sure this is going to work in my actual scenario where the @WantedLength would be something like 12192 millimeters (20'-0") and there could be a hundred different piece lengths or more. |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-09-15 : 12:14:49
|
Do you want all permutations or just one? N 56°04'39.26"E 12°55'05.63" |
 |
|
notmyrealname
98 Posts |
Posted - 2010-09-15 : 12:48:12
|
If there is a perfect match then there is no reason to continue. Those parts will be removed from the queue and the next @WantedLength will be processed. Imagine there are 100 parts that have to be batched onto 10 lengths of material (either constant or varying length).Right now what i am doing is sorting my queue by [Length] DESC and iterating through the queue. If the piece fits on the length of material then it is assigned to that piece. If it doesn't then it is skipped and we move on to the next piece in the queue. What this does is fill up each batch with the the longer pieces at the start and the shorter pieces at the end. this works pretty efficiently but it's not perfect.Take for instance my original numbers...10'7'7'6'4'2'Using this system it is determined that...10' fits so it is added7' fits so it is added7' does not fit so it is skipped6' does not fit so it is skipped4' does not fit so it is skipped2' fits so it is addedresulting in a batch of three piece (10+7+2) adding up to 19' with 1' of waste. The remaining pieces are then added to additional batches using the same principal.The waste is pretty low but you can clearly see that we can make an exact batch by using the 10+6+4 (or any of the other perfect combinations). This is why i was thinking of creating a list of all possible combinations (that don't exceed the @WantedLength) and then using that list to determine which parts go on each batch. I think it might take quite a processor to handle it unforetunately.Thanks again. You're a big help. |
 |
|
robvolk
Most Valuable Yak
15732 Posts |
|
notmyrealname
98 Posts |
Posted - 2010-09-15 : 13:25:05
|
No thanks. After working in a distribution center for a few years (and moving several times) i have decided that i never want to pack another box! |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-09-15 : 16:53:57
|
Have you tried this algorithm http://weblogs.sqlteam.com/peterl/archive/2008/08/12/How-to-sum-up-an-unknown-number-of-records.aspx ?-- Initialize the search parameterDECLARE @WantedValue INT = 19-- Stage the source dataDECLARE @Data TABLE ( RecID INT IDENTITY(1, 1) PRIMARY KEY CLUSTERED, MaxItems INT, CurrentItems INT DEFAULT 0, FaceValue INT, BestUnder INT DEFAULT 0, BestOver INT DEFAULT 1 )-- Aggregate the source dataINSERT @Data ( MaxItems, FaceValue )SELECT COUNT(*), QtyFROM ( VALUES (10), (7), (7), (6), (4), (2) ) AS d(Qty)GROUP BY QtyORDER BY Qty DESC-- Declare some control variablesDECLARE @CurrentSum INT, @BestUnder INT, @BestOver INT, @RecID INT -- If exact single wanted sum, select that item!IF EXISTS (SELECT * FROM @Data WHERE FaceValue = @WantedValue) BEGIN SELECT 0 AS SumType, 1 AS Items, FaceValue FROM @Data WHERE FaceValue = @WantedValue RETURN END-- If productsum is less to the wanted sum, select all items!IF (SELECT SUM(MaxItems * FaceValue) FROM @Data) < @WantedValue BEGIN SELECT -1 AS SumType, MaxItems AS Items, FaceValue FROM @Data RETURN END-- If productsum is equal to the wanted sum, select all items!IF (SELECT SUM(MaxItems * FaceValue) FROM @Data) = @WantedValue BEGIN SELECT 0 AS SumType, MaxItems AS Items, FaceValue FROM @Data RETURN END -- Delete all unworkable FaceValues, keep one greater FaceValue because of oversum. DELETEFROM @DataWHERE FaceValue > (SELECT MIN(FaceValue) FROM @Data WHERE FaceValue >= @WantedValue) -- Update MaxItems to a proper valueUPDATE @DataSET MaxItems = CASE WHEN 1 + (@WantedValue - 1) / FaceValue < MaxItems THEN 1 + (@WantedValue - 1) / FaceValue ELSE MaxItems END -- Update BestOver to a proper valueUPDATE @DataSET BestOver = MaxItems -- Initialize the control mechanismSELECT @RecID = MIN(RecID), @BestUnder = 0, @BestOver = SUM(BestOver * FaceValue)FROM @Data -- Do the loop!WHILE @RecID IS NOT NULL BEGIN -- Reset all "bits" not incremented UPDATE @Data SET CurrentItems = 0 WHERE RecID < @RecID -- Increment the current "bit" UPDATE @Data SET CurrentItems = CurrentItems + 1 WHERE RecID = @RecID -- Get the current sum SELECT @CurrentSum = SUM(CurrentItems * FaceValue) FROM @Data WHERE CurrentItems > 0 -- Stop here if the current sum is equal to the sum we want IF @CurrentSum = @WantedValue BREAK ELSE -- Update the current BestUnder if previous BestUnder is less IF @CurrentSum > @BestUnder AND @CurrentSum < @WantedValue BEGIN UPDATE @Data SET BestUnder = CurrentItems SET @BestUnder = @CurrentSum END ELSE -- Update the current BestOver if previous BestOver is more IF @CurrentSum > @WantedValue AND @CurrentSum < @BestOver BEGIN UPDATE @Data SET BestOver = CurrentItems SET @BestOver = @CurrentSum END -- Find the next proper "bit" to increment SELECT @RecID = MIN(RecID) FROM @Data WHERE CurrentItems < MaxItems END -- Now we have to investigate which type of sum to returnIF @RecID IS NULL IF @WantedValue - @BestUnder < @BestOver - @WantedValue -- If BestUnder is closer to the sum we want, choose that SELECT -1 AS SumType, BestUnder AS Items, FaceValue FROM @Data WHERE BestUnder > 0 ELSE -- If BestOver is closer to the sum we want, choose that SELECT 1 AS SumType, BestOver AS Items, FaceValue FROM @Data WHERE BestOver > 0ELSE -- We have an exact match SELECT 0 AS SumType, CurrentItems AS Items, FaceValue FROM @Data WHERE CurrentItems > 0 N 56°04'39.26"E 12°55'05.63" |
 |
|
Next Page
|