Fabbrica di insaccamento di frutta


21

La tua missione è quella di costruire un algoritmo (programma o funzione) in grado di ottimizzare l'imballaggio della frutta da un nastro trasportatore in sacchi da inviare ai rivenditori, ottimizzando per un numero maggiore di sacchi.

Ogni sacco deve pesare almeno un certo importo, ma l'eventuale eccesso viene perso in quanto il peso può essere utilizzato per riempire un altro sacco. La tua insacchettatrice ha sempre un lookahead di nfrutti dalla coda e può solo scegliere di aggiungere uno di questi nfrutti al (singolo) sacchetto che viene elaborato. Non può guardare oltre i nprimi elementi nella coda. Il programma sa sempre esattamente quanto peso c'è già nella borsa.

Un altro modo per visualizzarlo è avere un nastro trasportatore con un'area di carico di dimensioni nalla fine, da dove deve essere prelevato un frutto prima che arrivi un nuovo frutto. Ogni frutto rimanente e un sacchetto non pieno alla fine vengono scartati.

Figura 1: fabbrica di insaccamento di frutta

ingressi

  • Elenco / matrice dei pesi dei frutti in coda (numeri interi positivi)
  • Peso totale minimo per sacchi (numero intero positivo)
  • Lookahead n(numero intero positivo)

Produzione

Il tuo algoritmo dovrebbe restituire per tutti i sacchi i pesi dei frutti in essi contenuti, con qualunque mezzo sia conveniente per te e la tua lingua, sia quello standard che un valore di ritorno o qualcos'altro. Dovresti essere in grado di eseguire il programma e calcolare il tuo punteggio in un minuto sul tuo computer.

Esempio

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]

punteggio

Il tuo algoritmo verrà testato su sei esecuzioni su un lotto di 10000 arance che ho preparato per te, su lookahead che vanno da 2 a 7, inclusi su entrambe le estremità. Li imballerai in sacchi del peso di almeno 1000 unità. Le arance sono normalmente distribuite con un peso medio di 170 e una deviazione standard di 13, se questo è di qualche aiuto.

Il tuo punteggio sarà la somma del numero di sacchi delle sei prove. Vince il punteggio più alto. Le scappatoie standard non sono ammesse.

Semplice esempio di implementazione e test suite di piatti in Haskell


7
Andiamo gente, penso che ci siano ancora alcuni algoritmi di frutta a basso
peso

2
I programmi possono codificare il peso medio / la distribuzione? (supponiamo che funzioni ugualmente bene su lotti simili, ovviamente codificare tutto non è valido in quanto distrugge lo scopo di un lookahead limitato)
user202729

@ user202729: Sì, possono.
Angs

E codificare tutto è comunque una scappatoia standard proibita .
Ang

Non riesco a vedere che aspetto è
l4m2

Risposte:


8

Python 3, 9964 9981 borse

L'idea di questa soluzione è simile a quella di Jonathan, JayCe e Fortraan, ma con una funzione di punteggio =)

Questa soluzione aggiunge i migliori sottoinsiemi dell'area lookahead in base al score.

score fornisce un ordine sui sottoinsiemi utilizzando il seguente schema:

  • Un sottoinsieme che completa una borsa è meglio di uno che non lo è
  • Un sottoinsieme che completa una borsa è meglio di un altro se ha un peso in eccesso inferiore
  • Un sottoinsieme che non completa una borsa è meglio di un altro se la sua media è più vicina a ciò che dovrebbe essere in una borsa

expected_mean cerca di prevedere come dovrebbe apparire il resto dei valori (supponendo che la loro scelta sia ottimale).

UPD :

Ecco un'altra osservazione: è possibile posizionare eventuali arance dal sottoinsieme migliore nella borsa senza danneggiare le prestazioni dell'algoritmo. Lo spostamento di qualsiasi parte di esso consente comunque di spostare il resto in seguito, e il resto dovrebbe comunque essere l'opzione migliore (senza nuove arance) se il punteggio è corretto. Inoltre, in questo modo c'è la possibilità di migliorare dinamicamente l'insieme di candidati da mettere nel sacco vedendo più arance prima di riempire il sacco. E vuoi sapere quante più informazioni possibili, quindi non ha senso spostare più di un'arancia nella borsa in un dato 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)))

Provalo!


Molto bella! Ottiene il 1672 con un lookahead di 7, mai visto così in alto.
Angs

(sembra che il secondo argomento della tua powersetfunzione sia ridondante in questo caso perché è uguale len(list_)comunque?)
user202729

@utente Ho sperimentato questo parametro nella versione precedente. Probabilmente lo rimuoverà più tardi
Alex,

1
Congratulazioni per aver scoperto la potente combinazione del miglior singolo elemento dal miglior sottoinsieme e anche per aver ottenuto il miglior punteggio! La taglia è tua.
Angs

Un più semplice expected_mean(w)che dà buoni risultati:return (w+2) / max(1, round((w+2) / mean))
Angs

10

Python 3 , 9796 borse

Basandosi sulla risposta di 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

Questo si basa sul powerset dal ricettario di itertool. Per prima cosa trova il sottoinsieme ottimale del buffer in base alla riduzione al minimo della differenza dal peso target per tutti i sottoinsiemi, quindi seleziona un elemento da questo sottoinsieme in base allo stesso criterio. Se nessun sottoinsieme ottimale seleziona da tutto il buffer.


Benvenuti in PPCG!
Martin Ender,

@MartinEnder Grazie Martin per l'accoglienza positiva :)
JayCe

1
Ah sì, ho perso un trucco lì ... Non ho problemi con questo come un'altra risposta!
Jonathan Allan,

1
@JonathanAllan Grazie Jonathan ho abbreviato la mia risposta per accreditarti senza tutte le scuse. Questo può essere migliorato usando il fatto che si tratta di una distribuzione normale (170,13) - Sono sicuro che si può usare la probabilità di ottenere un frutto migliore nelle prossime analisi.
JayCe

@JayCe sembra pericolosamente vicino all'errore del giocatore.
qwr

6

C ++ 17, 9961.58 (media su alcuni semi casuali)

(scorri verso il basso per una spiegazione se non conosci 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';
}

// Curiosità: originariamente ho scritto <per >qui e mi chiedo
// perché il numero di sacchetti è addirittura inferiore a quello
dell'algoritmo // randomizzato

(se <utilizzato fondamentalmente l'algoritmo tenta di ridurre al minimo il numero di sacchi)

Ispirato da questa risposta .

Link TIO per 250 ripetizioni: provalo online!


Definisce una funzione (in realtà sembra solo una funzione, è una struttura) pick_orangeche, dato vector<int> weightsil peso delle arance e int remainil peso rimanente del sacco, restituisce l'indice dell'arancia che dovrebbe essere raccolto.

Algoritmo:

i 500tempi di ripetizione {
generano arance casuali (false) (distribuzione normale con media 170 e stddev 13) fino a quando non ci sono N_NEW_ORANGES=7arance che
scelgono qualsiasi sottoinsieme la cui somma è più piccola e non minore di remain(la funzione lo backtrackfa)
contrassegna tutte le arance in quel sottoinsieme come buone
}

calcolare la media del numero di volte in cui un'arancia viene contrassegnata come buona tra le arance (reali) con lo stesso peso
restituendo l'arancia migliore


Ci sono 3 costanti codificate nel programma che non possono essere dedotte dal problema:

  • Il seme casuale (questo non è importante)
  • N_NEW_ORANGES(la lunghezza della previsione). L'aumento di questo allunga il programma in modo esponenziale più lungo (perché backtrack)
  • numero di ripetizioni. L'aumento di questo allunga il programma in modo lineare.

Ok. Cambiare il seme in quello che dà la risposta migliore sembra però ottimizzare per il caso di test, quindi dovresti prendere la media di alcuni, diciamo 10, semi diversi come punteggio. Potresti pubblicare un collegamento TIO a una versione che fa meno ripetizioni per ridurre il tempo di esecuzione?
Angs

Finalmente è riuscito a compilare dopo aver ottenuto un nuovo gcc. Su 50 corse con semi casuali ha ottenuto una media di 9961,58. Molto impressionante ancora. Tuttavia, mi sono meravigliato: il tuo algoritmo si allena di nuovo su ogni borsa, esiste un set fisso di valori migliori che potrebbe essere memorizzato?
Angs

@Angs Non penso che ci sia un modo per usare la memorizzazione per aiutare in questo caso. Qualche idea?
user202729

Il mio sistema operativo viene fornito con gcc 5.4.0, con alcuni problemi invalid use of template-name ‘std::normal_distribution’. Nessun problema con gcc 7.1.0.
Angs

4

Python 2 , 9756 borse

Facciamo rotolare l'arancia ...

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

Provalo online!

Prende sempre il frutto dal buffer che riduce al minimo la differenza assoluta tra il nuovo peso e il peso target.


4

Python 3, 9806 borse

Basandoci sulle risposte di 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

Provalo online!

Come funziona

Supponi che la confezione contenga 900 unità e che ci siano 2 frutti disponibili: un frutto da 99 unità e un frutto da 101 unità. Se il frutto di 99 unità è più vicino all'inizio dell'elenco di lookahead, minlo selezionerà invece di 101. In questo caso, avremmo ora bisogno di un altro frutto per soddisfare le restanti 1 unità necessarie. In questi casi ho modificato il programma per favorire il frutto di maggior valore.

Lo fa ordinando e quindi invertendo l'elenco lookahead prima del powerset.


4

PHP, 9975 sacchetti

  • Se possibile, scegli 5 arance
  • Quando si avvia la borsa, scegliere un valore estremo, bilanciare in seguito
  • Se possibile riempire immediatamente la busta
  • Cerca di mantenere il peso del sacco vicino alla curva stimata (n * 200 per 5 sacchi, n * 167 per 6 sacchi, ecc.)

il più lungo di tutti gli invii, ma dovrebbe essere leggibile

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 Totale: 9975

Provalo


Bello! La cosa sorprendente per me è che utilizza l'attuale conteggio degli articoli - mi chiedo se si possa prendere in considerazione. Dopotutto, non importa se ci sono 3 articoli che pesano 120 ciascuno o 3 articoli che pesano 160 ciascuno.
Angs

@Angs probabilmente è possibile. L'attuale conteggio degli articoli è emerso come una semplice scorciatoia per l'idea "Ehi, a volte è possibile fare un sacco di 5 oggetti" e mi sono sintonizzato su come far funzionare i sacchetti di 5 oggetti. Con i miglioramenti del tempo libero arriverà :)
mleko

