Pabrik mengantongi buah

21

Misi Anda adalah untuk membangun algoritma (program atau fungsi) yang dapat mengoptimalkan pengemasan buah dari ban berjalan ke dalam tas untuk dikirim ke pengecer, mengoptimalkan untuk sejumlah besar tas.

Setiap kantung harus memiliki berat setidaknya jumlah tertentu, tetapi setiap kelebihannya akan hilang karena bobot tersebut dapat digunakan untuk mengisi kantung lain. Mesin pengepakan Anda selalu melihat nbuah - buahan dari antrian dan hanya dapat memilih untuk menambahkan nbuah - buahan ini ke tas (tunggal) yang sedang diproses. Itu tidak bisa melihat melampaui nelemen pertama dalam antrian. Program selalu tahu persis berapa banyak berat yang ada di dalam tas.

Cara lain untuk memvisualisasikan ini adalah memiliki ban berjalan dengan area pemuatan ukuran ndi ujung, dari mana buah harus diambil sebelum buah baru tiba. Buah sisa dan kantong yang tidak penuh di bagian akhir akan dibuang.

Gambar 1: Pabrik mengantongi buah

Input

  • Daftar / array bobot buah dalam antrian (bilangan bulat positif)
  • Berat total minimum untuk tas (bilangan bulat positif)
  • Lookahead n(bilangan bulat positif)

Keluaran

Algoritme Anda harus mengembalikan untuk semua kantong bobot buah-buahan di dalamnya, dengan cara apa pun yang nyaman bagi Anda dan bahasa Anda, baik itu stdin atau nilai balik atau sesuatu yang lain. Anda harus dapat menjalankan program dan menghitung skor Anda dalam satu menit di komputer Anda.

Contoh

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]

Mencetak gol

Algoritme Anda akan diuji pada enam kali berjalan pada batch 10.000 jeruk yang telah saya siapkan untuk Anda, pada lookaheads mulai dari 2 hingga 7, termasuk di kedua ujungnya. Anda harus mengemasnya ke dalam tas dengan berat setidaknya 1000 unit. Jeruk biasanya didistribusikan dengan berat rata-rata 170 dan standar deviasi 13, jika itu bisa membantu.

Skor Anda akan menjadi jumlah jumlah tas dari enam seri. Skor tertinggi menang. Celah standar tidak diijinkan.

Contoh implementasi sederhana dan test suite boilerplate di Haskell

Angs
sumber
7
Ayo orang-orang, saya pikir masih ada beberapa algoritma buah tergantung rendah yang masih menunggu untuk dipilih ...
Angs
2
Bisakah program memprogram hardcode berat rata-rata / distribusi? (menganggap bahwa ia bekerja sama baiknya pada batch yang sama, tentu saja hardcoding semuanya tidak valid karena menghancurkan tujuan lookahead terbatas)
user202729
@ user202729: Ya mereka bisa.
Angs
Dan hardcoding semuanya adalah lubang standar terlarang .
Angs
Saya tidak dapat melihat apa itu lookhead
l4m2

Jawaban:

8

Python 3, 9964 9981 tas

Gagasan solusi ini mirip dengan Jonathan, JayCe dan fortraan, tetapi dengan fungsi penilaian =)

Solusi ini menambahkan himpunan bagian terbaik dari area lookahead yang mengakses score.

score menyediakan pesanan melalui subset menggunakan skema berikut:

  • Subset yang menyelesaikan tas lebih baik daripada yang tidak
  • Satu subset menyelesaikan tas lebih baik dari yang lain jika beratnya kurang
  • Satu subset tidak menyelesaikan tas lebih baik dari yang lain jika rata-rata lebih dekat dengan apa yang diharapkan di dalam tas

expected_mean mencoba untuk memprediksi seperti apa sisa nilai seharusnya (dengan asumsi pilihan mereka optimal).

UPD :

Berikut adalah pengamatan lain: Anda dapat menempatkan jeruk dari subset terbaik ke dalam tas tanpa merusak kinerja algoritma. Memindahkan bagian mana pun dari itu masih memungkinkan untuk memindahkan sisanya setelahnya, dan sisanya harus tetap menjadi pilihan terbaik (tanpa jeruk baru) jika skornya tepat. Selain itu, dengan cara ini ada peluang untuk secara dinamis meningkatkan set kandidat untuk dimasukkan ke dalam tas dengan melihat lebih banyak jeruk sebelum mengisi tas. Dan Anda ingin mengetahui informasi sebanyak mungkin, sehingga tidak ada gunanya memindahkan lebih dari satu jeruk ke tas pada waktu tertentu.

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

Cobalah!

Alex
sumber
Sangat bagus! Ia mendapat 1672 dengan lookahead 7, tidak pernah melihat yang setinggi itu.
Angs
(Sepertinya argumen kedua untuk powersetfungsi Anda berlebihan dalam hal ini karena sama dengan len(list_)?)
user202729
@ Pengguna Saya bereksperimen dengan parameter ini di versi sebelumnya. Mungkin akan menghapusnya nanti
Alex
1
Selamat telah menemukan kombinasi kuat elemen tunggal terbaik dari subset terbaik dan juga memiliki skor terbaik! Hadiahnya adalah milikmu.
Angs
Yang lebih sederhana expected_mean(w)yang memberikan hasil yang baik:return (w+2) / max(1, round((w+2) / mean))
Angs
10

