Giải quyết mê cung mà không có khả năng trở lại


11

Tôi cần phải viết một chương trình sẽ giải quyết mê cung. Mê cung có cấu trúc đồ thị, trong đó mỗi nút - một số phòng và các cạnh - thoát ra các phòng khác:

nhập mô tả hình ảnh ở đây

Sự chỉ rõ:

  • Chúng tôi bắt đầu từ một căn phòng ngẫu nhiên.
  • Mê cung có ngõ cụt, 0 hoặc vài lối thoát.
  • Chúng tôi không biết gì về tất cả mê cung, chỉ có số phòng hiện tại và danh sách các cửa từ đó.
  • Khi chúng tôi vào phòng mới, chúng tôi không biết chúng tôi đến từ đâu (cánh cửa nào từ phòng hiện tại dẫn chúng tôi trở lại phòng trước).
  • Chúng ta có thể nhận ra khi chúng ta đạt được lối ra.
  • Mỗi bước chúng tôi di chuyển từ phòng hiện tại đến một số cửa có sẵn từ nó.
  • Nếu chúng ta ở phòng 6 chẳng hạn, chúng ta không thể nhảy để bắt đầu. Nó giống như chuyển động của robot.
  • Chúng tôi biết chính xác ID của phòng hiện tại. Chúng tôi biết ID của mỗi cửa trong phòng hiện tại (không phải trong tất cả mê cung).
  • Chúng tôi điều khiển robot.

Tôi đã xem tất cả các thuật toán đã biết, nhưng tất cả chúng đều yêu cầu ít nhất khả năng bổ sung để quay lại phòng trước. Theo đặc điểm kỹ thuật, chúng tôi không thể sử dụng bất kỳ thuật toán tìm kiếm đường đi ngắn nhất (thực ra, tôi không cần ngắn nhất), vì chúng tôi chỉ biết về phòng hiện tại. Chúng ta không thể sử dụng các thuật toán theo tay trái (phải-), vì không biết hướng thoát. Có lẽ, giải pháp duy nhất đáp ứng đặc điểm kỹ thuật là chọn lối ra ngẫu nhiên trong mỗi phòng với hy vọng rằng lối thoát thời gian nào đó sẽ được tìm thấy ...

Bất kỳ đề nghị làm thế nào để giải quyết mê cung như vậy theo cách thông minh hơn?


2
Đây có phải là bài tập về nhà không?
MichaelHouse

2
Đó là một phần của bài tập về nhà. (Tôi có nên đánh dấu điều này bằng cách nào đó?). Tất cả các phòng đều có ID duy nhất.
Kyrylo M

2
Các phòng được đánh số như vậy trong bản vẽ? Có lẽ một cái gì đó về việc di chuyển đến số cao hơn? (hoặc thấp hơn tùy thuộc vào nơi bạn bắt đầu)
MichaelHouse

2
Có phải danh sách các lối thoát luôn theo cùng một thứ tự? Như trong, chúng ta có thể tạo một bản đồ khi chúng ta đi? Tôi đang ở phòng 5 và tôi đến phòng thứ 2 trong danh sách các phòng, tôi tìm phòng 4. Vì vậy, bây giờ tôi biết rằng phòng 5-> 2 (phòng 4).
MichaelHouse

4
@archer, trong khi đăng bài đôi có thể giúp bạn có thêm câu trả lời - bây giờ, một người khác muốn có câu trả lời cho câu hỏi, phải tìm hai trang web khác nhau, với các bộ câu trả lời khác nhau. Đó là lý do tại sao các quy tắc muốn có những câu hỏi đơn lẻ , để giúp người khác dễ dàng nhận được sự giúp đỡ hơn.
Cyclops

Câu trả lời:


3

Hmm, bạn biết số lượng phòng thực tế. Vì vậy, bạn có thể xây dựng một cấu trúc dữ liệu. Tôi đoán danh sách các lối thoát hiểm không có số phòng kèm theo. Nhưng sau khi chọn ngẫu nhiên, bạn biết ít nhất, có một kết nối.

