Nhà máy đóng gói trái cây


21

Nhiệm vụ của bạn là xây dựng một thuật toán (chương trình hoặc chức năng) có thể tối ưu hóa việc đóng gói trái cây từ băng chuyền vào túi để gửi đến các nhà bán lẻ, tối ưu hóa cho số lượng túi lớn nhất.

Mỗi túi phải có trọng lượng ít nhất là một số tiền nhất định, nhưng bất kỳ khoản dư thừa nào đều bị mất lợi nhuận vì trọng lượng đó có thể được sử dụng để lấp đầy một túi khác. Máy đóng bao của bạn luôn có một nloại trái cây từ hàng đợi và chỉ có thể chọn thêm bất kỳ nloại trái cây nào vào túi (đơn) đang được chế biến. Nó không thể nhìn xa hơn các nyếu tố đầu tiên trong hàng đợi. Chương trình luôn biết chính xác có bao nhiêu cân trong túi.

Một cách khác để hình dung điều này là có một băng chuyền với diện tích tải nở cuối, từ đó phải lấy một quả trước khi quả mới đến. Bất kỳ trái cây còn sót lại và một túi không đầy đủ ở cuối được loại bỏ.

Hình 1: Nhà máy đóng gói trái cây

Đầu vào

  • Danh sách / mảng trọng lượng của trái cây trong hàng đợi (số nguyên dương)
  • Tổng trọng lượng tối thiểu cho túi (số nguyên dương)
  • Lookahead n(số nguyên dương)

Đầu ra

Thuật toán của bạn sẽ trả về cho tất cả các túi trọng lượng của các loại trái cây trong đó, bằng bất kỳ phương tiện nào thuận tiện cho bạn và ngôn ngữ của bạn, là stdin hoặc giá trị trả về hoặc thứ gì khác. Bạn sẽ có thể chạy chương trình và tính điểm của bạn trong một phút trên máy tính của bạn.

Thí dụ

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]

Chấm điểm

Thuật toán của bạn sẽ được kiểm tra trên sáu lần chạy trên một lô 10000 quả cam tôi đã chuẩn bị cho bạn, trên các bảng tra cứu từ 2 đến 7, bao gồm cả hai đầu. Bạn sẽ đóng gói chúng vào túi có trọng lượng ít nhất 1000 đơn vị. Cam thường được phân phối với trọng lượng trung bình là 170 và độ lệch chuẩn là 13, nếu đó là bất kỳ trợ giúp.

Điểm của bạn sẽ là tổng số túi trong sáu lần chạy. Điểm cao nhất sẽ thắng. Sơ hở tiêu chuẩn là không được phép.

Ví dụ đơn giản triển khai và thử nghiệm bộ nồi hơi trong Haskell


7
Thôi nào mọi người, tôi nghĩ vẫn còn một số thuật toán trái cây treo thấp vẫn đang chờ được chọn hái
Angs

2
Chương trình có thể mã hóa trọng lượng trung bình / phân phối? (giả sử rằng nó hoạt động tốt như nhau trên các lô tương tự, tất nhiên mã hóa mọi thứ đều không hợp lệ vì nó phá hủy mục đích của cái nhìn hạn chế)
user202729

@ user202729: Có, họ có thể.
Angs

Và tất cả mọi thứ đều là một lỗ hổng tiêu chuẩn bị cấm .
Angs

Tôi không thể nhìn thấy cái nhìn là gì
l4m2

Câu trả lời:


8

Túi Python 3, 9964 9981

Ý tưởng của giải pháp này tương tự như của Jonathan, JayCe và fortraan, nhưng với chức năng ghi điểm =)

Giải pháp này nối thêm các tập hợp con tốt nhất của khu vực nhìn vào tích lũy vào score.

score cung cấp một đơn đặt hàng trên các tập hợp con bằng cách sử dụng sơ đồ sau:

  • Một tập hợp con hoàn thành một túi tốt hơn một tập hợp không
  • Một tập hợp con hoàn thành một túi tốt hơn một tập hợp khác nếu nó có trọng lượng dư thừa ít hơn
  • Một tập hợp con không hoàn thành một túi tốt hơn một tập hợp khác nếu ý nghĩa của nó gần với những gì được dự kiến ​​sẽ ở trong một túi

