Fábrica de ensacamento de frutas


21

Sua missão é criar um algoritmo (programa ou função) que otimize a embalagem de frutas de uma correia transportadora em sacolas para serem enviadas aos varejistas, otimizando para um maior número de sacolas.

Cada sacola deve pesar pelo menos uma certa quantia, mas qualquer excesso é perda de lucro, pois esse peso pode ser usado para encher outra sacola. Sua máquina de ensacamento sempre tem uma lista de nfrutas na fila e pode optar por adicionar qualquer uma dessas nfrutas à sacola (única) que está sendo processada. Ele não pode olhar além dos nprimeiros elementos da fila. O programa sempre sabe exatamente quanto peso já existe na sacola.

Outra maneira de visualizar isso é ter uma correia transportadora com uma área de carga de tamanho nno final, de onde uma fruta deve ser retirada antes que uma nova fruta chegue. Qualquer fruta que sobrar e uma sacola não cheia no final são descartadas.

Figura 1: Fábrica de ensacamento de frutas

Entradas

  • Lista / matriz de pesos de frutas na fila (números inteiros positivos)
  • Peso total mínimo para sacos (número inteiro positivo)
  • Lookahead n(número inteiro positivo)

Saída

Seu algoritmo deve retornar para todas as sacolas os pesos das frutas nelas, por qualquer meio que seja conveniente para você e seu idioma, seja stdin ou um valor de retorno ou qualquer outra coisa. Você poderá executar o programa e calcular sua pontuação em um minuto no seu computador.

Exemplo

Total weight 1000, lookahead of 3 and fruit queue: 
[171,163,172,196,156,175,162,176,155,182,189,142,161,160,152,162,174,172,191,185]

One possible output (indented to show how the lookahead affects the bagging):
[171,163,172,    156,175,    176]
                        [162,    155,182,189,    161,160]
                                                        [152,162,174,172,191,185]

Pontuação

Seu algoritmo será testado em seis execuções em um lote de 10.000 laranjas que eu preparei para você, em viseiras que variam de 2 a 7, inclusive nas duas extremidades. Você deve embalá-los em sacos de pelo menos 1000 unidades. As laranjas são normalmente distribuídas com um peso médio de 170 e um desvio padrão de 13, se isso for de alguma ajuda.

Sua pontuação será a soma do número de malas das seis corridas. A pontuação mais alta vence. As brechas padrão não são permitidas.

Implementação de exemplo simples e clichê do conjunto de testes em Haskell


7
Vamos lá gente, eu acho que ainda há alguns algoritmos frutos mais baixos ainda esperam para ser escolhido ...
Angs

2
Os programas podem codificar o peso / distribuição médios? (supor que ele funciona igualmente bem em lotes semelhantes, é claro hardcoding tudo é inválida, uma vez que destrói o propósito de lookahead limitada)
user202729

@ user202729: Sim, eles podem.
Angs

E codificar tudo é uma brecha padrão proibida de qualquer maneira.
Angs

Não consigo ver o que é
lookhead

Respostas:


8

Python 3, 9964 9981 sacos

A ideia desta solução é semelhante à de Jonathan, JayCe e fortraan, mas com uma função de pontuação =)

Esta solução anexa os melhores subconjuntos da área de lookahead de acordo com o score.

score fornece uma ordem sobre os subconjuntos usando o seguinte esquema:

  • Um subconjunto que completa uma bolsa é melhor do que aquele que não é
  • Um subconjunto que completa uma bolsa é melhor que outro se tiver menos excesso de peso
  • Um subconjunto que não conclui uma sacola é melhor que outro, se sua média estiver mais próxima do esperado em uma sacola

expected_mean tenta prever como deve ser o restante dos valores (assumindo que a escolha é a ideal).

UPD :

Aqui está outra observação: você pode colocar qualquer laranja do melhor subconjunto na bolsa sem prejudicar o desempenho do algoritmo. Mover qualquer parte dela ainda permite mover o restante depois, e o restante ainda deve ser a melhor opção (sem novas laranjas) se a pontuação estiver correta. Além disso, dessa forma, é possível melhorar dinamicamente o conjunto de candidatos a serem colocados na sacola vendo mais laranjas antes de encher a sacola. E você deseja saber o máximo de informações possível, para que não faça sentido mover mais de uma laranja para a bolsa a qualquer momento.

import itertools as it
import math
from functools import partial
from collections import Counter


mean, std = 170, 13


def powerset(list_, max_items):
    return it.chain.from_iterable(it.combinations(list_, r) for r in range(1, max_items + 1))


def expected_mean(w):
    spread = std * 1
    return max(mean - spread, min(mean + spread, w / max(1, round(w / mean))))


