วิธีที่จะทำให้ขี้เมาช้าลงระหว่างทางกลับบ้าน


15

พิจารณารูปสี่เหลี่ยมจัตุรัส n และ n ตารางกราฟที่มีลักษณะเช่นนี้

กราฟตาราง

มันเป็นสิ่งสำคัญที่จะแจ้งให้ทราบว่ากราฟนี้คือ 11 11

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

เขาเริ่มที่มุมซ้ายล่างและเป้าหมายของเขาคือกลับบ้านซึ่งเป็นมุมบนขวามือ เวลาเป็นเพียงจำนวนก้าวที่เขาใช้

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

ความท้าทายคือการคิดว่าเป็นปฏิปักษ์ที่อันตรายที่สุดเท่าที่จะเป็นไปได้แล้วทดสอบบนกราฟขนาด100 คูณ 100 20 คูณ 20 ด้วยรูปวอล์คเกอร์แบบสุ่ม คะแนนของคุณเป็นเพียงเวลาเฉลี่ยที่จะใช้เวลาวอล์คเกอร์แบบสุ่มเพื่อกลับบ้านกว่า10 1,000 วิ่ง

คุณสามารถใช้ภาษาและไลบรารีใด ๆ ที่คุณต้องการตราบใดที่พวกเขาสามารถใช้งานได้อย่างอิสระและติดตั้งได้อย่างง่ายดายใน Linux

ฉันต้องใช้อะไรบ้าง

คุณควรใช้รหัสสำหรับวอล์คเกอร์แบบสุ่มและสำหรับฝ่ายตรงข้ามและรหัสควรรวมกันเพื่อให้ผลลัพธ์เมื่อรันเป็นเพียงค่าเฉลี่ย 1,000 การรันโดยใช้โค้ดคู่ต่อสู้ของคุณ รหัสวอล์คเกอร์แบบสุ่มควรง่ายมากในการเขียนเนื่องจากเขาเลือกจาก (x-1, y), (x + 1, y), (x, y-1) และ (x, y + 1) เพื่อให้แน่ใจว่า ไม่มีรายการใดถูกลบหรืออยู่นอกช่วง

รหัสปฏิปักษ์นั้นยากกว่าและต้องจำว่าคนขี้เมาเดินทางไปไหนแล้วดังนั้นเขาจึงไม่พยายามที่จะลบพวกเขาและเพื่อให้แน่ใจว่ายังมีเส้นทางกลับบ้านสำหรับคนขี้เมาซึ่งเป็นเล่ห์เหลี่ยมเล็กน้อย ทำเร็ว


ภาคผนวก 10 วิ่งไม่พอจริง ๆ แต่ฉันไม่ต้องการลงโทษผู้ที่จัดการเพื่อเดินนานจริงๆ ตอนนี้ฉันได้เพิ่มเป็น 1,000 เนื่องจากคำขอยอดนิยม อย่างไรก็ตามหากการเดินของคุณนานคุณจะไม่สามารถวิ่งได้ 1,000 ครั้งภายในระยะเวลาที่เป็นจริงโปรดรายงานจำนวนวิ่งสูงสุดที่คุณสามารถทำได้


ตารางคะแนนสูงสำหรับ 100 100

  • 976124.754 โดยเครื่องมือเพิ่มประสิทธิภาพ
  • 103000363.218 โดย Peter Taylor

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

ตารางคะแนนสูงสำหรับ 20 โดย 20

230,794.38 (100k runs) by justhalf
227,934 by Sparr 
213,000 (approx) by Peter Taylor
199,094.3 by stokastic
188,000 (approx) by James_pic
 64,281 by Geobits

2
ฉันไม่เข้าใจ คุณไม่สามารถลบขอบทั้งหมดที่จุดเริ่มต้นยกเว้นขอบที่สร้างเส้นทางที่ยาวที่สุดได้หรือไม่?
Peter Olson

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

3
ฉันไม่ได้เป็นแฟนของการกำหนดให้ทุกรายการเพื่อบูรณาการล้อ (วอล์คเกอร์) หากมีคนโพสต์ชุดทดสอบ / โครงร่างจากนั้นฉันจะอัปโหลดพวกเขาและใช้มัน
Sparr

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

3
10 รอบไม่เพียงพอ แม้จะมีเขาวงกต 10x10 ที่คงที่ แต่มีศัตรูที่ชาญฉลาดและเขาวงกต 100x100 อยู่คนเดียวค่าเบี่ยงเบนมาตรฐานอยู่ที่ประมาณ 50% ของคดีโดยเฉลี่ย ฉันวิ่ง 10,000 รอบและฉันก็ยังไม่คิดว่าจะเปรียบเทียบกับผลลัพธ์ที่คุ้มค่า
Sparr

คำตอบ:


10

230,794.38 ในการวิ่ง 20x20, 100k

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

รหัสอยู่ที่ด้านล่างของโพสต์นี้ มีการปรับปรุงเพื่อให้เร็วกว่ารุ่นก่อนหน้ามากถึง 1,000 ครั้งใน 23 วินาที

ด้านล่างเป็นตัวอย่างการใช้งานและตัวอย่างเขาวงกต:

วอล์คเกอร์ที่สมบูรณ์แบบ
ค่าเฉลี่ย: 230794.384
สูงสุด: 1514506
นาที: 25860
สร้างเสร็จใน 2317.374 วินาที
 _ _ _ _ _ _ _ _ _ _ _ _ 
| | | | | | | | | | | | | | | _ _ _ _  
| | | | | | | | | | | | | | | | _ _ _ _  
| | | | | | | | | | | | | | | _ _ _ _ |
| | | | | | | | | | | | | | | | _ _ _ _  
| | | | | | | | | | | | | | | _ _ _ _ |
| | | | | | | | | | | | | | | | _ _ _ _  
| | | | | | | | | | | | | | | _ _ _ _ |
| | | | | | | | | | | | | | _ | | _ _ _ _  
| | | | | | | | | | | | | _ _ _ _ _ _ |
| | | | | | | | | | | | | | _ _ _ _ _ _ _  
| | | | | | | | | | | | | _ _ _ _ _ _ |
| | | | | | | | | | | | | | _ _ _ _ _ _ _  
| | | | | | | | | | | | | _ _ _ _ _ _ |
| | | | | | _ | | _ | | _ | | _ | | _ _ _ _ _ _ _  
| | | | | _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | | | | | _ _ _ _ _ _ _ _ _ _ _ _ _  
| | | | | _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ | | _ | | _ _ _ _ _ _ _ _ _ _ _ _ _  
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | 


การส่งก่อนหน้า

ในที่สุดฉันสามารถจับคู่ผลลัพธ์ของ Sparr! = D

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

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

เสียงนี้คล้ายกับสิ่งที่ Sparr ประสบความสำเร็จตามที่แสดง ความแตกต่างของเขาเล็กเกินไปที่จะถือว่าดีกว่า แต่ฉันจะบอกว่าวิธีการของฉันนั้นมีพลังมากกว่าเขาเนื่องจากเขาวงกตของฉันนั้นสามารถปรับเปลี่ยนได้มากกว่า Sparr's =)

ผลลัพธ์ที่ได้จากเขาวงกตสุดท้ายตัวอย่าง:

EXTREME_DOUBLE_PATH
ค่าเฉลี่ย: 228034.89
สูงสุด: 1050816
นาที: 34170
เสร็จสมบูรณ์ใน 396.728 วินาที
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |


ส่วนการทดลอง

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

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

ฉันจะอธิบายแต่ละกลยุทธ์:

เส้นทางเดียว

นี่เป็นวิธีที่ง่ายที่สุดซึ่งจะสร้างเส้นทางเดียวจากรายการไปยังทางออก

SINGLE_PATH
ค่าเฉลี่ย: 162621.612
สูงสุด: 956694
นาที: 14838
สร้างเสร็จใน 149.430 วิ
 _ _ _ _ _ _ _ _ _ _ _
| | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |

เกาะ (ระดับ 0)

นี่เป็นวิธีการที่พยายามดักจับขี้เมาในเกาะที่อยู่โดดเดี่ยว ใช้งานไม่ได้ดีอย่างที่ฉันคาดไว้ แต่นี่เป็นหนึ่งในแนวคิดแรกของฉันดังนั้นฉันจึงรวมไว้

มีสองเส้นทางที่นำไปสู่ทางออกและเมื่อมึนเมาเข้ามาใกล้หนึ่งในนั้นฝ่ายตรงข้ามก็ปิดมันบังคับให้เขาหาทางออกอื่น (และอาจติดกับดักอีกครั้งในเกาะ)

เกาะ
เฉลี่ย: 74626.070
สูงสุด: 428560
ต่ำสุด: 1528
สร้างเสร็จใน 122.512 วิ
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _   
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |

เส้นทางคู่

นี่คือกลยุทธ์ที่ถูกกล่าวถึงมากที่สุดซึ่งมีสองเส้นทางที่มีความยาวเท่ากันจนถึงทางออก

DOUBLE_PATH
ค่าเฉลี่ย: 197743.472
สูงสุด: 1443406
นาที: 21516
แล้วเสร็จใน 308.177 วิ
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
 _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |

เกาะ (ระดับ 1)

แรงบันดาลใจจากหลายเส้นทางของเกาะและการเดินสูงนับในเส้นทางเดียวเราเชื่อมต่อเกาะกับทางออกและทำให้เขาวงกตเส้นทางเดียวในเกาะสร้างในสามเส้นทางทั้งหมดที่จะออกและคล้ายกับกรณีก่อนหน้าปิดใด ๆ ทางออกเมื่อขี้เมาเข้ามาใกล้

วิธีนี้ใช้งานได้ดีกว่าเส้นทางเดียวที่บริสุทธิ์เล็กน้อย แต่ก็ยังไม่สามารถเอาชนะเส้นทางคู่ได้

เกาะ
เฉลี่ย: 166265.132
สูงสุด: 1162966
นาที: 19544
สร้างเสร็จใน 471.982 วิ
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ | _
| | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | |  
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ |

เกาะ (ระดับ 2)

พยายามที่จะขยายแนวคิดก่อนหน้านี้ฉันสร้างเกาะซ้อนกันสร้างในเส้นทางทั้งหมดห้าเส้นทาง แต่ดูเหมือนว่าจะไม่ได้ผล

เกาะ
เฉลี่ย: 164222.712
สูงสุด: 927608
นาที: 22024
สร้างเสร็จใน 793.591s
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ | _ |  
| | | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| _ | _ | _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |

เกาะ (ระดับ 3)

สังเกตว่าเส้นทางคู่นั้นใช้งานได้ดีกว่าเส้นทางเดียวเรามาทำให้เกาะเป็นสองเท่ากัน!

ผลที่ได้คือการปรับปรุงบนเกาะ (ระดับ 1) แต่ก็ยังไม่ชนะคู่ที่บริสุทธิ์

สำหรับการเปรียบเทียบผลลัพธ์สำหรับเส้นทางคู่ของขนาดของเกาะคือ 131,134.42 เคลื่อนที่โดยเฉลี่ย ดังนั้นนี่เป็นการเพิ่มจำนวนการเคลื่อนไหวที่สำคัญ (ประมาณ 40k) แต่ไม่เพียงพอที่จะเอาชนะสองทาง

เกาะ
ค่าเฉลี่ย: 171730.090
สูงสุด: 769080
นาที: 29760
สร้างเสร็จใน 587.646 วินาที
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |  
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ |

เกาะ (ระดับ 4)

อีกครั้งทดลองกับเกาะที่ซ้อนกันและอีกครั้งมันทำงานได้ไม่ดีนัก

เกาะ
ค่าเฉลี่ย: 149723.068
สูงสุด: 622106
นาที: 25752
แล้วเสร็จใน 830.889s
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _    
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _ |  
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | |
| | _ | _ _ _ _ _ _ _ _ _ _ _ _ | | |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |

ข้อสรุป

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

นี่เป็นการบอกเป็นนัยเพิ่มเติมว่ากลยุทธ์พื้นฐานควรยังคงเป็นเส้นทางคู่และเราสามารถปรับเปลี่ยนได้เพียงวิธีการสร้างเส้นทางแบบไดนามิกซึ่งทำโดย Sparr ดังนั้นฉันเชื่อว่ากลยุทธ์ของเขาเป็นวิธีที่จะไป!

รหัส

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.TreeSet;

public class Walker {

    enum Strategy{
        SINGLE_PATH,
        ISLAND,
        DOUBLE_PATH,
        EXTREME_DOUBLE_PATH,
        PERFECT_DOUBLE_PATH,
    }

    int width,height;
    int x,y; //walker's position
    int dX,dY; //destination
    Point[][] points;
    int stepCount = 0;

