Завод фруктових мішків


21

Ваша місія полягає у створенні алгоритму (програми чи функції), який може оптимізувати пакування фруктів з конвеєра в мішки, які потрібно відправити роздрібним торговцям, оптимізуючи для найбільшої кількості мішків.

Кожен мішок повинен важити хоча б певну суму, але будь-який надлишок втрачає прибуток, оскільки ця вага може бути використана для заповнення іншого мішка. Ваша машина для упаковки завжди має вигляд nфруктів із черги і може лише додати будь-який із цих nфруктів до (одного) мішка, який обробляється. Він не може виходити за межі nперших елементів у черзі. Програма завжди точно знає, скільки ваги вже є в сумці.

Іншим способом візуалізації цього є транспортування стрічки з площею завантаження розміром nна кінці, звідки потрібно брати фрукти до появи нового фрукта. Будь-які фрукти, що залишилися, та неповний пакетик наприкінці викидаються.

Рисунок 1: Фабрика з виробництва фруктових мішків

Вхідні дані

  • Список / масив ваг фруктів у черзі (додатні цілі числа)
  • Мінімальна загальна вага для мішків (натуральне число)
  • Lookahead n(натуральне число)

Вихідні дані

Ваш алгоритм повинен повертати для всіх мішків ваги плодів у них будь-якими способами, зручними для вас та вашої мови, будь то стдин або повернене значення чи щось інше. Ви повинні мати можливість запустити програму та обчислити свій рахунок за одну хвилину на своєму комп’ютері.

Приклад

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]

Оцінка балів

Ваш алгоритм буде перевірений на шість етапів на партії з 10000 апельсинів, які я підготував для вас, на локомотивах від 2 до 7, включно з обох кінців. Ви повинні упакувати їх у мішки вагою не менше 1000 одиниць. Апельсини зазвичай розподіляються із середньою масою 170 та стандартним відхиленням 13, якщо це допоможе.

Ваш рахунок буде сумою кількості мішків за шість пробіжок. Виграє найвищий бал. Стандартні лазівки заборонені.

Простий приклад реалізації та випробування набірних котлів набору в Haskell


7
Давайте, люди, я думаю, що все ще існують алгоритми з низькими висячими фруктами, які ще чекають, щоб їх вибрали…
Ангели

2
Чи можуть програми жорсткого коду середньої ваги / розподілу? (припустимо, що він однаково добре працює на подібних партіях. Звичайно, жорстке кодування все недійсне, оскільки це руйнує мету обмеженого пошуку)
user202729

@ user202729: Так, вони можуть.
Ангс

І жорстке кодування все-таки заборонена стандартна лазівка .
Ангс

Я не можу побачити, що це за огляд
l4m2

Відповіді:


8

Сумки Python 3, 9964 9981

Ідея цього рішення схожа на ідеї Джонатана, Джейке та Фортрана, але з функцією оцінювання =)

Це рішення додає кращі підмножини області пошуку, що належить до score.

score забезпечує замовлення на підмножини, використовуючи наступну схему:

  • Підмножина, що комплектує сумку, краще, ніж та, яка це не так
  • Одне підмножина, що комплектує мішок, краще, ніж інше, якщо воно має меншу зайву вагу
  • Одне підмножина, що не заповнює сумку, краще, ніж інше, якщо її середнє значення ближче до того, що очікується в сумці

expected_mean намагається передбачити, як повинні виглядати решта значень (припускаючи, що їх вибір є оптимальним).

UPD :

Ось ще одне спостереження: ви можете помістити будь-які апельсини з найкращого набору в мішок, не зашкодивши роботі алгоритму. Переміщення будь-якої його частини все-таки дозволяє перенести решту після цього, а решта все-таки повинна бути найкращим варіантом (без нових апельсинів), якщо оцінка буде правильною. Більше того, таким чином є шанс динамічно покращити набір кандидатів, які потрібно помістити в мішок, побачивши більше апельсинів перед наповненням пакетика. І ви хочете дізнатися якомога більше інформації, тому немає сенсу переміщати більше ніж один апельсин до сумки в будь-який момент часу.

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

Спробуй це!


Дуже хороша! Він отримує 1672 рік із рівнем 7, ніколи не бачив такого високого.
Ангс

(схоже, що другий аргумент вашої powersetфункції в цьому випадку зайвий, тому що він все одно дорівнює len(list_)?)
user202729

@user Я експериментував з цим параметром у попередній версії. Можливо, буде видалено пізніше
Алекс

