## TWC 195: Special Speedy Frequency

In which count only what we must.

In Raku, Perl, and C {via Perl's Inline::C} .

(Still editing!)

# TWC Task #1 - Special Integers

Count the integers that have no repeating digits, between 1 and `\$n`.

## Observations:

• This is the more time-consuming of the two tasks, in both CPU and programmer time.

• Since the largest number with all-unique digits is 9876543_210, any requested `\$n` higher than that will have the same answer.

• Scanning every integer in the range will scale linearly, and since the largest input in in the 9 billions, we should look for a faster algorithm. The problem relates to combinations of individual digits, irrespective of their order, so something from combinatorics might be fruitful.

## Perl

Using simple one-at-a-time iteration:

``````use v5.36;
use List::Util qw<uniq>;
sub task1 ( \$n ) {
return 0 + grep { length == uniq split '' } 1..\$n;
}
``````

I also could have said:

``````return 0 + grep !/(.).*\1/, 1..\$n;
``````

, which would have been faster, but less clear.

Also, for large `\$n`, I observe that about 25% of the total time for `perl` to return control to my terminal happens after `perl` outputs the answer. I expect this is due to Perl not optimizing the scalar `grep` into a "count-only" version of itself, and so a huge list really is allocated and built by `grep`, which must be `DESTROY`ed at `END` time. Recoding as something like `/(.).*\1/ or \$c++ for 1..\$n` recovered all that cleanup time.

## C

This code takes 2m42s to calculate `task1(9_876_543_210)==8_877_690` using one-at-a-time iteration. That is nearly 3 minutes, at full `C` speed; no bouncing between Perl and C is done except at start and end of a calculation.

``````use v5.36;
use Inline 'C';
# ... Testing code omitted here
__END__
__C__
int is_special(long x) {
int ds[10];
memset(ds, 0, 10*sizeof(int));
while (x) {
if (ds[x % 10]++)
return 0;
x /= 10;
}
return 1;
}
int count_special(long in) {
long x = in > 9876543210 ? 9876543210 : in;
int  r = 0;
for ( ; x ; x-- ) {
if ( is_special(x) )
r++;
}
return r;
}
``````

## Raku

I have two versions that use combinatorics to solve the task. The first rips into the problem in two phases

(Ack! Must finish this explanation soon, but cannot right now.)

``````# https://oeis.org/A073531  Number of n-digit positive integers with all digits distinct.
constant @n-digits-distinct     = 0, 9, |( 9 X* [\*] (9...1) );
constant @n-digits-distinct-sum = [\+] @n-digits-distinct;

sub task1 ( UInt \$n --> UInt ) {
constant MAX = 9_876_543_210;
return &?ROUTINE(MAX) if \$n > MAX;

# Knuth's "falling powers"; kfp(9,3) == 9*8*7
sub kfp (\$n, \$k) { [*] ( (\$n-\$k) ^.. \$n ) }

my \$nc = \$n.chars;
my @totals;

push @totals, @n-digits-distinct-sum[\$nc - 1];

my SetHash \$used;
for \$n.comb».Numeric.kv -> UInt \$k, UInt \$digit {

my UInt \$combinations_in_rightward_places
= kfp(9 - \$k, \$nc - \$k - 1);

my Range \$space_below_digit = (     0 + (1 if \$k == 0   ))
.. (\$digit - (1 if \$k < \$nc-1));

my Set \$using_for_this_digit = \$space_below_digit (-) \$used;

push @totals, \$using_for_this_digit.elems
* \$combinations_in_rightward_places;

\$used{\$digit}++;
}

return @totals.sum;
}
``````

This version is simpler to understand, but does not perform as well. It does the initial optimization to skip over about .log10 places, then generates all the combinations with the correct leading digit, filtering on which ones are less than \$n.

``````sub task1_one_big_skip ( UInt \$n --> UInt ) {
constant MAX = 9_876_543_210;
return &?ROUTINE(MAX) if \$n > MAX;

my @totals;
my \$core  = \$n.chars - 1;

push @totals, @n-digits-distinct-sum[\$core];

push @totals, +combinations(9,\$core) * ([*] 1..\$core)

my \$L3 = 0;
for (0..9).grep(* != \$lead).combinations(\$core) -> @comb {
\$L3 += +@comb.permutations.grep: { (\$lead ~ .join) <= \$n };
}
push @totals, \$L3;

return @totals.sum;
}
``````

# TWC Task #2 - Most Frequent Even

Given a `@list` of integers, find the most frequent even numbers in the list, with smallest of those most frequent as a tie-breaker. return `-1` if no even numbers are in the list.

## Raku

``````@ns.grep( * %% 2 ).Bag.max({ .value, -.key }).?key // -1 ;
``````

Yes, the solution can be expressed in a single, um, expression.

The `Bag` counts the even numbers, giving a hash of key=originalnumber => value=countoftimesseen.

The `.max` method will receive `Pair` objects from the `Bag`, and find the maximum of each `Pair`'s `.value` (the count), with negative (because we want the lowest in a `.max`) `.key` (original number) as tie-breaker.

Now we just need to return the `.key` of the `Pair` that `.max` found, or `-1` if `grep` found no even numbers. But wait! `.max` returns `-Inf` when given a empty list; we cannot call `.key` on `-Inf`.

The `.?` methodop is the "Safe call operator". It works as `.`, but if the left-hand side lacks the requested method, it returns `Nil`, which is just what we need for `//` to trigger the `-1` return.

## Perl

``````use v5.36;
use List::Util    qw<min>;
use List::UtilsBy qw<max_by>;

my %bag;
\$bag{\$_}++ for grep { \$_ % 2 == 0 } @ns;
return -1 if not %bag;

return min max_by { \$bag{\$_} } keys %bag;
}
``````

Compared to the Raku code, having `%bag` as a separate variable does prevent a single-expression solution, but we gain clarity; returning `-1` happens much sooner in the dataflow, and in a place that is simpler to read.

Also, because we can refer to the count via (less efficient) hash-lookup, and because Perl's `max_by` does return all the participants in a tie, we can feed in only the keys, so `min` is operating only on the keys.

With great pleasure, we announce Bruce Gray
as the next Champion of The Weekly Challenge.
-- TWC 194 Mohammad S Anwar

The thing about me that's so impressive
is how infrequently I mention all of my successes
-- Video "I'm So Humble" {The only song by The Lonely Island that is Safe For Work}.