    public static void main(String[]args){
        int side = 20;
//      runOnce(side, Strategy.EXTREME_DOUBLE_PATH, 0);
        runOnce(side, Strategy.PERFECT_DOUBLE_PATH, 0);
//      for(Strategy strategy: Strategy.values()){
//          runOnce(side, strategy, 0);
//      }
//      runOnce(side, Strategy.ISLAND, 1);
//      runOnce(side, Strategy.ISLAND, 2);
//      Scanner scanner = new Scanner(System.in);
//      System.out.println("Enter side, strategy (SINGLE_PATH, ISLAND, DOUBLE_PATH, EXTREME_DOUBLE_PATH), and level:");
//      while(scanner.hasNext()){
//          side = scanner.nextInt();
//          Strategy strategy = Strategy.valueOf(scanner.next());
//          int level = scanner.nextInt();
//          scanner.nextLine();
//          runOnce(side, strategy, level);
//          System.out.println("Enter side, strategy (SINGLE_PATH, ISLAND, DOUBLE_PATH, EXTREME_DOUBLE_PATH), and level:");
//      }
//      scanner.close();
    }

    private static Walker runOnce(int side, Strategy strategy, int level) {
        Walker walker = null;
        long total = 0;
        int max = 0;
        int min = Integer.MAX_VALUE;
        double count = 1000;
        long start = System.currentTimeMillis();
        for(int i=0; i<count; i++){
            walker = new Walker(0,0,side,side,side-1,side-1, strategy, level, false);
            total += walker.stepCount;
            max = Math.max(walker.stepCount, max);
            min = Math.min(walker.stepCount, min);
//          System.out.println("Iteration "+i+": "+walker.stepCount);
        }
        System.out.printf("%s\nAverage: %.3f\nMax: %d\nMin:%d\n",strategy, total/count, max, min);
        System.out.printf("Completed in %.3fs\n", (System.currentTimeMillis()-start)/1000.0);
        walker.printPath();
        return walker;
    }

    private void createIsland(int botLeftX, int botLeftY, int topRightX, int topRightY){
        for(int i=botLeftY+1; i<topRightY; i++){
            if(i>botLeftY+1) deletePath(points[botLeftX][i].right());
            if(i<topRightY-1) deletePath(points[topRightX][i].left());
        }
        for(int i=botLeftX+1; i<topRightX; i++){
            if(i>botLeftX+1) deletePath(points[i][botLeftY].up());
            if(i<topRightX-1) deletePath(points[i][topRightY].down());
        }
    }

    private void createSinglePath(int botLeftX, int botLeftY, int topRightX, int topRightY){
        for(int i=botLeftY; i<topRightY; i++){
            if(i==topRightY-1 && (topRightY+1-botLeftY)%2==0){
                for(int j=botLeftX; j<topRightX; j++){
                    if(j==topRightX-1 && (j-botLeftX)%2==0){
                        deletePath(points[topRightX][topRightY].down());
                    } else {
                        deletePath(points[j][topRightY-1+((j-botLeftX)%2)].right());
                    }
                }
            } else {
                for(int j=botLeftX+(i-botLeftY)%2; j<topRightX+((i-botLeftY)%2); j++){
                    deletePath(points[j][i].up());
                }
            }
        }
    }

    private void createDoublePath(int botLeftX, int botLeftY, int topRightX, int topRightY){
        for(int i=botLeftY; i<topRightY; i++){
            if(i>botLeftY && (width%4!=1 || i<topRightY-1)) deletePath(points[width/2-1][i].right());
            if(i==topRightY-1 && (topRightY+1-botLeftY)%2==1){
                for(int j=botLeftX; j<topRightX; j++){
                    if((j-botLeftX)%2==0 || j<topRightX-1){
                        deletePath(points[j][topRightY-1+((j-botLeftX)%2)].right());
                    } else {
                        deletePath(points[topRightX-1][topRightY-1].right());
                    }
                }
            } else {
                if((i-botLeftY)%2==0){
                    for(int j=botLeftX+1; j<topRightX; j++){
                        deletePath(points[j][i].up());
                    }
                } else {
                    for(int j=botLeftX; j<topRightX+1; j++){
                        if(j!=width/2 && j!=width/2-1){
                            deletePath(points[j][i].up());
                        }
                    }
                }
            }
        }
    }

    public Walker(int startingX,int startingY, int Width, int Height, int destinationX, int destinationY, Strategy strategy, int level, boolean animate){
        width = Width;
        height = Height;
        dX = destinationX;
        dY = destinationY;
        x=startingX;
        y=startingY;
        points = new Point[width][height];
        for(int y=0; y<height; y++){
            for(int x=0; x<width; x++){
                points[x][y] = new Point(x,y);
            }
        }
        for(int y=0; y<height; y++){
            for(int x=0; x<width; x++){
                if(x<width-1) new Edge(points[x][y], points[x+1][y]);
                if(y<height-1) new Edge(points[x][y], points[x][y+1]);
            }
        }

        if(strategy == Strategy.SINGLE_PATH) createSinglePath(0,0,width-1,height-1);

        if(strategy == Strategy.DOUBLE_PATH) createDoublePath(0,0,width-1,height-1);

        List<EdgeList> edgeLists = new ArrayList<EdgeList>();
        if(strategy == Strategy.ISLAND){
            List<Edge> edges = new ArrayList<Edge>();
            if(level==0){
                createIsland(0,0,width-1,height-1);
                deletePath(points[width-2][height-2].right());
                deletePath(points[width-2][height-2].up());
            } else {
                for(int i=0; i<level; i++){
                    createIsland(i,i,width-1-i, height-1-i);
                }
                createDoublePath(level,level,width-1-level,height-1-level);
                for(int i=height-1; i>=height-level; i--){
                    edges.add(points[i-2][i].right());
                    edges.add(points[i][i-2].up());
                    edgeLists.add(new EdgeList(points[i-1][i].right(), points[i][i-1].up()));
                }
            }
            edges.add(points[width-1-level][height-1-level].down());
            edges.add(points[width-1-level][height-1-level].left());
            edgeLists.add(new EdgeList(edges.toArray(new Edge[0])));
        }

        int[] availableVerticals = new int[height];
        if(strategy == Strategy.EXTREME_DOUBLE_PATH){
            for(int i=1; i<width-1; i++){
                deletePath(points[i][0].up());
            }
            availableVerticals[0] = 2;
            for(int i=1; i<height; i++){
                availableVerticals[i] = width;
            }
        }

        boolean[][] available = new boolean[width][height];
        if(strategy == Strategy.PERFECT_DOUBLE_PATH){
            for(int x=0; x<width; x++){
                for(int y=0; y<height; y++){
                    if(x%2==1 && y%2==1){
                        available[x][y] = true;
                    } else {
                        available[x][y] = false;
                    }
                }
            }
        }
//      printPath();
        while(!walk()){
            if(animate)try{Thread.sleep(500);}catch(InterruptedException e){}
            if(strategy == Strategy.ISLAND){
                if(x==y && (x==1 || (x>=2 && x<=level))){
                    if(!hasBeenWalked(points[x][x].down())){
                        deletePath(points[x][x].down());
                    } else if(!hasBeenWalked(points[x][x].left())){
                        deletePath(points[x][x].left());
                    }
                }
            }
            if(strategy == Strategy.EXTREME_DOUBLE_PATH){
                Point cur = points[x][y];
                int untravelled = 0;
                for(Edge edge: cur.edges) if(edge!=null && !edge.walked) untravelled++;
                if(untravelled>1){
                    if(cur.up()!=null && availableVerticals[y]>2 && !cur.up().walked){
                        deletePath(cur.up());
                        availableVerticals[y]--;
                    }
                    if(cur.down()!=null && !cur.down().walked){
                        deletePath(cur.down());
                        availableVerticals[y-1]--;
                    }
                    if(cur.up()!=null && cur.left()!=null && !cur.left().walked){
                        deletePath(cur.left());
                        deletePath(points[x][y+1].left());
                    }
                    if(cur.up()!=null && cur.right()!=null && !cur.right().walked){
                        deletePath(cur.right());
                        if(y<height-1) deletePath(points[x][y+1].right());
                    }
                }
            }
            if(strategy == Strategy.PERFECT_DOUBLE_PATH){
                Point cur = points[x][y];
                int untravelled = 0;
                for(Edge edge: cur.edges) if(edge!=null && !edge.walked) untravelled++;
                if(x%2!=1 || y%2!=1){
                    if(untravelled>1){
                        if(cur.down()==null && hasBeenWalked(cur.right())){
                            if(canBeDeleted(cur.up())) deletePath(cur.up());
                        }
                        if(cur.down()==null && hasBeenWalked(cur.left())){
                            if(x%2==0 && y%2==1 && canBeDeleted(cur.right())) deletePath(cur.right());
                            else if(cur.right()!=null && canBeDeleted(cur.up())) deletePath(cur.up());
                        }
                        if(cur.left()==null && hasBeenWalked(cur.up())){
                            if(canBeDeleted(cur.right())) deletePath(cur.right());
                        }
                        if(cur.left()==null && hasBeenWalked(cur.down())){
                            if(x%2==1 && y%2==0 && canBeDeleted(cur.up())) deletePath(cur.up());
                            else if (cur.up()!=null && canBeDeleted(cur.right())) deletePath(cur.right());
                        }
                    }
                } else {
                    if(!hasBeenWalked(cur.left())){
                        if(x>1 && available[x-2][y]){
                            if(untravelled>1){
                                available[x-2][y] = false;
                                deletePath(cur.up());
                            }
                        } else if(cur.up()!=null){
                            if(canBeDeleted(cur.left())) deletePath(cur.left());
                            if(canBeDeleted(points[x][y+1].left())) deletePath(points[x][y+1].left());
                        }
                    }
                    if(!hasBeenWalked(cur.down())){
                        if(y>1 && available[x][y-2]){
                            if(untravelled>1){
                                available[x][y-2] = false;
                                deletePath(cur.right());
                            }
                        } else if(cur.right()!=null){
                            if(canBeDeleted(cur.down())) deletePath(cur.down());
                            if(canBeDeleted(points[x+1][y].down())) deletePath(points[x+1][y].down());
                        }
                    }
                }
            }
            if(strategy == Strategy.DOUBLE_PATH || strategy == Strategy.EXTREME_DOUBLE_PATH
                    || strategy == Strategy.PERFECT_DOUBLE_PATH){
                if(x==width-2 && y==height-1 && points[width-1][height-1].down()!=null){
                    deletePath(points[width-1][height-1].left());
                }
                if(x==width-1 && y==height-2 && points[width-1][height-1].left()!=null){
                    deletePath(points[width-1][height-1].down());
                }
            } else if(strategy == Strategy.ISLAND){
                for(EdgeList edgeList: edgeLists){
                    boolean deleted = false;
                    for(Edge edge: edgeList.edges){
                        if(edge.start.x == x && edge.start.y == y){
                            if(!hasBeenWalked(edge)){
                                deletePath(edge);
                                edgeList.edges.remove(edge);
                                if(edgeList.edges.size() == 1){
                                    edgeLists.remove(edgeList);
                                }
                                deleted = true;
                                break;
                            }
                        }
                    }
                    if(deleted) break;
                }
            }
            if(animate)printPath();
        }
    }

    public boolean hasBeenWalked(Edge edge){
        if(edge == null) return false;
        return edge.walked;
    }

    public boolean canBeDeleted(Edge edge){
        if(edge == null) return false;
        return !edge.walked;
    }

    public List<Edge> getAdjacentUntravelledEdges(){
        List<Edge> result = new ArrayList<Edge>();
        for(Edge edge: points[x][y].edges){
            if(edge!=null && !hasBeenWalked(edge)) result.add(edge); 
        }
        return result;
    }

    public void printPath(){
        StringBuilder builder = new StringBuilder();
        for(int y=height-1; y>=0; y--){
            for(int x=0; x<width; x++){
                Point point = points[x][y];
                if(this.x==x && this.y==y){
                    if(point.up()!=null) builder.append('?');
                    else builder.append('.');
                } else {
                    if(point.up()!=null) builder.append('|');
                    else builder.append(' ');
                }
                if(point.right()!=null) builder.append('_');
                else builder.append(' ');
            }
            builder.append('\n');
        }
        System.out.print(builder.toString());
    }

    public boolean walk(){
        ArrayList<Edge> possibleMoves = new ArrayList<Edge>();
        Point cur = points[x][y];
        for(Edge edge: cur.edges){
            if(edge!=null) possibleMoves.add(edge);
        }
        int random = (int)(Math.random()*possibleMoves.size());
        Edge move = possibleMoves.get(random);
        move.walked = true;
        if(move.start == cur){
            x = move.end.x;
            y = move.end.y;
        } else {
            x = move.start.x;
            y = move.start.y;
        }
        stepCount++;
        if(x==dX && y == dY){
            return true;
        } else {
            return false;
        }
    }

