3
\$\begingroup\$

Your friend Jack is a picky eater. He only likes certain foods, and he only likes to eat a certain amount of them each day. Despite this, Jack has very strict calorie and macronutrient goals that he strives to meet with his diet. To complicate things further, Jack's cravings change day by day, and he will only eat the foods that he wants, up to a certain amount. Your goal is to write a program that Jack can use to enter his goals and his cravings on a given day, and be shown a combination of those foods that most closely matches his dietary requirements.

Program Input - Accept the following data in any convenient format for your programming language. All integers, except for food names which are strings. Carbs, fats, and protein are given in grams.

  • Jack's Dietary Requirements:

    • A Calorie Target
    • A Carbohydrate Target
    • A Fat Target
    • A Protein Target
  • A list of foods (of any length), containing the following info:

    • The name of the food
    • Calories per serving
    • Carbohydrates per serving
    • Fat per serving
    • The maximum number of servings that Jack is willing to consume in a day

Program Output - Text, or any data structure that can assocaite the name of a food with the optimal number of servings

  • A list of the number of servings (integers) of each food that Jack should have to get closest to his diet requirements. (It is ok for the number of calories, protein, fat, or carbs to be above or below the requirements. Only return the closest solution).

The absolute difference score of a combination of foods will be scored as follows (lower scores are better):

  • 1 point for each calorie difference from Jack's goal. Ex, if the total number of calories for a given number of servings for each food adds up to 1800, and Jack's goal is 2000 calories, add 200 points to the score.
  • 10 points for each gram difference from Jack's goal. Ex, if a combination of foods and servings contains a total of 160 grams of protein, and Jack's goal is 150, add 100 points to the score.

Your program should return the solution with the lowest absolute difference score - a number of servings for each of Jack's craved foods. If multiple combinations have the same score, return any of them.

Example

Input:

{
    'goals':[
        'calories': 2000,
        'carbs': 200,
        'fats': 120,
        'protein': 150
    ]
    'cravings':[
        'chocolate':[
            'calories': 130,
            'carbs': 14,
            'fats': 8,
            'protein': 10,
            'max_servings': 99
        ]
        'beans': [
            'calories': 80,
            'carbs': 6,
            'fats': 2,
            'protein': 18,
            'max_servings': 99
        ]
    ]
}

Output:

chocolate 15
beans 0

This is , so the shortest code wins.

\$\endgroup\$
7
  • 2
    \$\begingroup\$ Is that code-golf or code-challenge or something? \$\endgroup\$
    – Fmbalbuena
    Mar 2, 2022 at 22:56
  • \$\begingroup\$ If this is code-golf, include this tag and also include some text "this is code golf, shortest code wins" in the post. If this is not code-golf, you need specify a objective winning criteria, otherwise it is off topic on this site. \$\endgroup\$
    – tsh
    Mar 3, 2022 at 1:47
  • \$\begingroup\$ Oops, forgot to include that line. Added. \$\endgroup\$
    – drmosley
    Mar 3, 2022 at 2:35
  • \$\begingroup\$ Do we need to use your list of goals and foods? You wrote "A list of foods (of any length)...", suggesting we should create our own list. But if the latter is the case, what's to stop us from putting together a food list that easily gives 0 absolute difference scores? For instance, using Jack's goals, all you need is 10 servings of a food that has 200 calories, 20 carbs, 12 fats, and 15 protein. \$\endgroup\$
    – theorist
    Mar 3, 2022 at 8:02
  • 1
    \$\begingroup\$ @KevinCruijssen Yes, multiple lists are totally fine as input. \$\endgroup\$
    – drmosley
    Mar 3, 2022 at 17:11

3 Answers 3

1
\$\begingroup\$

R, 100 87 85 bytes

function(m,n,g,p=t(expand.grid(Map(seq,m+1))-1))p[,order((.1^!0:3)%*%(n%*%p-g)^2)[1]]

Try it online!

Input is m=serving maxima (optionally named to indicate the names of the foods); n=nutrition information, as a matrix with food types as columns (in the same order as m), and nutrient types content as rows; and g=Jack's goals (in the same order as the rows of m).

Output is the optimal number of servings, in the same order as m and with the names if supplied.

Ungolfed:

servings=function(m,n,g){               
    p=t(expand.grid(Map(seq,m+1))-1)    
                                # Calculate all possible portions as matrix p
                                # (rows are foods, columns are different servings);
    o=n%*%p                     # Calculate o=obtained nutrients 
                                # by multiplying matrix n (nutrients) by matrix p;
    s=c(.1,1,1,1)%*%abs(o-g)    # Calculate s=scores
                                # by multiplying absolute differences between o and g
                                # by the 1x4 matrix of scoring weights (.1,1,1,1);
    result=p[,which.min(s)]     # The result is the column of p with the minumum score
    return(result)
}
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 34 33 30 25 23 20 bytes