1
Вітаємо вас з виявленням потужної комбінації найкращого одного елемента з найкращої підмножини, а також з найкращим балом! Бонус за вами.
Ангс

Простіший результат, expected_mean(w)який дає найкращі результати:return (w+2) / max(1, round((w+2) / mean))
Ангели

10

Мішки Python 3 , 9796

Спираючись на відповідь Джонатана:

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

Це спирається на живлення з кулінарної книги itertool. Спочатку знаходить оптимальне підмножина буфера на основі мінімізації різниці від цільової ваги для всіх підмножин, а потім вибирає елемент із цієї підмножини на основі одного критерію. Якщо з усього буфера не вибрано оптимальне підмножина.


Ласкаво просимо до PPCG!
Мартін Ендер

@MartinEnder Дякую Мартіну за привітання:
JayCe,

1
Ага так, я там пропустив хитрість ... У мене немає жодної проблеми з цим, як ще одна відповідь!
Джонатан Аллан

1
@JonathanAllan Спасибі Джонатан, я скоротив свою відповідь, щоб визнати вас без усіх вибачень. Це можна покращити, використовуючи той факт, що це нормальний (170,13) розподіл - я впевнений, що ймовірність отримати кращі плоди в наступному циклі (и) може бути використана.
JayCe

@JayCe звучить небезпечно близьким до помилок гравця.
qwr

6

C ++ 17, 9961,58 (в середньому за деякими випадковими насінням)

(прокрутіть униз для пояснення, якщо ви не знаєте 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';
}

// Кумедний факт: спочатку я написав <для >тут і дивно
// чому кількість мішків навіть менше , ніж у
рандомізованого алгоритму //

(якщо <використовується в основному, алгоритм намагається мінімізувати кількість мішків)

Натхненний цією відповіддю .

TIO-посилання на 250 повторів: Спробуйте в Інтернеті!


Визначає функцію (насправді вона просто схожа на функцію, це структура), pick_orangeяка, враховуючи vector<int> weightsвагу апельсинів та int remainвагу, що залишилася у мішку, повертає індекс апельсина, який слід зібрати.

Алгоритм:

повтор 500раз {
генерує випадкові (фальшиві) апельсини (нормальний розподіл із середнім 170 і StdDev 13) , поки є N_NEW_ORANGES=7апельсини
вибрати будь-яка підмножина, сума має найменше значення, і не менше , ніж remain(функція backtrackробить це)
позначити всі апельсини в цій підгрупі в якості гарного
}

середня кількість разів, коли апельсини відзначаються як добрі (справжні) апельсини з однаковою масою,
повертають найкращий апельсин


У програмі є 3 константи з твердим кодом, про які не можна зробити висновок про проблему:

  • Випадкове насіння (це не важливо)
  • N_NEW_ORANGES(довжина передбачення). Збільшуючи це, програма запускає експоненціально довше (адже зворотній трек)
  • кількість повторень. Збільшення цього робить програму лінійно довшою.

Добре. Зміна насіння на той, що дає найкращу відповідь, здається оптимізацією для тестового випадку, тому вам слід взяти середній показник у кількох, скажімо 10, різних насінинах. Чи можете ви опублікувати посилання TIO на версію, яка робить менше повторень, щоб зменшити час виконання?
Ангс

Нарешті отримав його для компілювання після отримання більш нового gcc. На 50 пробігах із випадковим насінням воно отримало в середньому 9961,58. Дуже вражає досі. Мене змусило замислитися, але - ваш алгоритм в основному знову тренується на кожній сумці, чи є фіксований набір найкращих значень, які можна запам'ятати?
Ангс

@Angs Я не думаю, що існує спосіб, який може використовувати запам'ятовування, щоб допомогти в цьому випадку. Будь-яка ідея?
користувач202729

Моя ОС постачається з gcc 5.4.0, у неї були деякі проблеми invalid use of template-name ‘std::normal_distribution’. Немає проблем з gcc 7.1.0.
Ангс

4

Мішки Python 2 , 9756

Давайте помаранчевий прокат ...

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

Спробуйте в Інтернеті!

Завжди бере фрукти з буфера, що мінімізує абсолютну різницю нової ваги та цільової ваги.


4

Мішки Python 3, 9806

Спираючись на відповіді Джонатана та Джейкі:

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

Спробуйте в Інтернеті!

Як це працює

