Datei hochladen, nach Git-Problemen
commit
c9976d54d1
|
@ -0,0 +1,411 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
SIN30=Math.sin(Math.PI/6)
|
||||
COS30=Math.cos(Math.PI/6)
|
||||
SIN90=Math.sin(Math.PI/2)
|
||||
COS90=Math.cos(Math.PI/2)
|
||||
totalFlow=0;
|
||||
done=false;
|
||||
iterations=0;
|
||||
vertices=[];
|
||||
verticesMap={};
|
||||
edgesCapacity={};
|
||||
edgesFlow={};
|
||||
shownEdges="capacity_and_flows";
|
||||
editMode="drag";
|
||||
draggedVertex=null;
|
||||
possibleNextSteps=[];
|
||||
flowVertices=["s"];
|
||||
function draw(){
|
||||
function drawEdge(vertexFrom,vertexTo,number,fillColor,strokeColor){
|
||||
co.fillStyle=fillColor;
|
||||
co.strokeStyle=strokeColor;
|
||||
xDistance=vertexFrom.x-vertexTo.x;
|
||||
yDistance=vertexFrom.y-vertexTo.y;
|
||||
let divideToGetLength15=Math.sqrt((xDistance)**2+(yDistance)**2)/15;
|
||||
let xOfTotal15=(vertexTo.x-vertexFrom.x)/divideToGetLength15
|
||||
let yOfTotal15=(vertexTo.y-vertexFrom.y)/divideToGetLength15
|
||||
let rotatedXOfTotal15=xOfTotal15*COS30-yOfTotal15*SIN30;
|
||||
let rotatedYOfTotal15=xOfTotal15*SIN30+yOfTotal15*COS30;
|
||||
let otherDirectionRotatedXOfTotal15=xOfTotal15*COS30+yOfTotal15*SIN30;
|
||||
let otherDirectionRotatedYOfTotal15=-xOfTotal15*SIN30+yOfTotal15*COS30;
|
||||
let rotated90DegX=xOfTotal15*COS90-yOfTotal15*SIN90;
|
||||
let rotated90DegY=xOfTotal15*SIN90+yOfTotal15*COS90;
|
||||
co.beginPath();
|
||||
co.moveTo(vertexFrom.x+rotatedXOfTotal15,vertexFrom.y+rotatedYOfTotal15);
|
||||
co.lineTo(vertexFrom.x+rotatedXOfTotal15-xDistance-xOfTotal15*2,vertexFrom.y+rotatedYOfTotal15-yDistance-yOfTotal15*2);
|
||||
co.lineTo(vertexFrom.x-xDistance-xOfTotal15*2,vertexFrom.y-yDistance-yOfTotal15*2);
|
||||
co.lineTo(vertexFrom.x+rotatedXOfTotal15-xDistance-xOfTotal15*2,vertexFrom.y+rotatedYOfTotal15-yDistance-yOfTotal15*2);
|
||||
co.lineTo(vertexFrom.x+rotatedXOfTotal15-otherDirectionRotatedXOfTotal15-xDistance-xOfTotal15*2,vertexFrom.y+rotatedYOfTotal15-otherDirectionRotatedYOfTotal15-yDistance-yOfTotal15*2);
|
||||
co.stroke();
|
||||
|
||||
co.fillText(number,(vertexFrom.x+vertexTo.x)/2+rotated90DegX,(vertexFrom.y+vertexTo.y)/2+rotated90DegY);
|
||||
}
|
||||
co=c.getContext("2d");
|
||||
co.textAlign="center";
|
||||
co.textBaseline="middle";
|
||||
co.fillStyle="#888888"
|
||||
co.fillRect(0,0,1000,1000);
|
||||
for(let vertex of vertices){
|
||||
if(vertex.special=="source"){
|
||||
co.fillStyle="#aaffaa";
|
||||
}else if(vertex.special=="target"){
|
||||
co.fillStyle="#ffaaaa";
|
||||
}else{
|
||||
co.fillStyle="white";
|
||||
for(let alreadyVisited of flowVertices){
|
||||
if(alreadyVisited==vertex.name){
|
||||
co.fillStyle="#aaaaff";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
co.beginPath();
|
||||
co.arc(vertex.x,vertex.y,15,0,Math.PI*2);
|
||||
co.fill();
|
||||
if(editMode=="flow"){
|
||||
for(let possibleStep of possibleNextSteps){
|
||||
if(possibleStep==vertex.name){
|
||||
co.strokeStyle="blue";
|
||||
co.beginPath();
|
||||
co.arc(vertex.x,vertex.y,15,0,Math.PI*2);
|
||||
co.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
co.fillStyle="black";
|
||||
co.fillText(vertex.name,vertex.x,vertex.y,20);
|
||||
|
||||
for(let otherVertex of vertices){
|
||||
if(shownEdges=="capacity"){
|
||||
if(edgesCapacity[vertex.name][otherVertex.name]>0){
|
||||
drawEdge(vertex,otherVertex,edgesCapacity[vertex.name][otherVertex.name],"black","white");
|
||||
}
|
||||
}else if(shownEdges=="flows"){
|
||||
if(edgesFlow[vertex.name][otherVertex.name]>0){
|
||||
drawEdge(vertex,otherVertex,edgesFlow[vertex.name][otherVertex.name],"black","#8888ff");
|
||||
}
|
||||
}else if(shownEdges=="residuals"){
|
||||
if(getResidualEdge(vertex.name,otherVertex.name)>0){
|
||||
drawEdge(vertex,otherVertex,getResidualEdge(vertex.name,otherVertex.name),"black","orange");
|
||||
}
|
||||
}else if(shownEdges=="capacity_and_flows"){
|
||||
if(edgesCapacity[vertex.name][otherVertex.name]>0){
|
||||
drawEdge(vertex,otherVertex,getEdge(edgesFlow,vertex.name,otherVertex.name)+"/"+edgesCapacity[vertex.name][otherVertex.name],"black","white");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(let i=0;i<flowVertices.length-1;i++){
|
||||
drawEdge(verticesMap[flowVertices[i]],verticesMap[flowVertices[i+1]],"","blue","blue");
|
||||
};
|
||||
}
|
||||
function getEdge(edge,node1,node2){
|
||||
let value=edge[node1][node2];
|
||||
if(value==undefined){
|
||||
return 0;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
function getResidualEdge(node1,node2){
|
||||
return getEdge(edgesCapacity,node1,node2)-getEdge(edgesFlow,node1,node2)+getEdge(edgesFlow,node2,node1);
|
||||
}
|
||||
function parseGraph(){
|
||||
try{
|
||||
done=false;
|
||||
iterations=0;
|
||||
totalFlow=0;
|
||||
vertices=[{name:"s",x:30,y:150,special:"source"},{name:"t",x:570,y:150,special:"target"}];
|
||||
verticesMap={s:vertices[0],t:vertices[1]};
|
||||
edgesCapacity={s:{},t:{}};
|
||||
edgesFlow={s:{},t:{}};
|
||||
possibleNextSteps=[];
|
||||
flowVertices=["s"];
|
||||
let verticesText = vDef.value.split(";");
|
||||
vertexNumber=0;
|
||||
for (let vertexName of verticesText){
|
||||
vertexNumber++;
|
||||
let trimmedVertexName=vertexName.trim();
|
||||
if(trimmedVertexName==""||trimmedVertexName=="s"||trimmedVertexName=="t"){
|
||||
continue;
|
||||
}
|
||||
if(verticesMap[trimmedVertexName]!=undefined){
|
||||
throw new Error("Knoten können nicht doppelt vorkommen");
|
||||
}
|
||||
vertices.push({name:trimmedVertexName,x:100+vertexNumber*25,y:150,special:""});
|
||||
verticesMap[trimmedVertexName]=vertices[vertices.length-1];
|
||||
edgesCapacity[trimmedVertexName]={};
|
||||
edgesFlow[trimmedVertexName]={};
|
||||
}
|
||||
for(let i=2;i<vertices.length;i++){
|
||||
vertices[i].x=300+Math.cos(Math.PI*2*i/(vertices.length-2))*150;
|
||||
vertices[i].y=150+Math.sin(Math.PI*2*i/(vertices.length-2))*135;
|
||||
}
|
||||
|
||||
let edgesText=eDef.value.split(";");
|
||||
for(let edgesLine of edgesText){
|
||||
if(edgesLine.trim()==""){
|
||||
continue;
|
||||
}
|
||||
let edgesLineSplit=edgesLine.split(":");
|
||||
let capacity=Number(edgesLineSplit[1]);
|
||||
let edgesVertices=edgesLineSplit[0].split(",");
|
||||
let fromName=edgesVertices[0].trim();
|
||||
let toName=edgesVertices[1].trim();
|
||||
|
||||
if(isNaN(capacity)){
|
||||
throw new Error("Zahl "+edgesLineSplit[1]+" nicht erkannt");
|
||||
}
|
||||
if(capacity<0){
|
||||
throw new Error("Zahl "+edgesLineSplit[1]+" ist negativ");
|
||||
}
|
||||
if(fromName==toName){
|
||||
throw new Error("Knoten kann keine Kante zu sich selbst haben");
|
||||
}
|
||||
if(verticesMap[fromName]==null){
|
||||
throw new Error("Knoten "+fromName+" nicht vorhanden");
|
||||
}
|
||||
if(verticesMap[toName]==null){
|
||||
throw new Error("Knoten "+toName+" nicht vorhanden");
|
||||
}
|
||||
if(edgesCapacity[fromName][toName]>0/*||edgesCapacity[toName][fromName]>0*/){//Kommentar wegmachen, um doppelte Kanten nicht zuzulassen
|
||||
throw new Error("Kante von "+fromName+" nach "+toName+" kommt mehrfach vor");
|
||||
}
|
||||
edgesCapacity[fromName][toName]=capacity;
|
||||
}
|
||||
|
||||
statusDef.innerText="Graph erfolgreich erstellt";
|
||||
statusSim.innerText="Graph neu erstellt";
|
||||
}catch(error){
|
||||
statusDef.innerText=error.toString();
|
||||
}
|
||||
calculatePossibleNextSteps();
|
||||
draw();
|
||||
}
|
||||
function bfs(){
|
||||
let searches=[["s"]];
|
||||
let visitedVertices=["s"];
|
||||
let end=false;
|
||||
while(searches.length>0){
|
||||
currentSearch=searches.shift();
|
||||
if(currentSearch[currentSearch.length-1]=="t"){
|
||||
return currentSearch;
|
||||
}
|
||||
for(let otherVertex in verticesMap){
|
||||
let skip=false;
|
||||
for(let visitedVertex of visitedVertices){
|
||||
if(visitedVertex==otherVertex){
|
||||
skip=true;
|
||||
break;
|
||||
console.log("skipping");
|
||||
}
|
||||
}
|
||||
if(!skip&&getResidualEdge(currentSearch[currentSearch.length-1],otherVertex)>0){
|
||||
searches.push(currentSearch.concat(otherVertex));
|
||||
visitedVertices.push(otherVertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function selectShortestPath(){
|
||||
result=bfs();
|
||||
if(result!=undefined){
|
||||
flowVertices=result;
|
||||
} else{
|
||||
flowVertices=["s"];
|
||||
}
|
||||
calculatePossibleNextSteps();
|
||||
draw();
|
||||
}
|
||||
function selectRandomPath(){
|
||||
flowVertices=["s"];
|
||||
let visitedVertices=["s"];
|
||||
for(let i=0;i<2000;i++){
|
||||
calculatePossibleNextSteps();
|
||||
let nextSteps=[];
|
||||
for(let step of possibleNextSteps){
|
||||
let alreadyVisited=false;
|
||||
for(let visitedVertex of visitedVertices){
|
||||
if(visitedVertex==step){
|
||||
alreadyVisited=true;
|
||||
}
|
||||
}
|
||||
if(!alreadyVisited){
|
||||
nextSteps.push(step);
|
||||
}
|
||||
}
|
||||
if(nextSteps.length==0){
|
||||
if(flowVertices.length==1){return}
|
||||
stepTo(flowVertices[flowVertices.length-2]);
|
||||
} else {
|
||||
let vertexToVisit=nextSteps[Math.floor(Math.random()*nextSteps.length)%nextSteps.length];
|
||||
visitedVertices.push(vertexToVisit);
|
||||
stepTo(vertexToVisit);
|
||||
if(vertexToVisit=="t"){
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("random path doesn't end after 2000 iterations");
|
||||
}
|
||||
function edmondsKarp(){
|
||||
while(!done){
|
||||
selectShortestPath();
|
||||
generateNewFlow();
|
||||
}
|
||||
}
|
||||
function randomFordFulkerson(){
|
||||
while(!done){
|
||||
selectRandomPath();
|
||||
generateNewFlow();
|
||||
}
|
||||
}
|
||||
function calculatePossibleNextSteps(){
|
||||
possibleNextSteps=[];
|
||||
let flowFrontVertex = flowVertices[flowVertices.length-1];
|
||||
if(flowFrontVertex=="t"){
|
||||
return;
|
||||
}
|
||||
for(let vertex in verticesMap){
|
||||
if(getResidualEdge(flowFrontVertex,vertex)>0){
|
||||
let alreadyVisited=false;
|
||||
for(let alreadyVisitedVertex of flowVertices){
|
||||
if(alreadyVisitedVertex==vertex){
|
||||
alreadyVisited=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!alreadyVisited){
|
||||
possibleNextSteps.push(vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function stepTo(vertex){
|
||||
for(let i=0;i<flowVertices.length;i++){
|
||||
if(flowVertices[i]==vertex){
|
||||
while(flowVertices.length>i+1){
|
||||
flowVertices.pop();
|
||||
}
|
||||
calculatePossibleNextSteps();
|
||||
draw();
|
||||
return;
|
||||
}
|
||||
}
|
||||
for(let possibleStep of possibleNextSteps){
|
||||
if(vertex==possibleStep){
|
||||
possible=true;
|
||||
flowVertices.push(vertex);
|
||||
calculatePossibleNextSteps();
|
||||
draw();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
function generateNewFlow(){
|
||||
if(done){
|
||||
return;
|
||||
}
|
||||
if(flowVertices[flowVertices.length-1]!="t"){
|
||||
statusSim.innerText="ausgewählter Pfad ist kein s-t-Pfad";
|
||||
return;
|
||||
}
|
||||
newFlow=Infinity;
|
||||
for(let i=0;i<flowVertices.length-1;i++){
|
||||
newFlow=Math.min(newFlow,getResidualEdge(flowVertices[i],flowVertices[i+1]));
|
||||
}
|
||||
for(let i=0;i<flowVertices.length-1;i++){
|
||||
let oppositeFlowMinusNewFlow=getEdge(edgesFlow,flowVertices[i+1],flowVertices[i])-newFlow;
|
||||
if(oppositeFlowMinusNewFlow>=0){
|
||||
edgesFlow[flowVertices[i+1]][flowVertices[i]]=oppositeFlowMinusNewFlow;
|
||||
} else {
|
||||
edgesFlow[flowVertices[i+1]][flowVertices[i]]=0;
|
||||
edgesFlow[flowVertices[i]][flowVertices[i+1]]= getEdge(edgesFlow,flowVertices[i],flowVertices[i+1])-oppositeFlowMinusNewFlow;
|
||||
}
|
||||
}
|
||||
iterations++;
|
||||
totalFlow+=newFlow;
|
||||
flowVertices=["s"];
|
||||
calculatePossibleNextSteps();
|
||||
draw();
|
||||
statusSim.innerText="Iteration "+iterations+". Augmentierender Pfad mit Flusskapazität "+newFlow+" hinzugefügt. Insgesamt hat der Fluss die Kapazität "+totalFlow+".";
|
||||
if(bfs()==undefined){
|
||||
statusSim.innerText+=" Es gibt keinen augmentierenden Pfad mehr, der Algorithmus ist fertig.";
|
||||
done=true;
|
||||
};
|
||||
}
|
||||
function resetToDefaultGraph(){
|
||||
vDef.value="u;v;w;x;y;z;";
|
||||
eDef.value="s,v:4;\ns,u:5;\ns,w:2;\nu,v:4;\nv,u:4;\nv,w:3;\nw,x:2;\nv,x:4;\nu,z:3;\nz,y:2;\nx,y:3;\nz,x:4;\ny,t:4;\nx,t:6;\nx,u:7;";
|
||||
}
|
||||
function initializeEventHandlers(){
|
||||
c.onmousedown=clickEventHandler;
|
||||
c.onmousemove=mousemoveEventHandler;
|
||||
document.onmouseup=globalMouseupEventHandler;
|
||||
}
|
||||
function clickEventHandler(event){
|
||||
let mousex=(event.offsetX*c.width)/c.clientWidth;
|
||||
let mousey=(event.offsetY*c.height)/c.clientHeight;
|
||||
for(let i=vertices.length-1;i>=0;i--){
|
||||
let vertex=vertices[i];
|
||||
if(Math.sqrt((vertex.x-mousex)**2+(vertex.y-mousey)**2)<=15){
|
||||
if(editMode=="drag"){
|
||||
draggedVertex=vertex;
|
||||
} else if(editMode=="flow"){
|
||||
stepTo(vertex.name);
|
||||
} else {
|
||||
console.log("unknown edit mode");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
function mousemoveEventHandler(event){
|
||||
let mousex=(event.offsetX*c.width)/c.clientWidth;
|
||||
let mousey=(event.offsetY*c.height)/c.clientHeight;
|
||||
if(draggedVertex!=null){
|
||||
draggedVertex.x=mousex;
|
||||
draggedVertex.y=mousey;
|
||||
draw();
|
||||
}
|
||||
}
|
||||
function globalMouseupEventHandler(event){
|
||||
draggedVertex=null;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
margin-left:8%;
|
||||
margin-right:8%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="initializeEventHandlers();resetToDefaultGraph();parseGraph()">
|
||||
<h1>Simulation des Ford-Fulkersen-Algorithmus</h1>
|
||||
|
||||
<h2> Graph definieren </h2>
|
||||
<b>Knoten</b>, mit Semikolon getrennt (s und t werden nicht mit angegeben)
|
||||
<br><textarea id="vDef" rows=5> </textarea>
|
||||
<br><b>Kanten</b> und Kapazitäten, im Format "u,v:kapazität;"
|
||||
<br><textarea id="eDef" rows=12> </textarea>
|
||||
<br><button onclick="parseGraph()">Graph erstellen</button><button onclick="resetToDefaultGraph()">Zurück zu Beispielgraph</button>
|
||||
<br><span id="statusDef"> </span>
|
||||
|
||||
<h2> Graphensimulation </h2>
|
||||
Zeige Kanten:
|
||||
<button onclick="shownEdges='capacity';draw()"> Kapazitäten </button>
|
||||
<button onclick="shownEdges='flows';draw()"> Flüsse </button>
|
||||
<button onclick="shownEdges='capacity_and_flows';draw()"> Kapazitäten und Flüsse </button>
|
||||
<button onclick="shownEdges='residuals';draw()"> Residualkapazitäten </button>
|
||||
<br> Mausklick:
|
||||
<button onclick="editMode='drag';draw()"> verschiebt Kanten </button>
|
||||
<button onclick="editMode='flow';draw()"> wählt Flusspfade aus </button>
|
||||
<br> Aktion: <button onclick="generateNewFlow()">Fluss aus ausgewähltem augmentierenden Pfad erstellen</button> <button onclick="selectShortestPath();">kürzesten s-t-Pfad auswählen (Edmonds-Karp)</button> <button onclick="selectRandomPath();">zufälligen s-t-Pfad auswählen</button> <button onclick="edmondsKarp()">Edmonds-Karp-Algorithmus vollständig durchlaufen lassen</button> <button onclick="randomFordFulkerson()">Zufalls-Ford-Fulkerson-Algorithmus vollständig durchlaufen lassen</button>
|
||||
<br> <span id="statusSim"> </span>
|
||||
<canvas id="c" width=600 height=300 style="width:100%"> </canvas>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue