DEV Community

Cover image for Weekly Challenge #085 Task #1 :: (Raku)
Myoungjin Jeon
Myoungjin Jeon

Posted on • Updated on

Weekly Challenge #085 Task #1 :: (Raku)

TASK #1 › Triplet Sum

Submitted by: Mohammad S Anwar

You are given an array of real numbers greater than zero.

Write a script to find if there exists a triplet (a,b,c) such that 1 < a+b+c < 2. Print 1 if you succeed otherwise 0.

Example 1:

Input: @R = (1.2, 0.4, 0.1, 2.5)
Output: 1 as 1 < 1a.2 + 0.4 + 0.1 < 2

Example 2:

Input: @R = (0.2, 1.5, 0.9, 1.1)
Output: 0

Example 3:

Input: @R = (0.5, 1.1, 0.3, 0.7)
Output: 1 as 1 < 0.5 + 1.1 + 0.3 < 2
Enter fullscreen mode Exit fullscreen mode

Floating vs Rational Number

In Raku, It seems that there are two types to save floating point values. actually we can express most of floating point as rational number like ...

1.7 -> 10 / 17
Enter fullscreen mode Exit fullscreen mode
  1. Rat
  2. FatRat

And document said...

Since, unlike Rat, FatRat arithmetics do not fall back Num at some point, there is a risk that repeated arithmetic operations generate pathologically large numerators and denominators.

So I guess Rat is enough for this task. and making a Rat is straightforward. Just add point even if the number can be integer value.

> (1.0).WHAT
(Rat)
> (1.7).numerator
17
> (1.7).denominator
10
> 1.Rat # or you can declare explicitly
Enter fullscreen mode Exit fullscreen mode

A Sample Made Easy

So we need triplet of rational number next code will generate random numbers suitable for the task.

  • I add more zeroes to generate more 0.* values *
> (10..99).pick(10).map({ (|(0 xx 10), |(0..3)).pick + $_/100 })
(3.24 0.75 0.83 0.74 0.58 0.85 1.68 0.81 0.59 0.12)
Enter fullscreen mode Exit fullscreen mode

Combinations of Triplet

Maybe I'm too obsessed with combinations 😂

> my @r = (10..99).pick(10).map({ (|(0 xx 10), |(0..3)).pick + $_/100 })
[0.23 0.1 0.13 0.56 0.81 0.71 1.4 0.79 0.16 0.52]
> @r.combinations(3).head(3) # head() can take an argment
((0.23 0.1 0.13) (0.23 0.1 0.56) (0.23 0.1 0.81))
Enter fullscreen mode Exit fullscreen mode

Sum of Triplet

Our desired value is between 1 and 2 (or 1.0 and 2.0 in clearer way to compare)

> @r.combinations(3).grep( -> \t { 1.0 < t.sum < 2.0 } );
((0.23 0.1 0.81) (0.23 0.1 0.71) (0.23 0.1 1.4) (0.23 0.1 0.79) ...
Enter fullscreen mode Exit fullscreen mode

Okay.. those results are too many, probably. 🤪

Finding Only One 💖

Human is too greedy. We are searching for more and leave the processor suffering even though all we need is one possible answer.

> @r.combinations(3).grep( -> \t { 1.0 < t.sum < 2.0 } ).head;
((0.23 0.1 0.81))
Enter fullscreen mode Exit fullscreen mode

and maybe flattening to get only triplet itself.

> @r.combinations(3).grep( -> \t { 1.0 < t.sum < 2.0 } ).head.flat;
(0.23 0.1 0.81)
Enter fullscreen mode Exit fullscreen mode

hang on... but we have 📔first()

First() is NOT Head

if we simply replace head() with first() ...

> @r.combinations(3).grep( -> \t { 1.0 < t.sum < 2.0 } ).first;
(0.23 0.1 0.81)
Enter fullscreen mode Exit fullscreen mode

It might look similar but don't need to flat() and also first() gives us the way to searching an object what we are to get.

> @r.combinations(3).first( -> \t { 1.0 < t.sum < 2.0 } );
Enter fullscreen mode Exit fullscreen mode

Good. Probably it does the similar thing like below code.

> @r.combinations(3).grep( -> \t { 1.0 < t.sum < 2.0 } ).lazy.first;
Enter fullscreen mode Exit fullscreen mode

Because grep() wants every cases made from combinations, and won't finish until check all of them. AND even worse, if you talk to your partner about his or her bad habit all at once, He/She will leave you before you are finished, right? I meant it is exhausting.
But by appending .lazy() it will evaluated every time when we want to eager to get the value.

I guess that it is possible that combinations() is also try to get every possible combinations before calling grep() so we can append .lazy() to combinations(). Even though the list is short it will only makes whole programme a bit slower, it is not painful so I added.

Final Code

#!/usr/bin/env raku

unit sub MAIN ( *@r where { @r.all ~~ Rat and @r.all > 0.0 } );

@r.
grep(* < 2.0).
sort.
combinations(3).
lazy.
first( 1.0 < *.sum < 2.0 )
andthen say("1.0 < "
           ~ "({join(' + ', $_.List)})"
           ~ " < 2.0")
orelse say "0";

Enter fullscreen mode Exit fullscreen mode

Above code is the second solution of mine and I'll leave first solution for comparison. I took second one because it looks more raku-ish.

#!/usr/bin/env raku

unit sub MAIN ( *@a where { @a.all ~~ Rat and @a.all > 0 } );

my $triplet-it = @a.combinations(3).iterator;

my $found = False;
loop ( my $t := $triplet-it.pull-one;
    ($t =:= IterationEnd).not
    ; $t := $triplet-it.pull-one ) {
    if 1.0 < $t.sum < 2.0 {
        $found = True;
        say("1.0 < "
           ~ "({join(' + ', $t.List)})"
           ~ " < 2.0");
        last;
    }
}

say 0 unless $found;
Enter fullscreen mode Exit fullscreen mode

Not bad. Isn't it? It looks more likely imperative or OOP approach.
Okay, that's all.
Thank you for reading !!
Please check out other challenges in 🐪PWC🦋

Discussion (0)