    public boolean isSolvable(){
        TreeSet<Point> reachable = new TreeSet<Point>();
        Queue<Point> next = new LinkedList<Point>();
        next.offer(points[x][y]);
        reachable.add(points[x][y]);
        while(next.size()>0){
            Point cur = next.poll();
            ArrayList<Point> neighbors = new ArrayList<Point>();
            if(cur.up()!=null) neighbors.add(cur.up().end);
            if(cur.right()!=null) neighbors.add(cur.right().end);
            if(cur.down()!=null) neighbors.add(cur.down().start);
            if(cur.left()!=null) neighbors.add(cur.left().start);
            for(Point neighbor: neighbors){
                if(!reachable.contains(neighbor)){
                    if(neighbor == points[dX][dY]) return true;
                    reachable.add(neighbor);
                    next.offer(neighbor);
                }
            }
        }
        return false;
    }

    public boolean deletePath(Edge toDelete){
        if(toDelete == null) return true;
//      if(toDelete.walked){
//          System.err.println("Edge already travelled!");
//          return false;
//      }
        int startIdx = toDelete.getStartIdx();
        int endIdx = toDelete.getEndIdx();
        toDelete.start.edges[startIdx] = null;
        toDelete.end.edges[endIdx] = null;
//      if(!isSolvable()){
//          toDelete.start.edges[startIdx] = toDelete;
//          toDelete.end.edges[endIdx] = toDelete;
//          System.err.println("Invalid deletion!");
//          return false;
//      }
        return true;
    }

    static class EdgeList{
        List<Edge> edges;

        public EdgeList(Edge... edges){
            this.edges = new ArrayList<Edge>();
            this.edges.addAll(Arrays.asList(edges));
        }
    }

    static class Edge implements Comparable<Edge>{
        Point start, end;
        boolean walked;

        public Edge(Point start, Point end){
            walked = false;
            this.start = start;
            this.end = end;
            this.start.edges[getStartIdx()] = this;
            this.end.edges[getEndIdx()] = this;
            if(start.compareTo(end)>0){
                Point tmp = end;
                end = start;
                start = tmp;
            }
        }

        public Edge(int x1, int y1, int x2, int y2){
            this(new Point(x1,y1), new Point(x2,y2));
        }

        public boolean exists(){
            return start.edges[getStartIdx()] != null || end.edges[getEndIdx()] != null;
        }

        public int getStartIdx(){
            if(start.x == end.x){
                if(start.y < end.y) return 0;
                else return 2;
            } else {
                if(start.x < end.x) return 1;
                else return 3;
            }
        }

        public int getEndIdx(){
            if(start.x == end.x){
                if(start.y < end.y) return 2;
                else return 0;
            } else {
                if(start.x < end.x) return 3;
                else return 1;
            }
        }

        public boolean isVertical(){
            return start.x==end.x;
        }

        @Override
        public int compareTo(Edge o) {
            int result = start.compareTo(o.start);
            if(result!=0) return result;
            return end.compareTo(o.end);
        }
    }

    static class Point implements Comparable<Point>{
        int x,y;
        Edge[] edges;

        public Point(int x, int y){
            this.x = x;
            this.y = y;
            edges = new Edge[4];
        }

        public Edge up(){ return edges[0]; }
        public Edge right(){ return edges[1]; }
        public Edge down(){ return edges[2]; }
        public Edge left(){ return edges[3]; }

        public int compareTo(Point o){
            int result = Integer.compare(x, o.x);
            if(result!=0) return result;
            result = Integer.compare(y, o.y);
            if(result!=0) return result;
            return 0;
        }
    }
}

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

1
เวลารวมอยู่ในข้อมูลโค้ดแล้ว ประมาณ 400 วินาทีสำหรับการวิ่ง 1,000 ครั้ง ซึ่งรวมถึงการตรวจสอบความสามารถในการแก้ปัญหาในการลบแต่ละเส้นทาง ฉันสามารถลบมันที่มีประมาณ 170s สำหรับการวิ่ง 1,000 ครั้ง ดังนั้นฉันสามารถทำได้ 20k วิ่งในเวลาประมาณหนึ่งชั่วโมง
justhalf

การเพิ่มประสิทธิภาพจริง ๆ แล้วฉันอาจรัน 100k ภายใน 3.5 ชั่วโมง
justhalf

คะแนนของฉันอยู่ที่ 100k และใช้เวลา 10 นาที @ justhalf ดีมากในเขาวงกตเส้นทางที่มีความยืดหยุ่นมากขึ้น ฉันรู้วิธีที่จะทำให้ดียิ่งขึ้น แต่ฉันไม่มีความอดทนที่จะใช้มันในตอนนี้
Sparr

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

10

227934 (20x20)

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

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <iostream>

#define DEBUG 0
#define ROUNDS 10000

#define Y 20
#define X 20
#define H (Y*2+1)
#define W (X*2+1)

int maze[H][W];
int scores[ROUNDS];

int x, y;

void print_maze(){
    char line[W+2];
    line[W+1]=0;
    for(int row=0;row<H;row++) {
        for(int col=0;col<W;col++) {
            switch(maze[row][col]) {
                case 0:
                    line[col]=' ';
                    break;
                case 1:
                    line[col]=row%2?'-':'|';
                    break;
                case 8:
                    line[col]=(row==y*2+1&&col==x*2+1)?'@':'?';
                    break;
                case 9:
                    line[col]=(row==y*2+1&&col==x*2+1)?'@':'*';
                    break;
            }
        }
        line[W]='\n';
        printf("%s",line);
    }
    printf("%d %d\n",y,x);
}

int main(){
    srand (time(NULL));
    long long total_turns = 0;
    for(int round=0;round<ROUNDS;round++) {
        for (int r=0;r<H;r++) {
            for (int c=0;c<W;c++) {
                maze[r][c]=0;
            }
        }
        maze[1][1]=9;
        maze[1][2]=1;
        maze[2][1]=1;
        maze[1][3]=8;
        maze[3][1]=8;
        int progress_l = 0;
        int progress_r = 0;
        int side = 0;
        int closed_exit = 0;
        x=0;
        y=0;
        if (DEBUG) print_maze();
        long long turn = 0;
        int in = 0;
        while (x!=X-1||y!=Y-1) {
            turn++;
            int r = y*2+1;
            int c = x*2+1;
            int dx=0, dy=0;
            if (DEBUG) {
                std::cin>>in;
                switch(in) {
                    case 0:
                        dy=-1; dx=0; break;
                    case 1:
                        dy=0; dx=1; break;
                    case 2:
                        dy=1; dx=0; break;
                    case 3:
                        dy=0; dx=-1; break;
                    default:
                        dy=0; dx=0; break;
                }
            } else {
                int exits = maze[r-1][c] + maze[r][c+1] + maze[r+1][c] + maze[r][c-1];
                int exit_choice = -1;
                do {
                    if (rand()%exits == 0) {
                        exit_choice = exits;
                        break;
                    } else {
                        exits--;
                    }
                }while(exits);

                --exits;

                if (maze[r-1][c]&&!dx&&!dy) {
                    if (exits) {
                        --exits;
                    } else {
                        dy = -1;
                        dx = 0;
                    }
                }
                if (maze[r][c+1]&&!dx&&!dy) {
                    if (exits) {
                        --exits;
                    } else {
                        dy = 0;
                        dx = 1;
                    }
                }
                if (maze[r+1][c]&&!dx&&!dy) {
                    if (exits) {
                        --exits;
                    } else {
                        dy = 1;
                        dx = 0;
                    }
                }
                if (maze[r][c-1]&&!dx&&!dy) {
                    if (exits) {
                        --exits;
                    } else {
                        dy = 0;
                        dx = -1;
                    }
                }
            }

            x+=dx;
            y+=dy;

            if(x==X-1 && y==Y-1) continue;

            if (x==0&&y==1) side=-1;
            if (x==1&&y==0) side=1;
            if (maze[y*2+1][x*2+1]==8) { // room needs another exit, maybe
                if (side==-1) { // left half of maze
                    if (y==1) { // top of a column
                        if (x%2) { // going up, turn right
                            maze[y*2+1][x*2+2]=1;
                            maze[y*2+1][x*2+3]=8;
                        } else { // going right, turn down
                            maze[y*2+2][x*2+1]=1;
                            maze[y*2+3][x*2+1]=8;
                        }
                    } else if (y==Y-1) { // bottom of a column
                        if (x%2 && x<(X-progress_r-3)) { // going right, turn up if there's room
                            maze[y*2+0][x*2+1]=1;
                            maze[y*2-1][x*2+1]=8;
                            progress_l=x+1;
                        } else { // going down or exiting, go right
                            if (x!=X-2 or closed_exit==1) {
                                maze[y*2+1][x*2+2]=1;
                                maze[y*2+1][x*2+3]=8;
                            } else {
                                closed_exit = -1;
                            }
                        }
                    } else { // in a column
                        if (maze[y*2+0][x*2+1]) { // going down
                            maze[y*2+2][x*2+1]=1;
                            maze[y*2+3][x*2+1]=8;
                        } else { // going up
                            maze[y*2+0][x*2+1]=1;
                            maze[y*2-1][x*2+1]=8;
                        }
                    }
                } else { // right half of maze
                    if (y==0) { // top row
                        if (x<X-1) { // go right
                            maze[y*2+1][x*2+2]=1;
                            maze[y*2+1][x*2+3]=8;
                        } else { // go down
                            maze[y*2+2][x*2+1]=1;
                            maze[y*2+3][x*2+1]=8;
                        }
                    } else if (y==Y-2) { // heading right to the exit
                        if (x<X-1) { // go right
                            maze[y*2+1][x*2+2]=1;
                            maze[y*2+1][x*2+3]=8;
                        } else { // go down
                            if (x!=X-1 or closed_exit==-1) {
                                maze[y*2+2][x*2+1]=1;
                                maze[y*2+3][x*2+1]=8;
                            } else {
                                closed_exit = 1;
                            }
                        }
                    } else if (y==Y-3) { // bottom of a column
                        if (x>progress_l+1) { // do we have room for another column?
                            if (!(x%2)&&y>1) { // going left, turn up
                                maze[y*2+0][x*2+1]=1;
                                maze[y*2-1][x*2+1]=8;
                            } else { // going down, turn left
                                maze[y*2+1][x*2+0]=1;
                                maze[y*2+1][x*2-1]=8;
                                progress_r=X-x-1;
                            }
                        } else { // abort, move down to escape row
                            maze[y*2+2][x*2+1]=1;
                            maze[y*2+3][x*2+1]=8;
                        }
                    } else if (y==1) { // top of a column
                        if (!(x%2)) { // going up, turn left
                            maze[y*2+1][x*2+0]=1;
                            maze[y*2+1][x*2-1]=8;
                        } else { // going left, turn down
                            maze[y*2+2][x*2+1]=1;
                            maze[y*2+3][x*2+1]=8;
                        }
                    } else { // in a column
                        if (maze[y*2+0][x*2+1]) { // going down
                            maze[y*2+2][x*2+1]=1;
                            maze[y*2+3][x*2+1]=8;
                        } else { // going up
                            maze[y*2+0][x*2+1]=1;
                            maze[y*2-1][x*2+1]=8;
                        }
                    }

                }
                maze[y*2+1][x*2+1]=9;
            }

            if (DEBUG) print_maze();
        }
        // print_maze();
        printf("turns:%lld\n",turn);
        scores[round] = turn;
        total_turns += turn;
    }
    printf("%d rounds in a %d*%d maze\n",ROUNDS,X,Y);
    long long avg = total_turns/ROUNDS;
    printf("average: % 10lld\n",avg);
    long long var = 0;
    for(int r=0;r<ROUNDS;r++){
        var += (scores[r]-avg)*(scores[r]-avg);
    }
    var/=ROUNDS;
    // printf("variance: %lld\n",var);
    int stddev=sqrt(var);
    printf("stddev:  % 10d\n",stddev);

}

เอาท์พุท (พร้อมเวลา):

...
turns:194750
turns:506468
turns:129684
turns:200712
turns:158664
turns:156550
turns:311440
turns:137900
turns:86948
turns:107134
turns:81806
turns:310274
100000 rounds in a 20*20 maze
average:     227934
stddev:      138349
real    10m54.797s
...

ตัวอย่างของเขาวงกตที่มีความยาวเท่ากันครึ่งทางถึงทางเดินซ้าย / ล่างแสดงให้เห็นว่าถูกตัดออกจากทางออก (ขวาล่าง):

  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
 |  _   _   _   _   _   _   _   _   _  |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | |_| |_| |_| |_| |_|
 | | | | | | | | | |_ _ _ _ _ _ _ _ _ _
 |_| |_| |_| |_| |_ _ _ _ _ _ _ _ _ _  !

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