expected_mean cố gắng dự đoán phần còn lại của các giá trị sẽ trông như thế nào (giả sử lựa chọn của chúng là tối ưu).

CẬP NHẬT :

Đây là một quan sát khác: bạn có thể đặt bất kỳ cam nào từ tập hợp con tốt nhất vào túi mà không làm ảnh hưởng đến hiệu suất của thuật toán. Di chuyển bất kỳ phần nào của nó vẫn cho phép di chuyển phần còn lại sau đó và phần còn lại vẫn là lựa chọn tốt nhất (không có cam mới) nếu việc ghi điểm là đúng. Hơn nữa, cách này có cơ hội để tự động cải thiện bộ ứng cử viên để bỏ vào túi bằng cách nhìn thấy nhiều cam hơn trước khi đổ đầy túi. Và bạn muốn biết càng nhiều thông tin càng tốt, vì vậy không có lý do gì để di chuyển nhiều hơn một quả cam vào túi bất cứ lúc nào.

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

Thử nó!


Rất đẹp! Nó được 1672 với cái nhìn 7, chưa bao giờ nhìn thấy một cái cao như vậy.
Angs

(có vẻ như đối số thứ hai cho powersethàm của bạn là dư thừa trong trường hợp này vì len(list_)dù sao nó cũng bằng ?)
user202729

@user Tôi đã thử nghiệm với tham số này trong phiên bản trước. Có thể sẽ xóa nó sau
Alex

1
Chúc mừng bạn đã khám phá sự kết hợp mạnh mẽ của yếu tố duy nhất tốt nhất trong tập hợp con tốt nhất và cũng có điểm số cao nhất! Tiền thưởng là của bạn.
Angs

Một đơn giản hơn expected_mean(w)mang lại kết quả tốt:return (w+2) / max(1, round((w+2) / mean))
Angs

10

Túi Python 3 , 9796

Dựa trên câu trả lời của 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

Điều này phụ thuộc vào powerset từ sách dạy nấu ăn của itertool. Đầu tiên tìm tập hợp con tối ưu của bộ đệm dựa trên việc giảm thiểu chênh lệch so với trọng lượng mục tiêu cho tất cả các tập hợp con, sau đó chọn một phần tử ra khỏi tập hợp con này dựa trên cùng một tiêu chí. Nếu không có tập hợp con tối ưu chọn từ toàn bộ bộ đệm.


Chào mừng đến với PPCG!
Martin Ender

@MartinEnder Cảm ơn Martin vì upvote chào đón :)
JayCe

1
À đúng rồi, tôi đã bỏ lỡ một mẹo ở đó ... Tôi không có vấn đề gì với câu trả lời này!
Jonathan Allan

1
@Jonathan ALLan Cảm ơn Jonathan Tôi đã rút ngắn câu trả lời của tôi để ghi nhận bạn mà không cần tất cả lời xin lỗi. Điều này có thể được cải thiện bằng cách sử dụng thực tế là phân phối (170,13) bình thường - tôi chắc chắn xác suất có được trái tốt hơn trong (các) lần chạy tiếp theo có thể được sử dụng.
JayCe

@JayCe nghe có vẻ nguy hiểm gần với ngụy biện của con bạc.
qwr

6

C ++ 17, 9961,58 (trung bình trên một số hạt ngẫu nhiên)

(cuộn xuống để giải thích nếu bạn không biết 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';
}

// Sự thật thú vị: ban đầu tôi đã viết <cho >đây và tự hỏi
// tại sao số lượng túi thậm chí còn ít hơn so với
// thuật toán ngẫu nhiên

(nếu <được sử dụng về cơ bản, thuật toán sẽ cố gắng giảm thiểu số lượng túi)

Lấy cảm hứng từ câu trả lời này .

Liên kết TIO cho 250 lần lặp lại: Hãy thử trực tuyến!


Xác định một hàm (thực ra nó trông giống như một hàm, nó là một cấu trúc) pick_orange , vector<int> weightsvới trọng lượng của cam và int remaintrọng lượng còn lại của túi, trả về chỉ số của màu cam nên chọn.