def score(weight_needed, candidate):
    c_sum = sum(candidate)
    c_mean = c_sum / len(candidate)
    if c_sum >= weight_needed:
        return int(-2e9) + c_sum - weight_needed
    return abs(expected_mean(weight_needed) - c_mean)


def f(oranges, min_bag_weight, lookahead):
    check = Counter(oranges)

    oranges = oranges.copy()
    result = []
    bag = []

    while oranges:
        weight_needed = min_bag_weight - sum(bag)

        lookahead_area = oranges[:lookahead]
        tail = oranges[lookahead:]

        to_add = min(powerset(lookahead_area, lookahead),
                     key=partial(score, weight_needed))
        to_add = min(powerset(to_add, 1),
                     key=partial(score, weight_needed))

        bag.extend(to_add)
        for x in to_add:
            lookahead_area.remove(x)
        oranges = lookahead_area + tail

        if sum(bag) >= min_bag_weight:
            result.append(bag)
            bag = []

    assert check == Counter(oranges) + Counter(bag) + sum(map(Counter, result), Counter())

    return result


if __name__ == '__main__':
    with open('oranges') as file:
        oranges = list(map(int, file))
    res = [f(oranges, 1000, l) for l in range(2, 7+1)]
    print(sum(map(len, res)))

Tente!


Muito agradável! Chega em 1672 com um lookahead de 7, nunca visto um tão alto.
Angs

(parece que o segundo argumento para a sua powersetfunção é redundante, neste caso, porque é igual a len(list_)de qualquer maneira?)
user202729

@user Eu experimentei esse parâmetro na versão anterior. Provavelmente removê-lo mais tarde
Alex

1
Parabéns por descobrir a poderosa combinação do melhor elemento único dentre o melhor subconjunto e também com a melhor pontuação! A recompensa é sua.
Angs

Um mais simples expected_mean(w)que dá bons resultados:return (w+2) / max(1, round((w+2) / mean))
Angs 25/04

10

Python 3 , 9796 sacos

Com base na resposta de Jonathan:

import itertools as it

def powerset(iterable):
    s = list(iterable)
    return it.chain.from_iterable(it.combinations(s, r) for r in range(len(s)+1))

def f(a,m,l):
 r=[];b=[]
 while a:
  c =  min(list(powerset(a[:l])),key=lambda w: abs(sum(b)+sum(w)-m))
  if sum(c)==0:
   c = a[:l]
  b+=[a.pop(a.index(min(c,key=lambda w: abs(sum(b)+w-m))))]
  if sum(b)>=m:r+=[b];b=[]
 return r

Isso depende do conjunto de ferramentas do livro de receitas do itertool. Primeiro, encontra o subconjunto ideal do buffer com base na minimização da diferença do peso alvo para todos os subconjuntos e, em seguida, seleciona um elemento desse subconjunto com base no mesmo critério. Se nenhum subconjunto ideal seleciona o buffer inteiro.


Bem-vindo ao PPCG!
22818 Martin Ender19

@MartinEnder Graças Martin para o upvote acolhedor :)
Jayce

1
Ah sim, eu perdi um truque lá ... Não tenho problema com isso, como outra resposta!
Jonathan Allan

1
@ JonathanAllan Obrigado Jonathan Reduzi minha resposta para creditar você sem todas as desculpas. Isso pode ser melhorado usando o fato de que é uma distribuição normal (170,13) - eu tenho certeza que a probabilidade de obter uma fruta melhor nas próximas execuções pode ser usada.
jayce

@JayCe soa perigosamente perto da falácia do jogador.
Qd #

6

C ++ 17, 9961,58 (média de algumas sementes aleatórias)

(role para baixo para obter uma explicação se você não conhece C ++)

#include<iostream>

#include<vector>
#include<random>

std::mt19937 engine(279); // random engine
// random distribution of the oranges
std::normal_distribution dist (170.,13.);

int constexpr N_NEW_ORANGES=7;

/** Input format: Current remaining weight of the bag (remain) and 
the weight of the oranges (weights)
Output: the index of the orange to be pick.
*/
struct pick_orange{
    std::vector<int>weights,sum_postfix;int remain;

    /// returns {min excess, mask}, (index) is the LSB
    std::pair<int,int> backtrack(int index, int remain) {
        if(sum_postfix[index]<remain)return {1e9,0};
        int min_excess=1e9, good_mask=0;
        for(int next=index;next<N_NEW_ORANGES;++next){
            if(weights[next]==remain){
                return {0, 1<<(next-index)};
            }else if(weights[next]>remain){
                if(weights[next]-remain<min_excess){
                    min_excess=weights[next]-remain;
                    good_mask=1<<(next-index);
                }
            }else{
                auto[excess,mask]=backtrack(next+1,remain-weights[next]);
                if(excess<min_excess){
                    min_excess=excess;
                    good_mask=(mask<<1|1)<<(next-index);
                }
            }
        }
        return {min_excess,good_mask};
    } 