สีที่ฉันประทับใจ คุณมีฉันโหวต!
stokastic

1
มันค่อนข้างน่าประทับใจ จำได้ไหมว่าเมื่อเราเพิ่งเข้ามาที่ใบหน้าขี้เมา?
Dennis

เป็นการยากที่จะมองเห็นกราฟของคุณบางทีคุณอาจเปลี่ยนการพิมพ์กราฟเป็นสิ่งที่คล้ายกับของฉัน
justhalf

1
@ เพียงแค่ความปรารถนาของคุณคือคำสั่งของฉัน
SPARR

1
@ เพียงแค่ฉันได้รับมันออกมาบนกระดาษ เพียงแค่ต้องเขียนตรรกะ ถ้าฉันไม่ทำมันเสร็จภายในสองสามวันฉันจะให้ร่างคุณเหรอ? :)
Sparr

6

135,488,307.9 สำหรับ 98x98

199094.3 สำหรับ 20x20

ฉันได้นำวิธีการแก้ปัญหาที่สร้างสองเส้นทางมาใช้จนจบและปิดหนึ่งในนั้นเมื่อคนขี้เมามาถึง นี่เป็นการจำลองความยาวของเส้นทางซึ่งอย่างน้อยที่สุดจะเป็น 1.5x ความยาวของเส้นทางเดียวตั้งแต่ต้นจนจบ หลังจากวิ่งไป 27 รอบฉันก็มีค่าเฉลี่ยประมาณ 135 ล้าน น่าเสียดายใช้เวลาหลายนาทีต่อการเดินเท้าดังนั้นฉันจะต้องวิ่งในอีกไม่กี่ชั่วโมงข้างหน้า หนึ่ง caveat - ตัวสร้างพา ธ สองทางของฉันทำงานได้เฉพาะถ้าขนาดของกราฟอยู่ในรูปแบบ 4 * n + 2 ซึ่งหมายถึงสิ่งที่ใกล้เคียงที่สุดที่ฉันจะได้รับ 100 คือ 102 หรือ 98 ฉันจะโพสต์ผลลัพธ์โดยใช้ 98 ซึ่งฉันคาดว่า ยังคงเกินกว่าวิธีการเส้นทางคดเคี้ยวไปมา ฉันจะทำงานบนระบบเส้นทางที่ดีขึ้นในภายหลัง ปัจจุบันผลลัพธ์ส่งผลในรูปแบบของ (numSteps, currentAverage) หลังจากการเดินแต่ละครั้ง

แก้ไข: แก้ไขแล้วตอนนี้โค้ดทำงานบนขนาดกราฟที่มีหลาย ๆ ของ 2 แทนที่จะเป็น 4 * n + 2

รหัส: (เพิ่มอาร์กิวเมนต์ 'จริง' ให้กับตัวสร้างวอล์คเกอร์ที่บรรทัด 187 สำหรับการวาดภาพเต่าของกราฟ)

import random
import turtle

WIDTH  = 20
HEIGHT = 20
L, U, R, D = 1, 2, 4, 8

def delEdge(grid, x1, y1, x2, y2):

    # check that coordinates are in-bounds
    if not (0 <= x1 < WIDTH):  return False
    if not (0 <= y1 < HEIGHT): return False
    if not (0 <= x2 < WIDTH):  return False
    if not (0 <= y2 < HEIGHT): return False

    # swap order such that x1 <= x2 and y1 <= y2
    if x2 < x1:
        x2 ^= x1
        x1 ^= x2
        x2 ^= x1
    if x2 < x1: print "Swap failure: {}, {}".format(x1, x2)

    if y2 < y1:
        y2 ^= y1
        y1 ^= y2
        y2 ^= y1
    if y2 < y1: print "Swap failure: {}, {}".format(y1, y2)

    # check that only one of the deltas is = 1
    dx = x2 - x1
    dy = y2 - y1

    if dx and dy:       return False
    if not (dx or dy):  return False
    if dx > 1:          return False
    if dy > 1:          return False

    #print "<{}, {}>, <{}, {}>".format(x1, y1, x2, y2)

    if dx > 0:
        try: grid[x1][y1].remove(R)
        except: pass
        try: grid[x2][y2].remove(L)
        except: pass
    if dy > 0:
        try: grid[x1][y1].remove(D)
        except: pass
        try: grid[x2][y2].remove(U)
        except: pass

    return True

def newGrid():

    grid = [[[] for y in xrange(HEIGHT)] for x in xrange(WIDTH)]

    for x in xrange(WIDTH):
        for y in xrange(HEIGHT):
            if x > 0:
                grid[x][y].append(L)
            if x < WIDTH-1:
                grid[x][y].append(R)
            if y > 0:
                grid[x][y].append(U)
            if y < HEIGHT-1:
                grid[x][y].append(D)

    return grid

class walker:

    def __init__(self, grid, mode, draw=False):
        self.x  = 0
        self.y  = 0
        self.dx = WIDTH-1
        self.dy = HEIGHT-1

        self.grid     = grid
        self.mode     = mode
        self.draw     = draw
        self.numSteps = 0

        self.initGrid()

    def initGrid(self):
        if self.mode == 0:
            #pass
            if self.draw: drawGrid(grid)

        elif self.mode == 1:

            for y in xrange(HEIGHT-1):
                if y % 2 == 0:
                    for x in xrange(WIDTH - 1):
                        delEdge(grid, x, y, x, y+1)
                else:
                    for x in xrange(1, WIDTH):
                        delEdge(grid, x, y, x, y+1)
            if self.draw: drawGrid(grid)

        elif self.mode == 2:
            for y in xrange(HEIGHT/2):
                if y % 2 == 0:
                    for x in xrange(1, WIDTH-1):
                        delEdge(grid, x, y, x, y+1)
                else:
                    for x in xrange(2, WIDTH):
                        delEdge(grid, x, y, x, y+1)
            for y in xrange(HEIGHT/2, HEIGHT-1):
                if y%2 == 0:
                    for x in xrange(1, WIDTH-1):
                        delEdge(grid, x, y, x, y+1)
                else:
                    for x in xrange(0, WIDTH-2):
                        delEdge(grid, x, y, x, y+1)
            for y in xrange(1, HEIGHT-1):
                midpoint = HEIGHT/2
                if HEIGHT % 4 == 0: 
                    midpoint = HEIGHT/2 + 1
                if y < midpoint:
                    delEdge(grid, 0, y, 1, y)
                else:
                    delEdge(grid, WIDTH-1, y, WIDTH-2, y)
            if self.draw: drawGrid(grid)

    def walk(self):
        self.numSteps += 1
        choices = grid[self.x][self.y]
        direction = random.choice(choices)
        #print (self.x, self.y), grid[self.x][self.y], direction
        if direction   == L: self.x -= 1
        elif direction == U: self.y -= 1
        elif direction == R: self.x += 1
        elif direction == D: self.y += 1

    def main(self):
        hasBlocked = False
        while (self.x, self.y) != (self.dx, self.dy):
            #print (self.x, self.y), (self.dx, self.dy)
            self.walk()
            if self.mode == 2:
                if not hasBlocked:
                    if (self.x, self.y) == (WIDTH-2, HEIGHT-1):
                        delEdge(self.grid, WIDTH-2, HEIGHT-1, WIDTH-1, HEIGHT-1)
                        hasBlocked = True
                    elif (self.x, self.y) == (WIDTH-1, HEIGHT-2):
                        delEdge(self.grid, WIDTH-1, HEIGHT-1, WIDTH-1, HEIGHT-2)
                        hasBlocked = True

        return self.numSteps

def drawGrid(grid):
    size = 3
    turtle.speed(0)
    turtle.delay(0)
    turtle.ht()
    for x in xrange(WIDTH):
        for y in xrange(HEIGHT):
            dirs = grid[x][y]
            for dir in dirs:
                if dir == L:
                    turtle.pu()
                    turtle.setpos((x*4, y*4))
                    turtle.pd()
                    turtle.setpos(((x-1)*4, y*4))
                elif dir == R:
                    turtle.pu()
                    turtle.setpos((x*4, y*4))
                    turtle.pd()
                    turtle.setpos(((x+1)*4, y*4))
                elif dir == U:
                    turtle.pu()
                    turtle.setpos((x*4, y*4))
                    turtle.pd()
                    turtle.setpos((x*4, (y-1)*4))
                elif dir == D:
                    turtle.pu()
                    turtle.setpos((x*4, y*4))
                    turtle.pd()
                    turtle.setpos((x*4, (y+1)*4))
    turtle.mainloop()

numTrials  = 100
totalSteps = 0.0
i = 0
try:
    while i < numTrials:
        grid = newGrid()

        w = walker(grid, 2)
        steps = w.main()
        totalSteps += steps
        print steps, totalSteps/(i+1)
        i += 1

    print totalSteps / numTrials

except KeyboardInterrupt:
    print totalSteps / i

ข้อมูลดิบ: (numSteps ปัจจุบันทำงานโดยเฉลี่ย)

358796490 358796490.0
49310430 204053460.0
106969130 171692016.667
71781702 146714438.0
49349086 127241367.6
40874636 112846912.333
487607888 166384194.571
56423642 152639125.5
71077302 143576700.667
101885368 139407567.4
74423642 133499937.818
265170542 144472488.167
59524778 137938048.923
86919630 134293876.143
122462528 133505119.6
69262650 129489965.25
85525556 126903823.529
161165512 128807250.667
263965384 135920836.632
128907594 135570174.5
89535930 133378067.619
97344576 131740181.636
98772132 130306788.174
140769524 130742735.5
198274280 133443997.28
95417374 131981434.846
226667006 135488307.852

ฉันลดขนาดกราฟลงเหลือ 20 20 เพื่อให้เวลารันเร็วขึ้น ฉันหวังว่ามันจะช่วย

ขณะนี้คุณชนะแล้ว :)

คะแนน 20 จาก 20 คะแนนของคุณมีมากกว่า 1,000 ครั้งหรือไม่

@ Lembik ใช่มันเป็น
stokastic

1
@Dennis au ตรงกันข้าม :)
Sparr

6

แนวทาง 4 เส้นทาง 213k

แนวทางเดียวคือ

Straight line from S to E

และคะแนนเฉลี่ยของ N^2และคะแนนเฉลี่ยของ

แนวทางสองทางคือ

Loop with S and E opposite each other

แต่เมื่อครั้งแรกที่คนขี้เมาถึงจุดสิ้นสุดก็ถูกตัด:

Loop is cut to give a curved line from S to E

(N/2)^2 + N^2มันคะแนนค่าเฉลี่ยของ

วิธีสี่เส้นทางใช้การตัดสองครั้ง:

Nested loops, joined in two forks, one either side of E Cut one of the forks on the E side ในอีกด้านหนึ่งให้ตัดส้อมที่ไม่ใช่ด้าน E  สิ่งนี้ทำให้เส้นทางที่ซับซ้อนหนึ่ง

สมมติว่าห่วงด้านนอกเป็นของความยาวและภายในวงของความยาวxN สำหรับความเรียบง่ายปกติฉันจะไป(1-x)NN=1

(x/2)^2จากจุดเริ่มต้นที่จะคะแนนตัดครั้งแรกค่าเฉลี่ยของ ตั้งแต่การตัดครั้งแรกจนถึงการตัดครั้งที่สองมีสองตัวเลือกความยาวxและ1-x; (1-x)x^2 + x(1-x)^2 = x-x^2นี้จะช่วยให้ค่าเฉลี่ยของ ในที่สุดเส้นทางที่เหลือให้1ในที่สุดเส้นทางที่เหลือให้N^2 (1 + x - 3/4 x^2)ดังนั้นคะแนนรวมเป็น

ผมเริ่มคิดว่าการรักษาเส้นทางที่มีอยู่ของความยาวเท่ากันในแต่ละขั้นตอนจะเป็นที่ดีที่สุดเพื่อให้วิธีการเริ่มต้นของฉันใช้การให้คะแนนของx = 1/2 1.3125 N^2แต่หลังจากที่ทำการวิเคราะห์ข้างต้นปรากฎว่าแยกที่ดีที่สุดที่จะได้รับเมื่อมีคะแนนx = 2/31.3333 N^2

1000 walks with average 210505.738 in 202753ms

1000 walks with average 212704.626 in 205191ms

ด้วยรหัส

import java.awt.Point;
import java.util.*;

// http://codegolf.stackexchange.com/q/37484/194
public class RandomWalker {
    private static final int SIZE = 19;
    private static final Point dest = new Point(SIZE, SIZE);

    private final Random rnd = new Random();
    private Point p = new Point(0, 0);
    private int step = 0;
    private Set<Set<Point>> edges;
    private Map<Set<Point>, String> cuttableEdgeNames;
    private Set<String> cutSequences;
    private String cutSequence = "";