Thuật toán:

500thời gian lặp lại {
tạo ra cam ngẫu nhiên (giả) (phân phối bình thường với giá trị trung bình 170 và stddev 13) cho đến khi có N_NEW_ORANGES=7cam
chọn bất kỳ tập hợp con nào có tổng nhỏ nhất và không nhỏ hơn remain(hàm backtrackthực hiện)
đánh dấu tất cả các cam trong tập con đó là tốt
}

tính trung bình số lần cam được đánh dấu là tốt của cam (thật) với trọng lượng bằng nhau
trả lại cam tốt nhất


Có 3 hằng số được mã hóa cứng trong chương trình không thể được suy ra từ vấn đề:

  • Hạt giống ngẫu nhiên (điều này không quan trọng)
  • N_NEW_ORANGES(độ dài dự đoán). Việc tăng này làm cho chương trình chạy dài hơn theo cấp số nhân (vì quay lui)
  • số lần lặp lại. Tăng điều này làm cho chương trình chạy tuyến tính lâu hơn.

Được. Thay đổi hạt giống thành hạt giống cho câu trả lời tốt nhất có vẻ như tối ưu hóa cho trường hợp thử nghiệm, vì vậy bạn nên lấy trung bình của một số ít, giả sử 10 hạt giống khác nhau làm điểm số của bạn. Bạn có thể đăng một liên kết TIO lên một phiên bản ít lặp lại để giảm thời gian chạy không?
Angs

Cuối cùng đã có nó để biên dịch sau khi nhận được một gcc mới hơn. Trên 50 lần chạy với các hạt ngẫu nhiên, nó có trung bình 9961,58. Vẫn rất ấn tượng. Mặc dù tôi tự hỏi - thuật toán của bạn về cơ bản tự đào tạo lại trên mỗi túi, có tập hợp cố định các giá trị tốt nhất có thể được ghi nhớ không?
Angs

@Angs Tôi không nghĩ có một cách có thể sử dụng ghi nhớ để giúp đỡ trong trường hợp này. Bất kỳ ý tưởng?
dùng202729

Hệ điều hành của tôi đi kèm với gcc 5.4.0, nó có một số vấn đề với invalid use of template-name ‘std::normal_distribution’. Không có vấn đề với gcc 7.1.0.
Angs

4

Con trăn 2Túi , 9756

Chúng ta hãy lăn cam ...

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

Hãy thử trực tuyến!

Luôn chọn trái cây từ bộ đệm giúp giảm thiểu sự khác biệt tuyệt đối của trọng lượng mới và trọng lượng mục tiêu.


4

Túi Python 3, 9806

Dựa trên câu trả lời của Jonathan và 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

Hãy thử trực tuyến!

Làm thế nào nó hoạt động

Nói rằng túi có 900 đơn vị trong đó, và có 2 trái cây có sẵn: một trái 99 đơn vị và một trái 101 đơn vị. Nếu trái 99 đơn vị gần với danh sách bắt đầu, thìmin sẽ chọn nó thay vì 101. Nếu điều này xảy ra, bây giờ chúng ta sẽ cần một trái cây khác để đáp ứng 1 đơn vị còn lại cần thiết. Tôi đã thay đổi chương trình để ủng hộ trái cây có giá trị cao hơn trong những trường hợp này.

Nó thực hiện điều này bằng cách sắp xếp và sau đó đảo ngược danh sách lookahead trước khi đặt quyền.


4

PHP, 9975 túi

  • Nếu có thể hãy cho 5 quả cam
  • Khi bắt đầu túi chọn giá trị cực cao, cân bằng sau
  • Nếu có thể hãy điền vào túi ngay lập tức
  • Cố gắng giữ trọng lượng túi gần với đường cong ước tính (n * 200 cho 5 túi, n * 167 cho 6 túi, v.v.)

dài nhất trong số tất cả các bài nộp nhưng nên đọc

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 Tổng cộng: 9975

Thử nó