Giả sử bạn đang ở phòng 4 và chọn một trong ba lối thoát ngẫu nhiên. Bây giờ hệ thống cho bạn biết, bạn đang ở phòng 6 và chỉ còn một lối thoát. Bạn chọn cái đó và quay lại phòng 4. Lúc này bạn đã thu thập được một số thông tin về cấu trúc của mê cung. Bây giờ bạn chọn một lối thoát khác và kết thúc trong phòng 5. Thông tin Moe về phòng 4 (một lối thoát đến 6, một lối thoát đến 5)

Bạn có thể chọn một lối thoát cụ thể? chúng có được đánh số không, giả sử nếu ở phòng 4 bạn chọn thoát một thì bạn luôn kết thúc ở 6? Nếu không, bạn ít nhất có thể tìm hiểu về các tuyến đường có thể.

Ok, chỉ cần đọc bình luận của bạn. Vì vậy, nếu lối ra có ID và những cái đó được liên kết tĩnh với một phòng (ngay cả khi bạn không biết cái nào để bắt đầu), bạn chỉ cần chọn một cái mới và nhớ các kết nối thoát / phòng và nhớ lối thoát nào đã được thử. Hãy thử các lối thoát chưa được kiểm tra cho đến khi bạn đã tìm kiếm từng phòng đơn.

Đó là cách thực sự đơn giản. Sau một vài bước, bạn sẽ có một danh sách kết nối ít nhiều hoàn chỉnh. Chỉ khi bạn vào một phòng mới, bạn mới có thể (trong một vài bước) chạy xung quanh một cách ngẫu nhiên. Nhưng với mỗi bước bạn có thêm thông tin và bất cứ khi nào bạn vào phòng được truy cập trước đó, bạn có thể đưa ra quyết định thông minh hơn (không kiểm tra lại phòng cuối 6, ví dụ như khi bạn tìm lại 4, vì nó không có lối thoát nào chưa được kiểm tra).

Chỉnh sửa Ý tưởng là thực hiện các bước ngẫu nhiên trước và ghi nhật ký thông tin bạn tìm thấy như tôi đã mô tả (mô tả của Dan ngắn gọn hơn). Nếu bạn thấy mình trong một căn phòng không có lối thoát không xác định, bạn có thể sử dụng bất kỳ đường dẫn nào đã biết để tìm đường đi ngắn nhất đến phòng có lối thoát mà bạn chưa khám phá.

Không chắc chắn 100%, nhưng tôi nghĩ Peter Norvig đã viết về một vấn đề tương tự (Mê cung Wumpus) trong cuốn sách "Trí tuệ nhân tạo: Cách tiếp cận hiện đại". Mặc dù nếu tôi nhớ đúng, nó ít hơn về việc tìm đường và nhiều hơn về việc ra quyết định liên quan đến một số thông tin mà hệ thống có thể nhận được về các phòng lân cận.

Biên tập:

Không, nó không chỉ ngẫu nhiên. Bằng cách theo dõi các lối thoát đã thử, bạn loại bỏ sự dư thừa không cần thiết. Trên thực tế, bạn thậm chí không cần phải chọn ngẫu nhiên.

Giả sử bạn bắt đầu trong phòng 4. Thông tin bạn nhận được: 3 lối ra A, B, C Bạn luôn chọn đầu tiên trước khi thoát không sử dụng. Vì vậy, hãy bắt đầu với A. Bạn kết thúc ở phòng 6. Bây giờ bạn nhớ 4A => 6 (và đã sử dụng) trong phòng 6, bạn nhận được thông tin 1 lối thoát A. Một lần nữa, bạn chọn lối thoát đầu tiên không sử dụng (và trong trường hợp này là chỉ thoát) Trở lại phòng để biết 6A => 4 (và không có lối thoát nào nữa trong phòng 6) Bây giờ bạn chọn lối thoát B tiếp theo và đến phòng 5 ...

Sớm hay muộn bạn sẽ tìm kiếm tất cả các phòng theo cách đó. Nhưng trong một vấn đề có hệ thống.