    public static void main(String[] args) {
        long start = System.nanoTime();
        long total = 0;
        int walks = 0;
        while (walks < 1000 && total < 1L << 40) {
            RandomWalker rw = new RandomWalker();
            total += rw.walk();
            walks++;
        }

        long timeTaken = System.nanoTime() - start;
        System.out.println(walks + " walks with average " + total / (double)walks + " in " + (timeTaken / 1000000) + "ms");
    }

    RandomWalker() {
        loadMaze(
            "+-+ +-+ +-+ +-+ +-+ +-+ +-+-+-+-+-+-+-+",
            "| | | | | | | | | | | | |             |",
            "+ + + + + + + + + + + + + +-+ +-+ +-+ +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + +-+ + + + + + + +",
            "| | | | | | | | | | |     | | | | | | |",
            "+ + + + + + + + + + + +-+-+ + + + + + +",
            "| | | | | | | | | | | |     | | | | | |",
            "+ + + + + + + + + + + + +-+ + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ +-+ +-+ +-+ +-+ +-+ + + + + + + + + +",
            "|                     | | | | | | | | |",
            "+ +-+ +-+ +-+ +-+ +-+ + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + + + + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | |",
            "+ + + + + + + + + + + +-+ + + + + + + +",
            "| | | | | | | | | | |     | | | | | | |",
            "+ + + + + + + + + + + +-+ + + + + + + +",
            "| | | | | | | | | | | | | | | | | | | d",
            "+ + + + + + + + + + + + + + +-+ +-+ +c+",
            "| | | | | | | | | | | | | |           |",
            "+ + + + + + + + + + + + + +-+-+-+-+-+ +",
            "| | | | | | | | | | | | |           f b",
            "+-+ +-+ +-+ +-+ +-+ +-+ +-+-+-+-+-+e+a+"
        );
        cutSequences = new HashSet<String>();
        cutSequences.add("ac");
        cutSequences.add("ad");
        cutSequences.add("be");
        cutSequences.add("bf");
    }

    private void loadMaze(String... row) {
        edges = new HashSet<Set<Point>>();
        cuttableEdgeNames = new HashMap<Set<Point>, String>();

        // Horizontal edges
        for (int y = 0; y <= SIZE; y++) {
            for (int x0 = 0; x0 < SIZE; x0++) {
                char ch = row[y * 2].charAt(x0 * 2 + 1);
                if (ch == ' ') continue;
                Set<Point> edge = new HashSet<Point>();
                edge.add(new Point(x0, y));
                edge.add(new Point(x0 + 1, y));
                edges.add(edge);
                if (ch != '-') cuttableEdgeNames.put(edge, "" + ch);
            }
        }

        // Vertical edges
        for (int y0 = 0; y0 < SIZE; y0++) {
            for (int x = 0; x <= SIZE; x++) {
                char ch = row[y0 * 2 + 1].charAt(x * 2);
                if (ch == ' ') continue;
                Set<Point> edge = new HashSet<Point>();
                edge.add(new Point(x, y0));
                edge.add(new Point(x, y0 + 1));
                edges.add(edge);
                if (ch != '|') cuttableEdgeNames.put(edge, "" + ch);
            }
        }
    }

    int walk() {
        while (!p.equals(dest)) {
            List<Point> neighbours = neighbours(p);
            int idx = rnd.nextInt(neighbours.size());
            p = neighbours.get(idx);
            step++;
        }

        return step;
    }

    List<Point> neighbours(Point p) {
        List<Point> rv = new ArrayList<Point>();
        if (p.x > 0) handlePossibleNeighbour(rv, p, new Point(p.x - 1, p.y));
        if (p.x < SIZE) handlePossibleNeighbour(rv, p, new Point(p.x + 1, p.y));
        if (p.y > 0) handlePossibleNeighbour(rv, p, new Point(p.x, p.y - 1));
        if (p.y < SIZE) handlePossibleNeighbour(rv, p, new Point(p.x, p.y + 1));
        return rv;
    }

    private void handlePossibleNeighbour(List<Point> neighbours, Point p1, Point p2) {
        if (edgeExists(p1, p2)) neighbours.add(p2);
    }

    private boolean edgeExists(Point p1, Point p2) {
        Set<Point> edge = new HashSet<Point>();
        edge.add(p1);
        edge.add(p2);

        // Is it cuttable?
        String id = cuttableEdgeNames.get(edge);
        if (id != null) {
            String prefix = cutSequence + id;
            for (String seq : cutSequences) {
                if (seq.startsWith(prefix)) {
                    // Cut it
                    cutSequence = prefix;
                    edges.remove(edge);
                    return false;
                }
            }
        }

        return edges.contains(edge);
    }
}

ฉันรู้ว่านั่นคือสาเหตุที่วิธีการเกาะของฉันไม่ทำงานฉันไม่ได้ทำให้ความยาวเส้นทางสมดุลกัน เพียงชี้แจงความเข้าใจของฉันความยาวจากในfถึงcโค้ดของคุณนั้นN/2ไม่ว่าจะผ่านe(และd) หรือไม่ใช่ไหม?
justhalf

ความยาวพา ธ yE เป็นอย่างไรแทนความยาว N / 2
Sparr

@ justhalf ใช่ มีจุดยอด 400 จุดดังนั้นจึงมีขอบ 401 (หลังจากตัดหนึ่งกราฟเป็นวงจรมิลโตเนียน) เส้นทางด้านนอกสองเส้นทางมี 100 ขอบแต่ละเส้นและห่วงด้านในเป็น 101 ขอบ
Peter Taylor

เข้าใจแล้ว สองข้อสังเกต: a) เขาวงกตที่มีขนาดใหญ่กว่าจะได้รับประโยชน์จากเส้นทางที่มากขึ้น 2 ^ n b) หากคุณทำให้ความยาวเส้นทางของคุณเป็นแบบไดนามิกคุณจะเอาชนะผู้นำปัจจุบันด้วยโซลูชันสองทางแบบไดนามิก (ตัวเองและ @justhalf)
Sparr

@Sparr: มันไม่ได้N^2 2^Nและใช่การทำให้ไดนามิกนี้ทำให้ดีที่สุดความท้าทายคือวิธีทำให้ไดนามิกในขณะที่ยังคงคุณสมบัติสี่เส้นทาง @PeterTaylor: ภาพที่ดี!
justhalf

5

ฉันทำการทดลองกับการแบ่งส่วนตารางเกือบทั้งหมดในทุกkแถว สิ่งนี้จะแปลงเป็นสิ่งที่คล้ายกับการเดินสุ่มkโดยN * N/kตาราง ตัวเลือกที่มีประสิทธิภาพมากที่สุดคือการฝานทุกแถวเพื่อให้เราบังคับให้คนขี้เมาไปซิกแซก

สำหรับเคส 20x20 ( SIZE=19) ฉันมี

time java RandomWalker 
1000 walks with average 148577.604

real    0m14.076s
user    0m13.713s
sys     0m0.360s

ด้วยรหัส

import java.awt.Point;
import java.util.*;

// http://codegolf.stackexchange.com/q/37484/194
// This handles a simpler problem where the grid is mutilated before the drunkard starts to walk.
public class RandomWalker {
    private static final int SIZE = 19;
    private final Random rnd = new Random();

    public static void main(String[] args) {
        RandomWalker rw = new RandomWalker();
        long total = 0;
        int walks = 0;
        while (walks < 1000 && total < 1L << 40) {
            total += rw.walk();
            walks++;
        }

        System.out.println(walks + " walks with average " + total / (double)walks);
    }

    int walk() {
        Point dest = new Point(SIZE, SIZE);
        Point p = new Point(0, 0);
        int step = 0;

        while (!p.equals(dest)) {
            List<Point> neighbours = neighbours(p);
            int idx = rnd.nextInt(neighbours.size());
            p = neighbours.get(idx);
            step++;
        }

        return step;
    }

    List<Point> neighbours(Point p) {
        List<Point> rv = new ArrayList<Point>();
        if (p.x > 0) handlePossibleNeighbour(rv, p, new Point(p.x - 1, p.y));
        if (p.x < SIZE) handlePossibleNeighbour(rv, p, new Point(p.x + 1, p.y));
        if (p.y > 0) handlePossibleNeighbour(rv, p, new Point(p.x, p.y - 1));
        if (p.y < SIZE) handlePossibleNeighbour(rv, p, new Point(p.x, p.y + 1));
        return rv;
    }

    private void handlePossibleNeighbour(List<Point> neighbours, Point p1, Point p2) {
        if (edgeExists(p1, p2)) neighbours.add(p2);
    }

    private boolean edgeExists(Point p1, Point p2) {
        return p1.x != p2.x || p1.x == SIZE * (Math.max(p1.y, p2.y) & 1);
    }
}

ฉันคิดถูกแล้วว่าการลบขอบทั้งหมดเกิดขึ้นก่อนที่การเดินจะเริ่มต้นในการแก้ปัญหาของคุณหรือไม่?

@ Lembik ใช่ ฉันคิดว่าความคิดเห็นที่ด้านบนจะทำให้ชัดเจน
Peter Taylor

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

จากความอยากรู้สิ่งนี้ใช้เวลานานแค่ไหนในการทำงาน (ทั้งหมดและต่อการทำงาน)
stokastic

@stokastic ประมาณ 3 วินาทีต่อการวิ่ง
Peter Taylor

3

สำหรับผู้ที่ไม่ต้องการบูรณาการล้อ

ไม่ต้องกังวล! ฉันจะสร้างมันใหม่เพื่อคุณ :)

นี่คือใน Java โดยวิธีการ

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

ฉันสมมติว่าทุกคนที่ฉลาดของคุณสามารถคิดออกเพื่อใส่ตัวเลขแบบสุ่มสำหรับตัวสร้างฉันทิ้งมันไว้กับคุณเพื่อให้คุณสามารถทดสอบบางกรณี นอกจากนี้เพียงแค่เรียกใช้ฟังก์ชั่น walk () เพื่อ (คุณเดา!) ทำให้การเดินขี้เมา (สุ่ม)

ฉันจะใช้ฟังก์ชั่น canComeHome () บางครั้ง โดยเฉพาะอย่างยิ่งหลังจากที่ฉันค้นหาวิธีที่ดีที่สุดที่จะทำ

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.TreeSet;

public class Walker {
    int width,height;
    int x,y; //walker's position (does anyone else keep thinking about zombies?!?)
    int dX,dY; //destination
    TreeSet<Edge> pathsNoLongerAvailable = new TreeSet<Edge>();
    TreeSet<Edge> previouslyTraveled = new TreeSet<Edge>();
    int stepCount = 0;

    public static void main(String[]args){
        int side = 10;
        Walker walker = null;
        int total = 0;
        double count = 1000;
        for(int i=0; i<count; i++){
            walker = new Walker(0,0,side,side,side-1,side-1);
            total += walker.stepCount;
            System.out.println("Iteration "+i+": "+walker.stepCount);
        }
        System.out.printf("Average: %.3f\n", total/count);
        walker.printPath();
    }

    public Walker(int startingX,int startingY, int Width, int Height, int destinationX, int destinationY){
        width = Width;
        height = Height;
        dX = destinationX;
        dY = destinationY;
        x=startingX;
        y=startingY;
        while(!walk()){
            // Do something
        }
    }

    public void printPath(){
        for(int i=0; i<width-1; i++){
            if(!pathsNoLongerAvailable.contains(new Edge(i,height-1,i+1,height-1))){
                System.out.print(" _");
            } else {
                System.out.print("  ");
            }
        }
        System.out.println();
        for(int i=height-2; i>=0; i--){
            for(int j=0; j<2*width-1; j++){
                if(j%2==0){
                    if(!pathsNoLongerAvailable.contains(new Edge(j/2,i,j/2,i+1))){
                        System.out.print("|");
                    } else {
                        System.out.print(" ");
                    }
                } else {
                    if(!pathsNoLongerAvailable.contains(new Edge(j/2,i,j/2+1,i))){
                        System.out.print("_");
                    } else {
                        System.out.print(" ");
                    }
                }
            }
            System.out.println();
        }
    }

    public boolean walk(){
        ArrayList<int[]> possibleMoves = new ArrayList<int[]>();
        if(x!=0 && !pathsNoLongerAvailable.contains(new Edge(x,y,x-1,y))){
            possibleMoves.add(new int[]{-1,0});
        }
        if(x!=width-1 && !pathsNoLongerAvailable.contains(new Edge(x,y,x+1,y))){
            possibleMoves.add(new int[]{1,0});
        }
        if(y!=0 && !pathsNoLongerAvailable.contains(new Edge(x,y,x,y-1))){
            possibleMoves.add(new int[]{0,-1});
        }
        if(y!=height-1 && !pathsNoLongerAvailable.contains(new Edge(x,y,x,y+1))){
            possibleMoves.add(new int[]{0,1});
        }
        int random = (int)(Math.random()*possibleMoves.size());
        int[] move = possibleMoves.get(random);
        previouslyTraveled.add(new Edge(x,y,x+move[0],y+move[1]));
        x+=move[0];
        y+=move[1];
        stepCount++;
        if(x==dX && y == dY){
            return true;
        } else {
            return false;
        }
    }