    int ans;

    pick_orange(std::vector<int> weights_,int remain_)
        :weights(std::move(weights_)),remain(remain_){

        int old_size=weights.size();

        std::vector<int> count (N_NEW_ORANGES, 0);
        weights.resize(N_NEW_ORANGES, 0);

        sum_postfix.resize(N_NEW_ORANGES+1);
        sum_postfix.back()=0;

        for(int _=0; _<500; ++_){

            for(int i=old_size;i<N_NEW_ORANGES;++i)
                weights[i] = dist(engine);

            // prepare sum postfix
            for(int i=N_NEW_ORANGES;i-->0;)
                sum_postfix[i]=weights[i]+sum_postfix[i+1];

            // auto[excess,mask]=backtrack(0,remain);
            int mask = backtrack(0,remain).second;

            for(int i=0; 

                mask
                // i < N_NEW_ORANGES

                ; mask>>=1, ++i){

                // if(mask&1)std::cout<<'(';
                // std::cout<<weights[i];
                // if(mask&1)std::cout<<')';
                // std::cout<<' ';

                count[i]+=mask&1;
            }

            // std::cout<<"| "<<remain<<" | "<<excess<<'\n';

        }

        std::vector<double> count_balanced(old_size, -1);
        for(int i=0;i<old_size;++i){
            if(count_balanced[i]>-1)continue;
            int sum=0,amount=0;
            for(int j=i;j<old_size;++j)
                if(weights[j]==weights[i]){sum+=count[j];++amount;}

            double avg=sum;avg/=amount;
            for(int j=i;j<old_size;++j)
                if(weights[j]==weights[i])count_balanced[j]=avg;
        }

        ans=old_size-1;
        for(int i=ans;i-->0;)
            if(count_balanced[i]>count_balanced[ans])ans=i;
        // Fun fact: originally I wrote `<` for `>` here and wonder
        // why the number of bags is even less than that of the
        // randomized algorithm
    }

    operator int()const{return ans;}
};


#include<iostream>
#include<fstream>
#include<algorithm>

int main(){
    // read input from the file "data"
    std::ifstream data ("data");
    std::vector<int> weights;
    int weight;while(data>>weight)weights.push_back(weight);

    int constexpr BAG_SIZE=1000;
    int total_n_bag=0;
    for(int lookahead=2;lookahead<=7;++lookahead){
        auto weights1=weights;
        std::reverse(weights1.begin(),weights1.end());

        int remain=BAG_SIZE,n_bag=0;
        std::vector<int> w;
        for(int _=lookahead;_--;){
            w.push_back(weights1.back());
            weights1.pop_back();
        }
        while(!weights1.empty()){
            int index=pick_orange(w,remain);

            remain-=w[index];
            if(remain<=0){
                ++n_bag;remain=BAG_SIZE;

                if(n_bag%100==0)
                    std::cout<<n_bag<<" bags so far..."<<std::endl;
            }
            w[index]=weights1.back();weights1.pop_back();
        }

        while(!w.empty()){
            int index=pick_orange(w,remain);
            remain-=w[index];
            if(remain<=0){++n_bag;remain=BAG_SIZE;}
            w.erase(w.begin()+index);
        }

        std::cout<<"lookahead = "<<lookahead<<", "
            "n_bag = "<<n_bag<<'\n';
        total_n_bag += n_bag;
    }

    std::cout<<"total_n_bag = "<<total_n_bag<<'\n';
}

// Curiosidade: originalmente escrevi <para >aqui e maravilha
// porque o número de sacos é ainda menos do que o do
algoritmo aleatório //

(se <for usado basicamente, o algoritmo tenta minimizar o número de malas)

Inspirado por esta resposta .

Link TIO para 250 repetições: Experimente online!


Define uma função (na verdade, apenas se parece com uma função, é uma estrutura) pick_orangeque, dado vector<int> weightso peso das laranjas e int remaino peso restante da sacola, retorna o índice da laranja que deve ser colhida.

Algoritmo:

os 500tempos de repetição {
geram laranjas aleatórias (falsas) (distribuição normal com média 170 e stddev 13) até que N_NEW_ORANGES=7laranjas
selecionem qualquer subconjunto cuja soma seja menor e não menor que remain(a função backtrackfaz isso)
marcar todas as laranjas nesse subconjunto como boas
}

calcule a média do número de vezes que as laranjas marcadas como boas das laranjas (reais) com peso igual
retornam a melhor laranja


