*This post is part of a series on Mohammad Anwar’s excellent Perl Weekly Challenge, where Perl and Raku hackers submit solutions to two different challenges every week. (It’s a lot of fun, if you’re into that sort of thing.)*

Task #1 this week is to implement the Diff-K algorithm, as explained by Mohammad:

You are given an array

@Nof positive integers (sorted) and another non negative integerk. Write a script to find if there exists 2 indicesiandjsuch thatA[i] – A[j] = kandi != j.

This one is pretty easy. We can boil down the solution into two operations for each element (**$_**) of **@N**:

- First, filter @N for elements where
**$k+$_**exists in**@N**. - For the remaining elements, return an array containing the indexes of
**$k+$_**and**$_**.

To make this easier and more efficient, we’ll create an **%idx_of** hash to store the index of each element in **@N**. This can be created in linear time, and gives us O(1) lookups for both operations, above.

I really like how easy it is to create a reverse hash like this in Raku:

my %idx_of = @N.antipairs;

In Perl, the procedure is familiar and comfortable, but I have to admit, not as nice!

my %idx_of = map { $_[$_] => $_ } 0..$#_;

Note that `reverse`

is quite often used to reverse hashes in Perl, but isn’t useful in this particular case, because we didn’t have a hash to reverse yet. Thus, it was easier to simply build the hash I wanted from scratch.

Here’s how the full implementation looks in Raku and Perl:

## Raku

#| Diff-K in O(n) time sub diff_k( $k, @N ) { my %idx_of = @N.antipairs; @N.grep( { %idx_of{ $k+$_ }:exists } ) ==> map( { [ %idx_of{ $k+$_,$_ } ] } ) }

Usage is easy:

my @list = < 0 2 3 5 6 7 9 10 11 14 15 16 17 18 19 21 22 25 28 29 31 32 33 34 35 37 38 40 41 44 46 47 48 49>; say @list; say diff_k(30, @list);

Output:

[0 2 3 5 6 7 9 10 11 14 15 16 17 18 19 21 22 25 28 29 31 32 33 34 35 37 38 40 41 44 46 47 48 49] ([21 1] [22 2] [24 3] [25 5] [27 7] [28 8] [29 9] [30 11] [31 12] [32 13] [33 14])

## Perl

The Perl code only differs syntactically.

sub diff_k { my $k = shift; my %idx_of = map { $_[$_] => $_ } 0..$#_; map { [ @idx_of{ $k+$_, $_ } ] } grep { exists $idx_of{ $k+$_ } } @_; }

In both cases, the list is fed through `grep`

first, then `map`

. Note the use of hash slices to pull out values of `%idx_of{ $k+$_ }`

and `%idx_of{ $_ }`

at once.

## Efficiency Analysis

Both the Raku and Perl versions follow the same operations, and both languages have the same big-O efficiency for those operations, so it suffices to look at just one. (OK, the Perl version does have an additional step in the `my $k = shift`

line, but `shift`

is a constant-time operation in Perl, so we can ignore it.)

- The
`%idx_of = map { ... }`

line is O(n) on the size of**@N**. The map block is just an array lookup and a list, which are both constant-time operations. - The
`grep`

also is O(n) on the size of**@N**. Its block only has an addition and O(1) hash lookup, so O(n) holds. - The
`map`

operates on whatever made it through`grep`

, so it certainly can’t be larger than*n*elements. The block has two hash lookups and an array ref creation, which are all O(1), thus this line is O(n) as well.

Therefore, this entire routine’s worst case performance is O(n). I don’t believe it is possible to obtain a better growth rate.

That’s it for this task! See you on the next one.