โรงงานบรรจุผลไม้


21

ภารกิจของคุณคือการสร้างอัลกอริทึม (โปรแกรมหรือฟังก์ชั่น) ที่สามารถเพิ่มประสิทธิภาพการบรรจุผลไม้จากสายพานลำเลียงลงในถุงเพื่อส่งไปยังผู้ค้าปลีกเพื่อปรับให้เหมาะสมกับจำนวนถุงมากที่สุด

กระเป๋าแต่ละใบจะต้องมีน้ำหนักอย่างน้อยจำนวนหนึ่ง แต่ส่วนเกินใด ๆ จะสูญเสียกำไรเนื่องจากน้ำหนักนั้นสามารถใช้เพื่อเติมถุงอื่นได้ เครื่องบรรจุถุงของคุณมีnผลไม้ที่มองจากคิวเสมอและอาจเลือกที่จะเพิ่มnผลไม้ใด ๆ เหล่านี้ลงในถุง (ถุงเดียว) ที่กำลังดำเนินการ ไม่สามารถดูเกินnองค์ประกอบแรกในคิว โปรแกรมรู้เสมอว่ามีน้ำหนักอยู่ในกระเป๋าเท่าไหร่

อีกวิธีในการมองเห็นสิ่งนี้คือการมีสายพานลำเลียงที่มีพื้นที่รับน้ำหนักขนาดใหญ่nในตอนท้ายจากจุดที่ผลไม้ต้องถูกนำไปก่อนที่ผลไม้ใหม่จะมาถึง ผลไม้ที่เหลือและถุงที่ไม่เต็มในตอนท้ายจะถูกทิ้ง

รูปที่ 1: โรงงานบรรจุผลไม้

ปัจจัยการผลิต

  • รายการ / อาร์เรย์ของน้ำหนักผลไม้ในคิว (จำนวนเต็มบวก)
  • น้ำหนักรวมขั้นต่ำสำหรับถุง (จำนวนเต็มบวก)
  • Lookahead n(จำนวนเต็มบวก)

เอาท์พุต

อัลกอริทึมของคุณควรกลับไปหาน้ำหนักของผลไม้ในถุงด้วยวิธีใดก็ตามที่สะดวกสำหรับคุณและภาษาของคุณไม่ว่าจะเป็น stdin หรือค่าตอบแทนหรืออย่างอื่น คุณควรจะสามารถรันโปรแกรมและคำนวณคะแนนในหนึ่งนาทีบนคอมพิวเตอร์ของคุณ

ตัวอย่าง

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]

เกณฑ์การให้คะแนน

อัลกอริทึมของคุณจะได้รับการทดสอบในการวิ่งหกครั้งในชุดส้ม 10,000 ชุดที่ฉันเตรียมไว้สำหรับคุณโดยมีตั้งแต่ 2 ถึง 7 รวมทั้งสองด้าน คุณจะบรรจุลงในถุงที่มีน้ำหนักอย่างน้อย 1,000 หน่วย โดยปกติแล้วส้มจะมีน้ำหนักเฉลี่ย 170 และค่าเบี่ยงเบนมาตรฐานเท่ากับ 13 ถ้าเป็นประโยชน์

คะแนนของคุณจะเป็นผลรวมของจำนวนถุงจากหกวิ่ง คะแนนสูงสุดชนะ ช่องโหว่มาตรฐานไม่ได้รับอนุญาต

ตัวอย่างการใช้งานที่ง่ายและชุดทดสอบสำเร็จรูปใน Haskell


7
มาที่คนฉันคิดว่ายังมีอัลกอริทึมผลไม้แขวนต่ำยังคงรอที่จะเลือก ...
อัง

2
โปรแกรมสามารถเข้ารหัสค่าน้ำหนัก / การกระจายได้หรือไม่ (สมมติว่ามันทำงานได้ดีพอ ๆ กันบนแบทช์ที่คล้ายกันแน่นอนว่า
ฮาร์ดโค้ด

@ user202729: ใช่พวกเขาทำได้
อัง

และฮาร์ดโค้ดทุกอย่างก็เป็นช่องโหว่มาตรฐานที่ต้องห้ามอยู่ดี
อัง

ฉันไม่เห็นว่า lookhead คืออะไร
l4m2

คำตอบ:


8

Python 3, 9964 9981 ถุง

แนวคิดของวิธีนี้คล้ายกับของ Jonathan, JayCe และ fortraan แต่มีฟังก์ชั่นให้คะแนน =)

