Escolher as refeições mais calóricas

9

Suponha que eu coma cinco refeições por dia e, como há sete dias na semana, tenho receitas para sete de cada refeição, para 35 receitas no total. Cada receita tem uma contagem de calorias. Cada dia deve conter uma receita por refeição e cada receita é fixada em uma refeição específica (por exemplo, você não pode comer panquecas no jantar). Todas as 35 receitas devem estar na solução, para que uma receita não possa ser repetida durante a semana.

Quero encontrar o arranjo de refeições que proporcionará a contagem de calorias mais uniforme por dia - ou seja, quero minimizar a diferença no total de calorias consumidas no dia a dia.

Este não é um problema de lição de casa - é realmente verdade! Não posso apresentar uma abordagem melhor do que a força bruta, e há 7! ^ 4 combinações, o que é muito.

dfaulken
fonte
3
Tenho a sensação de que essa é uma variação do problema do material de corte ou talvez do problema da embalagem do caixote do lixo .
Doval
Para esclarecer - você tem 7 receitas para "primeira refeição do dia", 7 para "2ª refeição", 7 para "3ª refeição" e assim por diante? Você atribuiria uma receita da "primeira refeição" a uma "última refeição do dia"? (Dito de outra forma, poderia servir panquecas para o jantar?)
Dan Pichelman
Corrigir; Você não faria.
dfaulken
2
Todas as 35 receitas têm contagens calóricas significativamente diferentes ? ! Se você fosse para arredondar as contagens de calorias para os mais próximos 10 ou 50 calorias, 7 ^ 4 pode facilmente tornar-se 3 ^ 4 - que é facilmente calculável via força bruta
Dan Pichelman
2
Cara, você come demais, comer 5 refeições / dia vai ficar com sobrepeso.
Pieter B

Respostas:

1

Para fazer uma abordagem mais formal ao seu problema:

Você tem 5 listas de 7 números cada. Você precisa criar 7 listas de 5 números cada e encontrar a solução que tenha a diferença mínima entre a lista que possui a maior soma de números e a menor.

Se você deseja encontrar a solução ideal sem heurística, acredito que você tem poucas opções além de enumerar, mas não precisa enumerar todas elas.

Qualquer que seja a solução encontrada, ao registrá-la como "a melhor encontrada até agora", registre o desempenho da sua métrica (acredito que seja a diferença min-max). Então, se um ramo de solução estiver claramente fora do caminho, pare de enumerá-lo. Protip: dias não criados terão, na melhor das hipóteses, uma contagem de calorias que é a média de todas as refeições restantes. Então, imagine que você tem listas [10, 2, 2, 1, 1, 0, 0]para todas as 5 refeições e criou a solução 10 em cada refeição para o dia 1. Você sabe que os dias restantes terão uma média de 5 calorias por dia, portanto a diferença será de pelo menos 45 e, se você encontrou anteriormente uma solução de, digamos max - min = 10, que não precisa ir mais longe. Você tentará diretamente outro menu para o dia 1.

Arthur Havlicek
fonte
Não é um problema do compartimento. Um problema no compartimento não é um número fixo de compartimentos e um número fixo de itens por compartimento.
Paparazzo
Sim você está certo. Vou corrigir isso.
Arthur Havlicek
0

Isso é apenas um truque, mas você fica perto
Apenas 3 refeições
Você basicamente fracassa nas refeições se isso aproxima os dois dias da média de C #

Uma abordagem melhor seria retornar um boolen no Flop e iterar até a conclusão.

Flop pode ficar mais esperto. Você pode não tomar café da manhã no almoço e jantar. Talvez haja permutações de código rígido. Isso é mais como uma classificação em que os valores de flop, em vez de classificação.

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
fonte
11
existe alguma maneira de adicionar uma explicação ou alguns comentários ao código para obter uma resposta mais útil / esclarecedora? Acho que entendo o que está acontecendo lá, mas não tenho certeza.
Adam Wells
@AdamWells Adicionei alguns comentários. O que você não entendeu?
Paparazzo
Apenas não clicou com flop. Faz sentido agora, obrigado!
Adam Wells
Não sei nem se esse é o código Java. É isso ? Desculpe, meus dias em Java e Cx estão muito atrasados. Onde está o principal, afinal?
Arthur Havlicek
@ArthurHavlicek O código C #. Eles parecem muito iguais.
Paparazzo
0

Primeiro, calcule a contagem média de calorias por refeição. Em seguida, calcule a contagem média de cores por dia. Essas serão as métricas com as quais se pode medir. Em seguida, classifique as refeições.

Agora basta escolher entre as refeições mais alta e mais baixa. Se uma refeição estiver no mesmo horário, você terá que ir para o próximo mais baixo ou mais alto até encontrar uma refeição que não esteja nesse horário (jantar, etc.). Faça isso nas primeiras 4 refeições (alta / baixa). Na quinta refeição, escolha uma refeição que mais se aproxime da média. Salve a quinta refeição em um balde separado. Enxágüe e repita 7 vezes.

Este será o seu conjunto inicial de refeições. Isso será bem equilibrado. Se você deseja uma distribuição ideal, pode ser feito um refinamento adicional com a 5ª refeição.

Percorra o balde da 5ª refeição e tente trocar as 5ª refeições entre os dias para verificar se as refeições estão ainda melhores. Você ainda terá que aplicar as mesmas regras (não mais que uma refeição por vez). Pode-se ou não obter um conjunto mais uniforme. Use as médias calculadas anteriormente para ver se há uma melhoria ou não. Haverá muito menos combinações, já que as primeiras 4 refeições são fixadas com base em alto / baixo.

Jon Raynor
fonte