<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Net Groups of Lines in a Table to $0.00 Con't in SAS Programming</title>
    <link>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470582#M285662</link>
    <description>&lt;P&gt;Hello - This is a continuation from an earlier post I had.&amp;nbsp; Again, thank you in advance for any help you can provide me.&amp;nbsp; I am trying to group smaller subsets of a larger population that net (or sum if you will) down to $0.00.&amp;nbsp; &amp;nbsp;My data has limited available fields, so I can only use the numeric field to complete this.&amp;nbsp; &amp;nbsp;In my earlier post, it looked like a "brute force" option was the best choice for me.&amp;nbsp; I am hoping the community can help me improve upon the logic that was provided to me.&amp;nbsp; Big thanks to all the earlier contributors, my knowledge of arrays, while very limited, has grown over the past few days.&amp;nbsp; A few notes...&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;1) Yes, the overall population does net to $0.00, but I need to try to group it into smaller subsets for the purpose of my analysis (new data attached)&lt;/P&gt;&lt;P&gt;2.) I DO NOT have SAS/OR, so I cannot use the OPTMODEL procedure.&amp;nbsp; I am running on SAS EG.&amp;nbsp; I can provide a list of the licenses I do have if that helps.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;3.) One thing I noticed when testing the script below on a new data set is that if there's a repeating value sequentially, the code may add the original value, plus itself when looping.&amp;nbsp; If possible, I'd like to avoid scenarios like this.&amp;nbsp; &amp;nbsp;For example, with {-3,-3,6} this code is adding the first element twice and then summing to zero with the 6, instead of taking elements 1 and 2.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;4.) For some additional background, the populations I'm dealing with can go up to 6000 lines.&amp;nbsp; I tested one of these larger populations for sets of 3.&amp;nbsp; It worked but took 5 hours to process.&amp;nbsp; I'm ok with the processing time, but am curious if there efficiencies which can applied, especially since I could have multiple larger datasets.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;5.) Example code I've been playing with is below.&amp;nbsp; Special thanks to&amp;nbsp;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/13884"&gt;@ballardw&lt;/a&gt; for writing this originally.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;My questions to the community...&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;a.) can the logic below be updated so that the array containing the values "shrinks" after subsets have been made?&amp;nbsp; I think this may reduce the number of iterations.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;b.) along the same idea as question a., is it possible for the array to not sum the same element against itself?&amp;nbsp; this may reduce the number of iterations as well (though not by much).&amp;nbsp; simply put, each element can only be used once when summing&lt;/P&gt;&lt;P&gt;c.) does this code need to be sequentially run the way it is currently set up?&amp;nbsp; say I want to search for subsets of 50 lines, do I need 50 steps going sequentially from 2-50, or can it be set up to run just the 50 lines matches first, and within the results are the subsets of 2, 3,4...n.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Thank you all again for you help.&amp;nbsp; This has been very insightful so far and I've been learning a lot.&amp;nbsp; If there are any other questions I can answer, or background I can provide to add additional clarity, please let me know.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;proc transpose data=have out=work.trans
  prefix=ba
;
var base_amt;
run;

data work.pairs ( keep=iout jout ival jval)
     work.leftover (keep=ba:)
;
   set work.trans end=last;
   array b ba:;
   do i= 1 to (dim(b)-1);
      do j= 2 to dim(b);
         if sum(b[i],b[j])= 0 then do;
            if not missing(b[i]) then iout=i;
            if not missing(b[j]) then jout=j;
            ival = b[i];
            jval = b[j];
            output work.pairs;
            call missing(b[i],b[j],iout,jout);
            leave;
         end;
      end;
   end;
  
   if last then output work.leftover;
run;

data work.trios ( keep=iout jout kout ival jval kval)
     work.leftover2 (keep=ba:)