การแก้ปัญหานี้ผนวกย่อยที่ดีที่สุดของพื้นที่ lookahead accoring 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 กับ lookahead จาก 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!
Martin Ender

@MartinEnder ขอบคุณ Martin สำหรับการโหวตที่ให้การต้อนรับ :)
JayCe

1
อ่าใช่ฉันพลาดเคล็ดลับที่นั่น ... ฉันไม่มีปัญหากับคำตอบนี้!
Jonathan Allan

1
@JanathanAllan ขอบคุณ Jonathan ฉันได้ตอบสั้น ๆ ว่าให้เครดิตคุณโดยไม่ต้องขอโทษ สิ่งนี้สามารถปรับปรุงได้โดยการใช้ความจริงที่ว่ามันคือการแจกแจงแบบปกติ (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ไม่ว่า)
ทำเครื่องหมายส้มทั้งหมดในเซตที่เป็นสิ่งที่ดี
}

หาค่าเฉลี่ยจำนวนครั้งที่ส้มถูกทำเครื่องหมายว่าดีของส้ม (ของจริง) ที่มีน้ำหนักเท่ากันจะให้
ผลส้มที่ดีที่สุด


มีค่าคงที่ hardcoded 3 ค่าในโปรแกรมที่ไม่สามารถอนุมานได้จากปัญหา:

  • เมล็ดสุ่ม (นี่ไม่สำคัญ)
  • N_NEW_ORANGES(ความยาวการทำนาย) การเพิ่มสิ่งนี้ทำให้โปรแกรมรันได้นานขึ้นแทน (เนื่องจาก backtrack)
  • จำนวนการทำซ้ำ การเพิ่มสิ่งนี้ทำให้โปรแกรมทำงานได้เป็นแนวยาวขึ้น

ตกลง. การเปลี่ยนเมล็ดเป็นคำตอบที่ดีที่สุดดูเหมือนว่าจะปรับให้เหมาะกับกรณีทดสอบดังนั้นคุณควรใช้ค่าเฉลี่ยของจำนวนเล็กน้อยพูด 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
อัง

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 หน่วยใกล้กับจุดเริ่มต้นของรายการ lookahead จากนั้นminจะเลือกผลไม้แทน 101 หากเกิดเหตุการณ์นี้เราจะต้องมีผลไม้อีกหนึ่งชนิดเพื่อเติมเต็ม 1 หน่วยที่เหลือที่จำเป็น ฉันเปลี่ยนโปรแกรมเพื่อประโยชน์ผลไม้ที่มีมูลค่าสูงกว่าในกรณีเหล่านี้

มันทำได้โดยการจัดเรียงแล้วย้อนกลับรายการ lookahead ก่อนการตั้งค่าพลังงาน


4

PHP, 9975 ถุง

  • ถ้าเป็นไปได้ไป 5 ส้ม
  • เมื่อถุงเริ่มต้นเลือกค่าที่มากที่สุดให้สมดุลในภายหลัง
  • ถ้าเป็นไปได้เติมถุงทันที
  • พยายามให้น้ำหนักถุงใกล้เคียงกับเส้นโค้งโดยประมาณ (n * 200 สำหรับ 5bag, n * 167 สำหรับ 6bag ฯลฯ )

ยาวที่สุดจากการส่งทั้งหมด แต่ควรอ่านได้

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

Python 3, 9855 9928 9947 9956 9964 ถุง

อ้างอิงจากรหัสเริ่มต้นของ Jonathan Allan แต่ไม่สามารถอ่านได้

แนวคิด: ตั้งแต่ 1,000/170 = 5.88 เราพยายามเลือกผลไม้ใกล้เคียงกับ 1,000/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))

9,947 ถุงโปรแกรมเป็นเรื่องง่ายโดยเฉพาะอย่างยิ่ง:

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
@ Alex ผมกล่าวดังนั้น: วิธี Nelder-มธุรส (กับถุงเชิงลบเป็นฟังก์ชั่นการสูญเสีย)
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

แร็กเกต / Scheme, 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โจนาธานอัลลันJaycefortraanอเล็กซ์โรมัน Czyborra codegolf หลามสามารถ cyclize กลับไป 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-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ในบุคคลuser202729Jo King OVS hencefore โปรดปรานสมควรจะได้เดินไปที่อเล็กซ์

GIMME BOUNTY!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.