in reply to Challenge: Sorting Sums Of Sorted Series

Given the mess that is Re: Challenge: Sorting Sums Of Sorted Series, I'll post a pair of working, fast solutions here. The solutions that follow use O(N+M) memory and are O(N^2*M) in time. The basic algorithm implemented is:

    Populate a queue with the sum of all elements of the short list with the first element of the other list. Keep track of the indices of each value. This queue is by definition sorted. Then loop until the queue is empty, doing the following:

  1. Shift and print the first element of the queue;
  2. Increment the second list index for the shifted element. Cycle loop if this index is outside the second list.
  3. Use a sorted insertion to add the sum at the new coordinates to the queue.

I wrote a version with an array for each tracked element (solution_2) and one where all three are lumped into a single entry.

sub solution_2 { # adaptive motion queue solution my ($list_ref1, $list_ref2) = @_; my @list1; my @list2; if (@$list_ref1 <= @$list_ref2) { @list1 = @$list_ref1; @list2 = @$list_ref2; } else { @list1 = @$list_ref2; @list2 = @$list_ref1; } my @queue = map $_+$list2[0], @list1; my @xs = (0 .. $#list1); my @ys = (0) x @list1; while (@queue) { print OUT shift(@queue), "\n"; my $x = shift @xs; my $y = shift @ys; next if ++$y >= @list2; my $sum = $list1[$x] + $list2[$y]; my $count = 0; $count++ until $count == @queue or $sum <= $queue[$count]; splice @queue, $count, 0, $sum; splice @xs, $count, 0, $x; splice @ys, $count, 0, $y; } } sub solution_3 { # adaptive motion queue solution, one array my ($list_ref1, $list_ref2) = @_; my @list1; my @list2; if (@$list_ref1 <= @$list_ref2) { @list1 = @$list_ref1; @list2 = @$list_ref2; } else { @list1 = @$list_ref2; @list2 = @$list_ref1; } my @queue = (); for (0 .. $#list1) { push @queue, join("x", $list1[$_]+$list2[0], "$_", "0"); } while (@queue) { my $entry = shift(@queue); my ($old, $x, $y) = split /x/, $entry; print OUT "$old\n"; next if ++$y >= @list2; my $sum = $list1[$x] + $list2[$y]; my $count = 0; { no warnings 'numeric'; $count++ until $count == @queue or $sum <= $queue[$count]; } splice @queue, $count, 0, "${sum}x${x}x${y}"; } }

And benchmarks on 4 element and 100 element equally distributed arrays:

Benchmark: timing 100 iterations of 1_array, 3_array, Baseline, LR_1.. +. 1_array: 10.651 wallclock secs (10.65 usr + 0.00 sys = 10.65 CPU) +@ 9.39/s (n=100) 3_array: 9.28456 wallclock secs ( 9.28 usr + 0.00 sys = 9.28 CPU) + @ 10.78/s (n=100) Baseline: 0.844141 wallclock secs ( 0.84 usr + 0.00 sys = 0.84 CPU +) @ 119.05/s (n=100) (warning: too few iterations for a reliable count) LR_1: 20.4537 wallclock secs (20.45 usr + 0.00 sys = 20.45 CPU) + @ 4.89/s (n=100) Rate LR_1 1_array 3_array Baseline LR_1 4.89/s -- -48% -55% -96% 1_array 9.39/s 92% -- -13% -92% 3_array 10.8/s 120% 15% -- -91% Baseline 119/s 2335% 1168% 1005% -- Benchmark: timing 100000 iterations of 1_array, 3_array, Baseline, LR_ +1... 1_array: 7.06968 wallclock secs ( 7.07 usr + 0.00 sys = 7.07 CPU) + @ 14144.27/s (n=100000) 3_array: 5.09259 wallclock secs ( 5.09 usr + 0.00 sys = 5.09 CPU) + @ 19646.37/s (n=100000) Baseline: 2.11864 wallclock secs ( 2.12 usr + 0.00 sys = 2.12 CPU) + @ 47169.81/s (n=100000) LR_1: 7.01637 wallclock secs ( 7.02 usr + 0.00 sys = 7.02 CPU) + @ 14245.01/s (n=100000) Rate 1_array LR_1 3_array Baseline 1_array 14144/s -- -1% -28% -70% LR_1 14245/s 1% -- -27% -70% 3_array 19646/s 39% 38% -- -58% Baseline 47170/s 233% 231% 140% --