Escoger las comidas más calóricas

9

Supongamos que como cinco comidas al día, y dado que hay siete días en una semana, tengo recetas para siete de cada comida, para 35 recetas en total. Cada receta tiene un conteo de calorías. Cada día debe contener una receta por comida, y cada receta se fija a una comida en particular (por ejemplo, no puede tener panqueques para la cena). Las 35 recetas deben estar en la solución, por lo que no se puede repetir una receta durante la semana.

Quiero encontrar la disposición de las comidas que proporcione el recuento de calorías más uniforme por día, es decir, quiero minimizar la diferencia en el total de calorías consumidas día a día.

Este no es un problema de tarea, ¡es realmente cierto! No se me ocurre un mejor enfoque que la fuerza bruta, y hay 7! ^ 4 combinaciones, que es mucho.

dfaulken
fuente
3
Tengo el presentimiento de que esta es una variación del problema del material de corte o tal vez el problema del embalaje del contenedor .
Doval
Para aclarar, ¿tiene 7 recetas para la "primera comida del día", 7 para la "segunda comida", 7 para la "tercera comida", y así sucesivamente? ¿Alguna vez asignarías una receta de "primera comida" a una "última comida del día"? (Dicho de otra manera, ¿servirías panqueques para la cena?)
Dan Pichelman
Correcto; no podrias.
dfaulken
2
¿Las 35 recetas tienen recuentos de calorías significativamente diferentes ? Si tuviera que redondear los recuentos de calorías a las 10 o 50 calorías más cercanas, 7! ^ 4 podría convertirse fácilmente en 3! ^ 4, que es fácilmente calculable a través de la fuerza bruta
Dan Pichelman
2
Amigo, comes demasiado, comer 5 comidas al día te hará tener sobrepeso.
Pieter B

Respuestas:

1

Para hacer un acercamiento más formal a su problema:

Tienes 5 listas de 7 números cada una. Necesita construir 7 listas de 5 números cada una, y encontrar la solución que tenga la mínima diferencia entre la lista que tiene la mayor suma de números y la que tiene la menor.

Si desea encontrar la solución óptima sin heurística, creo que no tiene más remedio que enumerar, pero no tiene que enumerarlos a todos.

Cualquiera que sea la solución que encuentre, cuando la registre como "mejor encontrada hasta ahora", registre su rendimiento con respecto a su métrica (creo que es la diferencia mínima-máxima). Luego, si una rama de la solución está claramente fuera de la carretera, deje de enumerarla. Protip: los días no construidos tendrán, en el mejor de los casos, un conteo de calorías que es el promedio de todas las comidas restantes. Entonces, imagina que tienes listas [10, 2, 2, 1, 1, 0, 0]para las 5 comidas, y creaste la solución 10 en cada comida para el día 1. Sabes que los días restantes tendrán un promedio de 5 calorías por día, por lo que la diferencia será de al menos 45 y si encontraste previamente una solución de, por ejemplo, max - min = 10no tienes que ir más allá. Intentará directamente otro menú para el día 1.

Arthur Havlicek
fuente
No es un problema de basura. Un problema de contenedor no es un número fijo de contenedores y un número fijo de artículos por contenedor.
paparazzo
Sí, tiene usted razón. Lo corregiré.
Arthur Havlicek
0

Esto es solo un truco, pero lo mantendrá cerca
Solo 3 comidas
Básicamente, las comidas fracasadas si hacen que los dos días estén más cerca del promedio de C #

Un mejor enfoque sería devolver un boolen en Flop e iterar hasta que se complete.

Flop podría ser más inteligente. Puede estar en una posición de no dejar caer el desayuno para dejar caer el almuerzo y la cena. Quizás haya permutaciones de código duro. Esto es más como una ordenación donde los valores de flop en lugar de ordenar.

