21
\$\begingroup\$

A drug comes in 5mg, 2.5mg, and 1mg sized pills. The drug is taken once a day. The total daily dose will be only one of the following (all in mg):

1, 2, 3, ... 20, 22.5, 25, 27.5, 30, 32.5, 35, 37.5, 40, 42.5, 45, 47.5, 50

In words: any whole number less or equal to 20, then at 2.5mg increments up to 50.

Your task is to determine how many pills of each size the patient should take to get to their total dose, minimizing the total number of pills they have to take.

Input: the total daily dose

Output: the respective number of 5mg, 2.5mg, and 1mg the patient needs to take, in any consistent format.

This is code golf. Fewest number of bytes wins.

Examples: (output is number of 1, 2.5, and 5 mg pills)

1 => [1, 0, 0]
4 => [4, 0, 0]
7 => [2, 0, 1]
19 => [4, 0, 3]
22.5 => [0, 1, 4]
40 => [0, 0, 8]

Irrelevant: this is based on a true story. The drug is prednisone and I wrote a program to automate the production of a calendar for patients to use to titrate their doses, which is a challenging task for many elderly patients to do safely even if you give them written instructions. This challenge was part of the programming task.

\$\endgroup\$
  • \$\begingroup\$ @JonathanAllan I believe there are no test cases so much as a hard list of cases. \$\endgroup\$ – Stef Sep 9 at 18:13
  • \$\begingroup\$ I once wrote program "calculate which plates to load on a barbell to get weight X. Plates come in 2.5, 5, 10, 25, 35, 45 weights" \$\endgroup\$ – aaaaa says reinstate Monica Sep 9 at 23:38
  • 1
    \$\begingroup\$ The standard for code golf is to score in bytes, not characters. \$\endgroup\$ – Bubbler Sep 10 at 0:21

20 Answers 20

13
\$\begingroup\$

R, 53 38 bytes

function(x)c(x%/%5,2*x%%1,(x<20)*x%%5)

Try it online!

Output is in the order (5mg, 2.5mg, 1mg).

For the 2.5mg pills:

  • if the dose is an integer, we need 0 such pills. Indeed, 1 pill of 2.5 mg would not give an integer, and 2 pills of 2.5 mg can be replaced by a single 5mg pill
  • if the dose x is not an integer, we need exactly 1 such pill (similar argument), and then we call the function with the integer x-2.5.

The number of 5 mg pills is the integer division of the input by 5.

Note that because the input is restricted, the 1 mg pills are never needed when the input is ≥20. If the input is <20, the number of 1mg pills is given by taking the input modulo 5.

| improve this answer | |
\$\endgroup\$
9
\$\begingroup\$

Python, 27 bytes

lambda x:[x//5,x%1*2,x*6%5]

Try it online!

An improvement to Manish Kundu's Python 3 solution.

The new part is x*6%5 for the last entry. The idea is to take the value mod 5 for whole x, but to give 0 for the non-integer doses, which are multiples of 2.5. To do this, we multiply by 6 before reducing mod 5. Because \$ 6 \equiv 1\$ modulo 5, multiplying by 6 leaves integers unchanged mod 5, but it turns multiples of 2.5 into multiples of 5, which then reduce to 0.

In the second entry, x%1*2 could also be x*2%2 as an equal-length alternative. This may be useful for porting, if a language's mod operation only works on whole numbers.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ x*6%5 is brilliant. \$\endgroup\$ – Bubbler Sep 10 at 0:22
5
\$\begingroup\$

Jelly, 10 bytes

d5ḞƑƇ;Ø.ṁ3

A monadic Link accepting the dosage which yields a list of numbers, [#5, #1, #2.5]

Try it online!

How?

d5ḞƑƇ;Ø.ṁ3 - Link: dosage     e.g. 22.5     OR   23
 5         - five                  5             5
d          - div-mod               [4, 2.5]      [4, 3]
    Ƈ      - filter keep if:
   Ƒ       -   is invariant under:
  Ḟ        -     floor             [4]           [4, 3]
      Ø.   - [0, 1]                [0, 1]        [0, 1]
     ;     - concatenate           [4, 0, 1]     [4, 3, 0, 1]
         3 - three                 3             3
        ṁ  - mould like            [4, 0, 1]     [4, 3, 0]
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ "keep if... is invariant under... floor". Jelly has some slick features. \$\endgroup\$ – Jonah Sep 9 at 21:06
5
\$\begingroup\$

Brachylog, 25 bytes

This defines a verb that takes as input the list of pills like [2, 1, 4] and outputs the sum of the pills milligrams. As this is Brachylog, we can then give an output like 24.5 an ask for a solution for the input.

Ṫℕᵐ<ᵛ⁵⁰;[1.0,2.5,5.0]\×ᵐ+

Try it online! (Note: you have to give the output as a float, e.g. 20.0.) Or try all possible inputs.

How it works

 Ṫℕᵐ<ᵛ⁵⁰;[1.0,2.5,5.0]\×ᵐ+
 Ṫ                          the input is a triplet [A, B, C]
  ℕᵐ                        all elements are >= 0
    <ᵛ⁵⁰                    and less than 50 (just to give the search some bounds)
        ;[1.0,2.5,5.0]      append the list of pill sizes
                      \     transpose: [[A,1.0],[B 2.5],[C,5.0]]
                       ×ᵐ   map multiplication over it
                         +  sum the result

Some notes: because Brachylog will try counting the trailing numbers up (0 0 48, 0 0 49, 0 1 0, 0 1 1, …), this guarantees that the first hit is also the solution with the fewest pills. 1,2.5,5 is not possible as otherwise the search will skip A = 0, C = 0 for whatever reason. Also, some day I'll find a better way to constrain numbers in 0 … X than ℕᵐ<ᵛX

| improve this answer | |
\$\endgroup\$
5
\$\begingroup\$

Rockstar, 127 120 118 110 100 bytes

Borrowing Robin's logic.

Output order is 1, 2.5, 5 separated by newlines

listen to N
let F be N/5
turn down F
let M be N
turn down M
let M be N-M
say N-M*5-F*5
say M*2
say F

Try it here (Code will need to be pasted in)

| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

J, 22 bytes

Returns in pill order 5 2.5 1, based on @Robin Ryder's answer

<.@%&5,(~:<.),5&|*20>]

Try it online!

How it works

<.@%&5,(~:<.),5&|*20>]
                  20>] x greater than 20?
              5&|*     multiply with x mod 5
       (~:<.),         prepend (x != floored x)
