การแบ่งองค์ประกอบของรายการอย่างยุติธรรม


12

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

ตัวอย่างเช่น: [5, 6, 2, 10, 2, 3, 4]ควรกลับมา([6, 5, 3, 2], [10, 4, 2])

ฉันต้องการทราบอัลกอริทึมในการแก้ปัญหานี้ โปรดทราบว่าฉันกำลังเรียนหลักสูตรการเขียนโปรแกรมเบื้องต้นออนไลน์ดังนั้นอัลกอริธึมที่เรียบง่ายจึงน่าชื่นชม

ฉันใช้รหัสต่อไปนี้ แต่ด้วยเหตุผลบางอย่างตัวตรวจสอบรหัสออนไลน์บอกว่าไม่ถูกต้อง

def partition(ratings):
    set1 = []
    set2 =[]
    sum_1 = 0
    sum_2 = 0
    for n in sorted(ratings, reverse=True):
        if sum_1 < sum_2:
            set1.append(n)
            sum_1 = sum_1 + n
        else:
            set2.append(n)
            sum_2 = sum_2 + n
    return(set1, set2)

อัปเดต:ฉันติดต่อผู้สอนและได้รับคำแนะนำว่าฉันควรกำหนดฟังก์ชั่น "ตัวช่วย" อื่นภายในฟังก์ชันเพื่อตรวจสอบชุดค่าผสมที่แตกต่างกันทั้งหมดจากนั้นฉันต้องตรวจสอบความแตกต่างขั้นต่ำ


2
Google "ปัญหาผลรวมย่อย"
John Coleman

@JohnColeman ขอบคุณสำหรับคำแนะนำของคุณ คุณช่วยแนะนำฉันในทิศทางที่ถูกต้องเกี่ยวกับวิธีใช้จำนวนเงินส่วนย่อยเพื่อแก้ปัญหาของฉันได้อย่างไร
EddieEC

6
มากยิ่งขึ้นโดยเฉพาะอย่างยิ่งคุณมีกรณีพิเศษของปัญหาเซตผลรวมที่เรียกว่าปัญหาพาร์ทิชัน บทความ Wikipedia เกี่ยวกับมันกล่าวถึงอัลกอริทึม
จอห์นโคลแมน


1
ขอบคุณทั้งคู่! ฉันขอขอบคุณความช่วยเหลืออย่างจริงใจ!
EddieEC

คำตอบ:


4

หมายเหตุ: แก้ไขเพื่อจัดการกรณีและปัญหาได้ดีขึ้นเมื่อผลรวมของตัวเลขทั้งหมดเป็นเลขคี่

การย้อนรอยเป็นไปได้สำหรับปัญหานี้

มันช่วยให้ตรวจสอบความเป็นไปได้ทั้งหมดซ้ำโดยไม่จำเป็นต้องใช้หน่วยความจำจำนวนมาก

มันจะหยุดทันทีที่พบทางออกที่ดีที่สุด: sum = 0ซึ่งsumเป็นความแตกต่างระหว่างผลรวมขององค์ประกอบของชุด A และผลรวมขององค์ประกอบของชุด B. แก้ไข: หยุดทันทีsum < 2เพื่อจัดการกรณีเมื่อผลรวมของตัวเลขทั้งหมด เป็นเลขคี่นั่นคือสอดคล้องกับความแตกต่างขั้นต่ำ 1 ถ้าผลรวมทั่วโลกนี้เป็นเลขคู่ความแตกต่างขั้นต่ำไม่สามารถเท่ากับ 1

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

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

นี่คือรหัสเทียม

การเริ่มต้น:

  • องค์ประกอบการจัดเรียง a[]
  • คำนวณผลรวมขององค์ประกอบที่เหลืออยู่: sum_back[i] = sum_back[i+1] + a[i];
  • ตั้งค่า "ความแตกต่าง" ขั้นต่ำเป็นค่าสูงสุด: min_diff = sum_back[0];
  • ใส่a[0]ใน A -> ดัชนีiขององค์ประกอบที่ตรวจสอบตั้งเป็น 1
  • Set up_down = true;: บูลีนนี้ระบุว่าขณะนี้เรากำลังก้าวไปข้างหน้า (จริง) หรือย้อนกลับ (เท็จ)