public static void MealEven()
{
    List<Day> Days = new List<Day>();
    Random rnd = new Random();
    decimal sum = 0;
    for(int i = 0; i<7; i ++)
    {
        int b = rnd.Next(100) + 40;
        int l = rnd.Next(100) + 60;
        int d = rnd.Next(100) + 80;
        Meal br = new Meal(enumMeal.b, b);
        Meal lu = new Meal(enumMeal.l, l);
        Meal di = new Meal(enumMeal.d, d);
        Day day = new Day(br, lu, di);
        Days.Add(day);
        sum += day.Calories;
    }
    decimal avg = sum / 7;
    foreach (Day d in Days.OrderBy(x => x.Calories))
        System.Diagnostics.Debug.WriteLine(d.Calories);
    System.Diagnostics.Debug.WriteLine("");

    Day low;
    Day high;
    Day lowLast = null;
    Day highLast = null;
    int count = 0;
    while (true)
    {   // first do high and low
        low = Days.OrderBy(x => x.Calories).FirstOrDefault();
        high = Days.OrderByDescending(x => x.Calories).FirstOrDefault();
        if (lowLast != null && lowLast == low && highLast == high)
            break;
        if (count > 1000)
            break;
        lowLast = low;
        highLast = high;
        count++;               
        Flop(ref high, ref low);
    }
    foreach (Day d in Days.OrderBy(x => x.Calories))
        System.Diagnostics.Debug.WriteLine("{0} {1} {2} {3}", d.Calories, d.B.Calories, d.L.Calories, d.D.Calories);
    System.Diagnostics.Debug.WriteLine("");

    // day a one on one pass
    for (int i = 0; i < 7; i ++)
    {
        for (int j = 0; j < 7; j++)
        {
            if (i == j)
                continue;
            Day d1 = Days[i];
            Day d2 = Days[j];
            Flop(ref d1, ref d2);
        }
    }

    foreach (Day d in Days.OrderBy(x => x.Calories))
        System.Diagnostics.Debug.WriteLine("{0} {1} {2} {3}", d.Calories, d.B.Calories, d.L.Calories, d.D.Calories);
    System.Diagnostics.Debug.WriteLine("");
}
public static void Flop (ref Day high, ref Day low)
{
    if(low.Calories > high.Calories)
    {
        int hold = low.B.Calories;
        low.B.Calories = high.B.Calories;
        high.B.Calories = hold;

        hold = low.L.Calories;
        low.L.Calories = high.L.Calories;
        high.L.Calories = hold;

        hold = low.D.Calories;
        low.D.Calories = high.D.Calories;
        high.D.Calories = hold;

    }
    decimal avg = (low.Calories + high.Calories) / (decimal)2;
    int bDiff = (high.B.Calories - low.B.Calories) < 0 ? 0 : (high.B.Calories - low.B.Calories);
    int lDiff = high.L.Calories - low.L.Calories < 0 ? 0 : (high.L.Calories - low.L.Calories);
    int dDiff = high.D.Calories - low.D.Calories < 0 ? 0 : (high.D.Calories - low.D.Calories);
    // only flop is one does not go past the average  
    if (bDiff > 0 && ((low.Calories + bDiff) < avg || (high.Calories - bDiff) > avg))
    {
        int hold = low.B.Calories;
        low.B.Calories = high.B.Calories;
        high.B.Calories = hold;
    }
    if (lDiff > 0 && ((low.Calories + lDiff) < avg || (high.Calories - lDiff) > avg))
    {
        int hold = low.L.Calories;
        low.L.Calories = high.L.Calories;
        high.L.Calories = hold;
    }
    if (dDiff > 0 && ((low.Calories + dDiff) < avg || (high.Calories - dDiff) > avg))
    {
        int hold = low.D.Calories;
        low.D.Calories = high.D.Calories;
        high.D.Calories = hold;
    }
}
public enum enumMeal {b, l, d};
public class Day
{
    public Meal B { get; set; }
    public Meal L { get; set; }
    public Meal D { get; set; }
    public Decimal Calories { get { return (Decimal)(B.Calories + L.Calories + D.Calories); } }
    public Day (Meal b, Meal l, Meal d )
    {
        B = b;
        L = l;
        D = d;
    }
}
public class Meal
{
    public enumMeal Type { get; set; }
    public int  Calories { get; set; }
    public Meal (enumMeal meal, int calories)
    {
        Type = meal;
        Calories = calories;
    }
}   
paparazzo
fuente
1
¿Hay alguna forma de agregar una explicación o algunos comentarios al código para obtener la respuesta que sea más útil / esclarecedora? Creo que entiendo lo que está sucediendo allí, pero no puedo estar seguro.
Adam Wells
@ AdamWells Agregué un par de comentarios. ¿Qué no entiendes?
paparazzo
Simplemente no hizo clic con flop. Tiene sentido ahora, gracias!
Adam Wells
Ni siquiera puedo decir si este es un código Java. Lo es ? Lo siento, mis días en Java y Cx están muy atrás. ¿Dónde está el principal de todos modos?
Arthur Havlicek
@ArthurHavlicek El código C #. Se parecen mucho a lo mismo.
paparazzo
0

Primero calcule el recuento promedio de calorías por comida. Luego calcule el recuento promedio de colores por día. Estas serán las métricas con las que uno puede medir. Luego ordena las comidas.

Ahora solo elija las comidas más altas y más bajas del tipo. Si una comida está en el mismo intervalo de tiempo, tendrá que ir al siguiente más bajo o más alto hasta que encuentre una comida que no esté en ese horario (cena, etc.). Haga esto para las primeras 4 comidas (alta / baja). En la quinta comida, elige una comida que te acerque más al promedio. Ahorre la quinta comida en un cubo separado. Enjuague y repita 7 veces.

Este será su conjunto inicial de comidas. Esto será bastante parejo. Si desea la distribución óptima, se puede hacer un mayor refinamiento con la quinta comida.

Revise el quinto cubo de comidas e intente intercambiar las comidas entre las cinco comidas entre días para ver si las comidas se equilibran aún más. Aún tendrá que aplicar las mismas reglas (no más de una comida por vez). Uno puede o no obtener un conjunto más parejo. Use los promedios calculados anteriormente para ver si hay una mejora o no. Habrá muchas menos combinaciones ya que las primeras 4 comidas son fijas en función de alto / bajo.

Jon Raynor
fuente