Existem 3 constantes codificadas no programa que não podem ser deduzidas do problema:

  • A semente aleatória (isso não é importante)
  • N_NEW_ORANGES(o comprimento da previsão). Aumentar isso faz com que o programa seja executado exponencialmente mais (porque retrocede)
  • número de repetições. Aumentar isso faz com que o programa execute linearmente mais.

Está bem. Mudar a semente para a que dá a melhor resposta parece otimizar o caso de teste; portanto, você deve considerar a média de algumas, digamos 10, sementes diferentes como sua pontuação. Você poderia postar um link TIO em uma versão que faz menos repetições para diminuir o tempo de execução?
Angs

Finalmente consegui compilar depois de obter um novo gcc. Em 50 corridas com sementes aleatórias, obteve uma média de 9961,58. Muito impressionante ainda. Me fez pensar: seu algoritmo basicamente se treina novamente em todas as malas; existe um conjunto fixo de melhores valores que podem ser memorizados?
Angs

@ Angs Eu não acho que exista uma maneira de usar a memorização para ajudar nesse caso. Qualquer ideia?
usar o seguinte comando

Meu sistema operacional vem com o gcc 5.4.0, com alguns problemas invalid use of template-name ‘std::normal_distribution’. Sem problemas com o gcc 7.1.0.
Angs

4

Python 2 , 9756 sacos

Vamos fazer a laranja rolar ...

def f(a,m,l):
 r=[];b=[]
 while a:
  b+=[a.pop(a.index(min(a[:l],key=lambda w:abs(sum(b)+w-m))))]
  if sum(b)>=m:r+=[b];b=[]
 return r

Experimente online!

Sempre escolhe a fruta do buffer, o que minimiza a diferença absoluta do novo peso e do peso alvo.


4

Python 3, 9806 sacos

Com base nas respostas de Jonathan e JayCe:

import itertools as it

def powerset(iterable):
    s = list(iterable)
    return it.chain.from_iterable(it.combinations(s, r) for r in range(len(s)+1))

def f(a,m,l):
 r=[];b=[]
 while a:
  c =  min(list(powerset(list(reversed(sorted(a[:l]))))),key=lambda w: abs((sum(b)+sum(w))-m))
  if sum(c)==0:
   c = a[:l]
  b+=[a.pop(a.index(min(c,key=lambda w: abs((sum(b)+w)-m))))]
  if sum(b)>=m:r+=[b];b=[]
 return r

Experimente online!

Como funciona

Digamos que a bolsa tenha 900 unidades e há 2 frutas disponíveis: uma fruta de 99 unidades e uma fruta de 101 unidades. Se o fruto de 99 unidades estiver mais próximo do início da lista de procura, minselecioná-lo-á em vez de 101. Se isso acontecer, precisaríamos agora de outro fruto para atender a 1 unidade restante necessária. Mudei o programa para favorecer os frutos de maior valor nesses casos.

Isso é feito classificando e revertendo a lista de visualizadores de objetos antes de definir a potência.


4

PHP, 9975 sacolas

  • Se possível, escolha 5 laranjas
  • Ao iniciar a mala, escolha um valor extremo, balance depois
  • Se possível, encha o saco imediatamente
  • Tente manter o peso da bolsa próximo à curva estimada (n * 200 para 5 bolsas, n * 167 para 6 bolsas etc.)

mais longo de todos os envios, mas deve ser legível

class Belt
{
    private $file;
    private $windowSize;
    private $buffer = [];

    public function __construct($filename, $windowSize) {
        $this->file = new \SplFileObject($filename);
        $this->windowSize = $windowSize;
        $this->loadBuffer();
    }

    public function reset($windowSize) {
        $this->file->seek(0);
        $this->windowSize = $windowSize;
        $this->buffer = [];
        $this->loadBuffer();
    }

    public function peekBuffer() {
        return $this->buffer;
    }

    public function pick($index) {
        if (!array_key_exists($index, $this->buffer)) {
            return null;
        }
        $value = $this->buffer[$index];
        unset($this->buffer[$index]);
        $this->buffer = \array_values($this->buffer);
        $this->loadBuffer();
        return $value;
    }

    private function loadBuffer() {
        for ($c = count($this->buffer); $c < $this->windowSize; $c++) {
            if ($this->file->eof()) {
                return;
            }
            $line = $this->file->fgets();
            if (false !== $line && "" !== $line) {
                $this->buffer[] = trim($line);
            }
        }
    }
}

class Packer
{

    const BAG_TARGET_WEIGHT = 1000;
    const MEAN_WEIGHT = 170;
    const MEAN_COUNT = 6; //ceil(self::BAG_WEIGHT/self::MEAN_WEIGHT);
    const MEAN_TARGET_WEIGHT = 167; //ceil(self::BAG_WEIGHT/self::MEAN_COUNT);