    public boolean isSolvable(){
        TreeSet<Point> reachable = new TreeSet<Point>();
        Queue<Point> next = new LinkedList<Point>();
        next.offer(new Point(x,y));
        reachable.add(new Point(x,y));
        while(next.size()>0){
            Point cur = next.poll();
            int x = cur.x;
            int y = cur.y;
            ArrayList<Point> neighbors = new ArrayList<Point>();
            if(x!=0 && !pathsNoLongerAvailable.contains(new Edge(x,y,x-1,y))){
                neighbors.add(new Point(x-1, y));
            }
            if(x!=width-1 && !pathsNoLongerAvailable.contains(new Edge(x,y,x+1,y))){
                neighbors.add(new Point(x+1, y));
            }
            if(y!=0 && !pathsNoLongerAvailable.contains(new Edge(x,y,x,y-1))){
                neighbors.add(new Point(x, y-1));
            }
            if(y!=height-1 && !pathsNoLongerAvailable.contains(new Edge(x,y,x,y+1))){
                neighbors.add(new Point(x, y+1));
            }
            for(Point neighbor: neighbors){
                if(!reachable.contains(neighbor)){
                    if(neighbor.compareTo(new Point(dX, dY))==0){
                        return true;
                    }
                    reachable.add(neighbor);
                    next.offer(neighbor);
                }
            }
        }
        return false;
    }

    public boolean hasBeenWalked(int x1, int y1, int x2, int y2){
        return previouslyTraveled.contains(new Edge(x1, y1, x2, y2));
    }

    public boolean hasBeenWalked(Edge edge){
        return previouslyTraveled.contains(edge);
    }

    public void deletePath(int startX, int startY, int endX, int endY){
        Edge toAdd = new Edge(startX,startY,endX,endY);
        if(hasBeenWalked(toAdd)){
            System.out.println("Edge already travelled!");
            return;
        }
        pathsNoLongerAvailable.add(toAdd);
        if(!isSolvable()){
            pathsNoLongerAvailable.remove(toAdd);
            System.out.println("Invalid deletion!");
        }
    }

    static class Edge implements Comparable<Edge>{
        Point start, end;

        public Edge(int x1, int y1, int x2, int y2){
            start = new Point(x1, y1);
            end = new Point(x2, y2);
            if(start.compareTo(end)>0){
                Point tmp = end;
                end = start;
                start = tmp;
            }
        }

        @Override
        public int compareTo(Edge o) {
            int result = start.compareTo(o.start);
            if(result!=0) return result;
            return end.compareTo(o.end);
        }
    }

    static class Point implements Comparable<Point>{
        int x,y;
        public Point(int x, int y){
            this.x = x;
            this.y = y;
        }
        public int compareTo(Point o){
            int result = Integer.compare(x, o.x);
            if(result!=0) return result;
            result = Integer.compare(y, o.y);
            if(result!=0) return result;
            return 0;
        }
    }
}

สิ่งนี้มีข้อบกพร่องและความไม่สอดคล้องกันบางอย่าง previouslyTraveled.add(new int[]{x,y,move[0],move[1]})ควรจะเป็นและx+move[0] และและการขาดประสิทธิภาพในการตรวจสอบเส้นทางที่ถูกลบ ฉันแก้ไขโค้ดของคุณแล้ว (พร้อมฟังก์ชั่นเพิ่มเติมเพื่อพิมพ์เขาวงกต) คุณสามารถย้อนกลับได้ถ้าคุณคิดว่าไม่เหมาะสม y+move[1]Width-1Height-1
justhalf

คุณไม่ได้ใช้อย่างถูกต้องEdge Comparable<Edge>หากคุณต้องการให้ขอบเปรียบเทียบเท่ากันแม้ว่าคุณจะย้อนกลับคุณจะต้องคำนึงถึงการกลับรายการด้วยเช่นกันในกรณีที่ไม่เท่ากัน วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการเปลี่ยนคอนสตรัคเตอร์เพื่อรักษาคะแนน
Peter Taylor

@ PeterTaylor: ขอบคุณสำหรับหัวขึ้น ฉันคิดถึงกรณีที่ไม่เท่ากัน แต่ไม่สามารถเข้าใจตัวเองได้ว่าทำไมมันถึงสำคัญ คุณรู้หรือไม่ว่าฉันสามารถค้นหาความต้องการในการนำไปปฏิบัติได้ที่Comparableใด
justhalf

1
docs.oracle.com/javase/7/docs/api/java/lang/Comparable.htmlกุญแจสำคัญคือจำเป็นต้องกำหนดการสั่งซื้อทั้งหมด แต่ถ้าAและBมีขอบเดียวกันตรงกันข้ามและCจะแตกต่างกันคุณจะได้รับA.compareTo(B) == B.compareTo(A) == 0แต่และA.compareTo(C) < 0 B.compareTo(C) > 0
Peter Taylor

แล้วตอนนี้ล่ะ? ฉันเพิ่มคลาสอื่น และฉันได้เพิ่มฟังก์ชั่นเพื่อตรวจสอบว่ามันแก้ไขได้ (หรือcanComeHome())
justhalf

3

64281

ปรับปรุงตั้งแต่กริดเปลี่ยนจาก 100x100 เป็น 20x20 (ทดสอบ 1,000 ครั้ง) คะแนน 100x100 (100 การทดสอบ) อยู่ที่ประมาณ 36M

แม้ว่าสิ่งนี้จะไม่ชนะการเดิน 1D แต่ฉันต้องการเล่นด้วยความคิดที่ฉันมี

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

หลังจากเล่นกับขนาดห้องฉันมาถึงข้อสรุปเดียวกับ Peter การแบ่งมันให้เล็กลงจะดีกว่า คะแนนที่ดีที่สุดมาพร้อมกับขนาดห้อง 2

Average score over 100 trials: 36051265

รหัสไม่เลอะเทอะยุ่ง คุณสามารถพลิกSHOWสวิตช์และมันจะแสดงภาพของเส้นทางทุกSHOW_INTขั้นตอนเพื่อให้คุณสามารถดูได้ในการดำเนินการ การรันที่เสร็จสิ้นแล้วจะมีลักษณะดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

(นี่คือภาพจากตาราง 100x100 ก่อนหน้า 20x20 เป็นเช่นนี้ แต่ก็เล็กลงรหัสด้านล่างนี้ได้รับการปรับปรุงสำหรับขนาด / การวิ่งใหม่)

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;

public class DrunkWalk {

    boolean SHOW = false;
    int SHOW_INT = 10;
    int SIZE = 20;
    Random rand = new Random();
    Point pos;
    int[][] edges;
    int[][] wally;
    int[] wallx;
    int roomSize = 2;
    JFrame frame;
    final BufferedImage img;

    public static void main(String[] args){
        long total=0,runs=1000;
        for(int i=0;i<runs;i++){
            int steps = new DrunkWalk().run();
            total += steps;
            System.out.println("("+i+") "+steps);
        }
        System.out.println("\n Average " + (total/runs) + " over " + runs + " trials.");
    }

    DrunkWalk(){
        edges = new int[SIZE][SIZE];
        for(int x=0;x<SIZE;x++){
            for(int y=0;y<SIZE;y++){
                if(x>0) edges[x][y] |= WEST;
                if(x+1<SIZE) edges[x][y] |= EAST;
                if(y>0) edges[x][y] |= NORTH;
                if(y+1<SIZE) edges[x][y] |= SOUTH;
            }
        }
        wallx = new int[SIZE/roomSize+1];
        wally = new int[SIZE/roomSize+1][SIZE/roomSize+1];
        pos = new Point(SIZE-1,SIZE-1);
        img = new BufferedImage(SIZE*6+1,SIZE*6+1, BufferedImage.TYPE_INT_RGB);
        frame = new JFrame(){
            public void paint(Graphics g) {
                g.drawImage(img, 50, 50, null);
            }
        };
        frame.setSize(700,700);
        if(SHOW)
            frame.show();
    }

    void draw(){
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Graphics g = img.getGraphics();
        g.setColor(Color.WHITE);
        g.clearRect(0, 0, img.getWidth(), img.getHeight());
        for(int x=0;x<SIZE;x++){
            for(int y=0;y<SIZE;y++){
                if((edges[x][y]&EAST)==EAST)
                    g.drawLine(x*6, y*6, x*6+5, y*6);
                if((edges[x][y]&SOUTH)==SOUTH)
                    g.drawLine(x*6, y*6, x*6, y*6+5);
            }
        }
        g.setColor(Color.RED);
        g.drawOval(pos.x*6-2, pos.y*6-2, 5, 5);
        g.drawOval(pos.x*6-1, pos.y*6-1, 3, 3);
        frame.repaint();
    }

    int run(){
        int steps = 0;
        Point home = new Point(0,0);
        while(!pos.equals(home)){
            if(SHOW&&steps%SHOW_INT==0){
                System.out.println(steps);
                draw();
            }
            step();
            adversary();
            steps++;
        }
        if(SHOW)
            draw();
        return steps;
    }

    void adversary(){
        int rx = pos.x / roomSize;
        int ry = pos.y / roomSize;
        int maxWalls = roomSize - 1;
        if(wally[rx][ry] < maxWalls){
            if(pos.y%roomSize==0)
                if(delete(pos.x,pos.y,NORTH))
                    wally[rx][ry]++;
        }
        maxWalls = SIZE-1;
        if(pos.x%roomSize==0){
            if(wallx[rx] < maxWalls)
                if(delete(pos.x, pos.y,WEST))
                    wallx[rx]++;


        }       
    }

    void step(){
        List<Integer> choices = getNeighbors(pos);
        Collections.shuffle(choices);
        int dir = choices.get(0);
        pos.x += dir==WEST?-1:dir==EAST?1:0;
        pos.y += dir==NORTH?-1:dir==SOUTH?1:0;
    }

    boolean delete(int x, int y, int dir){
        if((edges[x][y] & dir) != dir)
            return false;
        edges[x][y] -= dir;
        if(dir == NORTH)
            if(y>0) edges[x][y-1] -= SOUTH;
        if(dir == SOUTH)
            if(y+1<SIZE) edges[x][y+1] -= NORTH;
        if(dir == EAST)
            if(x+1<SIZE) edges[x+1][y] -= WEST;
        if(dir == WEST)
            if(x>0) edges[x-1][y] -= EAST;
        return true;
    }

    List<Integer> getNeighbors(Point p){
        if(p.x==SIZE || p.y==SIZE){
            System.out.println("wtf");
            System.exit(0);
        }
        List<Integer> choices = new ArrayList<Integer>();
        if((edges[p.x][p.y] & NORTH) == NORTH)
            choices.add(NORTH);
        if((edges[p.x][p.y] & SOUTH) == SOUTH)
            choices.add(SOUTH);
        if((edges[p.x][p.y] & EAST) == EAST)
            choices.add(EAST);
        if((edges[p.x][p.y] & WEST) == WEST)
            choices.add(WEST);
        return choices;
    }

    final static int NORTH=1,EAST=2,SOUTH=4,WEST=8;
}

ฉันเพิ่งสังเกตเห็นว่าเขาควรจะมาจากบ็อต / ซ้าย -> ด้านบน / ขวาในขณะที่ฉันไปบอท / ขวา -> ด้านบน / ซ้าย ฉันสามารถเปลี่ยนได้ถ้ามันสำคัญ แต่ ...
Geobits

นี่เป็นสิ่งที่ดีมากและเป็นวิธีแก้ปัญหาแบบไดนามิกครั้งแรกที่ฉันคิด ฉันสนใจว่าเส้นทางของคุณยังไม่นานนัก

ถ้าโดย "ไม่ได้ค่อนข้างยาว" คุณหมายถึง ~ 1/3 ตราบเท่าที่หนึ่งและ ~ 36x เท่ากับอีก? : P
Geobits

3

188k โดยมี 2 เส้นทาง

รายการที่ดีที่สุดทั้งหมดดูเหมือนจะใช้วิธีการสร้าง 2 เส้นทางแล้วตัดออกเมื่อเมาใกล้ถึงจุดสิ้นสุดของเส้นทาง ฉันไม่คิดว่าฉันสามารถเอาชนะการเข้ามาของครึ่งเวลาได้ แต่ฉันอดไม่ได้ที่จะสงสัยว่า: ทำไมต้องมี 2 ทาง? ทำไมไม่ใช่ 3 หรือ 5 หรือ 20