ในขณะที่วง:

  • ถ้า (up_down): ส่งต่อ

    • ทดสอบการคลอดก่อนกำหนดด้วยความช่วยเหลือของ sum_back
    • เลือกค่าที่เป็นไปได้มากที่สุดปรับsumตามตัวเลือกนี้
    • if (i == n-1): LEAF -> ทดสอบว่ามีการปรับปรุงค่าที่เหมาะสมและส่งคืนหรือไม่ถ้าค่าใหม่เท่ากับ 0 (แก้ไข:) if (... < 2); ย้อนกลับ
    • ถ้าไม่อยู่ในใบไม้: เดินหน้าต่อไป
  • ถ้า (! updown): ย้อนกลับ

    • ถ้าเรามาถึง i == 0 : คืน
    • ถ้าเป็นการเดินที่สองในโหนดนี้: เลือกค่าที่สองขึ้นไป
    • อื่น ๆ : ลงไป
    • ในทั้งสองกรณี: คำนวณsumค่าใหม่

นี่คือรหัสใน C ++ (ขออภัยไม่รู้ Python)

#include    <iostream>
#include    <vector>
#include    <algorithm>
#include    <tuple>

std::tuple<int, std::vector<int>> partition(std::vector<int> &a) {
    int n = a.size();
    std::vector<int> parti (n, -1);     // current partition studies
    std::vector<int> parti_opt (n, 0);  // optimal partition
    std::vector<int> sum_back (n, 0);   // sum of remaining elements
    std::vector<int> n_poss (n, 0);     // number of possibilities already examined at position i

    sum_back[n-1] = a[n-1];
    for (int i = n-2; i >= 0; --i) {
        sum_back[i] = sum_back[i+1] + a[i];
    }

    std::sort(a.begin(), a.end(), std::greater<int>());
    parti[0] = 0;       // a[0] in A always !
    int sum = a[0];     // current sum

    int i = 1;          // index of the element being examined (we force a[0] to be in A !)
    int min_diff = sum_back[0];
    bool up_down = true;

    while (true) {          // UP
        if (up_down) {
            if (std::abs(sum) > sum_back[i] + min_diff) {  //premature abandon
                i--;
                up_down = false;
                continue;
            }
            n_poss[i] = 1;
            if (sum > 0) {
                sum -= a[i];
                parti[i] = 1;
            } else {
                sum += a[i];
                parti[i] = 0;
            }

            if (i == (n-1)) {           // leaf
                if (std::abs(sum) < min_diff) {
                    min_diff = std::abs(sum);
                    parti_opt = parti;
                    if (min_diff < 2) return std::make_tuple (min_diff, parti_opt);   // EDIT: if (... < 2) instead of (... == 0)
                }
                up_down = false;
                i--;
            } else {
                i++;        
            }

        } else {            // DOWN
            if (i == 0) break;
            if (n_poss[i] == 2) {
                if (parti[i]) sum += a[i];
                else sum -= a[i];
                //parti[i] = 0;
                i--;
            } else {
                n_poss[i] = 2;
                parti[i] = 1 - parti[i];
                if (parti[i]) sum -= 2*a[i];
                else sum += 2*a[i];
                i++;
                up_down = true;
            }
        }
    }
    return std::make_tuple (min_diff, parti_opt);
}

int main () {
    std::vector<int> a = {5, 6, 2, 10, 2, 3, 4, 13, 17, 38, 42};
    int diff;
    std::vector<int> parti;
    std::tie (diff, parti) = partition (a);

    std::cout << "Difference = " << diff << "\n";

    std::cout << "set A: ";
    for (int i = 0; i < a.size(); ++i) {
        if (parti[i] == 0) std::cout << a[i] << " ";
    }
    std::cout << "\n";

    std::cout << "set B: ";
    for (int i = 0; i < a.size(); ++i) {
        if (parti[i] == 1) std::cout << a[i] << " ";
    }
    std::cout << "\n";
}

ปัญหาเดียวที่นี่ไม่ใช่ผลรวมที่ดีที่สุดจะเป็น 0 เสมอไปขอบคุณที่อธิบายได้ค่อนข้างดีเพราะฉันอ่าน C ++ ไม่ได้
EddieEC