    public static function pack(Belt $belt, Picker $picker) {
        $bag = ["oranges" => [], "buffers" => []];
        $bags = [];
        while ($oranges = $belt->peekBuffer()) {

            $index = $picker->pick($oranges, \array_sum($bag["oranges"]));
            $orange = $belt->pick($index);
            $bag["oranges"][] = $orange;
            $bag["buffers"][] = $oranges;

            if (\array_sum($bag["oranges"]) >= self::BAG_TARGET_WEIGHT) {
                $bags[] = $bag;
                $bag = ["oranges" => [], "buffers" => []];
            }
        }
        return $bags;
    }
}

class Base
{
    public static function bestPermutation($elements, $weight = 0) {
        if (\array_sum($elements) < Packer::BAG_TARGET_WEIGHT - $weight) {
            return null;
        }
        $permute = function ($weight, $elements) use (&$permute) {
            if ($weight >= Packer::BAG_TARGET_WEIGHT) {
                return [];
            }
            $best = \PHP_INT_MAX;
            $bestElements = [];
            foreach ($elements as $key => $value) {
                $sum = $weight + $value;
                $els = [$value];
                if ($sum < Packer::BAG_TARGET_WEIGHT) {
                    $subSet = $elements;
                    unset($subSet[$key]);
                    $els = $permute($weight + $value, $subSet);
                    $els[] = $value;
                    $sum = $weight + \array_sum($els);
                }
                if ($sum >= Packer::BAG_TARGET_WEIGHT && $sum < $best) {
                    $best = $sum;
                    $bestElements = $els;
                }
            }
            return $bestElements;
        };
        $best = $permute($weight, $elements);

        return $best;
    }

    public function pickLightestOutOfHeavierThan($buffer, $targetWeight) {
        $b = -1;
        $bW = PHP_INT_MAX;
        foreach ($buffer as $key => $value) {
            if ($targetWeight <= $value && $value < $bW) {
                $b = $key;
                $bW = $value;
            }
        }
        return $b;
    }

    public function pickClosestTo($buffer, $targetWeight) {
        $b = -1;
        $bW = PHP_INT_MAX;
        foreach ($buffer as $key => $value) {
            $diff = \abs($targetWeight - $value);
            if ($diff < $bW) {
                $b = $key;
                $bW = $diff;
            }
        }
        return $b;
    }

    public function pickFurthestFrom($buffer, $targetWeight) {
        $b = -1;
        $bW = \PHP_INT_MIN;
        foreach ($buffer as $key => $value) {
            $diff = \abs($targetWeight - $value);
            if ($diff > $bW) {
                $b = $key;
                $bW = $diff;
            }
        }
        return $b;
    }

    public function findMax($buffer) {
        $i = -1;
        $m = 0;
        foreach ($buffer as $k => $v) {
            if ($v > $m) {
                $m = $v;
                $i = $k;
            }
        }
        return $i;
    }

    public function findMin($buffer) {
        $i = -1;
        $m = \PHP_INT_MAX;
        foreach ($buffer as $k => $v) {
            if ($v < $m) {
                $m = $v;
                $i = $k;
            }
        }
        return $i;
    }

    public function minimalOrangeCount($buffer, $weight) {
        $elementsToAdd = ceil((Packer::BAG_TARGET_WEIGHT - $weight) / Packer::MEAN_WEIGHT);
        $buffer = \array_merge($buffer,
            \array_fill(0, \floor($elementsToAdd / 2), Packer::MEAN_WEIGHT - 7),
            \array_fill(0, \floor($elementsToAdd / 2), Packer::MEAN_WEIGHT + 7),
            \array_fill(0, $elementsToAdd - \floor($elementsToAdd / 2) * 2, Packer::MEAN_WEIGHT)
        );
        \rsort($buffer);
        $orangeCount = 0;
        foreach ($buffer as $w) {
            $weight += $w;
            $orangeCount++;
            if ($weight >= Packer::BAG_TARGET_WEIGHT) {
                return $orangeCount;
            }
        }
        return $orangeCount + (Packer::BAG_TARGET_WEIGHT - $weight) / Packer::MEAN_WEIGHT;
    }
}