TL; DR : 2 เส้นทางดูเหมือนว่าจะเหมาะสมที่สุด

ดังนั้นฉันจึงทำการทดลอง จากกรอบการทำงานของ Stretch Maniac ฉันได้เขียนรายการเพื่อทดสอบเส้นทางจำนวนมาก คุณสามารถปรับแต่งfeatureSizeพารามิเตอร์เพื่อเปลี่ยนจำนวนเส้นทาง A featureSizeของ 20 ให้ 1 เส้นทาง 10 ให้ 2 เส้นทาง 7 ให้ 3, 5 ให้ 4 และอื่น ๆ

import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;

public class Walker {
    final int width,height;
    int x,y; //walker's position (does anyone else keep thinking about zombies?!?)
    final int dX,dY; //destination
    final int featureSize;
    Set<Edge> pathsNoLongerAvailable = new HashSet<>();
    Set<Edge> previouslyTraveled = new HashSet<>();
    int stepCount = 0;
    private final BitSet remainingExits;

    public static void main(String[]args){
        int side = 20;
        Walker walker = null;
        int total = 0;
        int featureSize = 10;
        double count = 1000;
        for(int i=0; i<count; i++){
            walker = new Walker(0,0,side,side,side-1,side-1, featureSize);
            total += walker.stepCount;
            System.out.println("Iteration "+i+": "+walker.stepCount);
        }
        System.out.printf("Average: %.3f\n", total/count);
        walker.printPath();
    }

    public Walker(int startingX,int startingY, int Width, int Height, int destinationX, int destinationY, int featureSize){
        width = Width;
        height = Height;
        dX = destinationX;
        dY = destinationY;
        x=startingX;
        y=startingY;
        this.featureSize = featureSize;

        deleteBars();

        remainingExits = new BitSet();
        for (int yy = 0; yy < height; yy++) {
            if (!pathsNoLongerAvailable.contains(new Edge(width - 2, yy, width - 1, yy))) {
                remainingExits.set(yy);
            }
        }

        while(!walk()){
            if (x == width - 2
                    && remainingExits.get(y)
                    && remainingExits.cardinality() > 1) {
                deletePath(x, y, x + 1, y);
                remainingExits.set(y, false);
            }
        }
    }

    private void deleteBars() {
        for (int xx = 0; xx < width - 1; xx++) {
            for (int yy = 0; yy < height / featureSize + 1; yy++) {
                if (xx != 0) deletePath(xx, featureSize * yy + featureSize - 1, xx, featureSize * yy + featureSize);
                boolean parity = xx % 2 == 0;
                if (yy == 0) parity ^= true; // First path should be inverted
                for (int i = 0; i < featureSize && featureSize * yy + i < height; i++) {
                    if (i == 0 && !parity) continue;
                    if ((i == featureSize - 1 || featureSize * yy + i == height - 1) && parity) continue;
                        deletePath(xx, featureSize * yy + i, xx + 1, featureSize * yy + i);
                }
            }
        }
    }

    public void printPath(){
        for(int i=0; i<width-1; i++){
            if(!pathsNoLongerAvailable.contains(new Edge(i,height-1,i+1,height-1))){
                System.out.print(" _");
            } else {
                System.out.print("  ");
            }
        }
        System.out.println();
        for(int i=height-2; i>=0; i--){
            for(int j=0; j<2*width-1; j++){
                if(j%2==0){
                    if(!pathsNoLongerAvailable.contains(new Edge(j/2,i,j/2,i+1))){
                        System.out.print("|");
                    } else {
                        System.out.print(" ");
                    }
                } else {
                    if(!pathsNoLongerAvailable.contains(new Edge(j/2,i,j/2+1,i))){
                        System.out.print("_");
                    } else {
                        System.out.print(" ");
                    }
                }
            }
            System.out.println();
        }
    }

    public boolean walk(){
        ArrayList<int[]> possibleMoves = new ArrayList<int[]>();
        if(x!=0 && !pathsNoLongerAvailable.contains(new Edge(x,y,x-1,y))){
            possibleMoves.add(new int[]{-1,0});
        }
        if(x!=width-1 && !pathsNoLongerAvailable.contains(new Edge(x,y,x+1,y))){
            possibleMoves.add(new int[]{1,0});
        }
        if(y!=0 && !pathsNoLongerAvailable.contains(new Edge(x,y,x,y-1))){
            possibleMoves.add(new int[]{0,-1});
        }
        if(y!=height-1 && !pathsNoLongerAvailable.contains(new Edge(x,y,x,y+1))){
            possibleMoves.add(new int[]{0,1});
        }
        int random = ThreadLocalRandom.current().nextInt(possibleMoves.size());
        int[] move = possibleMoves.get(random);
        previouslyTraveled.add(new Edge(x,y,x+move[0],y+move[1]));
        x+=move[0];
        y+=move[1];
        stepCount++;
        if(x==dX && y == dY){
            return true;
        } else {
            return false;
        }
    }

    public boolean isSolvable(){
        Set<Point> reachable = new HashSet<>();
        Queue<Point> next = new LinkedList<>();
        next.offer(new Point(x,y));
        reachable.add(new Point(x,y));
        while(next.size()>0){
            Point cur = next.poll();
            int x = cur.x;
            int y = cur.y;
            ArrayList<Point> neighbors = new ArrayList<>();
            if(x!=0 && !pathsNoLongerAvailable.contains(new Edge(x,y,x-1,y))){
                neighbors.add(new Point(x-1, y));
            }
            if(x!=width-1 && !pathsNoLongerAvailable.contains(new Edge(x,y,x+1,y))){
                neighbors.add(new Point(x+1, y));
            }
            if(y!=0 && !pathsNoLongerAvailable.contains(new Edge(x,y,x,y-1))){
                neighbors.add(new Point(x, y-1));
            }
            if(y!=height-1 && !pathsNoLongerAvailable.contains(new Edge(x,y,x,y+1))){
                neighbors.add(new Point(x, y+1));
            }
            for(Point neighbor: neighbors){
                if(!reachable.contains(neighbor)){
                    if(neighbor.compareTo(new Point(dX, dY))==0){
                        return true;
                    }
                    reachable.add(neighbor);
                    next.offer(neighbor);
                }
            }
        }
        return false;
    }

    public boolean hasBeenWalked(int x1, int y1, int x2, int y2){
        return previouslyTraveled.contains(new Edge(x1, y1, x2, y2));
    }

    public boolean hasBeenWalked(Edge edge) {
        return previouslyTraveled.contains(edge);
    }

    public void deletePath(int startX, int startY, int endX, int endY){
        Edge toAdd = new Edge(startX,startY,endX,endY);
        if(hasBeenWalked(toAdd)){
            System.out.println("Edge already travelled!");
            return;
        }
        pathsNoLongerAvailable.add(toAdd);
        if(!isSolvable()){
            pathsNoLongerAvailable.remove(toAdd);
            System.out.println("Invalid deletion!");
        }
    }

    public static class Edge implements Comparable<Edge>{
        Point start, end;

        public Edge(int x1, int y1, int x2, int y2){
            start = new Point(x1, y1);
            end = new Point(x2, y2);
            if(start.compareTo(end)>0){
                Point tmp = end;
                end = start;
                start = tmp;
            }
        }

        @Override
        public int compareTo(Edge o) {
            int result = start.compareTo(o.start);
            if(result!=0) return result;
            return end.compareTo(o.end);
        }

        @Override
        public String toString() {
            return start.toString() + "-" + end.toString();
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 83 * hash + Objects.hashCode(this.start);
            hash = 83 * hash + Objects.hashCode(this.end);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final Edge other = (Edge) obj;
            if (!Objects.equals(this.start, other.start)) {
                return false;
            }
            if (!Objects.equals(this.end, other.end)) {
                return false;
            }
            return true;
        }


    }

    static class Point implements Comparable<Point>{
        int x,y;
        public Point(int x, int y){
            this.x = x;
            this.y = y;
        }
        public int compareTo(Point o){
            int result = Integer.compare(x, o.x);
            if(result!=0) return result;
            result = Integer.compare(y, o.y);
            if(result!=0) return result;
            return 0;
        }
        @Override
        public String toString() {
            return "(" + x + "," + y + ")";
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 23 * hash + this.x;
            hash = 23 * hash + this.y;
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final Point other = (Point) obj;
            if (this.x != other.x) {
                return false;
            }
            if (this.y != other.y) {
                return false;
            }
            return true;
        }


    }
}

มีการเพิ่มประสิทธิภาพบางอย่างที่ฉันสามารถทำได้ แต่ยังไม่ได้และมันไม่สนับสนุนการใช้กลอุบายแบบปรับแต่งใด ๆ ที่ justhalf ใช้

อย่างไรก็ตามนี่คือผลลัพธ์สำหรับfeatureSizeค่าต่างๆ:

20 (1 path):  156284 
10 (2 paths): 188553
7 (3 paths):  162279
5 (4 paths):  152574
4 (5 paths):  134287
3 (7 paths):  118843
2 (10 paths): 94171
1 (20 paths): 64515

และนี่คือแผนที่ที่มี 3 เส้นทาง:

 _   _   _   _   _   _   _   _   _    
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| |_| |_| |_| |_| |_| |_| |_| |_| |_| |
|_   _   _   _   _   _   _   _   _   _|
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| |_| |_| |_| |_| |_| |_| |_| |_| |_| |
|  _   _   _   _   _   _   _   _   _  |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |
|_| |_| |_| |_| |_| |_| |_| |_| |_| | |

ขอบคุณสำหรับสิ่งนี้. ดูเหมือนว่าเงินทั้งหมดอยู่ในเล่ห์เหลี่ยมที่ปรับได้แล้วตอนนี้ :)

ทำไมคุณตัดเส้นทางที่ด้านล่าง? คุณสามารถตัดเส้นทางระหว่างเส้นทางที่ต่ำกว่าและเส้นทางกลางเพื่อให้ได้คะแนนที่ดีกว่าฉันคิดว่า
justhalf

@ justhalf ใช่ฉันคาดหวังว่ามันจะ ฉันตัดสินใจที่จะไม่ทำเช่นนั้นเพราะมันจะทำให้โค้ดมีความซับซ้อนมากขึ้นและมันจะไม่เป็นผลงานที่ชนะ
James_pic

1
เส้นทางทั้งสาม (สมมติว่าเหมาะสมที่สุด 3 เส้นทาง) โดยเฉลี่ยจะเท่ากับเส้นทางเดียว: ปล่อยให้Nเป็นความยาวเส้นทาง (ซึ่งก็คือn^2-1) เส้นทางเดียวโดยเฉลี่ยต้องมีการN^2เคลื่อนไหวในขณะที่เส้นทางทั้งสาม(N/3)^2 + (2N/3)^2 + (2N/3)^2 = N^2บวกค่าที่ค่อนข้างเล็กดังนั้นสาม เส้นทางไม่ได้รับผลกำไรที่สำคัญเหนือเส้นทางเดียว (การคำนวณขึ้นอยู่กับผลลัพธ์ความน่าจะเป็นซึ่งระบุว่าการเคลื่อนที่แบบสุ่มบนเส้นทางความยาว 1-D Nต้องใช้N^2การเคลื่อนที่เฉลี่ยจากปลายด้านหนึ่งไปอีกด้านหนึ่ง)
justhalf

@justhalf ที่ดี ฉันพยายามดิ้นรนเพื่อหาข้อโต้แย้งแรกที่ดีว่าทำไม 2 จึงดีที่สุด แต่นี่มันตอกย้ำ
James_pic

2

131k (20x20)

ความพยายามครั้งแรกของฉันคือการลบขอบแนวนอนทั้งหมดยกเว้นแถวด้านบนและด้านล่างจากนั้นทุกครั้งที่ผู้เดินไปถึงด้านล่างของคอลัมน์ฉันจะลบขอบด้านหน้าของเขาจนกว่าเขาจะได้เยี่ยมชมด้านล่างของทุกคอลัมน์และในที่สุด สามารถไปถึงทางออกได้ ซึ่งส่งผลให้มีค่าเฉลี่ย 1/8 ของขั้นตอนมากเท่ากับวิธีการเดิน 1d ของ @ PeterTaylor

ต่อไปฉันตัดสินใจที่จะลองทำอะไรสักอย่างที่เป็นวงจรมากขึ้น ฉันได้แบ่งเขาวงกตเป็นชุดบั้งกลวงที่ซ้อนกันและกำหนดให้เขาสำรวจเส้นรอบวงของรูปตัววีแต่ละอันอย่างน้อย 1.5 เท่า นี่มีเวลาเฉลี่ยประมาณ 131k ก้าว

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <math.h>