หากผลรวมที่ดีที่สุดไม่เท่ากับ 0 รหัสจะพิจารณาความเป็นไปได้ทั้งหมดโดยจดจำวิธีที่ดีที่สุด เส้นทางที่ไม่ได้ตรวจสอบคือเส้นทางที่เรามั่นใจว่าเส้นทางเหล่านั้นไม่เหมาะสม if I == 0สอดคล้องกับการกลับมาครั้งนี้ ฉันทดสอบโดยแทนที่ 10 โดย 11 ในตัวอย่างของคุณ
Damien

3

ฉันคิดว่าคุณควรทำแบบฝึกหัดต่อไปด้วยตัวเองมิฉะนั้นคุณจะไม่เรียนรู้มากนัก สำหรับเรื่องนี้นี่เป็นวิธีแก้ปัญหาที่พยายามใช้คำแนะนำจากอาจารย์ของคุณ:

def partition(ratings):

    def split(lst, bits):
        ret = ([], [])
        for i, item in enumerate(lst):
            ret[(bits >> i) & 1].append(item)
        return ret

    target = sum(ratings) // 2
    best_distance = target
    best_split = ([], [])
    for bits in range(0, 1 << len(ratings)):
        parts = split(ratings, bits)
        distance = abs(sum(parts[0]) - target)
        if best_distance > distance:
            best_distance = distance
            best_split = parts
    return best_split

ratings = [5, 6, 2, 10, 2, 3, 4]
print(ratings)
print(partition(ratings))

เอาท์พุท:

[5, 6, 2, 10, 2, 3, 4]
([5, 2, 2, 3, 4], [6, 10])

โปรดทราบว่าผลลัพธ์นี้แตกต่างจากที่คุณต้องการ แต่ทั้งคู่ถูกต้อง

อัลกอริทึมนี้ขึ้นอยู่กับข้อเท็จจริงที่ว่าในการเลือกเซตย่อยที่เป็นไปได้ทั้งหมดของชุดที่กำหนดด้วยองค์ประกอบ N คุณสามารถสร้างจำนวนเต็มทั้งหมดด้วย N บิตและเลือกรายการ I-th ขึ้นอยู่กับค่าของบิต I-th ฉันปล่อยให้คุณเพิ่มสองสามบรรทัดเพื่อหยุดทันทีที่best_distanceเป็นศูนย์ (เพราะมันไม่ได้ดีขึ้นแน่นอน)

บิตบนบิต (โปรดทราบว่า0bเป็นคำนำหน้าสำหรับหมายเลขไบนารีใน Python):

หมายเลขไบนารี: 0b0111001 == 0·2⁶+1·2⁵+1·2⁴+1·2³+0·2²+0·2¹+1·2⁰ == 57

เลื่อนจาก 1 ไปทางขวา: 0b0111001 >> 1 == 0b011100 == 28

เลื่อนไปทางซ้าย 1: 0b0111001 << 1 == 0b01110010 == 114

เลื่อนจาก 4 ไปทางขวา: 0b0111001 >> 4 == 0b011 == 3

Bitwise &(และ):0b00110 & 0b10101 == 0b00100

วิธีตรวจสอบว่าบิตที่ 5 (ดัชนี 4) เป็น 1: (0b0111001 >> 4) & 1 == 0b011 & 1 == 1

หนึ่งตามด้วยศูนย์ 7: 1 << 7 == 0b10000000

7 รายการ: (1 << 7) - 1 == 0b10000000 - 1 == 0b1111111

ทั้งหมดรวมกัน 3 บิต: 0b000==0, 0b001==1, 0b010==2, 0b011==3, 0b100==4, 0b101==5, 0b110==6, 0b111==7(ทราบว่า0b111 + 1 == 0b1000 == 1 << 3)


ขอบคุณมาก! คุณช่วยอธิบายสิ่งที่คุณทำได้ไหม นอกจากนี้การใช้ << คืออะไร ตัวอย่างสิ่งเหล่านี้ฉันไม่เคยเรียนรู้วิธีการทำ แต่ฉันรู้ว่าฉันจำเป็นต้องสร้างความเป็นไปได้ทั้งหมดและคืนสิ่งที่แตกต่างให้เกรงว่า!
EddieEC