Lý do duy nhất cho những gì bạn sẽ cần một con đường tìm kiếm là, khi bạn thấy mình trong một căn phòng nơi tất cả các lối thoát hiểm đã được khám phá. Sau đó, bạn sẽ muốn tìm một cách trực tiếp đến phòng tiếp theo với các lối thoát chưa được khám phá để sở hữu với tìm kiếm của bạn. Vì vậy, mẹo chính là ít hơn để biết lối thoát nào dẫn đến phòng nào (mặc dù điều này có thể hữu ích) nhưng để theo dõi các lối thoát chưa được thử.

Cách này chẳng hạn, bạn có thể tránh chạy trong vòng tròn mọi lúc, điều gì sẽ là rủi ro cho cách tiếp cận hoàn toàn ngẫu nhiên.

Trong ví dụ của bạn mê cung này rất có thể sẽ không quan trọng lắm. Nhưng trong một mê cung rộng lớn với nhiều phòng và kết nối và có thể các phòng được sắp xếp hình tròn phức tạp, hệ thống này đảm bảo rằng bạn sẽ tìm thấy lối ra với càng ít thử nghiệm càng tốt.

(Tôi nghĩ đó có lẽ là hệ thống tương tự như Byte56 đã xuất hiện trong Game thủ)


Có, tôi có thể chọn lối ra cụ thể (cửa) ra khỏi phòng. Họ có ID duy nhất. Vì vậy, đề nghị của bạn là khám phá toàn bộ mê cung đầu tiên và sau đó sử dụng bất kỳ thuật toán được biết đến?
Kyrylo M

Tôi bắt đầu viết chương trình và nhận được một câu hỏi mới ... Đầu tiên tôi khám phá tất cả các phòng để xây dựng cấu trúc với tất cả các kết nối. Bước thứ hai sẽ là tìm đường dẫn. Nhưng tôi sẽ ngừng xây dựng khi tất cả các kết nối sẽ được thêm vào. Nhưng bằng cách đó tôi sẽ đạt được lối thoát nào ... Vì vậy, đây chỉ là thuật toán chọn hướng ngẫu nhiên ...
Kyrylo M

10

Tôi nhận ra rằng bạn có thể đã nhận được ý chính từ các câu trả lời khác, nhưng đó là một câu hỏi thú vị và tôi cảm thấy như đang thực hiện một mã hóa Python nhỏ. Đây là cách tiếp cận hướng đối tượng của tôi. Ấn độ xác định phạm vi.

Biểu diễn đồ thị

Biểu đồ có thể dễ dàng được lưu trữ dưới dạng khóa, từ điển giá trị trong đó khóa là id phòng và giá trị là một mảng các phòng mà nó dẫn đến.

map = {
1:[5, 2],
2:[1, 3, 5],
3:[2, 4],
4:[3, 5, 6],
5:[2, 4, 1],
6:[4]
}

Giao diện đại lý

Đầu tiên chúng ta nên suy nghĩ về những thông tin nào mà tác nhân có thể học được từ môi trường và các hoạt động mà nó sẽ có thể thực hiện. Điều này sẽ đơn giản hóa suy nghĩ về thuật toán.

Trong trường hợp này, nhân viên sẽ có thể truy vấn môi trường cho id của căn phòng mà nó đang ở, nó sẽ có thể lấy được số lượng cửa trong phòng mà nó đang ở ( lưu ý đây không phải là id của các phòng cánh cửa dẫn đến! ) và anh ta sẽ có thể di chuyển qua một cánh cửa bằng cách chỉ định một chỉ số cửa. Bất cứ điều gì khác mà một đại lý biết phải được tìm ra bởi chính các đại lý.

class AgentInterface(object):
    def __init__(self, map, starting_room):
        self.map = map
        self.current_room = starting_room

    def get_door_count(self):
        return len(self.map[self.current_room])

    def go_through_door(self, door):
        result = self.current_room = self.map[self.current_room][door]
        return result

Kiến thức đại lý

Khi nhân viên lần đầu tiên vào bản đồ, nó chỉ biết số lượng cửa trong phòng và id của căn phòng hiện tại. Tôi cần tạo ra một cấu trúc lưu trữ thông tin mà nhân viên đã học được như cửa nào không có xuyên qua, và nơi những cánh cửa dẫn đến điều đó đã được thông qua.