sÝ.«â€˜Σ³*øOα`T/O}нø

-1 byte inspired by @DominicVanEssen's R answer, calculating the scores as 10% of the calories and 100% of the grams instead of 100% calories and 1000% of the grams, and an additional -1 byte could now be achieved by changing the input-order of [calories, carbs, fats, protein] to [carbs, fats, protein, calories]
-3 bytes thanks to @JonathanAllan

Takes four separated inputs:
1. A string-list of names (e.g. ["chocolate","beans"]);
2. An integer-list of max_servings (e.g. [99,99]);
3. A list of integer-lists of [carbs, fats, protein, calories] (e.g. [[14,8,10,130],[6,2,18,80]]);
4. And a list of integers for the intended goal, also [carbs, fats, protein,calories] (e.g. [200,120,150,2000]).
Output as a list of [name, amount] pairs.

Try it online.

Explanation:

Step 1: Generate amount-lists for each possible diet based on the first input-list:

s          # Swap to push both the first (implicit) input-list of names as well as
           # second (implicit) input-list of max_servings to the stack
 Ý         # Map each inner integer to a list in the range [0,max_servings]
  .«       # Right-reduce this list by:
    â      #  Taking the cartesian product
     €˜    # After that, flatten each inner nested list
           # (we now have a list of all possible diet-combinations)

Try just step 1 online.

Step 2: Calculate the (modified) score of each possible diet-combination based on the second input, and sort the diet-combinations based on that:

Σ          # Sort the list of diet-combinations by:
 ³*        #  Multiply each individual integer by values in the third input-list of
           #  [carbs,fats,protein,calories]-quartets
   ø       #  Zip/transpose; swapping rows/columns
    O      #  Sum the other inner lists
     α     #  Get the absolute difference with the fourth (implicit) input-list with the
           #  diet-goal
      `    #  Pop and push all four values to the stack
       T/  #  Divide the top calories by 10
         O #  Take the sum of the four values on the stack
 }         # Close the sort-by
           # (we've now sorted all possible diet-combinations by their scores)

Try steps 1 and 2 online.

Step 3: Get the diet-combination with the lowest score, pair it with the names of the first input, and output it as result:

н          # Pop and push the first diet-combination of the sorted list
 ø         # Create pairs with the list of names that's still on the stack
           # (after which the result is output implicitly)
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Save three bytes by sorting... sÝ.«â€˜Σ³*øOα`T/O}нø TIO \$\endgroup\$ Mar 4, 2022 at 19:33
  • \$\begingroup\$ @JonathanAllan Ah, of course. Thanks for the -3! :) \$\endgroup\$ Mar 5, 2022 at 12:35
0
\$\begingroup\$

Charcoal, 75 73 bytes

⊞υE⁵∧¬ι⟦⟧Fη«≔υζ≔⟦⟧υF⊕⊟ιFζ⊞υEλ⎇ν⁺μ×κ§ιν⁺μ⟦§ι⁰κ⟧»⪫⊟⌊Eυ⟦ΣEθ×↔⁻λ§ι⊕μ∨¬μχ§ι⁰⟧ 

Try it online! Link is to verbose version of code. Takes as first input a list of targets and as second input a list of foods each being a list of names, nutritional values and maximum servings and outputs a space separated string of foods and servings e.g. chocolate 15 beans 0. Explanation:

⊞υE⁵∧¬ι⟦⟧Fη«≔υζ≔⟦⟧υF⊕⊟ιFζ⊞υEλ⎇ν⁺μ×κ§ιν⁺μ⟦§ι⁰κ⟧»

Form the Cartesian product of the ranges of all possible servings of all input foods, including their names, plus also calculating the nutrition each combination of servings provides.

⪫⊟⌊Eυ⟦ΣEθ×↔⁻λ§ι⊕μ∨¬μχ§ι⁰⟧ 

Calculate the score for each combination of servings and output the combination of servings with the lowest score.

65 bytes using the newer version of Charcoal on ATO:

⊞υE⁵∧¬ι⟦⟧Fη≔ΣE⊕⊟ιEυEμ⎇π⁺ξ×κ§ιπ⁺ξ⟦§ι⁰κ⟧υ⪫⊟⌊Eυ⟦ΣEθ×↔⁻λ§ι⊕μ∨¬μχ§ι⁰⟧ 

Attempt This Online! Link is to verbose version of code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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