ฉันเพิ่ม microlesson บนเลขฐานสองและการทำงานบิต
Walter Tross

คุณอาจไม่ควรกำหนดฟังก์ชั่นภายในฟังก์ชั่นอื่น
AMC

1
@ AlexanderCécile มันขึ้นอยู่กับ ในกรณีนี้ฉันคิดว่ามันเป็นที่ยอมรับและปรับปรุงความสะอาดและต่อไปมันเป็นสิ่งที่ OP ได้รับการแนะนำจากอาจารย์ของเขา (ดูการอัปเดตในคำถามของเขา)
วอลเตอร์ Tross

1
@MiniMax การเรียงสับเปลี่ยนของรายการ N คือ N !, แต่ชุดย่อยของพวกเขาคือ 2 ^ N: รายการแรกสามารถอยู่ในชุดย่อยหรือไม่: 2 ความเป็นไปได้; รายการที่สองสามารถอยู่ในเซตย่อยหรือไม่: × 2; รายการที่สาม ... และอื่น ๆ N ครั้ง
วอลเตอร์ Tross

1

อัลกอริทึมต่อไปนี้ทำสิ่งนี้:

  • เรียงลำดับรายการ
  • ทำให้สมาชิกอยู่ในรายการaแปลก ๆ ในรายการbเพื่อเริ่มต้น
  • สุ่มย้ายและสลับรายการระหว่างaและbหากการเปลี่ยนแปลงนั้นดีขึ้น

ฉันได้เพิ่มคำแถลงการพิมพ์เพื่อแสดงความคืบหน้าในรายการตัวอย่างของคุณ:

# -*- coding: utf-8 -*-
"""
Created on Fri Dec  6 18:10:07 2019

@author: Paddy3118
"""

from random import shuffle, random, randint

#%%
items = [5, 6, 2, 10, 2, 3, 4]

def eq(a, b):
    "Equal enough"
    return int(abs(a - b)) == 0

def fair_partition(items, jiggles=100):
    target = sum(items) / 2
    print(f"  Target sum: {target}")
    srt = sorted(items)
    a = srt[::2]    # every even
    b = srt[1::2]   # every odd
    asum = sum(a)
    bsum = sum(b)
    n = 0
    while n < jiggles and not eq(asum, target):
        n += 1
        if random() <0.5:
            # move from a to b?
            if random() <0.5:
                a, b, asum, bsum = b, a, bsum, asum     # Switch
            shuffle(a)
            trial = a[0]
            if abs(target - (bsum + trial)) < abs(target - bsum):  # closer
                b.append(a.pop(0))
                asum -= trial
                bsum += trial
                print(f"  Jiggle {n:2}: Delta after Move: {abs(target - asum)}")
        else:
            # swap between a and b?
            apos = randint(0, len(a) - 1)
            bpos = randint(0, len(b) - 1)
            trya, tryb = a[apos], b[bpos]
            if abs(target - (bsum + trya - tryb)) < abs(target - bsum):  # closer
                b.append(trya)  # adds to end
                b.pop(bpos)     # remove what is swapped
                a.append(tryb)
                a.pop(apos)
                asum += tryb - trya
                bsum += trya - tryb
                print(f"  Jiggle {n:2}: Delta after Swap: {abs(target - asum)}")
    return sorted(a), sorted(b)

if __name__ == '__main__':
    for _ in range(5):           
        print('\nFinal:', fair_partition(items), '\n')  

เอาท์พุท:

  Target sum: 16.0
  Jiggle  1: Delta after Swap: 2.0
  Jiggle  7: Delta after Swap: 0.0

Final: ([2, 3, 5, 6], [2, 4, 10]) 

  Target sum: 16.0
  Jiggle  4: Delta after Swap: 0.0

Final: ([2, 4, 10], [2, 3, 5, 6]) 

  Target sum: 16.0
  Jiggle  9: Delta after Swap: 3.0
  Jiggle 13: Delta after Move: 2.0
  Jiggle 14: Delta after Swap: 1.0
  Jiggle 21: Delta after Swap: 0.0

Final: ([2, 3, 5, 6], [2, 4, 10]) 

  Target sum: 16.0
  Jiggle  7: Delta after Swap: 3.0
  Jiggle  8: Delta after Move: 1.0
  Jiggle 13: Delta after Swap: 0.0