Lớp này đại diện cho thông tin về một phòng duy nhất. Tôi đã chọn lưu trữ các cửa không mong muốn như một setvà các cửa được truy cập là một dictionary, trong đó chìa khóa là id cửa và giá trị là id của căn phòng mà nó dẫn đến.

class RoomKnowledge(object):
    def __init__(self, unvisited_door_count):
        self.unvisited_doors = set(range(unvisited_door_count))
        self.visited_doors = {}

Thuật toán đại lý

  • Mỗi khi nhân viên vào phòng, nó sẽ tìm kiếm từ điển kiến ​​thức để biết thông tin về phòng. Nếu không có mục nào cho căn phòng này thì nó sẽ tạo một cái mới RoomKnowledgevà thêm nó vào từ điển kiến ​​thức của nó.

  • Nó kiểm tra xem phòng hiện tại có phải là phòng mục tiêu không, nếu vậy thì nó trở lại.

  • Nếu có những cánh cửa trong căn phòng này mà chúng tôi chưa ghé thăm, chúng tôi sẽ đi qua cánh cửa và lưu trữ nơi nó dẫn đến. Chúng tôi sau đó tiếp tục vòng lặp.

  • Nếu không có bất kỳ cánh cửa nào không có cửa, chúng tôi quay lại các phòng mà chúng tôi đã ghé thăm để tìm một cánh cửa không có cửa.

Các Agentkế thừa lớp từ AgentInterfacelớp.

class Agent(AgentInterface):

    def find_exit(self, exit_room_id):
        knowledge = { }
        room_history = [] # For display purposes only
        history_stack = [] # Used when we need to backtrack if we've visited all the doors in the room
        while True:
            room_knowledge = knowledge.setdefault(self.current_room, RoomKnowledge(self.get_door_count()))
            room_history.append(self.current_room)

            if self.current_room==exit_room_id:
                return room_history

            if len(room_knowledge.unvisited_doors)==0:
                # I have destination room id. I need door id:
                door = find_key(room_knowledge.visited_doors, history_stack.pop())
                self.go_through_door(door)
            else:   
                history_stack.append(self.current_room)
                # Enter the first unopened door:
                opened_door = room_knowledge.unvisited_doors.pop()
                room_knowledge.visited_doors[opened_door]=self.go_through_door(opened_door)

Chức năng hỗ trợ

Tôi đã phải viết một chức năng sẽ tìm thấy một khóa trong một từ điển được đưa ra một giá trị, vì khi quay lại, chúng tôi biết id của căn phòng mà chúng tôi đang cố gắng để đến, nhưng không sử dụng cửa nào để đi đến đó.

def find_key(dictionary, value):
    for key in dictionary:
        if dictionary[key]==value:
            return key

Kiểm tra

Tôi đã thử nghiệm tất cả các kết hợp của vị trí bắt đầu / kết thúc trong bản đồ được đưa ra ở trên. Đối với mỗi sự kết hợp, nó in ra các phòng thăm.

for start in range(1, 7):
    for exit in range(1, 7):
        print("start room: %d target room: %d"%(start,exit))
        james_bond = Agent(map, start)
        print(james_bond.find_exit(exit))

Ghi chú

Việc quay lui không hiệu quả lắm - trong trường hợp xấu nhất, nó có thể đi qua mọi phòng để đến phòng liền kề, nhưng quay lui là khá hiếm - trong các thử nghiệm ở trên, nó chỉ quay lại ba lần. Tôi đã tránh đưa xử lý ngoại lệ vào để giữ cho mã ngắn gọn. Mọi ý kiến ​​về Python của tôi đều đánh giá cao :)