Python 3 , 9796 tas

Membangun jawaban 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

Ini bergantung pada powerset dari buku masak itertool. Pertama menemukan subset optimal dari buffer berdasarkan meminimalkan perbedaan dari berat target untuk semua subset, dan kemudian mengambil elemen dari subset ini berdasarkan kriteria yang sama. Jika tidak ada subset optimal memilih dari seluruh buffer.

JayCe
sumber
Selamat datang di PPCG!
Martin Ender
@MartinEnder Terima kasih Martin atas sambutan selamat datang :)
JayCe
1
Ah ya saya melewatkan trik di sana ... Saya tidak punya masalah dengan ini sebagai jawaban lain!
Jonathan Allan
1
@JonathanAllan Terima kasih, Jonathan, saya telah mempersingkat jawaban saya untuk memuji Anda tanpa semua permintaan maaf. Ini dapat ditingkatkan dengan menggunakan fakta bahwa itu adalah distribusi normal (170,13) - Saya yakin kemungkinan mendapatkan buah yang lebih baik dalam menjalankan selanjutnya dapat digunakan.
JayCe
@JayCe terdengar sangat dekat dengan kekeliruan penjudi.
qwr
6

C ++ 17, 9961.58 (rata-rata di atas beberapa benih acak)

(gulir ke bawah untuk penjelasan jika Anda tidak tahu 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';
}

// Bahkan Fun: awalnya saya menulis <untuk >di sini dan heran
// mengapa jumlah tas bahkan kurang dari itu dari
// algoritma acak

(jika <digunakan pada dasarnya algoritma mencoba untuk meminimalkan jumlah tas)

Terinspirasi oleh jawaban ini .

TIO link untuk 250 pengulangan: Coba online!


Menentukan fungsi (sebenarnya hanya terlihat seperti fungsi, itu adalah sebuah struct) pick_orangeyang, mengingat vector<int> weightsberat jeruk dan int remainsisa berat tas, mengembalikan indeks jeruk yang harus dipetik.

Algoritma:

ulangi 500kali {
menghasilkan jeruk acak (palsu) (distribusi normal dengan rata-rata 170 dan stddev 13) sampai ada N_NEW_ORANGES=7jeruk
memilih subset yang jumlahnya paling kecil dan tidak lebih kecil dari remain(fungsi backtrackmelakukan itu)
menandai semua jeruk di subset itu sebagai baik
}

rata-rata berapa kali jeruk ditandai sebagai yang baik dari jeruk (nyata) dengan berat yang sama
mengembalikan jeruk terbaik


Ada 3 konstanta hardcode dalam program yang tidak dapat disimpulkan dari masalah:

  • Benih acak (ini tidak penting)
  • N_NEW_ORANGES(panjang prediksi). Peningkatan ini membuat program berjalan lebih lama secara eksponensial (karena mundur)
  • jumlah pengulangan. Peningkatan ini membuat program berjalan lebih lama secara linear.
pengguna202729
sumber
Baik. Mengganti seed menjadi yang memberikan jawaban terbaik sepertinya mengoptimalkan untuk test case, jadi Anda harus mengambil rata-rata beberapa, katakan 10, seed berbeda sebagai skor Anda. Bisakah Anda memposting tautan TIO ke versi yang tidak terlalu banyak pengulangan untuk menjalankan runtime?
Angs
Akhirnya mendapatkannya dikompilasi setelah mendapatkan gcc yang lebih baru. Pada 50 berjalan dengan biji acak itu mendapat rata-rata 9961,58. Masih sangat mengesankan. Membuat saya bertanya-tanya - pada dasarnya algoritma Anda melatih dirinya sendiri pada setiap tas, apakah ada set nilai-nilai terbaik yang bisa diingat?
Angs
@Angs Saya tidak berpikir ada cara yang dapat menggunakan menghafal untuk membantu dalam kasus ini. Ada ide?
user202729
OS saya hadir dengan gcc 5.4.0, ada beberapa masalah dengan invalid use of template-name ‘std::normal_distribution’. Tidak ada masalah dengan gcc 7.1.0.
Angs
4

Python 2 , 9756 tas

Mari kita mulai menggulung jeruk ...

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

Cobalah online!

Selalu memetik buah dari buffer yang meminimalkan perbedaan absolut dari berat baru dan berat target.

Jonathan Allan
sumber
4

Python 3, 9806 tas

Membangun jawaban Jonathan dan 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

Cobalah online!

Bagaimana itu bekerja