;
   set work.leftover end=last;
   array b ba:;
   do i= 1 to (dim(b)-2);
      do j= 2 to (dim(b)-1);
         do k= 3 to dim(b);
            if sum(b[i],b[j],b[k])= 0 then do;
               if not missing(b[i]) then iout=i;
               if not missing(b[j]) then jout=j;
               if not missing(b[k]) then kout=k;
               ival = b[i];
               jval = b[j];
               kval = b[k];
               output work.trios;
               call missing(b[i],b[j],b[k],iout,jout,kout);
               leave;
            end;
         end;
      end;
   end;
   if last then output work.leftover2;
run;&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Fri, 15 Jun 2018 12:52:54 GMT</pubDate>
    <dc:creator>adornodj</dc:creator>
    <dc:date>2018-06-15T12:52:54Z</dc:date>
    <item>
      <title>Net Groups of Lines in a Table to $0.00 Con't</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470582#M285662</link>
      <description>&lt;P&gt;Hello - This is a continuation from an earlier post I had.&amp;nbsp; Again, thank you in advance for any help you can provide me.&amp;nbsp; I am trying to group smaller subsets of a larger population that net (or sum if you will) down to $0.00.&amp;nbsp; &amp;nbsp;My data has limited available fields, so I can only use the numeric field to complete this.&amp;nbsp; &amp;nbsp;In my earlier post, it looked like a "brute force" option was the best choice for me.&amp;nbsp; I am hoping the community can help me improve upon the logic that was provided to me.&amp;nbsp; Big thanks to all the earlier contributors, my knowledge of arrays, while very limited, has grown over the past few days.&amp;nbsp; A few notes...&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;1) Yes, the overall population does net to $0.00, but I need to try to group it into smaller subsets for the purpose of my analysis (new data attached)&lt;/P&gt;&lt;P&gt;2.) I DO NOT have SAS/OR, so I cannot use the OPTMODEL procedure.&amp;nbsp; I am running on SAS EG.&amp;nbsp; I can provide a list of the licenses I do have if that helps.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;3.) One thing I noticed when testing the script below on a new data set is that if there's a repeating value sequentially, the code may add the original value, plus itself when looping.&amp;nbsp; If possible, I'd like to avoid scenarios like this.&amp;nbsp; &amp;nbsp;For example, with {-3,-3,6} this code is adding the first element twice and then summing to zero with the 6, instead of taking elements 1 and 2.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;4.) For some additional background, the populations I'm dealing with can go up to 6000 lines.&amp;nbsp; I tested one of these larger populations for sets of 3.&amp;nbsp; It worked but took 5 hours to process.&amp;nbsp; I'm ok with the processing time, but am curious if there efficiencies which can applied, especially since I could have multiple larger datasets.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;5.) Example code I've been playing with is below.&amp;nbsp; Special thanks to&amp;nbsp;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/13884"&gt;@ballardw&lt;/a&gt; for writing this originally.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;My questions to the community...&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;a.) can the logic below be updated so that the array containing the values "shrinks" after subsets have been made?&amp;nbsp; I think this may reduce the number of iterations.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;b.) along the same idea as question a., is it possible for the array to not sum the same element against itself?&amp;nbsp; this may reduce the number of iterations as well (though not by much).&amp;nbsp; simply put, each element can only be used once when summing&lt;/P&gt;&lt;P&gt;c.) does this code need to be sequentially run the way it is currently set up?&amp;nbsp; say I want to search for subsets of 50 lines, do I need 50 steps going sequentially from 2-50, or can it be set up to run just the 50 lines matches first, and within the results are the subsets of 2, 3,4...n.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Thank you all again for you help.&amp;nbsp; This has been very insightful so far and I've been learning a lot.&amp;nbsp; If there are any other questions I can answer, or background I can provide to add additional clarity, please let me know.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;proc transpose data=have out=work.trans
  prefix=ba
;
var base_amt;
run;

data work.pairs ( keep=iout jout ival jval)
     work.leftover (keep=ba:)
;
   set work.trans end=last;
   array b ba:;
   do i= 1 to (dim(b)-1);
      do j= 2 to dim(b);
         if sum(b[i],b[j])= 0 then do;
            if not missing(b[i]) then iout=i;
            if not missing(b[j]) then jout=j;
            ival = b[i];
            jval = b[j];
            output work.pairs;
            call missing(b[i],b[j],iout,jout);
            leave;
         end;
      end;
   end;
  
   if last then output work.leftover;