Final: ([2, 3, 5, 6], [2, 4, 10]) 

  Target sum: 16.0
  Jiggle  5: Delta after Swap: 0.0

Final: ([2, 4, 10], [2, 3, 5, 6]) 

ขอบคุณมาก แต่ฉันควรจะทำโดยไม่ต้องนำเข้าอะไร
EddieEC

1

เนื่องจากฉันรู้ว่าฉันต้องสร้างรายการที่เป็นไปได้ทั้งหมดฉันต้องสร้างฟังก์ชัน "ตัวช่วย" เพื่อช่วยสร้างความเป็นไปได้ทั้งหมด หลังจากทำเช่นนั้นฉันจริงที่จะตรวจสอบความแตกต่างขั้นต่ำและการรวมกันของรายการที่มีความแตกต่างขั้นต่ำนั้นเป็นทางออกที่ต้องการ

ฟังก์ชั่นตัวช่วยจะเรียกซ้ำและตรวจสอบความเป็นไปได้ทั้งหมดของการรวมกันของรายการ

def partition(ratings):

    def helper(ratings, left, right, aux_list, current_index):
        if current_index >= len(ratings):
            aux_list.append((left, right))
            return

        first = ratings[current_index]
        helper(ratings, left + [first], right, aux_list, current_index + 1)
        helper(ratings, left, right + [first], aux_list, current_index + 1)

    #l contains all possible sublists
    l = []
    helper(ratings, [], [], l, 0)
    set1 = []
    set2 = []
    #set mindiff to a large number
    mindiff = 1000
    for sets in l:
        diff = abs(sum(sets[0]) - sum(sets[1]))
        if diff < mindiff:
            mindiff = diff
            set1 = sets[0]
            set2 = sets[1]
    return (set1, set2)

ตัวอย่าง: r = [1, 2, 2, 3, 5, 4, 2, 4, 5, 5, 2]พาร์ทิชันที่ดีที่สุดจะเป็น: มีความแตกต่างของ([1, 2, 2, 3, 5, 4], [2, 4, 5, 5, 2])1

r = [73, 7, 44, 21, 43, 42, 92, 88, 82, 70]พาร์ทิชันที่ดีที่สุดจะเป็น: มีความแตกต่างของ([73, 7, 21, 92, 88], [44, 43, 42, 82, 70])0


1
ตั้งแต่คุณถามฉัน: ทางออกของคุณใช้ได้ถ้าคุณกำลังเรียนรู้ มีเพียงปัญหาเดียวเท่านั้นที่คุณโชคดีไม่ได้เตะก่อนที่ปัญหาอื่น ๆ จะเหมือนกับการแก้ไขปัญหาอื่น ๆ : ใช้พื้นที่แทนเลขชี้กำลัง (O (n2ⁿ)) แต่เวลาเอ็กซ์โปเนนเชียลเริ่มต้นขึ้นเมื่อนานก่อน อย่างไรก็ตามการหลีกเลี่ยงการใช้พื้นที่แบบเอ็กซ์โปเนนเชียลนั้นเป็นเรื่องง่าย
วอลเตอร์ Tross

1

นี่คือตัวอย่างที่ค่อนข้างละเอียดซึ่งมีจุดประสงค์เพื่อการศึกษามากกว่าการแสดง มันแนะนำแนวคิด Python ที่น่าสนใจบางอย่างเช่นความเข้าใจในรายการและเครื่องกำเนิดไฟฟ้ารวมถึงตัวอย่างที่ดีของการเรียกซ้ำในกรณีที่จำเป็นต้องตรวจสอบกรณีที่เหมาะสม ส่วนขยายเช่นเฉพาะทีมที่มีจำนวนผู้เล่นเท่ากันเท่านั้นที่สามารถใช้งานได้ง่ายในแต่ละฟังก์ชั่นที่เหมาะสม