Katakanlah bahwa tas itu memiliki 900 unit di dalamnya, dan ada 2 buah yang tersedia: buah 99 unit dan buah 101 unit. Jika 99 unit buah lebih dekat dengan awal daftar lookahead, maka minakan memilihnya daripada 101. Jika ini terjadi, kita sekarang akan membutuhkan buah lain untuk memenuhi 1 unit yang dibutuhkan. Saya mengubah program untuk mendukung buah yang bernilai lebih tinggi dalam kasus-kasus ini.

Ia melakukan ini dengan menyortir dan kemudian membalik daftar lookahead sebelum mengatur ulang.

fortraan
sumber
4

PHP, 9975 tas

  • Jika mungkin pilih 5 jeruk
  • Saat memulai tas memilih nilai ekstrem, seimbangkan nanti
  • Jika mungkin segera isi tas
  • Usahakan agar berat tas tetap dekat dengan perkiraan kurva (n * 200 untuk 5 bag, n * 167 untuk 6 bag, dll)

terpanjang dari semua pengiriman tetapi harus dapat dibaca

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

Cobalah

mleko
sumber
Bagus! Apa yang mengejutkan bagi saya adalah bahwa ia menggunakan jumlah item saat ini - Saya bertanya-tanya apakah daripada dapat diperhitungkan. Lagi pula, tidak masalah jika ada 3 item yang masing-masing berbobot 120 atau 3 item yang masing-masing berbobot 160.
Angs
@ Angs mungkin itu mungkin. Hitungan item saat ini keluar sebagai cara pintas sederhana untuk ide "Hei, kadang-kadang mungkin untuk melakukan 5 item tas" dan saya menyetel agar 5 item tas berfungsi. Dengan perbaikan waktu luang akan datang :)
mleko
3

Python 3, 9855 9928 9947 9956 9964 tas

Berdasarkan kode starter Jonathan Allan, tetapi tidak dapat dibaca.

Gagasan: Sejak 1000/170 = 5.88, kami mencoba memilih buah yang mendekati 1000/6 (Saya mengutak-atik konstanta ajaib). Namun, jika buah terakhir dalam kantong dapat meminimalkan limbah, kami menggunakannya sebagai gantinya.

Solusi ini memiliki target jumlah tas untuk setiap buah yang ditambahkan. Saya mungkin akan berhenti di sini. Saya menggunakan Nelder-Mead untuk menemukan targetsarray saya :

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

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

Program tas 9947 sangat sederhana:

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
qwr
sumber
1
Baik! Btw, hanya memetik item terakhir sehingga meminimalkan limbah cukup kuat dengan sendirinya dan memberikan 9862 kantong.
Angs
Bagaimana Anda menemukan itu targets? Pelatihan tentang data acak?
Alex
1
@Alex I menyatakan demikian: Metode Nelder-Mead (dengan kantong negatif sebagai fungsi kerugian)
qwr
2

Ruby , 9967 tas

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

Cobalah online!

Jika Anda memiliki cukup berat untuk mengisi tas, temukan subset yang paling ringan yang bisa mengisi tas dan gunakan oranye paling ringan dari subset itu. Jika tidak, dapatkan sisa berat sedekat mungkin dengan kelipatan 170.

MegaTom
sumber
2

Racket / Skema, 9880 tas

Untuk memutuskan potongan buah mana yang akan ditambahkan ke dalam tas, bandingkan berat tas yang optimal dengan berat kantong dengan potongan buah tambahan. Jika beratnya optimal, gunakan itu. Jika kelebihan berat badan, minimalkan jumlah berlebih. Jika beratnya kurang, minimalkan jumlah berlebih setelah mencoba meninggalkan celah yang optimal.

;; 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
Kevin
sumber
1

Haskell , 9777 tas

Ini adalah upaya pertama saya:

  • dengan rakus mengisi tas dengan bets ketika itu bisa,
  • atau membuang semua jeruk ke dalam tas ketika tidak bisa.
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]

Cobalah online!

Roman Czyborra
sumber
1

Haskell , 9981 tas

The AngsJonathan AllanJayCefortraan AlexRoman Czyborra codegolf python bisa cyclize kembali ke Haskell untuk beberapa menambahkan kemurnian matematika sepanjang kereta utama yang sama pemikiran

  • hanya satu jeruk dipecat sebelum jeruk baru ditambahkan ke dalam pertimbangan
  • Bias lebih suka buah yang cukup ((<miss)==False<True )
  • Bias lebih suka buah dekat dengan bilangan bulat yang paling mungkin
  • untuk bilangan bulat itu membalikkan
    (m-n)/sqrt(n)==(n+1-m)/sqrt(n+1) <==> n=sqrt(m^2-1/4)-1/2 dari 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)

dibumbui dengan beberapa hal yang tidak perlu

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]

Cobalah online!

tanpa menghasilkan keuntungan numerik yang berbeda di atas 9981 jaring jeruk yang dipanen sebelumnya, sementara pengepak tas 10k011 saya yang mengambil jeruk yang tidak layak keluar dari kantong tidak tertutup didiskualifikasi oleh user69850 secara pribadi user202729Jo Kingovs karenanya sebelum hadiah yang layak pergi ke Alex

GIMME BOUNTY!

Roman Czyborra
sumber