class Picker extends Base
{
    public function pick($buffer, $weight) {
        $weightNeeded = Packer::BAG_TARGET_WEIGHT - $weight;

        $minimalOrangeCount = $this->minimalOrangeCount($buffer, $weight);
        $orangeTargetWeight = ceil($weightNeeded / $minimalOrangeCount);

        if (0 === $weight) {
            $mean = \array_sum($buffer) / count($buffer);
            if ($mean > $orangeTargetWeight) {
                return $this->findMin($buffer);
            } elseif ($mean < $orangeTargetWeight) {
                return $this->findMax($buffer);
            }
            return $this->pickFurthestFrom($buffer, $orangeTargetWeight);
        }

        $i = $this->pickLightestOutOfHeavierThan($buffer, $weightNeeded);
        if (-1 !== $i) {
            return $i;
        }
        $i = $this->pickClosestTo($buffer, $orangeTargetWeight);
        return -1 !== $i ? $i : 0;
    }
}

$bagCount = 0;
$belt = new Belt(__DIR__ . "/oranges.txt", 0);
for ($l = 2; $l <= 7; $l++) {
    $belt->reset($l);
    $bags = Packer::pack($belt, new Picker());
    $bagCount += count($bags);
    printf("%d -> %d\n", $l, count($bags));
}
echo "Total: $bagCount\n";

2 -> 1645 3 -> 1657 4 -> 1663 5 -> 1667 6 -> 1671 7 -> 1672 Total: 9975

Tente


Agradável! O que é surpreendente para mim é que ele usa a contagem atual de itens - eu me pergunto se isso pode ser fatorado. Afinal, não importa se existem 3 itens com peso de 120 cada ou 3 itens com peso de 160 cada.
Angs

@ Angs provavelmente é possível. A contagem atual de itens surgiu como um atalho simples para a idéia "Ei, às vezes é possível fazer uma sacola de 5 itens" e eu falei sobre como colocar 5 sacolas de itens em funcionamento. Com as melhorias de tempos livres virá :)
mleko

3

Python 3, 9855 9928 9947 9956 9964 sacos

Baseado no código de partida de Jonathan Allan, mas sem o poder de ser legível.

Idéia: Como 1000/170 = 5,88, tentamos selecionar frutas próximas a 1000/6 (eu brinquei com as constantes mágicas). No entanto, se a última fruta na bolsa puder minimizar o desperdício, nós a usaremos.

Esta solução possui metas de soma de sacos para cada fruta adicionada. Provavelmente vou parar por aqui. Usei o Nelder-Mead para encontrar minha targetsmatriz:

[  165.79534144   343.58443287   522.58081597   680.76516204   845.93431713 1063.17204861]
def f(a, m, l, targets):
    bags = []
    bag = []
    bag_sum = 0
    while a:
        buffer = a[:l]
        finishers = tuple(filter(lambda w: bag_sum + w >= m, buffer))
        if finishers:
            next_fruits = [min(finishers)]

        else:
            ind = len(bag)
            next_fruits = [min(buffer, key=lambda w: abs(targets[ind]-bag_sum-w))]

        for next_fruit in next_fruits:
            bag.append(a.pop(a.index(next_fruit)))
            bag_sum += bag[-1]

        if sum(bag) >= m:
            bags.append(bag)
            bag = []  # Reset bag
            bag_sum = 0

    return bags

9956 sacos

from itertools import combinations

def f(a,m,l):
    bags = []
    bag = []
    while a:
        buffer = a[:l]
        next_fruit = None
        single_fruit = True

        finishers = [w for w in buffer if sum(bag) + w >= m ]
        if finishers: next_fruit = min(finishers)

        if not next_fruit:
            if len(buffer) >= 4 and sum(bag) < 600:
                next_fruits = min(combinations(buffer, 2), key=
                                  lambda ws: abs(2*169-sum(ws)))
                for fruit in next_fruits:
                    bag.append(a.pop(a.index(fruit)))

                single_fruit = False  # Skip adding single fruit

            else:
                next_fruit = min(buffer, key=lambda w: abs(171.5-w))

        if single_fruit:
            bag.append(a.pop(a.index(next_fruit)))

        if sum(bag)>=m:
            bags.append(bag)
            bag = []

    return bags


oranges = [int(x.strip()) for x in open("fruit.txt").readlines()]
bagLists = []
for lookahead in (2,3,4,5,6,7):
    bagLists.append(f(oranges[:], 1000, lookahead))


totalBagsOver1000 = sum(map(len, bagLists))
print('bags: ', (totalBagsOver1000))

O programa 9947 bags é particularmente simples:

def f(a,m,l):
    bags = []
    bag = []
    while a:
        buffer = a[:l]
        next_fruit = None

        finishers = [w for w in buffer if sum(bag) + w >= m ]
        if finishers: next_fruit = min(finishers)

        if not next_fruit:
            next_fruit = min(buffer, key=lambda w: abs(171.5-w))

        bag.append(a.pop(a.index(next_fruit)))

        if sum(bag)>=m:
            bags.append(bag)
            bag = []

    return bags