def listFairestWeakTeams(ratings):
    current_best_weak_team_rating = -1
    fairest_weak_teams = []
    for weak_team in recursiveWeakTeamGenerator(ratings):
        weak_team_rating = teamRating(weak_team, ratings)
        if weak_team_rating > current_best_weak_team_rating:
            fairest_weak_teams = []
            current_best_weak_team_rating = weak_team_rating
        if weak_team_rating == current_best_weak_team_rating:
            fairest_weak_teams.append(weak_team)
    return fairest_weak_teams


def recursiveWeakTeamGenerator(
    ratings,
    weak_team=[],
    current_applicant_index=0
):
    if not isValidWeakTeam(weak_team, ratings):
        return
    if current_applicant_index == len(ratings):
        yield weak_team
        return
    for new_team in recursiveWeakTeamGenerator(
        ratings,
        weak_team + [current_applicant_index],
        current_applicant_index + 1
    ):
        yield new_team
    for new_team in recursiveWeakTeamGenerator(
        ratings,
        weak_team,
        current_applicant_index + 1
    ):
        yield new_team


def isValidWeakTeam(weak_team, ratings):
    total_rating = sum(ratings)
    weak_team_rating = teamRating(weak_team, ratings)
    optimal_weak_team_rating = total_rating // 2
    if weak_team_rating > optimal_weak_team_rating:
        return False
    elif weak_team_rating * 2 == total_rating:
        # In case of equal strengths, player 0 is assumed
        # to be in the "weak" team
        return 0 in weak_team
    else:
        return True


def teamRating(team_members, ratings):
    return sum(memberRatings(team_members, ratings))    


def memberRatings(team_members, ratings):
    return [ratings[i] for i in team_members]


def getOpposingTeam(team, ratings):
    return [i for i in range(len(ratings)) if i not in team]


ratings = [5, 6, 2, 10, 2, 3, 4]
print("Player ratings:     ", ratings)
print("*" * 40)
for option, weak_team in enumerate(listFairestWeakTeams(ratings)):
    strong_team = getOpposingTeam(weak_team, ratings)
    print("Possible partition", option + 1)
    print("Weak team members:  ", weak_team)
    print("Weak team ratings:  ", memberRatings(weak_team, ratings))
    print("Strong team members:", strong_team)
    print("Strong team ratings:", memberRatings(strong_team, ratings))
    print("*" * 40)

เอาท์พุท:

Player ratings:      [5, 6, 2, 10, 2, 3, 4]
****************************************
Possible partition 1
Weak team members:   [0, 1, 2, 5]
Weak team ratings:   [5, 6, 2, 3]
Strong team members: [3, 4, 6]
Strong team ratings: [10, 2, 4]
****************************************
Possible partition 2
Weak team members:   [0, 1, 4, 5]
Weak team ratings:   [5, 6, 2, 3]
Strong team members: [2, 3, 6]
Strong team ratings: [2, 10, 4]
****************************************
Possible partition 3
Weak team members:   [0, 2, 4, 5, 6]
Weak team ratings:   [5, 2, 2, 3, 4]
Strong team members: [1, 3]
Strong team ratings: [6, 10]
****************************************

1

ระบุว่าคุณต้องการแม้แต่ทีมที่คุณรู้คะแนนเป้าหมายของการจัดอันดับของแต่ละทีม นี่คือผลรวมของการจัดอันดับหารด้วย 2

ดังนั้นรหัสต่อไปนี้ควรทำในสิ่งที่คุณต้องการ

from itertools import combinations

ratings = [5, 6, 2, 10, 2, 3, 4]

target = sum(ratings)/2 

difference_dictionary = {}
for i in range(1, len(ratings)): 
    for combination in combinations(ratings, i): 
        diff = sum(combination) - target
        if diff >= 0: 
            difference_dictionary[diff] = difference_dictionary.get(diff, []) + [combination]

# get min difference to target score 
min_difference_to_target = min(difference_dictionary.keys())
strong_ratings = difference_dictionary[min_difference_to_target]
first_strong_ratings = [x for x in strong_ratings[0]]

weak_ratings = ratings.copy()
for strong_rating in first_strong_ratings: 
    weak_ratings.remove(strong_rating)

เอาท์พุต

first_strong_ratings 
[6, 10]

weak_rating 
[5, 2, 2, 3, 4]