Скажіть, що в мішку є 900 одиниць, і є 2 фрукти в наявності: фрукт 99 одиниць і плід 101 одиниця. Якщо плід 99 одиниць ближче до початку списку пошуку, тоді minвиберемо його замість 101. Якщо це станеться, нам зараз знадобиться інший плід, щоб виконати решту 1 необхідної одиниці. Я змінив програму, щоб надати перевагу більш цінним фруктам у цих випадках.

Це робиться шляхом сортування, а потім ревертування списку пошуку перед вимкненням.


4

PHP, 9975 мішків

  • По можливості піти по 5 апельсинів
  • Під час запуску сумки виберіть надзвичайне значення, збалансуйте пізніше
  • По можливості негайно заповнюйте мішок
  • Намагайтеся тримати вагу сумки близькою до розрахункової кривої (n * 200 для 5 bag, n * 167 для 6 bag тощо)

найдовше з усіх поданих матеріалів, але має бути читабельним

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 Всього: 9975

Спробуй це


Приємно! Що дивно для мене, це те, що він використовує поточну кількість предметів - мені цікаво, чи можна це визначити. Зрештою, не має значення, чи є 3 вироби вагою 120 кожен або 3 вироби вагою 160 кожен.
Ангс

@Angs, можливо, це можливо. Поточний підрахунок предметів з'явився як простий ярлик для ідеї "Ей, іноді можливо зробити мішок з 5 предметами", і я розібрався з тим, щоб 5 мішків предметів працювали. З вільним часом прийде покращення :)
млеко

3

Сумки Python 3, 9855 9928 9947 9956 9964

Базується на початковому коді Джонатана Аллана, але він не має сили для читання.

Ідея: оскільки 1000/170 = 5,88, ми намагаємось відібрати плоди, близькі до 1000/6 (я познайомився з магічними константами). Однак якщо останній фрукт у мішку може мінімізувати відходи, ми використовуємо це замість цього.

Це рішення має мішені суми для кожного фрукта. Я, мабуть, зупинюсь тут. Я використовував Nelder-Mead для пошуку свого targetsмасиву:

[  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 мішків

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

Програма 9947 сумок особливо проста:

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
Добре! Btw, лише вибираючи останній предмет, щоб мінімізувати відходи, він досить потужний сам по собі і дає 9862 мішки.
Ангс

Як ви придумали це targets? Навчання випадковим даним?
Олексій

1
@ Алекс я заявив так: метод Нелдера-Мід (з негативними пакетами як функція втрати)
qwr

2

Рубі , 9967 мішків

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

Спробуйте в Інтернеті!

Якщо у вас є достатня вага для наповнення сумки, знайдіть найлегший підмножина, яка може наповнити мішок, і використовуйте найлегший апельсин цього підмножини. В іншому випадку наберіть вагу, що залишилася, якомога ближче до кратного 170.


2

Ракетка / схема, 9880 мішків

Щоб вирішити, який шматочок фруктів додати до мішка, порівняйте оптимальну вагу мішка з вагою мішка з додатковим шматочком фрукта. Якщо це оптимальна вага, використовуйте його. Якщо це зайва вага, мінімізуйте зайву кількість. Якщо вона має недостатню вагу, мінімізуйте зайву кількість, намагаючись залишити оптимальний пробіл.

;; 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 мішків

Це була моя перша спроба:

  • він жадібно наповнював мішок партією, коли міг,
  • або збила всі апельсини в мішечок, коли не могла.
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]

Спробуйте в Інтернеті!


1

Haskell , 9981 мішок

The AngsJonathan AllanJayCefortraanAlexRoman Czyborra codegolf python міг повернутись до Хаскеллу для деякої додаткової математичної чистоти по тому самому головному подуму думки

  • лише один апельсин випиляється до того, як новий апельсин буде доданий до уваги
  • упередженість воліє хапаючи фрукти ( (<miss)==False<True)
  • зміщення віддає перевагу фруктам, близьким до найбільш вірогідного цілочисленного заповнення
  • для цього цілого реверсу
    (m-n)/sqrt(n)==(n+1-m)/sqrt(n+1) <==> n=sqrt(m^2-1/4)-1/2 від А 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)

приправлений деякими непотрібними безглуздями

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]

Спробуйте в Інтернеті!

не піддаючись іншим числовим коефіцієнтом посилення на вершину 9981 мереж апельсинів , зібраних вище в той час як мої 10k011 мішків пакер захоплення непридатних апельсинів назад з незакритих сумок були дискваліфікований user69850 в персонах user202729Джо Кінговс hencefore заслужена Баунті пішла Alex

GIMME BOUNTY!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.