1
Boa! Btw, basta escolher o último item, a fim de minimizar o desperdício é bastante poderoso por si só e dá 9862 sacos.
precisa saber é

Como você inventou isso targets? Treinamento em dados aleatórios?
Alex

1
@Alex I indicado da seguinte forma: Método de Nelder-Mead (com bolsas negativos como função de perda)
QWR

2

Ruby , 9967 sacos

def pick a, n
  if a.sum < n
    #return a.max
    d = n % 170
    return a.min_by{|w|
      [(w - d).abs, (w - d - 170).abs].min
    }
  end
  
  subsets = (0..a.length).map do |i|
    a.combination(i).to_a
  end.flatten(1)
  
  subsets.select!{|s|s.sum >= n}
  least_overkill = subsets.min_by{|s|s.sum}
  #puts "best: #{least_overkill.sort}"
  return least_overkill.min
end

def run list, weight, n
  bags = 0
  in_bag = 0
  while list.size > 0
    x = pick(list[0...n], weight - in_bag)
    i = list.index(x)
    raise new Exeption("not a valid weight") if(!i || i >= n)
    list.delete_at i
    in_bag += x
    if in_bag >= weight
      #puts in_bag
      in_bag = 0
      bags += 1
    end
  end
  return bags
end

Experimente online!

Se você tiver peso suficiente para encher o saco, encontre o subconjunto mais leve que possa encher o saco e use a laranja mais clara desse subconjunto. Caso contrário, obtenha o peso restante o mais próximo possível de um múltiplo de 170.


2

Raquete / esquema, 9880 sacos

Para decidir qual peça de fruta adicionar à sacola, compare o peso ideal da sacola com o peso da sacola com a peça adicional de fruta. Se for o peso ideal, use-o. Se estiver acima do peso, minimize a quantidade em excesso. Se estiver abaixo do peso, minimize a quantidade excessiva depois de tentar deixar um espaço ideal.

;; types

(define-struct bagger (fruit look tray bag bags)) ; fruit bagger

;; constants

(define MBW 1000) ; minimum bag weight
(define AFW 170) ; average piece-of-fruit weight
(define GAP (- MBW AFW)) ; targeted gap
(define FRUIT (file->list "fruit-supply.txt")) ; supplied fruit

;; utility functions

(define (weigh-it ls)
  (if (empty? ls)
      0
      (+ (car ls) (weigh-it (cdr ls)))))

(define (ref-to-car ls ref)
  (if (zero? ref)
      ls
      (let ((elem (list-ref ls ref)))
        (cons elem (remove elem ls)))))

;; predicates

(define (bag-empty? bgr) (empty? (bagger-bag bgr)))
(define (bag-full? bgr) (>= (weigh-it (bagger-bag bgr)) MBW))
(define (fruit-empty? bgr) (empty? (bagger-fruit bgr)))
(define (tray-empty? bgr) (empty? (bagger-tray bgr)))
(define (tray-full? bgr) (= (length (bagger-tray bgr)) (bagger-look bgr)))
(define (target-not-set? target value) (and (empty? target) (empty? value)))

;; pick best piece of fruit

(define (pf-rec tray bag i target value diff)
  (if (or (target-not-set? target value) (< diff value))
      (pick-fruit (cdr tray) bag (add1 i) i diff)
      (pick-fruit (cdr tray) bag (add1 i) target value)))

(define (pick-fruit tray bag i target value)
  (if (empty? tray)
      target
      (let ((weight (weigh-it (cons (car tray) bag))))
        (cond
          ((= weight MBW) i)
          ((> weight MBW) (pf-rec tray bag i target value (- weight MBW)))
          ((< weight MBW)
           (if (> weight GAP)
               (pf-rec tray bag i target value (- weight GAP))
               (pf-rec tray bag i target value (modulo (- MBW weight) AFW))))))))

;; load tray, bag, bags, etc.

(define (load-bag bgr)
  (let* ((tray (bagger-tray bgr))
         (bag (bagger-bag bgr))
         (weight (+ (weigh-it tray) (weigh-it bag))))
    (if (= weight MBW)
        (struct-copy bagger bgr
                     (tray empty)
                     (bag (append tray bag)))
        (let ((new-tray (ref-to-car tray (pick-fruit tray bag 0 empty empty))))
          (struct-copy bagger bgr
                       (tray (cdr new-tray))
                       (bag (cons (car new-tray) bag)))))))

(define (load-bags bgr)
  (struct-copy bagger bgr
               (bag empty)
               (bags (cons (bagger-bag bgr) (bagger-bags bgr)))))

(define (load-tray bgr)
  (struct-copy bagger bgr
               (fruit (cdr (bagger-fruit bgr)))
               (tray (cons (car (bagger-fruit bgr)) (bagger-tray bgr)))))