run;

data work.trios ( keep=iout jout kout ival jval kval)
     work.leftover2 (keep=ba:)
;
   set work.leftover end=last;
   array b ba:;
   do i= 1 to (dim(b)-2);
      do j= 2 to (dim(b)-1);
         do k= 3 to dim(b);
            if sum(b[i],b[j],b[k])= 0 then do;
               if not missing(b[i]) then iout=i;
               if not missing(b[j]) then jout=j;
               if not missing(b[k]) then kout=k;
               ival = b[i];
               jval = b[j];
               kval = b[k];
               output work.trios;
               call missing(b[i],b[j],b[k],iout,jout,kout);
               leave;
            end;
         end;
      end;
   end;
   if last then output work.leftover2;
run;&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 15 Jun 2018 12:52:54 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470582#M285662</guid>
      <dc:creator>adornodj</dc:creator>
      <dc:date>2018-06-15T12:52:54Z</dc:date>
    </item>
    <item>
      <title>Re: Net Groups of Lines in a Table to $0.00 Con't</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470664#M285664</link>
      <description>&lt;P&gt;For #3, I think the j=2 should instead be j=i+1 in both places.&amp;nbsp; Similarly, k=3 should be k=j+1.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Can you please post the 6000-observation data set you mentioned in #4?&lt;/P&gt;</description>
      <pubDate>Fri, 15 Jun 2018 17:56:29 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470664#M285664</guid>
      <dc:creator>RobPratt</dc:creator>
      <dc:date>2018-06-15T17:56:29Z</dc:date>
    </item>
    <item>
      <title>Re: Net Groups of Lines in a Table to $0.00 Con't</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470669#M285665</link>
      <description>&lt;P&gt;Rob - thanks again for looking at this.&amp;nbsp; Attached is another example with just under 6000 lines.&amp;nbsp; I'm testing your notes for #3 right now.&amp;nbsp;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 15 Jun 2018 18:18:24 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470669#M285665</guid>
      <dc:creator>adornodj</dc:creator>
      <dc:date>2018-06-15T18:18:24Z</dc:date>
    </item>
    <item>
      <title>Re: Net Groups of Lines in a Table to $0.00 Con't</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470741#M285666</link>
      <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/215002"&gt;@adornodj&lt;/a&gt;,&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Thanks for providing the "full-size" dataset.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;I think the increase from about 300 to almost 6000 observations is bad news with regard to processing time, especially for any kind of brute-force approach. Please note that, for example, the number of triples from a set of 6000 elements (approx. 3.6E10) is more than 8000 times the number of triples from a set of 300 elements (approx. 4.5E6). For quadruples the factor is already &amp;gt;160000, for quintuples 3.3 million, ...&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;So, without sophisticated algorithms like those implemented in SAS/OR, it seems challenging to me to get much further than triples.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;As to your question c: I think it's wise to stick to the sequential processing (singletons, pairs, triples, ...) and to remove any zero-sum subsets found. If you were to start with 50-element subsets, just consider comb(300, 50)=3.1E57.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;SAS hash objects combined with some basic mathematical considerations might be useful to reduce processing time. Unlike arrays hash objects can grow and shrink as needed (cf. your question a.).&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;After removing singletons, i.e. zeros, and pairs {x, -x} (which is easy), triples could perhaps more efficiently be searched as follows (rough, untested ideas, I will try to refine and implement them over the weekend):&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Split positive amounts and the absolute values of negative amounts into two hash objects POS and NEG (both with options multidata: 'y' [because of duplicates], ordered: 'y').&amp;nbsp;Note that any triple summing to zero must consist of either two positive elements and one negative element or vice versa.&lt;/LI&gt;
