Фабрика по упаковке фруктов


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
Давай, люди, я думаю, что все еще есть некоторые алгоритмы с низко висящими фруктами, которые все еще ждут, чтобы их взяли ...
Angs

2
Могут ли программы жестко закодировать средний вес / распределение? (предположим, что он одинаково хорошо работает в аналогичных пакетах, конечно,
жесткое кодирование

@ user202729: Да, они могут.
Анг

И все равно жесткое кодирование - это запрещенная стандартная лазейка .
Angs

Я не вижу, что это за головокружение
l4m2

Ответы:


8

Питон 3, 9964 9981 пакет

Идея этого решения аналогична Jonathan, JayCe и fortraan, но с функцией подсчета =)

Это решение добавляет лучшие подмножества области просмотра в соответствии с 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, никогда не видел такого высокого.
Angs

(похоже, что второй аргумент вашей powersetфункции избыточен в этом случае, потому что он в len(list_)любом случае равен ?)
user202729

@user Я экспериментировал с этим параметром в предыдущей версии. Вероятно, удаляю это позже
Алекс

1
Поздравляем с открытием мощного сочетания лучшего единственного элемента из лучшего подмножества, а также с лучшим результатом! Щедрость твоя.
Angs

Проще, expected_mean(w)что дает хорошие результаты:return (w+2) / max(1, round((w+2) / mean))
Angs

10

Питон 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

Это зависит от powerset из кулинарной книги 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 на версию, которая делает меньше повторений, чтобы остановить время выполнения?
Angs

Наконец получил его для компиляции после получения более нового gcc. На 50 опытов со случайными семенами он получил в среднем 9961,58. Очень впечатляет до сих пор. Меня удивило - ваш алгоритм в основном тренируется снова на каждой сумке, есть ли фиксированный набор лучших значений, которые можно запомнить?
Angs

@Angs Я не думаю, что есть способ использовать запоминание, чтобы помочь в этом случае. Есть идеи?
user202729

Моя ОС поставляется с gcc 5.4.0, с ней были некоторые проблемы invalid use of template-name ‘std::normal_distribution’. Нет проблем с gcc 7.1.0.
Angs

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

Питон 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 мешков, n * 167 для 6 мешков и т. Д.)

самый длинный из всех представлений, но должен быть читабельным

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 сумок. Со свободным временем придут улучшения :)
mleko

3

Питон 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
Хорошо! Кстати, только выбор последнего предмета, чтобы минимизировать отходы, сам по себе довольно мощный и дает 9862 мешка.
Ангс

Как ты это придумал targets? Тренировка по случайным данным?
Алекс


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 пакет

AngsДжонатан AllanJaycefortraanAlexRoman Czyborra codegolf питон может циклизации обратно Haskell для некоторой добавленной математической чистоты вдоль одной и той же основной ход мысли

  • только один апельсин уволен, прежде чем новый апельсин будет добавлен в рассмотрение
  • уклон предпочитает достаточное количество фруктов ( (<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-х) * SQRT (1), ABS (2-х) * SQRT (2), ABS (3-х) * SQRT ( 3), ABS (4-х) * 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

Дай мне BOUNTY!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.