;; run the bagger factory

(define (run-bagger-aux bgr)
  (cond
    ((bag-full? bgr) (run-bagger-aux (load-bags bgr)))
    ((bag-empty? bgr)
     (cond
       ((tray-full? bgr) (run-bagger-aux (load-bag bgr)))
       ((tray-empty? bgr)
        (if (fruit-empty? bgr)
            (length (bagger-bags bgr))
            (run-bagger-aux (load-tray bgr))))
       (else
        (if (fruit-empty? bgr)
            (run-bagger-aux (load-bag bgr))
            (run-bagger-aux (load-tray bgr))))))
    (else
     (cond
       ((tray-full? bgr) (run-bagger-aux (load-bag bgr)))
       ((tray-empty? bgr)
        (if (fruit-empty? bgr)
            (run-bagger-aux (load-bags bgr))
            (run-bagger-aux (load-tray bgr))))
       (else
        (if (fruit-empty? bgr)
            (run-bagger-aux (load-bag bgr))
            (run-bagger-aux (load-tray bgr))))))))

(define (run-bagger fruit look)
  (run-bagger-aux (make-bagger fruit look empty empty empty)))

;; stackexchange problem run

(define (run-problem fruit looks)
  (if (empty? looks)
      0
      (+ (run-bagger fruit (car looks)) (run-problem fruit (cdr looks)))))

(run-problem FRUIT '(2 3 4 5 6 7)) ; result = 9880

1

Haskell , 9777 bolsas

Esta foi minha primeira tentativa:

  • enchia avidamente uma sacola com um lote quando podia,
  • ou jogou todas as laranjas no saco quando não podia.
options[]=[(0,([],[]))]
options(first:rest)=[option|(sum,(now,later))<-options rest,
 option<-[(sum+first,(first:now,later)),(sum,(now,first:later))]]
bags _[_]_=[]
bags(w_sum,w_bag)(w_kilo:w_iyts)w_view=
 let(w_take,w_remd)=splitAt(w_view)w_iyts;
     w_fill=filter((>=(w_kilo-w_sum)).fst)(options w_take)
 in if null w_fill then bags(w_sum+sum w_take,w_bag++w_take)(w_kilo:w_remd)w_view
    else let(_,(w_now,w_later))=minimum w_fill in
         (w_bag++w_now):bags(0,[])(w_kilo:w_later++w_remd)w_view
main=print.sum$map(length.bags(0,[])(1000:batch))[2..7]

Experimente online!


1

Haskell , 9981 bolsas

O AngsJonathan AllanJayCefortraanAlexRoman Czyborra codegolf python poderia pedalar de volta a Haskell para obter alguma pureza matemática adicional ao longo da mesma linha principal de pensamento

  • apenas uma laranja é saqueada antes de uma nova laranja ser adicionada em consideração
  • viés prefere frutas suficientes ( (<miss)==False<True)
  • viés prefere frutas próximas ao preenchimento inteiro mais provável
  • para esse número inteiro, inverta a
    (m-n)/sqrt(n)==(n+1-m)/sqrt(n+1) <==> n=sqrt(m^2-1/4)-1/2 partir de https://en.wikipedia.org/wiki/Sum_of_normally_distributed_random_variables

    https://m.wolframalpha.com/input/?i=plot+abs (1-x) * sqrt (1), abs (2-x) * sqrt (2), abs (3-x) * sqrt ( 3), abs (4-x) * sqrt (4)

temperado com algumas inutilidade desnecessárias

subsets[]=[[]];subsets(f:t)=[r|s<-subsets t,r<-[s,f:s]]
mean=uncurry(div).(id***max 1).(sum&&&length)
bags[]_ _=[];bags batch(miss:have)n=let
 goal=div miss$ceiling(sqrt((fromIntegral miss/170)^2+1/4)-1/2)
 best=minimumBy.comparing.(((<miss)&&&(abs.(goal-))).); desk=take n batch
 pick=best id.best(if sum desk<miss then mean else sum).filter(>[]).subsets$desk
 in if pick < miss then bags(delete pick batch)(miss-pick:pick:have)n
       else (pick:have):bags(delete pick batch)[miss+sum have]n
main=print$id&&&sum$map(length.bags batch[1000])[2..7]

Experimente online!

sem gerar um ganho numérico diferente no topo das 9981 redes de laranjas colhidas antes, enquanto meu empacotador de sacolas 10k011 que pegava laranjas impróprias de volta de sacolas não fechadas foi desqualificado por user69850 na persona user202729Jo Kingportanto, a recompensa merecida foi para Alex

GIMME BOUNTY!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.