&lt;LI&gt;Traverse POS (using a hash iterator) and for each element a&amp;gt;0 traverse NEG as well, but only as long as the values b (&amp;gt;0!) there satisfy b&amp;lt;=a/2 (DO-WHILE loop).&lt;/LI&gt;
&lt;LI&gt;In each step search NEG for a-b using the CHECK and HAS_NEXT methods (special case: a-b=b).&lt;/LI&gt;
&lt;LI&gt;If found, {a, -b, -(a-b)} form a zero-sum triple to be output. Delete the three items from the hash objects using REMOVE/REMOVEDUP (possible conflict with iterator).&lt;/LI&gt;
&lt;LI&gt;Once finished, POS and NEG switch their roles.&lt;BR /&gt;&lt;BR /&gt;&lt;/LI&gt;
&lt;/OL&gt;</description>
      <pubDate>Fri, 15 Jun 2018 23:12:02 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470741#M285666</guid>
      <dc:creator>FreelanceReinh</dc:creator>
      <dc:date>2018-06-15T23:12:02Z</dc:date>
    </item>
    <item>
      <title>Re: Net Groups of Lines in a Table to $0.00 Con't</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470895#M285667</link>
      <description>&lt;P&gt;For comparison, here's what the greedy approach from PROC OPTMODEL yields:&lt;/P&gt;
&lt;DIV class="branch"&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" rules="all" frame="box" cellspacing="0" cellpadding="5" summary="Procedure Print: Data Set WORK.GROUP_SIZE_FREQS"&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="r header" scope="col"&gt;group_size&lt;/TH&gt;
&lt;TH class="r header" scope="col"&gt;group_size_freq&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;296&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;TD class="r data"&gt;17&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;TD class="r data"&gt;9&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;5&lt;/TD&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;8&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;9&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;13&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;30&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;63&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;373&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;4213&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;</description>
      <pubDate>Sun, 17 Jun 2018 17:27:32 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/470895#M285667</guid>
      <dc:creator>RobPratt</dc:creator>
      <dc:date>2018-06-17T17:27:32Z</dc:date>
    </item>
    <item>
      <title>Re: Net Groups of Lines in a Table to $0.00 Con't</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/471030#M285668</link>
      <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/215002"&gt;@adornodj&lt;/a&gt;,&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;As promised, here is the implementation of the 5-step plan I outlined in my previous post. For simplicity, it assumes that the variable containing the amounts in dataset HAVE is named X (rather than BASE_AMT or TR_AMT). First I implemented the search for pairs using a similar technique (as a preliminary practice). The algorithm is greedy in the sense that zero-sum groups are extracted right after detection, although (in the case of triples) this might sacrifice maximization of the number of groups in the end.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;/* Macro for extracting zero-sum pairs
   (parameters: hash objects with absolute values of pos./neg. amounts) */

%macro pairs(h1, h2);
do while(&amp;amp;h1.i.next()=0); /* traverse hash object 1 */
  if &amp;amp;h2..check()=0 then do; /* search hash object 2 */
    grp+1;
    output;
    y=x; /* save value x before PREV() overwrites it */
    &amp;amp;h2..removedup();
    &amp;amp;h1..check(); /* set pointer for the other deletion */
    rc=&amp;amp;h1.i.prev(); /* move iterator aside to prepare deletion */
    x=-y;
    output;
    &amp;amp;h1..removedup(key: y);
  end;
end;
%mend pairs;

/* Macro for extracting zero-sum triples
   (parameters: hash objects with absolute values of pos./neg. amounts) */

%macro triples(h1, h2);
%local m;
%if %upcase(&amp;amp;h1)=POS %then %let m=-;
rc1=&amp;amp;h1.i.first();
do while(rc1=0); /* traverse hash object 1 */
  a=x;
  rc2=&amp;amp;h2.i.first();
  do while(rc2=0 &amp;amp; x&amp;lt;=a/2); /* partially traverse hash object 2 */
    b=x;
    d=round(a-b,.001);
    if &amp;amp;h2..check(key: d)=0 then do; /* search hash object 2 */
      &amp;amp;h2..has_next(result: r); /* take care of special case d=b */
      if d ne b | r then do;
        grp+1;
        x=&amp;amp;m.-a;
        output;
        x=&amp;amp;m.b;
        output;
        x=&amp;amp;m.d;
        output;
        if d=b then rc=&amp;amp;h2.i.prev(); /* move iterator aside if necessary */
        &amp;amp;h2..removedup(key: d);
        &amp;amp;h1..check(key: a); /* set pointer for 2nd deletion */
        rc=&amp;amp;h1.i.prev(); /* move iterator aside */
        &amp;amp;h1..removedup(key: a);
        &amp;amp;h2..check(key: b); /* set pointer for 3rd deletion */
        if d ne b then rc=&amp;amp;h2.i.prev(); /* move iterator aside if necessary */
        &amp;amp;h2..removedup(key: b);
        leave; /* stop traversing hash object 2 */
      end;
    end;
    rc2=&amp;amp;h2.i.next();
  end;
  rc1=&amp;amp;h1.i.next();