<.@%&5,                prepend (divide x by 5 and floor)
| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

Excel, 64 bytes

=MOD(A1,5)-2.5*(A1<>INT(A1))&","&1*(A1<>INT(A1))&","&TRUNC(A1/5)

Input is in cell A1.

Working backwards:
TRUNC(A1/5) gives the max number of 5mg pills by dividing and truncating.
1*(A1<>INT(A1)) gives the 2.5mg pill count as 1 if the dose is decimal and 0 if it isn't.
MOD(A1,5)-2.5*(A1<>INT(A1)) gives the 1mg pill count by taking whatever's left from dividing by 5 and subtracting 2.5 if the dose is a decimal.

Results

| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

Python 3, 68 64 57 51 43 38 36 34 32 bytes

lambda x:[x//5,x%1*2,(x<20)*x%5]

Try it online!

Outputs in order (5, 2.5, 1).

Explanation: Uses as many 5 mg pills as possible, one pill of 2.5 mg if x % 5 = 2.5, otherwise none, and remaining sum is formed using 1 mg pills.

-11 bytes thanks to Stef

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ lambda x:[x//5,1*(x%1==.5),(x%1!=.5)*x%5] saves two bytes \$\endgroup\$ – Stef Sep 9 at 17:05
  • \$\begingroup\$ Actually you never need the 1g pills if input >= 20. lambda x:[x//5,1*(x%1==.5),(x<20)*x%5] \$\endgroup\$ – Stef Sep 9 at 17:10
  • \$\begingroup\$ Thanks a lot ^^ @Stef \$\endgroup\$ – Manish Kundu Sep 9 at 17:12
  • \$\begingroup\$ Actually you can use the result of x%1 directly without testing it: lambda x:[x//5,2*(x%1),(x<20)*x%5] \$\endgroup\$ – Stef Sep 9 at 17:16
  • \$\begingroup\$ Thanks, 36 bytes now. \$\endgroup\$ – Manish Kundu Sep 9 at 17:20
3
\$\begingroup\$

05AB1E, 14 12 11 bytes

5‰`D.ïD≠Š*)

Output is in the order [5, 2.5, 1].

Try it online or verify all test cases.

Explanation:

5‰           # Divmod the (implicit) input by 5: [input//5, input%5]
  `          # Pop and push both values separated to the stack
   D         # Duplicate the input%5
    .ï       # Check if it's an integer (1 if truthy; 0 if falsey)
      D      # Duplicate that
       ≠     # Check that it's NOT equal to 1 (1 becomes 0; 0 becomes 1)
        Š    # Triple swap the top three values on the stack: a,b,c → c,a,b
         *   # Multiply the top two
          )  # Wrap all three values on the stack into a list
             # (after which it is output implicitly as result) 
| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

Wolfram Language (Mathematica), 37 bytes

#&@@{2,5,10}~FrobeniusSolve~⌊2#⌋&

Try it online!

Unfortunately, NumberDecompose[8,{10,5,2}] is {0,1,3/2}, not {0,0,4}.

The documentation for NumberDecompose states that "For integers, NumberDecompose returns the last solution found by FrobeniusSolve", but this is evidently not the case. Try it online!

| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6), 31 bytes

Outputs an array in the order 5mg, 2.5mg, 1mg. Essentially the same logic as @RobinRyder.

v=>[x=v/5|0,v%1&&1,v-(v%1+x)*5]

Try it online!


JavaScript (ES6), 27 bytes

A port of @ManishKundu's answer suggested by @KevinCruijssen.

v=>[v/5|0,v%1*2,(v<20)*v%5]

Try it online!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ &&1 can be *2 for -1. \$\endgroup\$ – Kevin Cruijssen Sep 9 at 17:36
  • 1
    \$\begingroup\$ A port of the Python answer is 27 bytes. \$\endgroup\$ – Kevin Cruijssen Sep 9 at 17:51
  • \$\begingroup\$ Damn, I was hoping we could get it down to 30 before you noticed \$\endgroup\$ – Stef Sep 9 at 17:54
2
\$\begingroup\$

Charcoal, 16 bytes

NθI⟦÷θ⁵﹪⊗θ²﹪×⁶θ⁵

Try it online! Port of @xnor's solution, saving 3 bytes over a generic solution such as a port of the other solutions. Explanation:

Nθ

Input the dose.

I⟦

Output the result on separate lines.

÷θ⁵

Integer divide the dose by 5.

﹪⊗θ²

Modulo the doubled dose by 2.

﹪×⁶θ⁵

Modulo the sextupled dose by 5.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

APL (Dyalog Unicode), 17 bytes

|⌊0 ¯1 5|.2 1 6×⎕

Try it online!

|∘⌊÷∘5,¯1∘|,5|6∘×

Try it online!

3↑0 1,⍨(⊢∩⌊)0 5⊤⎕

Try it online!

First two are the ports of xnor's Python answer giving 5mg 2.5mg 1mg, and the last is from Jonathan Allan's Jelly answer giving 5mg 1mg 2.5mg. The first and third are full programs, and the second is a tacit function.

How all these work

|⌊0 ¯1 5|.2 1 6×⎕  ⍝ Input: single number x
         .2 1 6×⎕  ⍝ (x/5)(x)(6x)
  0 ¯1 5|          ⍝ (x/5)(x%-1)(6x%5); x%-1==-0.5 if x%1==0.5
|⌊                 ⍝ Floor then abs

|∘⌊÷∘5,¯1∘|,5|6∘×  ⍝ Input: single number x
            5|6∘×  ⍝ 6x%5
       ¯1∘|        ⍝ x%-1
   ÷∘5             ⍝ x/5
|∘⌊   ,    ,       ⍝ Concatenate all and floor then abs

3↑0 1,⍨(⊢∩⌊)0 5⊤⎕  ⍝ Input: single number x
            0 5⊤⎕  ⍝ (x//5)(x%5)
       (⊢∩⌊)       ⍝ Discard non-integers
  0 1,⍨            ⍝ Append two numbers 0 1
3↑                 ⍝ Take 3 from the head
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Pyth, 16 bytes

/Q5*2%Q1*%Q5<Q20

Try it online!

Same as my Python solution. Output contains 3 lines: number of 5 mg pills, 2.5 mg pills and 1 mg pills respectively.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Retina 0.8.2, 40 bytes

^\d+
$*
^(1{5})*(1)*(11.5)*$
$#1 $#3 $#2

Try it online! Link includes test suite. Explanation:

^\d+
$*

Convert the integer part of the dose to unary.

^(1{5})*(1)*(11.5)*$

Divmod by 5, then split the remainder into 1s and an optional 2.5 (if the original dose was not an integer).

$#1 $#3 $#2

Output the numbers of the pills in the order 5s, 2.5s, 1s.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Arn, 12 11 bytes

Based off of Manish Kundu's and xnor's answers. Gives [1mg, 5mg, 2.5mg]

ê═]²*►U¦█–)

Try it!

Explained

Unpacked: [*6%5:v/5:+%1

[ Begin sequence of elements

  *6%5 Perform (_ times 6) mod 5

  :v/5 Floor _ divided by 5

  :+%1 Double _ mod 1

] End sequence, implied

Variable _ is implied. Initialized to STDIN when a program begins.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -pl, 32 bytes

$_=join' ',int$_/5,0+/\D/,$_*6%5

Try it online!

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

T-SQL, 31 bytes

SELECT @%5-@%1*5,@%1*2,str(@)/5

Try it online

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Go, 104 bytes

import ."math"
func f(x float64)(int,float64,int){return int(x/5),Ceil(x)-Floor(x),int(Min(20/x,1)*x)%5}

Try it online!

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

C (clang), 72 69 67 bytes

y;f(float x){printf("%d,%d,%d",(y=x*10)/50,y=y%50==25,!y*(y=x)%5);}

Try it online!

Saved 1 thanks to @ceilingcat

Saved 2 thanks to @rtpax

Output n#5mg, n#2.5, n#1

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.