การแปลงปัญหาเครื่องหลังที่ถูก จำกัด เป็น 0/1 ปัญหาเครื่องหลัง


12

ฉันเจอปัญหาที่เป้าหมายคือการใช้การเขียนโปรแกรมแบบไดนามิก (แทนที่จะเป็นวิธีการอื่น) มีระยะห่างที่จะขยายและชุดสายเคเบิลที่มีความยาวต่างกัน จำนวนสายเคเบิลขั้นต่ำที่ต้องใช้ในการขยายระยะทางคือเท่าไหร่?

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

ตัวอย่างเช่นสายเคเบิลที่กำหนด:
1 x 10ft,
1 x 7ft,
1 x 6ft,
5 x 3ft,
6 x 2ft,
7 x 1ft

หากระยะห่างเป้าหมายคือ 13ft อัลกอริทึม DP เลือก 7 + 6 เพื่อขยายระยะทาง อัลกอริทึมโลภน่าจะเลือกได้ 10 + 3 แต่มันก็คือความเสมอภาคสำหรับจำนวนสายเคเบิลขั้นต่ำ ปัญหาเกิดขึ้นเมื่อพยายามขยาย 15 ฟุต อัลกอริทึม DP จบลงด้วยการเลือก 6 + 3 + 3 + 3 เพื่อรับสายเคเบิล 4 ในขณะที่อัลกอริทึมโลภเลือก 10 + 3 + 2 สำหรับ 3 สายเท่านั้น

อย่างไรก็ตามทำการสแกนแบบเบา ๆ ของการแปลงที่มีขอบเขตเป็น 0/1 ดูเหมือนว่าวิธีการที่รู้จักกันดีในการแปลงหลายรายการเป็น {p, 2p, 4p ... } คำถามของฉันคือการแปลงนี้ทำงานอย่างไรถ้า p + 2p + 4p ไม่ได้เพิ่มจำนวนหลายรายการ ตัวอย่างเช่น: ฉันมีสาย 3ft 5 ฟุต ฉันเพิ่ม {3, 2x3, 4x3} ไม่ได้เพราะ 3 + 2x3 + 4x3> 5x3 ฉันควรเพิ่ม {3, 4x3} แทนหรือไม่

[ฉันกำลังพยายามที่จะห้อมล้อมกระดาษ "ปัญหา Trail Trail โอเรกอน" แต่ขณะนี้ดูเหมือนว่าวิธีการที่ใช้ไม่มีการเขียนโปรแกรมแบบไดนามิก]


1
ฉันคิดว่านี่เหมาะสำหรับmath.stackexchange.comหรือแม้แต่mathoverflow.net
Oded

3
ฉันถูกฉีกขาดระหว่าง stackoverflow ทั่วไปและที่นี่ การอ่านคำถามที่พบบ่อยทั้งสองไซต์โครงสร้างข้อมูลและอัลกอริธึมของไซต์นี้เป็นอันดับแรก อ่านคำถามที่พบบ่อยสำหรับเว็บไซต์คณิตศาสตร์ดูเหมือนว่าจะแนะนำให้ถามที่เว็บไซต์ cstheory แทน
มด

คำตอบ:


1

อาจเป็นความผิดพลาดในรหัสของคุณ ฉันเขียนโปรแกรม DP ตามที่ Naryshkin พูดถึง สำหรับเป้าหมายช่วง 13 มันรายงาน 6 + 7 และ 15 มันรายงาน 2 + 6 + 7

# weight: cable length
# total weight: target span
# value: 1 for each cable
# want minimum number of cables, i.e. minimum total value

def knapsack_01_exact_min(weights, values, W):
    # 0-1 knapsack, exact total weight W, minimizing total value
    n = len(weights)
    values = [0] + values
    weights = [0] + weights
    K = [[0 for i in range(W+1)] for j in range(n+1)]
    choice = [[0 for i in range(W+1)] for j in range(n+1)]
    for i in range(1, n+1):
        for w in range(1, W+1):
            K[i][w] = K[i-1][w]
            choice[i][w] = '|'
            if w >= weights[i]:
                t = K[i-1][w-weights[i]]
                if (w==weights[i] or t) and (K[i][w]==0 or t+values[i] < K[i][w]):
                    choice[i][w] = '\\'
                    K[i][w] = t+values[i]
    return K[n][W], choice

def print_choice(choice, weights):
    i = len(choice)-1
    j = len(choice[0])-1
    weights = [0] + weights
    while i > 0 and j > 0:
        if choice[i][j]=='\\':
            print weights[i],
            j -= weights[i]
        i -= 1
    print