Câu trả lời hoành tráng! Đáng buồn thay, không thể có hai câu trả lời :( Tôi đã viết chương trình bằng C # và thường sử dụng gần như cùng một ý tưởng. Để quay lại được sử dụng thuật toán tìm kiếm theo chiều sâu.
Kyrylo M

4

Về cơ bản, bạn có một biểu đồ hướng, trong đó mọi phòng được kết nối được kết nối bởi hai lối đi không xác định - một theo một trong hai hướng. Giả sử bạn bắt đầu trong nút 1, và cửa ABdẫn ra ngoài. Bạn không biết những gì nằm ngoài mỗi cánh cửa, vì vậy bạn chỉ cần chọn cửa A. Bạn có thể phòng 2, trong đó có cửa C, DE. Bây giờ bạn biết rằng cửa Adẫn từ phòng này 1sang phòng khác 2, nhưng bạn không biết làm thế nào để quay lại, vì vậy bạn chọn cửa một cách ngẫu nhiên C. Bạn trở về phòng 1! Bây giờ bạn biết làm thế nào để có được giữa các phòng 12. Tiếp tục khám phá qua cánh cửa gần nhất cho đến khi bạn tìm thấy lối ra!


4

Vì các phòng luôn theo thứ tự trong danh sách thoát, chúng tôi có thể tạo bản đồ nhanh cho các phòng trong khi tìm lối ra.

Mã giả của tôi hơi Java, xin lỗi, gần đây tôi đã sử dụng nó rất nhiều.

Unvisited_Rooms là một hashmap giữ ID phòng và một danh sách các chỉ mục của các phòng chưa được ánh xạ Hoặc một mảng 2d, bất cứ điều gì hoạt động.

Tôi tưởng tượng thuật toán có thể đi như thế này:

Unvisited_Rooms.add(currentRoom.ID, currentRoom.exits) //add the starting room exits
while(Unvisited_Rooms.Keys.Count > 0 && currentRoom != end) //keep going while there are unmapped exits and we're not at the end
    Room1 = currentRoom
    ExitID = Room1.get_first_unmapped_Room() //returns the index of the first unmapped room
    if(ExitID == -1) //this room didn't have any more unmapped rooms, it's totally mapped
        PathTo(Get_Next_Room_With_Unmapped_Exits) //we need to go to a room with unmapped exits
        continue //we need to start over once we're there, so we don't create false links
    GoToExit(ExitID) //goes to the room, setting current room to the room on the other side
    Room1.Exits[exitID].connection = currentRoom.ID //maps the connection for later path finding
    Unvisited_Rooms[Room1.ID].remove(exitID) //removes the index so we don't worry about it
    if(Unvisited_Rooms[Room1.ID].size < 1) //checks if all the rooms exits have been accounted for
        Unvisited_Rooms.remove(Room1.ID)  //removes the room if it's exits are all mapped
    Unvisited_Rooms.add(currentRoom.ID, currentRoom.unvisited_exits) //adds more exits to the list

If(currentRoom != end && Unvisited_Rooms.Keys.Count < 1)
   print(No exit found!)
else
   print(exit is roomID: currentRoom.ID)

Bạn sẽ cần sử dụng một trong những công cụ tìm đường dẫn nút chung cho các phòng PathTo () trên "bản đồ" Hy vọng điều đó đủ rõ ràng để bạn bắt đầu một thứ gì đó.


Đây là một upvote, @ Byte56 - chiếm 2/3 số dấu bạn đã mất. :)
Cyclops

2

Tôi không quá rõ ràng về các yêu cầu của bạn, nhưng nếu những điều sau đây được cho phép, nó có thể là một thuật toán đơn giản để làm theo. Có lẽ là một lỗi trong đó, đặc biệt là vì nó sử dụng chức năng đệ quy. Ngoài ra, nó sẽ kiểm tra xem một cánh cửa dẫn đến căn phòng bạn đến từ đâu, nhưng tôi không biết con đường hình tròn ba phòng sẽ xử lý như thế nào, nó có thể cứ lặp đi lặp lại mãi mãi trong ba căn phòng đó. Bạn có thể phải thêm một lịch sử để đảm bảo không có phòng nào được kiểm tra hai lần. Nhưng bằng cách đọc mô tả của bạn có thể không được phép.

solved = FALSE

SearchRoom(rooms[0], rooms[0])    // Start at room 1 (or any room)
IF solved THEN
  // solvable
ELSE
  // unsolvable
ENDIF
END