3

Python 3, 9855 9928 9947 9956 9964 borse

Basato sul codice di avviamento di Jonathan Allan, ma non modificato per essere leggibile.

Idea: da 1000/170 = 5.88, proviamo a selezionare frutti vicini a 1000/6 (ho armeggiato con le costanti magiche). Tuttavia, se l'ultimo frutto nella confezione può ridurre al minimo gli sprechi, lo utilizziamo invece.

Questa soluzione ha obiettivi di somma bagaglio per ogni frutto aggiunto. Probabilmente mi fermerò qui. Ho usato Nelder-Mead per trovare il mio targetsarray:

[  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 borse

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))

Il programma di borse 9947 è particolarmente semplice:

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
Buono! A proposito, scegliere solo l'ultimo oggetto in modo da ridurre al minimo gli sprechi è abbastanza potente da solo e dà 9862 sacchetti.
Angs

Come ti è venuto in mente targets? Allenarsi su dati casuali?
Alex,

1
@Alex L'ho affermato: metodo Nelder-Mead (con sacchi negativi come funzione di perdita)
qwr

2

Rubino , 9967 borse

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

Provalo online!

Se hai abbastanza peso per riempire il sacco, trova il sottoinsieme più leggero che può riempire il sacco e usa l'arancione più chiaro di quel sottoinsieme. In caso contrario, avvicinare il peso rimanente il più vicino possibile a un multiplo di 170.


2

Racchetta / schema, 9880 sacchetti

Per decidere quale frutto aggiungere al sacco, confrontare il peso ottimale del sacco con il peso del sacco con il frutto aggiuntivo. Se è il peso ottimale, usalo. Se è in sovrappeso, ridurre al minimo la quantità in eccesso. Se è sottopeso, ridurre al minimo la quantità in eccesso dopo aver cercato di lasciare uno spazio ottimale.

;; 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 borse

Questo era il mio primo tentativo:

  • riempiva avidamente una busta con un lotto quando poteva,
  • o ha scaricato tutte le arance nel sacchetto quando non è stato possibile.
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]

Provalo online!


1

Haskell , 9981 borse

The AngsJonathan AllanJayCefortraanAlex → Il pitone codegolf romano Czyborra potrebbe tornare in bicicletta a Haskell per un po 'di purezza matematica aggiunta lungo lo stesso treno di pensiero

  • solo un'arancia viene licenziata prima di aggiungere una nuova arancia
  • il pregiudizio preferisce frutti sufficienti ( (<miss)==False<True)
  • il bias preferisce i frutti vicini al riempimento intero più probabile
  • per quel numero intero inverti il
    (m-n)/sqrt(n)==(n+1-m)/sqrt(n+1) <==> n=sqrt(m^2-1/4)-1/2 da a 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)

condito con alcune inutilità inutili

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]

Provalo online!

senza produrre un diverso guadagno numerico in cima alle 9981 reti di arance raccolte prima mentre il mio packer per sacchetti da 10k011 che afferrava arance non idonee da sacchetti non chiusi veniva squalificato dall'utente69850 in persona user202729Jo Kingovs quindi la meritata ricompensa andò ad Alex

GIMME BOUNTY!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.