#define DEBUG 0
#define ROUNDS 10000

#define Y 20
#define X 20
#define H (Y*2+1)
#define W (X*2+1)

int maze[H][W];
int scores[ROUNDS];

int x, y;

void print_maze(){
    char line[W+2];
    line[W+1]=0;
    for(int row=0;row<H;row++) {
        for(int col=0;col<W;col++) {
            switch(maze[row][col]) {
                case 0:
                    line[col]=' ';
                    break;
                case 1:
                    line[col]=row%2?'-':'|';
                    break;
                case 9:
                    line[col]=(row==y*2+1&&col==x*2+1)?'@':' ';
                    break;
            }
        }
        line[W]='\n';
        printf("%s",line);
    }
    printf("%d %d\n",y,x);
}

int main(){
    srand (time(NULL));
    long long total_turns = 0;
    for(int round=0;round<ROUNDS;round++) {
        for (int r=0;r<H;r++) {
            for (int c=0;c<W;c++) {
                if (r==0 || r==H-1 || c==0 || c==W-1) maze[r][c]=0; // edges
                else if (r%2) { // rows with cells and E/W paths
                    if (c%2) maze[r][c] = 9; // col with cells
                    else if (r==1 || r==H-2) maze[r][c]=1; // E/W path on N/Smost row
                    else if (c>r) maze[r][c]=1; // E/W path on chevron perimeter
                    else maze[r][c]=0; // cut path between cols
                } else { // rows with N/S paths
                    if (c%2==0) maze[r][c] = 0; // empty space
                    else if (c==1 || c==W-2) maze[r][c]=1; // N/S path on E/Wmost row
                    else if (r>c) maze[r][c]=1; // N/S path on chevron perimeter
                    else maze[r][c]=0;
                }
            }
        }
        int progress = 0;
        int first_cut = 0;
        x=0;
        y=0;
        if(DEBUG) print_maze();
        long long turn = 0;
        while (x!=X-1||y!=Y-1) {
            if(DEBUG) std::cin.ignore();
            turn++;
            int r = y*2+1;
            int c = x*2+1;
            int exits = maze[r-1][c] + maze[r][c+1] + maze[r+1][c] + maze[r][c-1];
            int exit_choice = -1;
            do {
                if (rand()%exits == 0) {
                    exit_choice = exits;
                    break;
                } else {
                    exits--;
                }
            }while(exits);
            int dx=0, dy=0;
            --exits;
            if (maze[r-1][c]&&!dx&&!dy) {
                if (exits) {
                    --exits;
                } else {
                    dy = -1;
                    dx = 0;
                }
            }
            if (maze[r][c+1]&&!dx&&!dy) {
                if (exits) {
                    --exits;
                } else {
                    dy = 0;
                    dx = 1;
                }
            }
            if (maze[r+1][c]&&!dx&&!dy) {
                if (exits) {
                    --exits;
                } else {
                    dy = 1;
                    dx = 0;
                }
            }
            if (maze[r][c-1]&&!dx&&!dy) {
                if (exits) {
                    --exits;
                } else {
                    dy = 0;
                    dx = -1;
                }
            }
            x+=dx;
            y+=dy;
            if (first_cut==0) {
                if(x==X-1 && y==progress*2+1) {
                    first_cut = 1;
                    maze[y*2+2][x*2+1]=0;
                }
                if(y==Y-1 && x==progress*2+1) {
                    first_cut = 2;
                    maze[y*2+1][x*2+2]=0;
                }
            }
            else if (first_cut==1) {
                if (y==Y-1 && x==progress*2) {
                    maze[y*2+1][x*2+2]=0;
                    progress++;
                    first_cut=0;
                }
                else if (y==Y-2 && x==progress*2+1) {
                    maze[y*2+2][x*2+1]=0;
                    progress++;
                    first_cut=0;
                }
            }
            else if (first_cut==2) {
                if (x==X-1 && y==progress*2) {
                    maze[y*2+2][x*2+1]=0;
                    progress++;
                    first_cut=0;
                }
                else if (x==X-2 && y==progress*2+1) {
                    maze[y*2+1][x*2+2]=0;
                    progress++;
                    first_cut=0;
                }
            }
            if(DEBUG) print_maze();
        }
        // printf("turns:%lld\n",turn);
        scores[round] = turn;
        total_turns += turn;
    }
    long long avg = total_turns/ROUNDS;
    printf("average: % 10lld\n",avg);
    long long var = 0;
    for(int r=0;r<ROUNDS;r++){
        var += (scores[r]-avg)*(scores[r]-avg);
    }
    var/=ROUNDS;
    // printf("variance: %lld\n",var);
    int stddev=sqrt(var);
    printf("stddev:  % 10d\n",stddev);

}

0

ไม่ทำอะไร

เนื่องจากผู้ชายเคลื่อนไหวแบบสุ่มคน ๆ นั้นอาจคิดว่าการลบโหนดใด ๆ จะเป็นการเพิ่มโอกาสในการกลับบ้านในระยะยาวเท่านั้น

ขั้นแรกให้ดูกรณีแบบหนึ่งมิติซึ่งสามารถทำได้โดยการลบโหนดจนกว่าคุณจะจบลงด้วยเส้นทางไก่เขี่ยโดยไม่ต้องหยุดหรือรอบที่เข้าชม (เกือบ) ทุกจุดกริด บนN x Nกริดความยาวสูงสุดของพา ธ ดังกล่าวคือL = N*N - 2 + N%2 (98 สำหรับกริด 10x10) การเดินไปตามเส้นทางสามารถอธิบายได้ด้วยเมทริกซ์การเปลี่ยนแปลงตามที่สร้างขึ้นโดยT1dเดินไปตามเส้นทางที่สามารถอธิบายได้ด้วยเมทริกซ์การเปลี่ยนแปลงเป็นที่สร้างขึ้นโดย เมทริกซ์การเปลี่ยนแปลง

(ความไม่สมดุลเล็กน้อยทำให้ยากที่จะหาวิธีการวิเคราะห์ยกเว้นเมทริกซ์ที่มีขนาดเล็กมากหรือไม่มีที่สิ้นสุด แต่เราได้คำตอบเชิงตัวเลขที่เร็วกว่าที่จะทำให้เมทริกซ์ทแยงมุมอยู่ดี)
สถานะเวกเตอร์มีหนึ่งเดียว1ที่ตำแหน่งเริ่มต้นและหลังKขั้นตอน(T1d**K) * stateทำให้เรามีการกระจายความน่าจะเป็นที่ระยะห่างจากจุดเริ่มต้นที่แน่นอน (ซึ่งเท่ากับค่าเฉลี่ยของการ2**Kเดินไปตามเส้นทางที่เป็นไปได้ทั้งหมด!)

เรียกใช้การจำลองสำหรับ10*L**2ขั้นตอนและประหยัดองค์ประกอบสุดท้ายของเวกเตอร์รัฐหลังจากที่แต่ละขั้นตอนซึ่งจะช่วยให้เราน่าจะเป็นของที่ได้ทำมันไปยังเป้าหมายหลังจากที่จำนวนหนึ่งของขั้นตอน - cd(t)การกระจายความน่าสะสม การแยกความแตกต่างทำให้เรามีโอกาสpในการบรรลุเป้าหมายอย่างแน่นอนในเวลาที่แน่นอน เพื่อหาเวลาเฉลี่ยที่เรารวมt*p(t) dt
เวลาเฉลี่ยในการเข้าถึงเป้าหมายนั้นเป็นสัดส่วนL**2กับปัจจัยที่ไปอย่างรวดเร็วมากถึง 1 ค่าเบี่ยงเบนมาตรฐานเกือบคงที่ที่ประมาณ 79% ของเวลาเฉลี่ย
กราฟนี้แสดงเวลาเฉลี่ยในการเข้าถึงเป้าหมายสำหรับความยาวเส้นทางที่แตกต่างกัน (สอดคล้องกับขนาดกริดของ 5x5 ถึง 15x15) ป้อนคำอธิบายรูปภาพที่นี่

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

จากนั้นเราจะเห็นได้ว่ากลยุทธ์ดูอัลพา ธ ที่สมดุลทำงานได้ดีที่สุดที่นี่ สำหรับกริดขนาดใหญ่ที่ค่าใช้จ่ายในการทำเส้นทางมากกว่าเล็กน้อยเมื่อเทียบกับขนาดของเราเราอาจจะดีกว่าที่จะเพิ่มจำนวนเส้นทางคล้ายกับที่ Peter Taylor อธิบายไว้ แต่รักษาความยาวให้สมดุล

ถ้าเราไม่ลบโหนดใด ๆ เลย?

จากนั้นเราจะมีโหนดที่สามารถเดินได้มากเป็นสองเท่าบวกกับทิศทางที่เป็นไปได้สี่ทิศทางแทนที่จะเป็นสองเส้นทาง ดูเหมือนว่ามันทำให้ไม่น่าไปไหนมาไหนเลย อย่างไรก็ตามการจำลองแสดงให้เห็นเป็นอย่างอื่นหลังจากนั้นเพียง 100 ขั้นตอนใน 10x10 ตารางคนสวยมีแนวโน้มที่จะบรรลุเป้าหมายของเขาดังนั้น trappin เขาในหมู่เกาะที่เป็นความพยายามที่ไร้ประโยชน์เนื่องจากคุณมีการซื้อขายที่มีศักยภาพN**2ที่คดเคี้ยวยาวเส้นทางกับเวลาที่เสร็จสิ้นเฉลี่ยN**4สำหรับ เกาะที่ผ่านN**2ขั้นตอน

ความน่าจะเป็นของการเดินบนกริด 2d

from numpy import *
import matplotlib.pyplot as plt

def L(N): # maximal length of a path on an NxN grid
    return N*N - 2 + N%2

def T1d(N): # transition along 1d path
    m = ( diag(ones(N-1),1) + diag(ones(N-1),-1) )/2.
    m[1,0] = 1
    m[-2,-1] = 0
    m[-1,-1] = 1
    return m

def walk(stepmatrix, state, N):
    data = zeros(N)
    for i in xrange(N):
        data[i] = state[-1]
        state = dot(stepmatrix, state)
    return data

def evaluate(data):
    rho = diff(data)/data[-1]
    t = arange(len(rho))
    av = sum(rho*t)
    stdev = sum((t-av)**2 * rho)**.5
    print 'average: %f\nstd: %f'%(av, stdev)
    return rho, av, stdev

gridsize = 10
M = T1d(L(gridsize))
initpos = zeros(L(gridsize))
initpos[0] = 1
cd = walk(M, initpos, L(gridsize)**2*5)

plt.subplot(2,1,1)
plt.plot(cd)
plt.title('p of reaching the goal after N steps')
plt.subplot(2,1,2)
plt.plot(evaluate(cd)[0])
plt.title('p of reaching the goal at step N')
plt.show()


''' 
# uncomment to run the 2D simulation
# /!\ WARNING /!\ generates a bunch of images, dont run on your desktop

def links(k,n):
    x = [k-n, k+n]
    if k%n != 0: x.append(k-1)
    if k%n != n-1: x.append(k+1)
    x = [i for i in x if 0<= i <n*n]
    return x

N = 10 # gridsize    

MM = zeros((N*N, N*N)) # build transition matrix
for i in range(N*N):
    temp = links(i,N)
    for j in temp: MM[i,j] = 1./len(temp)
MM[:,-1] = zeros(N*N)
MM[-1,-1] = 1

pos = zeros(N*N)
pos[0] = 1
for i in range(N*N):
    plt.imsave('grid_%.2d'%i, kron(pos.reshape((N,N)), ones((10,10))), cmap='gray')
    pos = dot(MM, pos)
'''

+1 สำหรับความพยายามและกราฟที่ดี แต่นี่ไม่ได้ตอบคำถามและสองคำแรกนั้นขัดแย้งกับบทสรุปของการวิเคราะห์ของคุณ และคุณควรติดป้ายแกนของกราฟ กราฟความน่าจะเป็นของคุณมีขนาดเท่าใดสำหรับกริด
justhalf

ภาพที่น่ารัก แต่ฉันไม่แน่ใจว่าคุณมีคำถามที่ถูกต้อง ตัวอย่างเช่น "เนื่องจากผู้ชายเคลื่อนไหวแบบสุ่มเราอาจคิดว่าการลบโหนดใด ๆ จะเป็นการเพิ่มโอกาสในการกลับบ้านในระยะยาวเท่านั้น" 1) ผู้ชายมักจะกลับถึงบ้านในที่สุดภายใต้กฎที่กำหนดไว้ดังนั้นสิ่งนี้ดูเหมือนจะไม่เกี่ยวข้องและ 2) เรากำลังลบขอบไม่ใช่โหนด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.