// Recursive function, should run until it searches every room or finds the exit
FUNCTION SearchRoom: curRoom, prevRoom
  // Is this room the 'exit' room
  IF curRoom.IsExit() THEN
    solved = TRUE
    RETURN
  ENDIF

  // Deadend?  (skip starting room)
  IF (curRoom.id <> prevRoom.id) AND (curRoom.doors <= 1) THEN RETURN

  // Search each room the current room leads to
  FOREACH door IN curRoom
    // Skip the room we just came from
    IF door.id <> prevRoom.id THEN
      SearchRoom(door, curRoom)
    ENDIF
    IF solved THEN EXIT LOOP
  NEXT

  RETURN
ENDFUNCTION

[Chỉnh sửa] Đã thêm 'id' vào kiểm tra trước đó và được cập nhật để hướng đối tượng nhiều hơn.


1
Tôi không biết trên mỗi bước tôi đến từ đâu. Vì vậy, thuật toán này không giải quyết được nhiệm vụ. Nhưng cảm ơn vì đã cố gắng.
Kyrylo M

3
Vì vậy, bạn đang nói rằng bạn KHÔNG ĐƯỢC PHÉP để biết phòng trước? Hoặc bạn không biết phòng trước? Mã ở trên theo dõi các phòng trước cho bạn. Nếu bạn không được phép biết, tôi không nghĩ rằng có một giải pháp hợp lệ ngoài việc lặp lại ngẫu nhiên mê cung cho số lần thử 'x' và nếu bạn không thể tìm thấy lối ra, bạn có thể cho rằng mê cung là không thể giải quyết được .
Doug.McFarlane

1
"Tôi không biết". Tôi lại nhìn mã. Vấn đề thứ hai là sử dụng đệ quy là có vấn đề. Hãy tưởng tượng, rằng bạn đang ở trong mê cung như vậy. Làm thế nào bạn sẽ sử dụng thuật toán đệ quy để tìm lối ra?
Kyrylo M

Ngoài ra, điều gì xảy ra nếu bạn bắt đầu ở phòng 6? curRoom.doors <= 1, do đó, hàm trả về ngay lập tức, biết rằng nó đang ở một ngõ cụt, nhưng nghĩ rằng nó đã khám phá toàn bộ mê cung.
dlras2

Điều này là gần, nhưng nó sẽ thổi stack nếu có chu kỳ trong đồ thị có độ dài lớn hơn hai.
khoan hồng

1

Câu trả lời ngắn gọn là tìm kiếm theo chiều sâu với quay lui. Bạn có thể làm chiều rộng trước nếu bạn thích, nhưng con robot nhỏ của bạn sẽ đi bộ nhiều hơn sau đó.

Cụ thể hơn, giả sử chúng tôi đã đưa ra:

// Moves to the given room, which must have a door between
// it and the current room.
moveTo(room);

// Returns the list of room ids directly reachable from
// the current room.
getDoors();

// Returns true if this room is the exit.
isExit();

Để tìm lối ra, chúng ta chỉ cần:

void escape(int startingRoom) {
  Stack<int> path = new Stack<int>();
  path.push(startingRoom);
  escape(path);
}

boolean escape(Stack<int> path) {
  for (int door : getDoors()) {
    // Stop if we've escaped.
    if (isExit()) return true;

    // Don't walk in circles.
    if (path.contains(door)) continue;

    moveTo(door);
    path.push(door);
    if (escape(path)) return true;

    // If we got here, the door didn't lead to an exit. Backtrack.
    path.pop();
    moveTo(path.peek());
  }
}

Gọi escape()bằng ID của phòng bắt đầu và nó sẽ di chuyển robot đến lối ra (bằng cách gọi moveTo()).


Tôi nghĩ escape(int startingRoom)nên gọiescape(Stack<int> path)
CiscoIPPhone

1
Tôi nghĩ rằng if (path.contains(door)) continue;vi phạm các yêu cầu của anh ta - người đại diện không thực sự biết nếu một cánh cửa dẫn trở lại một căn phòng mà anh ta đã ở trừ khi anh ta đi qua cửa.
CiscoIPPhone

Cảm ơn, đã sửa! Vâng, bây giờ tôi nhìn vào các yêu cầu, vấn đề có vẻ hơi tanh. Nếu bạn không thể quay lại, điều tốt nhất bạn có thể hy vọng là đi bộ ngẫu nhiên.
hào phóng
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.