สิ่งที่คุณกำลังอธิบายคือปัญหาการแบ่งกลุ่ม ฉันขอโทษที่ต้องบอกว่ามันเป็นปัญหาที่แก้ไม่ได้จริงๆ แต่วิธีหนึ่งที่ฉันอยากจะแนะนำสำหรับเรื่องนี้คืออัลกอริทึมที่ใช้กราฟ-Cut Graph-Cut แสดงภาพเป็นกราฟของโหนดที่เชื่อมต่อในเครื่อง มันแบ่งย่อยส่วนประกอบที่เชื่อมต่อของกราฟแบบซ้ำ ๆ เพื่อให้เส้นแบ่งระหว่างองค์ประกอบย่อยทั้งสองมีความยาวน้อยที่สุดโดยใช้ทฤษฎีบท Max-flow-min-cutและอัลกอริทึมของFord Fulkerson
โดยพื้นฐานแล้วคุณเชื่อมต่อแผ่นน้ำทั้งหมดลงในกราฟ กำหนดน้ำหนักให้กับขอบในกราฟซึ่งสอดคล้องกับความแตกต่างระหว่างแผ่นน้ำที่อยู่ติดกัน ฉันคิดว่าในกรณีของคุณน้ำหนักทั้งหมดอาจเป็น 1 คุณจะต้องเล่นกับแผนการถ่วงน้ำหนักที่แตกต่างกันเพื่อให้ได้ผลลัพธ์ที่ต้องการ คุณอาจต้องเพิ่มน้ำหนักที่รวมถึงคำคุณศัพท์ไปยังชายฝั่ง
จากนั้นค้นหาส่วนประกอบที่เชื่อมต่อทั้งหมดของกราฟ เหล่านี้เป็นทะเล / ทะเลสาบที่เห็นได้ชัดและอื่น ๆ
สุดท้ายสำหรับแต่ละองค์ประกอบที่เกี่ยวโยงกันซ้ำแบ่งองค์ประกอบดังกล่าวที่ขอบเชื่อมต่อใหม่ทั้งสององค์ประกอบย่อยมีน้ำหนักต่ำสุด แบ่งย่อยแบบวนซ้ำเรื่อย ๆ จนกว่าส่วนประกอบย่อยทั้งหมดจะมีขนาดเล็กที่สุด (เช่นขนาดสูงสุดของทะเล) หรือหากขอบที่ตัดทั้งสององค์ประกอบมีน้ำหนักสูงเกินไป สุดท้ายติดฉลากส่วนประกอบที่เชื่อมต่อทั้งหมดที่เหลืออยู่
ในทางปฏิบัติสิ่งนี้จะตัดทะเลออกจากกันในช่องทาง แต่ไม่ครอบคลุมมหาสมุทรขนาดใหญ่
นี่คือใน pseudocode:
function SegmentGraphCut(Map worldMap, int minimumSeaSize, int maximumCutSize)
Graph graph = new Graph();
// First, build the graph from the world map.
foreach Cell cell in worldMap:
// The graph only contains water nodes
if not cell.IsWater():
continue;
graph.AddNode(cell);
// Connect every water node to its neighbors
foreach Cell neighbor in cell.neighbors:
if not neighbor.IsWater():
continue;
else:
// The weight of an edge between water nodes should be related
// to how "similar" the waters are. What that means is up to you.
// The point is to avoid dividing bodies of water that are "similar"
graph.AddEdge(cell, neighbor, ComputeWeight(cell, neighbor));
// Now, subdivide all of the connected components recursively:
List<Graph> components = graph.GetConnectedComponents();
// The seas will be added to this list
List<Graph> seas = new List<Graph>();
foreach Graph component in components:
GraphCutRecursive(component, minimumSeaSize, maximumCutSize, seas);
// Recursively subdivides a component using graph cut until all subcomponents are smaller
// than a minimum size, or all cuts are greater than a maximum cut size
function GraphCutRecursive(Graph component, int minimumSeaSize, int maximumCutSize, List<Graph> seas):
// If the component is too small, we're done. This corresponds to a small lake,
// or a small sea or bay
if(component.size() <= minimumSeaSize):
seas.Add(component);
return;
// Divide the component into two subgraphs with a minimum border cut between them
// probably using the Ford-Fulkerson algorithm
[Graph subpartA, Graph subpartB, List<Edge> cut] = GetMinimumCut(component);
// If the cut is too large, we're done. This corresponds to a huge, bulky ocean
// that can't be further subdivided
if (GetTotalWeight(cut) > maximumCutSize):
seas.Add(component);
return;
else:
// Subdivide each of the new subcomponents
GraphCutRecursive(subpartA, minimumSeaSize, maximumCutSize);
GraphCutRecursive(subpartB, minimumSeaSize, maximumCutSize);
แก้ไข : โดยวิธีต่อไปนี้เป็นสิ่งที่อัลกอริทึมจะทำกับตัวอย่างของคุณด้วยขนาดน้ำทะเลขั้นต่ำที่ตั้งไว้ที่ประมาณ 40 โดยมีขนาดการตัดสูงสุดที่ 1 ถ้าน้ำหนักขอบทั้งหมดเป็น 1:
ด้วยการเล่นกับพารามิเตอร์คุณจะได้รับผลลัพธ์ที่แตกต่าง ยกตัวอย่างเช่นขนาดตัดสูงสุด 3 จะส่งผลให้ช่องอื่น ๆ อีกมากมายได้รับการแกะสลักออกจากทะเลหลักและทะเล # 1 จะได้รับการแบ่งย่อยในครึ่งเหนือและใต้ ขนาดต่ำสุดของทะเลที่ 20 จะส่งผลให้ทะเลกลางได้รับการแบ่งครึ่งเช่นกัน