Tốt đẹp! Điều đáng ngạc nhiên đối với tôi là nó sử dụng số lượng vật phẩm hiện tại - tôi tự hỏi liệu có thể được tính đến hơn không. Rốt cuộc, không có vấn đề gì nếu có 3 mục có trọng số 120 mỗi hoặc 3 mục có trọng số 160 mỗi mục.
Angs

@Angs có lẽ là có thể. Số lượng vật phẩm hiện tại xuất hiện như một lối tắt đơn giản cho ý tưởng "Này, đôi khi có thể làm được 5 túi vật phẩm" và tôi đã điều chỉnh để có được 5 túi vật phẩm hoạt động. Với cải tiến thời gian miễn phí sẽ đến :)
mleko

3

Túi Python 3, 9855 9928 9947 9956 9964

Dựa trên mã khởi động của Jonathan Allan, nhưng không thể đọc được.

Ý tưởng: Kể từ 1000/170 = 5,88, chúng tôi cố gắng chọn các loại trái cây gần 1000/6 (tôi loay hoay với các hằng số ma thuật). Tuy nhiên, nếu trái cây cuối cùng trong túi có thể giảm thiểu chất thải, chúng tôi sử dụng thay thế.

Giải pháp này có mục tiêu tổng túi cho mỗi trái cây được thêm vào. Có lẽ tôi sẽ dừng ở đây. Tôi đã sử dụng Nelder-Mead để tìm targetsmảng của mình :

[  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 túi

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

Các túi 9947 chương trình đặc biệt đơn giản:

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
Tốt Btw, chỉ cần chọn mục cuối cùng để giảm thiểu chất thải là khá mạnh và cung cấp cho 9862 túi.
Angs

Làm thế nào bạn đến với những người targets? Đào tạo về dữ liệu ngẫu nhiên?
Alex

1
@Alex Tôi đã nói như vậy: Phương pháp Nelder-Mead (với các túi âm là hàm mất)
qwr

2

Ruby , 9967 túi

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

Hãy thử trực tuyến!

Nếu bạn có đủ trọng lượng để lấp đầy túi, hãy tìm tập hợp con nhẹ nhất có thể lấp đầy túi và sử dụng màu cam nhạt nhất của tập hợp con đó. Nếu không, hãy lấy trọng lượng còn lại càng gần càng tốt với bội số của 170.


2

Vợt / Lược đồ, 9880 túi

Để quyết định nên thêm miếng trái cây nào vào túi, so sánh trọng lượng túi tối ưu với trọng lượng của túi với miếng trái cây bổ sung. Nếu đó là trọng lượng tối ưu, sử dụng nó. Nếu nó thừa cân, giảm thiểu số lượng vượt quá. Nếu nó thiếu cân, hãy giảm thiểu lượng dư thừa sau khi cố gắng để lại một khoảng cách tối ưu.

;; 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 túi

Đây là nỗ lực đầu tiên của tôi:

  • Nó tham lam lấp đầy một cái túi với một lô khi có thể,
  • hoặc xả tất cả cam vào túi khi không thể.
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]

Hãy thử trực tuyến!


1

Haskell , 9981 túi

The AngsJonathan AllanJayCefortraanAlexRoman Czyborra pyegolf có thể quay trở lại Haskell để có thêm sự thuần khiết toán học dọc theo cùng một dòng suy nghĩ chính

  • chỉ một quả cam bị sa thải trước khi một quả cam mới được thêm vào
  • thiên vị thích trái cây hiệu quả ((<miss)==False<True )
  • bias thích trái cây gần với số nguyên có khả năng nhất
  • cho số nguyên đó đảo ngược
    (m-n)/sqrt(n)==(n+1-m)/sqrt(n+1) <==> n=sqrt(m^2-1/4)-1/2 từ https://en.wikipedia.org/wiki/Sum_of_normally_distribution_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)

dày dạn với một số vô nghĩa không cần thiết

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]

Hãy thử trực tuyến!

mà không năng suất tăng số khác nhau trên đỉnh 9981 lưới bao cam thu hoạch vện khi 10k011 túi của tôi Packer grabbing cam không thích hợp sao lưu ra khỏi túi khép kín đã bị loại bởi user69850 trong tính cách user202729Jo VuaOVS hencefore bounty xứng đáng đi đến Alex

GIMME BOUNTY!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.