end;
%mend triples;

/* Create dataset with variables GRP_SIZE (1, 2, 3), GRP (group no.) and X
   (formerly TR_AMT or BASE_AMT) containing singletons, pairs and triples
   of X values summing to zero */

data groups;
dcl hash pos(multidata: 'y', ordered: 'y');
pos.definekey('x');
pos.definedone();
dcl hiter posi('pos');

dcl hash neg(multidata: 'y', ordered: 'y');
neg.definekey('x');
neg.definedone();
dcl hiter negi('neg');

grp_size=1;
do until(last);
  set have end=last;

  /* Write singletons {0} to output dataset and absolute values of
     non-zero elements to hash objects */
  if x=0 then do;
    grp+1;
    output;
  end;
  else if x&amp;gt;0 then pos.add();
  else if x&amp;lt;0 then neg.add(key: abs(x), data: abs(x));
end;

/* Write pairs {x, -x} to output dataset and remove their components
   from hash objects */
grp_size=2;
if pos.num_items&amp;lt;neg.num_items /* IF condition for efficiency */
  then %pairs(pos, neg)
  else %pairs(neg, pos)

/* Write zero-sum triples to output dataset and remove their components
   from hash objects */
grp_size=3;
%triples(pos, neg) /* one positive and two negative components */
%triples(neg, pos) /* one negative and two positive components */
keep x grp:;
stop;
run;

/* Create input dataset for future search for zero-sum quadruples etc. */

proc sql;
create table leftover as
(select x from have)
except all
(select x from groups);
quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;With the "large"&amp;nbsp;input file (5398 obs.) you provided I obtained 296 pairs and 17 triples (as did &lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/1636"&gt;@RobPratt&lt;/a&gt;&amp;nbsp;-- thanks for sharing the summarized results!), resulting in 296*2+17*3=643 observations in my output dataset GROUPS. (Results with your first input file [335 obs.] match as well.)&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;NOTE: There were 5398 observations read from the data set WORK.HAVE.
NOTE: The data set WORK.GROUPS has 643 observations and 3 variables.
NOTE: DATA statement used (Total process time):
      real time           0.06 seconds
      cpu time            0.06 seconds&lt;/PRE&gt;
&lt;P&gt;As you can see, the code runs&amp;nbsp;so quickly that going a step further (to quadruples) using similar techniques seems realistic. The&amp;nbsp;code amendment to achieve this is not quite straightforward, though.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 18 Jun 2018 11:00:58 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/471030#M285668</guid>
      <dc:creator>FreelanceReinh</dc:creator>
      <dc:date>2018-06-18T11:00:58Z</dc:date>
    </item>
    <item>
      <title>Re: Net Groups of Lines in a Table to $0.00 Con't</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/472618#M285669</link>
      <description>&lt;P&gt;Because only 335 observations have nonnegative values and each group must contain at least one such observation, 335 is an upper bound on the number of groups.&amp;nbsp; So you cannot improve much beyond the greedy 331-group solution for this instance.&lt;/P&gt;</description>
      <pubDate>Fri, 22 Jun 2018 21:59:16 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Net-Groups-of-Lines-in-a-Table-to-0-00-Con-t/m-p/472618#M285669</guid>
      <dc:creator>RobPratt</dc:creator>
      <dc:date>2018-06-22T21:59:16Z</dc:date>
    </item>
  </channel>
</rss>