lens = [10, 7, 6] + 5*[3] + 6*[2] + 7*[1]
values = (3+5+6+7)*[1]
span = 13
v, choice = knapsack_01_exact_min(lens, values, span)
print "need %d cables to span %d:" % (v,span),
print_choice(choice, lens)

span = 15
v, choice = knapsack_01_exact_min(lens, values, span)
print "need %d cables to span %d:" % (v,span),
print_choice(choice, lens)

หากคุณปรับลำดับของความยาวอินพุตก็สามารถให้โซลูชันที่เหมาะสมอื่น ๆ ได้ ตัวอย่างเช่นlens = 5*[3] + 6*[2] + 7*[1] + [10, 7, 6]จะให้ 15 = 10 + 2 + 3


คุณได้รับข้อความสั่ง if ที่ไหน: 'if (w-weights [i] == 0 หรือ t) และ (K [i] [w] == 0 หรือ t + ค่า [i] <K [i] [w] ): '? หากลืมตอนนี้แหล่งที่มาของอัลกอริทึม DP ของฉัน แต่ฉันไม่มีการตรวจสอบเป็นศูนย์ให้ตรวจสอบเฉพาะ '(t + ค่า [i] <K [ฉัน] [w])'
มด

1
คุณกำลังแก้ไขน้ำหนักรวมที่แน่นอนซึ่งหมายความว่าเมื่อใดก็ตามที่มีการเลือกรายการเราจำเป็นต้องตรวจสอบให้แน่ใจว่าเป็นไปตามน้ำหนักที่แน่นอน (ของขั้นตอนปัจจุบัน) ดังนั้นเมื่อเราตัดสินใจเลือกรายการข้อที่สอง "t + ค่า [i] <K [i] [w]" ทำให้เรามั่นใจว่าเราจะมีมูลค่ารวมที่น้อยกว่า แต่ก่อนหน้านั้นเรายังต้องเติมน้ำหนักให้เต็มตามที่ต้องการเช่นไอเท็มแรกที่ i-1 จะต้องสามารถฟูลฟิลน้ำหนักได้ (w-weights [i]) ดังนั้นประโยคแรก "ถ้า K [i-1] [w - น้ำหนัก [i]] "(ฉันใช้ตัวแปรชั่วคราว t สำหรับสิ่งนั้น)
jsz

มีสองเช็คเพิ่มเติม "w == น้ำหนัก [i]" และ "K [i] [w] == 0"; พวกเขามีความจำเป็นและเนื่องจากวิธีการเริ่มต้นตาราง; ฉันคิดว่าคุณจะสามารถทำมันออกมาได้ดังนั้นฉันจะไม่ลงรายละเอียด (ฉันเปลี่ยนน้ำหนัก w [i] == 0 เป็น w == น้ำหนัก [i] ควรมีความชัดเจนมากขึ้น)
jsz

1

วิธีที่ฉันเคยเห็นใช้ในการแปลงปัญหาเป้หลังเป็น 0/1 หนึ่งคือการมีหลายรายการที่เหมือนกัน พูดถ้าคุณมีรายการดังต่อไปนี้ (กำหนดเป็นน้ำหนักยูทิลิตี้):

  • 2 x 1, 2
  • 3 x 2, 3

คุณจะแปลงเป็นปัญหา 0/1 โดยใช้รายการด้วย

  • 1, 2
  • 1, 2
  • 2, 3
  • 2, 3
  • 2, 3

และใช้อัลกอริทึม 0/1 เพื่อแก้ปัญหา คุณมีแนวโน้มที่จะมีวิธีแก้ไขปัญหาที่หลากหลายอย่างถูกต้องเท่ากันดังนั้นคุณจึงเลือกวิธีแก้ปัญหาโดยพลการ


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


ใช่นั่นคือสิ่งที่ฉันทำเพื่อเติมน้ำหนักและความคุ้มค่า ฉันคำนวณหาค่าสูงสุดแทนที่จะเป็นขั้นต่ำ ตอนนี้ฉันเพิ่งเปลี่ยนรหัสเพื่อคำนวณเป็นนาทีตามที่คุณแนะนำและเริ่มต้นแถว 0 ของตาราง DP เป็น MAXINT ยังคงเป็นผลลัพธ์เดียวกันการแก้ปัญหาการเขียนโปรแกรมแบบไดนามิกสำหรับปัญหาเป้ยังคงจบลงด้วยการเลือก 6 + 3 + 3 + 3 แทน 10 + 3 + 2 หรือ 7 + 6 + 2
มด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.