Gozintograph: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
|||
| Zeile 1: | Zeile 1: | ||
Ein '''Gozintograph''' (von engl. | Ein '''Gozintograph''' (von engl. ''goes into'' = „geht hinein“) ist ein gerichteter Graph, der die Zerlegung eines Endprodukts in seine Einzelteile oder Komponenten beschreibt. | ||
Jede Kante stellt dabei eine „Gozinto“-Beziehung dar: Sie zeigt von einer Komponente | Jede Kante stellt dabei eine „Gozinto“-Beziehung dar: Sie zeigt von einer Komponente auf das Produkt, in das sie eingeht. Der Gozintograph ist ein zentrales Hilfsmittel in der Produktionsplanung und Stücklistenverwaltung. | ||
== Definition == | == Definition == | ||
Ein Gozintograph ist ein gerichteter, azyklischer Graph | |||
:<math>G=(V,E)</math> | |||
mit: | |||
* <math>V</math> als Menge der Knoten (Produkte oder Komponenten), | |||
* <math>E \subseteq V \times V</math> als Menge der gerichteten Kanten. | |||
Zusätzlich besitzt jede Kante ein Gewicht <math>a_{ij} \in \mathbb{N}</math>. | |||
Eine Kante <math>(v_i,v_j)\in E</math> mit Gewicht <math>a_{ij}</math> bedeutet, dass zur Herstellung des Produkts <math>v_j</math> genau <math>a_{ij}</math> Einheiten der Komponente <math>v_i</math> benötigt werden. | |||
Da rekursive Stücklisten ausgeschlossen werden, enthält ein Gozintograph keine Zyklen. | |||
== Zusammenhang zu Matrizen == | == Zusammenhang zu Matrizen == | ||
Die Informationen eines Gozintographen lassen sich in einer sogenannten '''Gozintomatrix''' darstellen. | Die Informationen eines Gozintographen lassen sich in einer sogenannten '''Gozintomatrix''' darstellen. | ||
Dies ist eine Matrix | |||
:<math>A=(a_{ij})</math> | |||
bei der das Element <math>a_{ij}</math> die Anzahl der Einheiten der Komponente <math>i</math> angibt, die unmittelbar zur Herstellung des Produkts <math>j</math> benötigt werden. | |||
Unter der Voraussetzung, dass der Gozintograph zyklusfrei ist und <math>I-A</math> invertierbar ist, kann der Gesamtbedarf aller Komponenten über die Gleichung | |||
:<math> | |||
\mathbf{x}=(I-A)^{-1}\mathbf{y} | |||
</math> | |||
bestimmt werden, wobei: | |||
* <math>\mathbf{y}</math> den Vektor der Endprodukte, | |||
* <math>\mathbf{x}</math> den Vektor der insgesamt benötigten Komponentenmengen | |||
beschreibt. | |||
== Beispiele == | |||
=== Produktion eines Produkts aus Einzelteilen === | === Produktion eines Produkts aus Einzelteilen === | ||
Im folgenden Beispiel werden fünf Bauteile | Im folgenden Beispiel werden fünf Bauteile <math>B_1,B_2,B_3,B_4,B_5</math> aus vier Einzelteilen <math>E_1,E_2,E_3,E_4</math> gefertigt. | ||
Die Pfeile zeigen, welche Einzelteile in welches Bauteil eingehen. Die Zahlen an den Pfeilen geben die Stückzahl an. | Die Pfeile zeigen, welche Einzelteile in welches Bauteil eingehen. Die Zahlen an den Pfeilen geben die benötigte Stückzahl an. | ||
<!-- VARIANTE B – FINAL OPTIMIERT --> | <!-- VARIANTE B – FINAL OPTIMIERT --> | ||
<html> | <html> | ||
| Zeile 51: | Zeile 74: | ||
} | } | ||
.node-text, .count-text { | .node-text, .count-text { | ||
font-family:sans-serif; | font-family:sans-serif; | ||
| Zeile 83: | Zeile 105: | ||
const svg = document.getElementById("gozinto_svg_2"); | const svg = document.getElementById("gozinto_svg_2"); | ||
const scale = 100; | const scale = 100; | ||
const yOffset = 0; | const yOffset = 0; | ||
const xOffsetGlobal = 120; | const xOffsetGlobal = 120; | ||
function svgEl(name, attrs){ | function svgEl(name, attrs){ | ||
| Zeile 101: | Zeile 122: | ||
} | } | ||
function createNode(id, cx, cy, w, h, label){ | function createNode(id, cx, cy, w, h, label){ | ||
cx += xOffsetGlobal/scale; | cx += xOffsetGlobal/scale; | ||
const g = svgEl("g", {"data-id":id}); | const g = svgEl("g", {"data-id":id}); | ||
const rect = svgEl("rect", { | const rect = svgEl("rect", { | ||
class:"node-rect", | class:"node-rect", | ||
x:(cx-w/2)*scale, y:(cy-h/2)*scale + yOffset, | x:(cx-w/2)*scale, | ||
width:w*scale, height:h*scale, rx:6, ry:6 | y:(cy-h/2)*scale + yOffset, | ||
width:w*scale, | |||
height:h*scale, | |||
rx:6, | |||
ry:6 | |||
}); | }); | ||
const text = svgEl("text", { | const text = svgEl("text", { | ||
class:"node-text", | class:"node-text", | ||
x:cx*scale, y:cy*scale+yOffset, | x:cx*scale, | ||
y:cy*scale+yOffset, | |||
"text-anchor":"middle", | "text-anchor":"middle", | ||
"dominant-baseline":"middle" | "dominant-baseline":"middle" | ||
}); | }); | ||
text.textContent = label; | text.textContent = label; | ||
| Zeile 126: | Zeile 152: | ||
const node = {id,cx,cy,w,h,rect,text,g}; | const node = {id,cx,cy,w,h,rect,text,g}; | ||
let dragging=false, start={}; | let dragging=false, start={}; | ||
| Zeile 138: | Zeile 163: | ||
rect.addEventListener("pointermove", e=>{ | rect.addEventListener("pointermove", e=>{ | ||
if(!dragging) return; | if(!dragging) return; | ||
const p = getSVGcoords(e); | const p = getSVGcoords(e); | ||
node.cx = start.cx + (p.x - start.px)/scale; | node.cx = start.cx + (p.x - start.px)/scale; | ||
node.cy = start.cy + (p.y - start.py)/scale; | node.cy = start.cy + (p.y - start.py)/scale; | ||
updateNode(node); | updateNode(node); | ||
updateAllEdges(); | updateAllEdges(); | ||
| Zeile 160: | Zeile 188: | ||
} | } | ||
function intersectRectBorder(node, tx, ty){ | function intersectRectBorder(node, tx, ty){ | ||
const cx=node.cx, cy=node.cy, w2=node.w/2, h2=node.h/2; | const cx=node.cx, cy=node.cy, w2=node.w/2, h2=node.h/2; | ||
const dx=tx-cx, dy=ty-cy; | const dx=tx-cx, dy=ty-cy; | ||
let pts=[]; | let pts=[]; | ||
if(Math.abs(dx)>1e-9){ | if(Math.abs(dx)>1e-9){ | ||
let t1=(-w2)/dx; let y1=cy+t1*dy; | let t1=(-w2)/dx; | ||
if(t1>0 && y1>=cy-h2 && y1<=cy+h2) pts.push({x:cx-w2,y:y1,t:t1}); | let y1=cy+t1*dy; | ||
let t2=(w2)/dx; let y2=cy+t2*dy; | |||
if(t2>0 && y2>=cy-h2 && y2<=cy+h2) pts.push({x:cx+w2,y:y2,t:t2}); | if(t1>0 && y1>=cy-h2 && y1<=cy+h2) | ||
pts.push({x:cx-w2,y:y1,t:t1}); | |||
let t2=(w2)/dx; | |||
let y2=cy+t2*dy; | |||
if(t2>0 && y2>=cy-h2 && y2<=cy+h2) | |||
pts.push({x:cx+w2,y:y2,t:t2}); | |||
} | } | ||
if(Math.abs(dy)>1e-9){ | if(Math.abs(dy)>1e-9){ | ||
let t3=(-h2)/dy; let x3=cx+t3*dx; | let t3=(-h2)/dy; | ||
if(t3>0 && x3>=cx-w2 && x3<=cx+w2) pts.push({x:x3,y:cy-h2,t:t3}); | let x3=cx+t3*dx; | ||
let t4=(h2)/dy; let x4=cx+t4*dx; | |||
if(t4>0 && x4>=cx-w2 && x4<=cx+w2) pts.push({x:x4,y:cy+h2,t:t4}); | if(t3>0 && x3>=cx-w2 && x3<=cx+w2) | ||
pts.push({x:x3,y:cy-h2,t:t3}); | |||
let t4=(h2)/dy; | |||
let x4=cx+t4*dx; | |||
if(t4>0 && x4>=cx-w2 && x4<=cx+w2) | |||
pts.push({x:x4,y:cy+h2,t:t4}); | |||
} | } | ||
pts.sort((a,b)=>a.t-b.t); | pts.sort((a,b)=>a.t-b.t); | ||
return pts[0] || {x:cx,y:cy}; | return pts[0] || {x:cx,y:cy}; | ||
} | } | ||
function pointOnCircle(cx,cy,R,tx,ty){ | function pointOnCircle(cx,cy,R,tx,ty){ | ||
const dx=tx-cx | const dx=tx-cx; | ||
const dy=ty-cy; | |||
const d=Math.sqrt(dx*dx+dy*dy); | const d=Math.sqrt(dx*dx+dy*dy); | ||
if(d<1e-9) return {x:cx,y:cy}; | if(d<1e-9) return {x:cx,y:cy}; | ||
return {x:cx+R*dx/d, y:cy+R*dy/d}; | |||
return { | |||
x:cx+R*dx/d, | |||
y:cy+R*dy/d | |||
}; | |||
} | } | ||
function makeArrowHead(x,y,ux,uy,size){ | function makeArrowHead(x,y,ux,uy,size){ | ||
let px=-uy, py=ux; | let px=-uy, py=ux; | ||
return `M ${x} ${y} | return `M ${x} ${y} | ||
L ${x-ux*size+px*size*0.5} ${y-uy*size+py*size*0.5} | L ${x-ux*size+px*size*0.5} ${y-uy*size+py*size*0.5} | ||
| Zeile 199: | Zeile 252: | ||
function makeConnection(fromNode,toNode,amount,yMid,xOffset){ | function makeConnection(fromNode,toNode,amount,yMid,xOffset){ | ||
const g=svgEl("g",{}); | const g=svgEl("g",{}); | ||
const lineA=svgEl("path",{class:"edge-line"}); | const lineA=svgEl("path",{class:"edge-line"}); | ||
const lineB=svgEl("path",{class:"edge-line"}); | const lineB=svgEl("path",{class:"edge-line"}); | ||
| Zeile 207: | Zeile 262: | ||
text.textContent=amount; | text.textContent=amount; | ||
g.appendChild(lineA); | g.appendChild(lineA); | ||
g.appendChild(lineB); | g.appendChild(lineB); | ||
| Zeile 212: | Zeile 268: | ||
g.appendChild(text); | g.appendChild(text); | ||
g.appendChild(arrow); | g.appendChild(arrow); | ||
svg.appendChild(g); | svg.appendChild(g); | ||
let e={fromNode,toNode,amount,yMid,xOffset,circle,text,lineA,lineB,arrow}; | let e={ | ||
fromNode, | |||
toNode, | |||
amount, | |||
yMid, | |||
xOffset, | |||
circle, | |||
text, | |||
lineA, | |||
lineB, | |||
arrow | |||
}; | |||
edges.push(e); | edges.push(e); | ||
updateEdge(e); | updateEdge(e); | ||
} | } | ||
function updateEdge(e){ | function updateEdge(e){ | ||
const cx=(e.fromNode.cx+e.toNode.cx)/2+(e.xOffset||0); | const cx=(e.fromNode.cx+e.toNode.cx)/2+(e.xOffset||0); | ||
const cy=e.yMid; | const cy=e.yMid; | ||
| Zeile 230: | Zeile 301: | ||
const pCircleOut=pointOnCircle(cx,cy,R,pT.x,pT.y); | const pCircleOut=pointOnCircle(cx,cy,R,pT.x,pT.y); | ||
const px=p=>[p.x*scale, p.y*scale+yOffset]; | const px=p=>[p.x*scale,p.y*scale+yOffset]; | ||
const F=px(pF), Ci=px(pCircleIn), Co=px(pCircleOut), T=px(pT); | |||
const F=px(pF), | |||
Ci=px(pCircleIn), | |||
Co=px(pCircleOut), | |||
T=px(pT); | |||
e.lineA.setAttribute("d",`M ${F[0]} ${F[1]} L ${Ci[0]} ${Ci[1]}`); | e.lineA.setAttribute("d",`M ${F[0]} ${F[1]} L ${Ci[0]} ${Ci[1]}`); | ||
| Zeile 240: | Zeile 315: | ||
e.circle.setAttribute("r",R*scale); | e.circle.setAttribute("r",R*scale); | ||
e.text.setAttribute( | e.text.setAttribute("x",cx*scale-5); | ||
e.text.setAttribute( | e.text.setAttribute("y",cy*scale+yOffset+5); | ||
let ux=T[0]-Co[0] | let ux=T[0]-Co[0]; | ||
let uy=T[1]-Co[1]; | |||
e.arrow.setAttribute("d",makeArrowHead(T[0],T[1],ux,uy,10)); | let L=Math.sqrt(ux*ux+uy*uy); | ||
if(L<1e-6) L=1; | |||
ux/=L; | |||
uy/=L; | |||
e.arrow.setAttribute( | |||
"d", | |||
makeArrowHead(T[0],T[1],ux,uy,10) | |||
); | |||
} | } | ||
function updateAllEdges(){ edges.forEach(updateEdge); } | function updateAllEdges(){ | ||
edges.forEach(updateEdge); | |||
} | |||
const nodes={}; | const nodes={}; | ||
nodes.E1=createNode("E1",0,0.5,1.0,0.5,"E1"); | nodes.E1=createNode("E1",0,0.5,1.0,0.5,"E1"); | ||
nodes.E2=createNode("E2",2.5,0.5,1.0,0.5,"E2"); | nodes.E2=createNode("E2",2.5,0.5,1.0,0.5,"E2"); | ||
| Zeile 261: | Zeile 345: | ||
nodes.E4=createNode("E4",7.5,0.5,1.0,0.5,"E4"); | nodes.E4=createNode("E4",7.5,0.5,1.0,0.5,"E4"); | ||
nodes.B1=createNode("B1",0.75,4.5,1.0,0.5,"B1"); | nodes.B1=createNode("B1",0.75,4.5,1.0,0.5,"B1"); | ||
nodes.B2=createNode("B2",2.5,4.5,1.0,0.5,"B2"); | nodes.B2=createNode("B2",2.5,4.5,1.0,0.5,"B2"); | ||
| Zeile 268: | Zeile 351: | ||
nodes.B5=createNode("B5",10,4.5,1.0,0.5,"B5"); | nodes.B5=createNode("B5",10,4.5,1.0,0.5,"B5"); | ||
makeConnection(nodes.E1,nodes.B1,"2",2.2,-0.2); | makeConnection(nodes.E1,nodes.B1,"2",2.2,-0.2); | ||
makeConnection(nodes.E2,nodes.B1,"1",2.2, 0.2); | makeConnection(nodes.E2,nodes.B1,"1",2.2,0.2); | ||
makeConnection(nodes.E1,nodes.B2,"2",2.2,-0.2); | makeConnection(nodes.E1,nodes.B2,"2",2.2,-0.2); | ||
makeConnection(nodes.E2,nodes.B2,"1",2.2, 0.2); | makeConnection(nodes.E2,nodes.B2,"1",2.2,0.2); | ||
makeConnection(nodes.E1,nodes.B3,"1",2.2,-0.25); | makeConnection(nodes.E1,nodes.B3,"1",2.2,-0.25); | ||
makeConnection(nodes.E2,nodes.B3,"1",2.2, 0.0); | makeConnection(nodes.E2,nodes.B3,"1",2.2,0.0); | ||
makeConnection(nodes.E3,nodes.B3,"1",2.2, 0.25); | makeConnection(nodes.E3,nodes.B3,"1",2.2,0.25); | ||
makeConnection(nodes.E1,nodes.B4,"2",2.2,-0.3); | makeConnection(nodes.E1,nodes.B4,"2",2.2,-0.3); | ||
makeConnection(nodes.E3,nodes.B4,"1",2.2, 0.0); | makeConnection(nodes.E3,nodes.B4,"1",2.2,0.0); | ||
makeConnection(nodes.E4,nodes.B4,"1",2.2, 0.3); | makeConnection(nodes.E4,nodes.B4,"1",2.2,0.3); | ||
makeConnection(nodes.E1,nodes.B5,"1",2.2,-0.2); | makeConnection(nodes.E1,nodes.B5,"1",2.2,-0.2); | ||
makeConnection(nodes.E4,nodes.B5,"2",2.2, 0.2); | makeConnection(nodes.E4,nodes.B5,"2",2.2,0.2); | ||
updateAllEdges(); | updateAllEdges(); | ||
})(); | })(); | ||
</script> | </script> | ||
</html> | </html> | ||
Die Gozintomatrix zum oberen Gozintographen kann | Die Gozintomatrix zum oberen Gozintographen kann aus folgender Tabelle abgeleitet werden: | ||
{| class="wikitable" | {| class="wikitable" | ||
! | ! !! B1 !! B2 !! B3 !! B4 !! B5 | ||
|- | |- | ||
| '''E1''' | | '''E1''' || 2 || 2 || 1 || 2 || 1 | ||
|- | |- | ||
| '''E2''' | | '''E2''' || 1 || 1 || 1 || 0 || 0 | ||
|- | |- | ||
| '''E3''' | | '''E3''' || 0 || 0 || 1 || 1 || 0 | ||
|- | |- | ||
| ''' | | '''E4''' || 0 || 0 || 0 || 1 || 2 | ||
|} | |} | ||
und ist durch | |||
:<math> | :<math> | ||
A = \begin{pmatrix} | A = | ||
\begin{pmatrix} | |||
2 & 2 & 1 & 2 & 1 \\ | 2 & 2 & 1 & 2 & 1 \\ | ||
1 & 1 & 1 & 0 & 0 \\ | 1 & 1 & 1 & 0 & 0 \\ | ||
| Zeile 315: | Zeile 398: | ||
0 & 0 & 0 & 1 & 2 | 0 & 0 & 0 & 1 & 2 | ||
\end{pmatrix} | \end{pmatrix} | ||
</math> | </math> | ||
gegeben | gegeben. | ||
Beispielsweise lässt sich aus der ersten Spalte ablesen, dass zur Herstellung eines Bauteils <math>B_1</math> | |||
* 2 Einheiten von <math>E_1</math>, | |||
* 1 Einheit von <math>E_2</math>, | |||
* 0 Einheiten von <math>E_3</math>, | |||
* 0 Einheiten von <math>E_4</math> | |||
benötigt werden. | |||
=== Produktion von Spielwaren aus Rohstoffen über Zwischenprodukte === | |||
= | |||
Ein Spielwarenhersteller produziert aus drei Rohstoffen <math>R_1,R_2,R_3</math> zunächst die beiden Zwischenprodukte <math>Z_1,Z_2</math>, aus denen anschließend die drei Endprodukte <math>E_1,E_2,E_3</math> gefertigt werden. | |||
Die Pfeile im Gozintographen geben an, wie viele Mengeneinheiten eines Materials zur Produktion einer Mengeneinheit des entstehenden Produkts benötigt werden. | |||
Beispiel: Für die Herstellung einer Einheit <math>Z_1</math> werden 3 Einheiten <math>R_1</math> und 4 Einheiten <math>R_2</math> benötigt. | |||
[[Kategorie:Lineare_Algebra]] | [[Kategorie:Lineare_Algebra]] | ||
[[Kategorie:AHR_WuV_Mathe_GK]] | [[Kategorie:AHR_WuV_Mathe_GK]] | ||