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

The Bear's Den

Enter at your own risk

Count Unique

Task 1: Unique Number

Submitted by: Mohammad Sajid Anwar


You are given an array of integers, @ints, where every elements appears more than once except one element.

Write a script to find the one element that appears exactly one time.

Example 1

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

Example 2

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

Example 3

Input: @ints = (1)
Output: 1

Example 4

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

Solution

The usual approach to find a unique element in a list would be to count each value and pick those having a count of one. This requires two passes: one over the list and one over the values thereof.

Here we have very special conditions:

Utilizing these requirement, the task may be solved in a single pass:

At the end, the running sum will be the sum over all unique numbers. As there shall be only one such number, the sum equals the unique value. Additionally we may count the number of unique numbers and reject lists that do not conform to the requirements.

Using a hash to record each value’s state:

use strict;
use warnings;

sub unique_number {
    my ($sum, $cnt, %seen) = (0, 0);
    ($seen{$_}, $sum, $cnt) = exists $seen{$_} ?
        (0, $sum - $seen{$_} * $_, $cnt - $seen{$_}) :
        (1, $sum + $_, $cnt + 1) for @_;

    $cnt == 1 ? $sum : undef;
}

See the full solution to task 1.

Task 2: Digit Count Value

Submitted by: Mohammad Sajid Anwar


You are given an array of positive integers, @ints.

Write a script to return true if for every index i in the range 0 <= i < size of array, the digit i occurs exactly the $ints[$i] times in the given array otherwise return false.

Example 1

Input: @ints = (1, 2, 1, 0)
Ouput: true

$ints[0] = 1, the digit 0 occurs exactly 1 time.
$ints[1] = 2, the digit 1 occurs exactly 2 times.
$ints[2] = 1, the digit 2 occurs exactly 1 time.
$ints[3] = 0, the digit 3 occurs 0 time.

Example 2

Input: @ints = (0, 3, 0)
Ouput: false

$ints[0] = 0, the digit 0 occurs 2 times rather than 0 time.
$ints[1] = 3, the digit 1 occurs 0 time rather than 3 times.
$ints[2] = 0, the digit 2 occurs exactly 0 time.

Solution

In a verbatim interpretation of the task’s description, there is no input that would give true as the result, because

an array of positive integers

cannot conform to the given condition. As the examples contradict the requested positiveness, we should relax the condition to non-negative.

Furthermore, we need to make a decision about the allowed size of the array. The digit i must be in the range 0..9. In a strict view, there cannot be an array having a length larger than 10 as there are no digits beyond 9. On the other hand, we might allow such arrays given that all elements $ints[$k] == 0 for $k > 9. In the following, we’ll consider both views.

Just for checking the condition, either view is simple to implement. However, the far more interesting question is: Which arrays actually fulfill the condition?

Independently of such view, we may come to some insights about valid arrays. The analysis is rather boring and I’ll omit a derivation here.

Strict

Combining the above insights, there are one a handful of arrays that fulfill the task’s condition under the strict view We may easily check against these:

use strict;
use warnings;

BEGIN {
    @::strict_arr = (
        [1, 2, 1, 0],
        [2, 0, 2, 0],
        [2, 1, 2, 0, 0],
        [3, 2, 1, 1, 0, 0, 0],
        [4, 2, 1, 0, 1, 0, 0, 0],
        [5, 2, 1, 0, 0, 1, 0, 0, 0],
        [6, 2, 1, 0, 0, 0, 1, 0, 0, 0],
    );

    $::strict_re = qr{^(?:@{[join '|', map join(':', @$_), @::strict_arr]})$};
}

sub digit_count_number_strict {
    join(':', @_) =~ $::strict_re;
}

Relaxed

In the relaxed view, we allow arrays having a length larger than 10, but we require $ints[$k] == 0 for $k > 9.

In the strict view we don’t need to care about another aspect of this task: the meaning of Counting digits, as all numbers are below 10 and therefore are identical to digits. Allowing larger lengths, we arrive at numbers of more than one digit, but according to the task we shall count digits. Thus we split each number of @ints into its digits.

Actually there are an infinite number of such arrays as each of the following, for any value of $p >= 0, is valid:

@ints = (10**$p, 2, 1, (0) x (10**$p - $p));

So it looks like we need a true check on the given array in this case.

Solving this special case using ‘PDL’ again.

Creating a ndarray consisting of the digits of the given array, find the number of occurrences of each digit, restrict to values within the length of the array and compare the result to the original array.

use strict;
use warnings;
use PDL;

sub digit_count_number {
    my $l = long @_;
    my $d = long map split(//), @_;
    my ($cnt, $val) = rle $d->qsort;
    ($cnt, $val) = where $cnt, $val, $val < @_;
    my $freq = zeroes $l;
    $freq->index($val) .= $cnt;
    all $l == $freq;
}

See the full solution to task 2.


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.