Follow @solverworld Tweet this
When I read that I should eat food X because it has a lot of Vitamin Q, I always wonder if you could eat all the foods that someone recommends. This food here because it has a lot of Vitamin A, this other one because it has a ton of Vitamin K, and so on. How do you know that that new food isn’t going to overload your diet in some other dimension, like too much sugar, fat, etc?
Well, let’s see how math can help answer that question.
We are going to look at an optimization method called Linear Programming and see if we can figure out an Optimal Diet.
If you want to follow along and try out some of the programs for yourself, they are available at https://gitlab.com/dgrunberg/optimal-diet. You will need to download the USDA data separately, links are provided in the README file.
DISCLAIMER: Do not follow any diets proposed below without consulting your doctor. You’ll probably get sick. Or die of a kale overdose.
Nutrition Data
First we have to start with a source of data. The USDA has conveniently collected nutritional information on 8,000 foods for us at the website www.ars.usda.gov. They provide the data on how much of various nutrients are in a 100g serving of each food. The data is provided in a text file format that is fairly easy to parse with your favorite programming language.
We have massaged the data into the following form, which will make it easier for us to discuss algorithms:
Let
\[
Q=[q_{ij}]
\]
where \(q_{ij}\) is the amount of nutrient \(j\) that a serving of food \(i\) contains.
Then a diet can be represented by a vector \(x=[x_i]\) where each value is the number of servings of that food that we are consuming (per day). This is what mathematicians call thinking abstractly: we can call a string of numbers \(x\) a diet without a second thought.
Then
\[
Q^T x
\]
represents the vector of nutrients in the diet.
Now one more thing – our diet has certain restrictions. Let’s start with minimums: each nutrient may have a minimum amount that we must consume. So, let \(b\)
be the vector of minimum amounts of each nutrient that we require (assume for the moment that every nutrient has a minimum). Then we can write:
\[
Q^T x \geq b
\]
If only some of the nutrients have minimums, we would only include the rows of Q^T that correspond to them in the equation.
Linear Programming
Linear Programming is an optimization problem (and solution) that looks like:
Find an x that minimizes
\[
c^T x
\]
subject to:
\[
\begin{align}
Ax &\leq b\\
x &\geq 0
\end{align}
\]
There are numerous ways to access a Linear Programming algorithm – e.g. MATLAB or Octave. In the code provided with this post, we will be using the scipy package scipy.optimize.linprog. What happens when we try it?
Results
The first attempt I did was to minimize total calories while meeting the RDAs for various nutrients.
The first attempt gave this. It has 1103 calories. You could fill in the rest of your calories with sugar, as there is no maximum on sugar calories.
food portions food-description 10062 1.18 Pork chop 11458 10.42 Spinach 01082 16.23 Milk
Looks appetizing, doesn’t it? Well, as you look into the solution, you see that Vitamin D ends up being a problem, which contributes to the large milk dose.
In any case, I decided to switch to making the total calories fixed at 2000. I needed find something else to minimize, so I choose total servings. There some other things that could be interesting, such as price. We would need a database of food prices to proceed there. Maybe a future post.
The first try with 2000 calorie diet gives this:
food portions food-description 11090 0.84 Broccoli, raw 11147 0.02 Chard, swiss, raw 16028 1.66 Beans, kidney, all types, mature seeds, cooked, boiled, without salt 20051 2.90 Rice, white, medium-grain, enriched, cooked 18075 0.22 Bread, whole-wheat, commercially prepared 01128 0.33 Egg, whole, cooked, fried 01082 13.33 Milk, lowfat, fluid, 1% milkfat, with added vitamin A and vitamin D 12062 0.80 Nuts, almonds, blanched 11357 1.00 Potatoes, white, flesh and skin, baked 15121 2.78 Fish, tuna, light, canned in water, drained solids
Seems reasonable, except for all the milk. When I tried to limit the milk servings, the optimizer could find a solution. Here is the result if you temporarily eliminate the Vitamin D requirement:
food portions food-description 11090 0.83 Broccoli, raw 11124 0.89 Carrots, raw 16028 5.76 Beans, kidney, all types, mature seeds, cooked, boiled, without salt 20051 0.53 Rice, white, medium-grain, enriched, cooked 18075 2.08 Bread, whole-wheat, commercially prepared 01270 0.42 Cheese, cheddar, sharp, sliced 15061 2.25 Fish, perch, mixed species, cooked, dry heat 12062 0.71 Nuts, almonds, blanched
Except for all the beans, looks like getting closer to palatable. The problem is that when it tries to replace the milk with tuna, which has Vitamin D also, it runs into the saturated fat limit. Ah, the problems constraints give optimizers.
One more try, then I will let you try things out on your own. On a list of high Vitamin D foods, I found salmon and almond milk. Let’s add those as options and see what we get. Here is the full program output so you can see it for yourself:
food id portions food-description 11090 0.85 Broccoli, raw 11124 0.73 Carrots, raw 16028 6.00 Beans, kidney, all types, mature seeds, cooked, boiled, without salt 20051 0.77 Rice, white, medium-grain, enriched, cooked 18075 1.62 Bread, whole-wheat, commercially prepared 01270 0.70 Cheese, cheddar, sharp, sliced 15061 1.39 Fish, perch, mixed species, cooked, dry heat 12062 0.45 Nuts, almonds, blanched 15086 1.15 Fish, salmon, sockeye, cooked, dry heat Calories by category: Protein 676 33.2% Fat 585 28.7% Carbs, less fiber 775 38.1% TOTAL 2035 nutrient units min max value name major contributor 203 PROCNT g 50.00 0.00 168.99 Protein (Beans, kid: 52.0) 204 FAT g 0.00 65.00 65.00 Total lipid (fat) (Cheese, ch: 23.8) 205 CHOCDF g 0.00 0.00 250.74 Carbohydrate, by dif (Beans, kid: 136.9) 208 ENERC_KCAL kcal 0.00 0.00 2228.44 Energy (Beans, kid: 762.3) 291 FIBTG g 25.00 0.00 57.11 Fiber, total dietary (Beans, kid: 38.4) 301 CA mg 1300.00 0.00 1300.00 Calcium, Ca (Cheese, ch: 501.1) 303 FE mg 18.00 0.00 23.13 Iron, Fe (Beans, kid: 13.3) 304 MG mg 420.00 0.00 645.05 Magnesium, Mg (Beans, kid: 252.1) 305 P mg 1250.00 0.00 2532.93 Phosphorus, P (Beans, kid: 828.3) 306 K mg 4700.00 0.00 4700.00 Potassium, K (Beans, kid: 2430.9) 307 NA mg 500.00 1500.00 1500.00 Sodium, Na (Bread, who: 737.3) 309 ZN mg 11.00 0.00 16.32 Zinc, Zn (Beans, kid: 6.0) 312 CU mg 0.90 0.00 2.61 Copper, Cu (Beans, kid: 1.3) 315 MN mg 2.30 0.00 8.79 Manganese, Mn (Bread, who: 3.5) 317 SE ug 55.00 0.00 140.90 Selenium, Se (Bread, who: 41.6) 320 VITA_RAE ug 900.00 9000.00 900.00 Vitamin A, RAE (Carrots, r: 607.5) 323 TOCPHA mg 15.00 0.00 18.08 Vitamin E (alpha-toc (Nuts, almo: 10.8) 324 VITD IU 800.00 0.00 800.00 Vitamin D (Fish, salm: 771.1) 401 VITC mg 90.00 0.00 90.00 Vitamin C, total asc (Broccoli, : 76.1) 404 THIA mg 1.20 0.00 2.23 Thiamin (Beans, kid: 1.0) 405 RIBF mg 1.30 0.00 1.85 Riboflavin (Beans, kid: 0.3) 406 NIA mg 16.00 0.00 29.25 Niacin (Fish, salm: 11.7) 410 PANTAC mg 5.00 0.00 6.53 Pantothenic acid (Fish, salm: 1.5) 415 VITB6A mg 1.70 0.00 2.61 Vitamin B-6 (Fish, salm: 1.0) 417 FOL ug 400.00 0.00 1018.39 Folate, total (Beans, kid: 780.3) 418 VITB12 ug 2.40 0.00 8.83 Vitamin B-12 (Fish, salm: 5.1) 430 VITK1 ug 120.00 0.00 161.19 Vitamin K (phylloqui (Broccoli, : 86.7) 601 CHOLE mg 0.00 300.00 300.00 Cholesterol (Fish, perc: 160.0) 606 FASAT g 0.00 20.00 18.60 Fatty acids, total s (Cheese, ch: 13.6)