มีตัวแยกอื่น ๆ ที่มีสิ่งfairnessเหล่านี้พร้อมใช้งานทั้งหมดเพื่อค้นหาภายใน tuple strong_ratings ฉันเพิ่งเลือกดูอันแรกเนื่องจากจะมีอยู่เสมอสำหรับรายการการจัดอันดับใด ๆ ที่คุณส่งผ่าน (ระบุlen(ratings) > 1)


ความท้าทายของคำถามนี้คือไม่ต้องนำเข้าสิ่งใดตามที่ฉันพูดถึงในคำถาม ขอบคุณสำหรับข้อมูลของคุณ!
EddieEC

0

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

lis = [5, 6, 2, 10, 2, 3, 4]
lis.sort()
lis.reverse()

bucket_1 = []
bucket_2 = []

for item in lis:
    if sum(bucket_1) <= sum(bucket_2):
        bucket_1.append(item)
    else:
        bucket_2.append(item)

print("Bucket 1 : {}".format(bucket_1))
print("Bucket 2 : {}".format(bucket_2))

ผลผลิต:

Bucket 1 : [10, 4, 2]
Bucket 2 : [6, 5, 3, 2]

แก้ไข:

อีกวิธีหนึ่งคือการสร้างรายการย่อยทั้งหมดที่เป็นไปได้ สมมติว่าคุณมี l1 ซึ่งเป็นหนึ่งในส่วนย่อยของรายการจากนั้นคุณสามารถรับรายการ l2 เช่นนั้น l2 = list (ดั้งเดิม) - l1 จำนวนชุดย่อยที่เป็นไปได้ทั้งหมดของรายการขนาด n คือ 2 ^ n เราสามารถแสดงว่ามันเป็นค่าจำนวนเต็มตั้งแต่ 0 ถึง 2 ^ n -1 ยกตัวอย่างสมมติว่าคุณมีรายการ = [1, 3, 5] จากนั้นไม่มีค่าผสมที่เป็นไปได้คือ 2 ^ 3 เช่น 8 ตอนนี้เราสามารถเขียนชุดค่าผสมทั้งหมดดังนี้:

  1. 000 - [] - 0
  2. 001 - [1] - 1
  3. 010 - [3] - 2
  4. 011 - [1,3] - 3
  5. 100 - [5] - 4
  6. 101 - [1,5] - 5
  7. 110 - [3,5] - 6
  8. 111 - [1,3,5] - 7 และ l2 ในกรณีนี้สามารถหาได้ง่ายโดยการหา xor ด้วย 2 ^ n-1

สารละลาย:

def sum_list(lis, n, X):
    """
    This function will return sum of all elemenst whose bit is set to 1 in X
    """
    sum_ = 0
    # print(X)
    for i in range(n):
        if (X & 1<<i ) !=0:
            # print( lis[i], end=" ")
            sum_ += lis[i]
    # print()
    return sum_

def return_list(lis, n, X):
    """
    This function will return list of all element whose bit is set to 1 in X
    """
    new_lis = []
    for i in range(n):
        if (X & 1<<i) != 0:
            new_lis.append(lis[i])
    return new_lis

lis = [5, 6, 2, 10, 2, 3, 4]
n = len(lis)
total = 2**n -1 

result_1 = 0
result_2 = total
result_1_sum = 0
result_2_sum = sum_list(lis,n, result_2)
ans = total
for i in range(total):
    x = (total ^ i)
    sum_x = sum_list(lis, n, x)
    sum_y = sum_list(lis, n, i)

    if abs(sum_x-sum_y) < ans:
        result_1 =  x
        result_2 = i
        result_1_sum = sum_x
        result_2_sum = sum_y
        ans = abs(result_1_sum-result_2_sum)

"""
Produce resultant list
"""

bucket_1 = return_list(lis,n,result_1)
bucket_2 = return_list(lis, n, result_2)

print("Bucket 1 : {}".format(bucket_1))
print("Bucket 2 : {}".format(bucket_2))

ผลผลิต:

Bucket 1 : [5, 2, 2, 3, 4]
Bucket 2 : [6, 10]

สวัสดีถ้าคุณอ่านคำถามเดิมของฉันคุณจะเห็นว่าฉันใช้วิธีโลภแล้วและถูกปฏิเสธ ขอบคุณสำหรับการป้อนข้อมูลของคุณ!
EddieEC

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