site.base_url }}/">The Bear's Den -->

The Bear's Den

Enter at your own risk

Bits, Digits and Numbers

Task 1: Element Digit Sum

Submitted by: Mohammad Sajid Anwar


You are given an array of integers, @ints.

Write a script to evaluate the absolute difference between element and digit sum of the given array.

Example 1

Input: @ints = (1,2,3,45)
Output: 36

Element Sum: 1 + 2 + 3 + 45 = 51
Digit Sum: 1 + 2 + 3 + 4 + 5 = 15
Absolute Difference: | 51 - 15 | = 36

Example 2

Input: @ints = (1,12,3)
Output: 9

Element Sum: 1 + 12 + 3 = 16
Digit Sum: 1 + 1 + 2 + 3 = 7
Absolute Difference: | 16 - 7 | = 9

Example 3

Input: @ints = (1,2,3,4)
Output: 0

Element Sum: 1 + 2 + 3 + 4 = 10
Digit Sum: 1 + 2 + 3 + 4 = 10
Absolute Difference: | 10 - 10 | = 0

Example 4

Input: @ints = (236, 416, 336, 350)
Output: 1296

Solution

The task will be generalized to an arbitrary base \(b > 1\). For any non-negative integer we have

\[n = \sum_{i=0}^l d_i b^i \ge \sum_{i=0}^l d_i\]

and thus there is no need to take the absolute value of the difference between a number and the sum of its digits.

Using some functions from Math::Prime::Util, the task may be solved in a single statement:

use strict;
use warnings;
use Math::Prime::Util qw(vecreduce vecsum todigits);

sub eds {
    my $base = shift;
    vecreduce {$a + $b - vecsum todigits $b, $base} 0, @_;
}

See the full solution.

Task 2: Multiply by Two

Submitted by: Mohammad Sajid Anwar


You are given an array of integers, @ints and an integer $start.

Write a script to do the followings:

a) Look for $start in the array @ints, if found multiply the number by 2
b) If not found stop the process otherwise repeat

In the end return the final value.

Example 1

Input: @ints = (5,3,6,1,12) and $start = 3
Output: 24

Step 1: 3 is in the array so 3 x 2 = 6
Step 2: 6 is in the array so 6 x 2 = 12
Step 3: 12 is in the array so 12 x 2 = 24

24 is not found in the array so return 24.

Example 2

Input: @ints = (1,2,4,3) and $start = 1
Output: 8

Step 1: 1 is in the array so 1 x 2 = 2
Step 2: 2 is in the array so 2 x 2 = 4
Step 3: 4 is in the array so 4 x 2 = 8

8 is not found in the array so return 8.

Example 3

Input: @ints = (5,6,7) and $start = 2
Output: 2

2 is not found in the array so return 2.

Solution

The task can be solved in a single pass through the list as we know the target values beforehand: \(\mathit{start}\,\cdot\,2^i\). Actually, we need to know which is the smallest of the target values that is missing from the list. For this end we define a bit vector of target indicators that is all-zero initially. For every target value we encounter, the corresponding indicator bit will be set. Finally, the least significant bit that is not set leads to the requested result.

The core of this implementation is some bit fiddling. In the following “\(\sim\)” is the bitwise NOT and “\(\&\)” the bitwise AND operation.

Determine if an integer is a power of two

\(n\,\&\,(n - 1) = 0 \quad \Leftrightarrow \quad n = 0 \text{ or } n = 2^k\)

Why does this work?

Determine the value of the least significant bit that is zero:

\(v = (n + 1)\,\&\,\sim n\)

Why does this work?

Implementation

The task can be solved in a single run of some simple steps:

Finally find the value corresponding to the least significant zero-bit in the collected powers. This gives the required factor for $start.

The false positive power-of-two test for zero does not harm because zero does not account for the collected powers.

use strict;
use warnings;
use experimental 'prototypes';

sub mb2 ($start, @ints) {
    my $p = 0;
    for my $i (@ints) {
        next if $i % $start;
        my $c = $i / $start;
        $p |= $c unless $c & ($c - 1);
    }
    $start * (($p + 1) & ~$p);
}

See the full solution.


If you have a question about this post or if you like to comment on it, feel free to open an issue in my github repository.