home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
language
/
examples
/
polytop1.lsp
< prev
next >
Wrap
Lisp/Scheme
|
1993-10-23
|
20KB
|
534 lines
;; Berechnung des Volumens konvexer Polytope
;; Bruno Haible 5.9.1990
;; Ein Polytop sei als konvexe Hülle endlich vieler Punkte (z.B. seiner Ecken)
;; gegeben. Es wird eine Triangulation und das Volumen berechnet.
#|
(require 'lgs_Q) ; Funktion solve-homLGS
|#
;; Lösen linearer Gleichungssysteme über Q
;; Bruno Haible 25.4.1989
; Elimination:
; Löst ein homogenes LGS, d.h. bestimmt den Kern einer Matrix.
; > A: eine mxn-Matrix rationaler Zahlen
; < eine Liste der Basisvektoren des Kernes von A,
; das sind jeweils Vektoren der Länge n aus teilerfremden ganzen Zahlen
; < 2.Wert: Anzahl der eliminierten Zeilen (>=0, <=m)
(defun solve-homLGS (A)
(let ((m (array-dimension A 0))
(n (array-dimension A 1)))
; A kopieren:
(setq A
(let ((B (make-array `(,m ,n))))
(dotimes (i m) (dotimes (j n) (setf (aref B i j) (aref A i j))))
B
) )
(let ((spaltenabsätze nil) 2.Wert)
; A auf Treppennormalform bringen:
(let ((i 0)) ; i = Zeile, ab der noch ausgeräumt werden muß
(dotimes (j n) ; j = untersuchte Spalte
; Suche betragsgrößtes Element unterhalb (i,j):
(let ((i0 nil))
(do ((i1 i (1+ i1))
(v 0))
((= i1 m))
(if (> (abs (aref A i1 j)) v)
(setq i0 i1 v (abs (aref A i1 j)))
) )
; i0 = Zeile des betragsgrößten Elements, oder
; i0 = nil falls alle fraglichen Elemente =0 sind.
(if i0
(progn
; Zeilen i und i0 vertauschen
(unless (= i i0)
(dotimes (j0 n) (rotatef (aref A i j0) (aref A i0 j0)))
)
; Zeile i normalisieren
(let ((x (/ (aref A i j))))
(dotimes (j0 n) (setf (aref A i j0) (* (aref A i j0) x)))
)
; Zeilen unterhalb von Zeile i ausräumen durch Subtraktion
; eines Vielfaches von Zeile i:
(do ((i1 (1+ i) (1+ i1)))
((= i1 m))
(let ((x (aref A i1 j)))
(dotimes (j1 n)
(setf (aref A i1 j1) (- (aref A i1 j1) (* x (aref A i j1))))
) ) )
; Spaltenabsatz
(push i spaltenabsätze)
(incf i)
)
(push nil spaltenabsätze)
) ) )
(setq 2.Wert (- m i))
)
; A hat Treppennormalform, z.B.:
; 1 .............
; 0 1 ...........
; 0 0 0 0 1 .....
; 0 0 0 0 0 0 0 0
; Dann enthält die Liste der Spaltenabsätze, in umgekehrter Reihenfolge:
; 0 1 n n 2 n n n
; (also die Zeilennummer beim Absatzanfang, n=NIL sonst)
; Nun den Kern aufbauen:
(let ((Kern nil))
(do ((j (1- n) (1- j))
(L spaltenabsätze (cdr L)))
((< j 0))
(if (car L)
; Spaltenabsatz in Spalte j
(dolist (B Kern)
(do ((i (car L))
(s 0)
(j1 (1+ j) (1+ j1)))
((= j1 n) (setf (aref B j) (- s)))
(setq s (+ s (* (aref A i j1) (aref B j1))))
) )
; kein Spaltenabsatz in Spalte j
(progn
(dolist (B Kern) (setf (aref B j) 0)) ; Komponente j frei wählbar
(let ((B (make-array n)))
(setf (aref B j) 1)
(do ((j1 (1+ j) (1+ j1)))
((= j1 n))
(setf (aref B j1) 0)
)
(push B Kern) ; neues Basiselement #(..... 1 0 ... 0) im Kern
) ) ) )
; alle Vektoren des Kerns ganzzahlig und teilerfremd machen:
(dolist (B Kern)
(let ((nenner (do ((j 0 (1+ j))
(x 1 (lcm x (denominator (aref B j)))))
((= j n) x)
)) )
(dotimes (j n) (setf (aref B j) (* (aref B j) nenner)))
)
(let ((faktor (do ((j 0 (1+ j))
(x 0 (gcd x (aref B j))))
((= j n) x)
)) )
; faktor /=0, da sonst B der Nullvektor wäre.
(dotimes (j n) (setf (aref B j) (/ (aref B j) faktor)))
) )
(values Kern 2.Wert)
) ) ) )
; Determinante:
; Bestimmt die Determinante einer quadratischen Matrix.
; > A: eine nxn-Matrix rationaler Zahlen
; < deren Determinante
(defun det (A)
(let ((n (array-dimension A 0)))
(assert (= n (array-dimension A 1)))
; A kopieren:
(setq A
(let ((B (make-array `(,n ,n))))
(dotimes (i n) (dotimes (j n) (setf (aref B i j) (aref A i j))))
B
) )
; A auf Treppennormalform bringen:
(let ((det 1)) ; det = bisherige Faktoren der Determinante
(dotimes (i n) ; i = Zeile, ab der noch ausgeräumt werden muß
(let ((j i)) ; j = untersuchte Spalte
; Suche betragsgrößtes Element unterhalb (i,j):
(let ((i0 nil))
(do ((i1 i (1+ i1))
(v 0))
((= i1 n))
(if (> (abs (aref A i1 j)) v)
(setq i0 i1 v (abs (aref A i1 j)))
) )
; i0 = Zeile des betragsgrößten Elements, oder
; i0 = nil falls alle fraglichen Elemente =0 sind.
(if i0
(progn
; Zeilen i und i0 vertauschen
(unless (= i i0)
(dotimes (j0 n) (rotatef (aref A i j0) (aref A i0 j0)))
(setq det (- det))
)
; Zeile i normalisieren
(let ((x (aref A i j)))
(setq det (* det x))
(setq x (/ x))
(dotimes (j0 n) (setf (aref A i j0) (* (aref A i j0) x)))
)
; Zeilen unterhalb von Zeile i ausräumen durch Subtraktion
; eines Vielfaches von Zeile i:
(do ((i1 (1+ i) (1+ i1)))
((= i1 n))
(let ((x (aref A i1 j)))
(dotimes (j1 n)
(setf (aref A i1 j1) (- (aref A i1 j1) (* x (aref A i j1))))
) ) ) )
(return-from det 0)
) ) ) )
det
) ) )
;; Wir bewegen uns im R^d, d>=0. Ein Punkt ist ein Vektor aus seinen d
;; Koordinaten (rationale Zahlen).
; Gleichheit von Punkten:
(defun punkt= (p1 p2)
(equalp p1 p2) ; Vergleich aller Koordinaten
)
;; Ein Simplex ist eine (ungeordnete) Liste von k Punkten, k>=1, deren
;; konvexe Hülle die Dimension k-1 hat.
; Dimension eines Simplex:
(defun simplex-dim (simplex)
(1- (length simplex))
)
; Gleichheit von Simplizes:
(defun simplex= (simplex1 simplex2)
(and (= (simplex-dim simplex1) (simplex-dim simplex2)) ; müssen gleiche Dimension haben
(dolist (punkt1 simplex1 t) ; und jede Ecke von simplex1
(unless (position punkt1 simplex2 :test #'punkt=) ; muß auch Ecke von simplex2 sein
(return nil)
) )
) )
; Bildet ein (k-1)-dimensionales Simplex aus k Punkten:
(defun make-simplex (punkte)
(if (endp punkte)
(error "Zu wenig Punkte für ein Simplex.")
(if ; Löse das LGS x1*p1 + ... + xk*pk = 0, x1+...+xk=0
(let* ((d (length (first punkte)))
(k (length punkte))
(A (make-array (list (+ d 1) k))))
; LGS aufstellen:
(dotimes (j k)
(let ((punkt (elt punkte j)))
(assert (= (length punkt) d))
(dotimes (i d) (setf (aref A i j) (aref punkt i)))
(setf (aref A d j) 1)
) )
; LGS lösen. Der Kern muß leer sein, also (p1,1), ..., (pk,1) linear
; unabhängig, also p1,...,pk affin unabhängig.
(null (solve-homLGS A))
)
punkte
(error "Punkte für ein Simplex sind affin abhängig: ~S" punkte)
) ) )
; Stellt fest, ob ein Punkt in der affinen Hülle eines Simplex liegt.
(defun on-affine-p (punkt simplex)
; Löse das LGS x1*p1 + ... + xk*pk + x*p = 0, x1+...+xk+x=0
(let* ((d (length punkt))
(k (length simplex))
(A (make-array (list (+ d 1) (+ k 1)))))
; LGS aufstellen:
(dotimes (j k)
(let ((punkt (elt simplex j)))
(assert (= (length punkt) d))
(dotimes (i d) (setf (aref A i j) (aref punkt i)))
(setf (aref A d j) 1)
) )
(dotimes (i d) (setf (aref A i k) (aref punkt i)))
(setf (aref A d k) 1)
; LGS lösen:
(let ((kern (solve-homLGS A)))
(assert (<= (length kern) 1)) ; LGS durfte nicht mehrdeutig lösbar sein
(not (endp kern)) ; falls es unlösbar war, liegt der Punkt nicht drin.
) ) )
; Stellt fest, ob zwei Punkte auf derselben Seite der affinen Hülle eines
; Simplex liegen.
(defun on-same-side-p (punkt1 punkt2 simplex)
; Löse das LGS x1*p1 + ... + xk*pk + y1*punkt1 + y2*punkt2 = 0, x1+...+xk+y1+y2=0
(let* ((d (length punkt1))
(k (length simplex))
(A (make-array (list (+ d 1) (+ k 2)))))
; LGS aufstellen:
(dotimes (j k)
(let ((punkt (elt simplex j)))
(assert (= (length punkt) d))
(dotimes (i d) (setf (aref A i j) (aref punkt i)))
(setf (aref A d j) 1)
) )
(let ((j k))
(assert (= (length punkt1) d))
(dotimes (i d) (setf (aref A i j) (aref punkt1 i)))
(setf (aref A d j) 1)
)
(let ((j (+ k 1)))
(assert (= (length punkt2) d))
(dotimes (i d) (setf (aref A i j) (aref punkt2 i)))
(setf (aref A d j) 1)
)
; LGS lösen:
(let ((kern (solve-homLGS A)))
(ecase (length kern)
(0 (error "Simplex und zwei Punkte sind windschief."))
(1 (let ((lsg (first kern))) ; Lösungvektor #(x1 ... xk y1 y2)
; y1*y2>0 -> eine echte Konvexkombination von punkt1 und punkt2
; liegt in der Ebene, also liegen die Punkte in
; verschiedenen Halbebenen der Ebene.
(<= (* (signum (aref lsg k)) (signum (aref lsg (+ k 1)))) 0)
) )
(2 t) ; beide Punkte liegen in der affinen Hülle des Simplex
) ) ) )
; Liefert den Mittelpunkt eines Simplex:
(defun simplex-mittelpunkt (simplex)
(let ((k (length simplex))) ; Anzahl der Punkte
(map 'vector #'(lambda (x) (/ x k)) ; durch sie dividieren, nachdem man
(apply #'map 'vector #'+ simplex) ; alle Koordinaten addiert hat
) ) )
; Liefert das Volumen eines Simplex der Dimension d:
(defun simplex-vol (simplex)
(let* ((d (length (first simplex)))
(d+1 (+ d 1)))
(if (= (length simplex) d+1)
; volldimensionaler Simplex conv({p0,...,pd}) hat Volumen
; 1 | p00 ... p0d 1 |
; --- abs det | ............. |
; d ! | pd0 ... pdd 1 |
(let ((A (make-array (list d+1 d+1))))
(dotimes (i d+1)
(let ((punkt (elt simplex i)))
(dotimes (j d) (setf (aref A i j) (aref punkt j)))
(setf (aref A i d) 1)
) )
(/ (abs (det A)) (! d))
)
; niederdimensionaler Simplex hat d-Volumen 0
0
) ) )
; Stellt fest, ob ein Punkt in einem Simplex liegt.
(defun on-simplex-p (punkt simplex)
; Löse das LGS x1*p1 + ... + xk*pk + x*p = 0, x1+...+xk+x=0
(let* ((d (length punkt))
(k (length simplex))
(A (make-array (list (+ d 1) (+ k 1)))))
; LGS aufstellen:
(dotimes (j k)
(let ((punkt (elt simplex j)))
(assert (= (length punkt) d))
(dotimes (i d) (setf (aref A i j) (aref punkt i)))
(setf (aref A d j) 1)
) )
(dotimes (i d) (setf (aref A i k) (aref punkt i)))
(setf (aref A d k) 1)
; LGS lösen:
(let ((kern (solve-homLGS A)))
(assert (<= (length kern) 1)) ; LGS durfte nicht mehrdeutig lösbar sein
(if (endp kern) ; falls es unlösbar war, liegt der Punkt nicht drin.
nil
(let ((lsg (first kern)))
; x auf -1 normieren:
(let ((-x (- (aref lsg k))))
(dotimes (j k) (setf (aref lsg j) (/ (aref lsg j) -x)))
)
; Nun ist x1*p1 + ... + xk*pk = p .
; Falls hierbei alle xi>=0 sind, liegt p im Simplex.
(dotimes (j k t)
(unless (>= (aref lsg j) 0) (return nil))
)
) ) ) ) )
; Stellt fest, ob ein Punkt im Innern eines Simplex liegt.
(defun in-simplex-p (punkt simplex)
; Löse das LGS x1*p1 + ... + xk*pk + x*p = 0, x1+...+xk+x=0
(let* ((d (length punkt))
(k (length simplex))
(A (make-array (list (+ d 1) (+ k 1)))))
; LGS aufstellen:
(dotimes (j k)
(let ((punkt (elt simplex j)))
(assert (= (length punkt) d))
(dotimes (i d) (setf (aref A i j) (aref punkt i)))
(setf (aref A d j) 1)
) )
(dotimes (i d) (setf (aref A i k) (aref punkt i)))
(setf (aref A d k) 1)
; LGS lösen:
(let ((kern (solve-homLGS A)))
(assert (<= (length kern) 1)) ; LGS durfte nicht mehrdeutig lösbar sein
(if (endp kern) ; falls es unlösbar war, liegt der Punkt nicht drin.
nil
(let ((lsg (first kern)))
; x auf -1 normieren:
(let ((-x (- (aref lsg k))))
(dotimes (j k) (setf (aref lsg j) (/ (aref lsg j) -x)))
)
; Nun ist x1*p1 + ... + xk*pk = p .
; Falls hierbei alle xi>0 sind, liegt p im Innern des Simplex.
(dotimes (j k t)
(unless (> (aref lsg j) 0) (return nil))
)
) ) ) ) )
;; Eine Triangulation eines Polytops P der Dimension k ist eine Liste von
;; Simplizes der Dimension k, deren Vereinigung = P und deren paarweiser
;; Durchschnitt jeweils höchstens (k-1)-dimensional ist.
;; Ein allgemeines Polytop wird als Triangulation dargestellt.
; Dimension eines Polytops:
(defun polytop-dim (polytop)
(if (endp polytop)
-1 ; leeres Polytop hat Dimension 0
(simplex-dim (first polytop)) ; sonst die Dimension aller Simplizes
) )
; Simplex als Polytop:
(defun simplex-polytop (simplex)
(list simplex) ; Triangulation besteht als dem Simplex selbst
)
; Stellt fest, ob ein Punkt in einem Polytop liegt.
(defun on-polytop-p (punkt polytop)
; Liegt der Punkt in einer der Simplizes der Triangulation?
(dolist (simplex polytop nil)
(when (on-simplex-p punkt simplex) (return t))
) )
; Liefert zu einem Polytop der Dimension k eine Liste aller Randsimplizes,
; alle derselben Dimension k-1.
(defun simplex-rand (simplex)
(if (zerop (simplex-dim simplex)) ; Hat Dimension 0 ?
nil ; Rand ist leer
; P = conv{p0,...,pk} mit k>0 -> Die Randsimplizes sind die Simplizes
; conv{p0,...,pj-1,pj+1,...,pk} (pj fehlt) für j=0,...,k.
(let ((rand nil))
(do ((L1 nil (cons (car L2) L1))
(L2 simplex (cdr L2)))
((endp L2))
(push (revappend L1 (cdr L2)) rand)
)
(nreverse rand)
) ) )
; Liefert zu einem Polytop eine Liste aller Randsimplizes. Sie haben alle
; eine um 1 geringere Dimension als das Polytop selbst.
(defun polytop-rand (polytop)
; Der Rand setzt sich zusammen aus den Randsimplizes der Triangulierung.
; Solche Randsimplizes, die dabei doppelt vorkommen, liegen im Innern des
; Polytops und werden daher gestrichen.
(let ((rand nil))
(dolist (simplex polytop)
(let ((rand-neu (simplex-rand simplex)))
(dolist (rand-simplex rand-neu)
(if (member rand-simplex rand :test #'simplex=)
(setq rand (delete rand-simplex rand :test #'simplex=))
(setq rand (cons rand-simplex rand))
) ) ) )
(nreverse rand)
) )
; Stellt fest, ob ein Punkt im Innern eines Polytops liegt.
(defun in-polytop-p (punkt polytop)
; Liegt der Punkt im Polytop, aber nicht auf dem Rand?
(and (on-polytop-p punkt polytop)
(not (some #'(lambda (randsimplex) (on-simplex-p punkt randsimplex))
(polytop-rand polytop)
) ) ) )
; Liefert die konvexe Hülle eines Polytops und eines Punktes.
(defun polytop+punkt (polytop punkt)
(if (endp polytop) ; Polytop leer?
(simplex-polytop (make-simplex (list punkt))) ; gibt einpunktiges Polytop
; liegt der Punkt in der affinen Hülle des Polytops (= der affinen
; Hülle eines beliebigen Simplex der Triangulation) ?
(if (on-affine-p punkt (first polytop))
; Dimension vergrößert sich nicht: Bilde die Randsimplizes des Polytops,
; die den Punkt vom Polytop trennen, und verbinde sie jeweils mit dem
; Punkt. Diese neuen Simplizes kommen zur Triangulation dazu.
(let ((einpunkt (simplex-mittelpunkt (first polytop))) ; Ein Punkt im Polytop
(neue-simplizes nil))
(dolist (rand (polytop-rand polytop))
(unless (on-same-side-p punkt einpunkt rand)
(push (make-simplex (cons punkt rand)) neue-simplizes)
) )
(append polytop (nreverse neue-simplizes))
)
; Dimension vergrößert sich um 1: Verbinde den Punkt mit allen
; Simplizes des Polytops. Dies liefert die neue Triangulation.
(mapcar #'(lambda (simplex) (make-simplex (cons punkt simplex))) polytop)
) ) )
; Liefert die konvexe Hülle einer Liste von Punkten als Polytop.
(defun conv (punkte)
(let ((polytop nil)) ; leeres Polytop
(dolist (punkt punkte) ; schrittweise um je einen der Punkte erweitern
(setq polytop (polytop+punkt polytop punkt))
)
polytop
) )
; Liefert das Volumen (der Dimension d) eines Polytops.
(defun polytop-vol (polytop)
(apply #'+ (mapcar #'simplex-vol polytop))
)
; Liefert die Liste der Ecken der konvexen Hülle von endlich vielen Punkten.
(defun conv-ecken (punkte)
(setq punkte (remove-duplicates punkte :test #'punkt=))
(remove-if
#'(lambda (punkt)
(on-polytop-p punkt (conv (remove punkt punkte :test #'punkt=)))
)
punkte
) )
;; Beispiel: Hn(r) bei festem n.
;; Ecken sind die n! Permutationsmatrizen, aufgefaßt im R^(n^2), durch
;; Weglassen der rechten und unteren Spalte projiziert in den R^((n-1)^2).
; Permutation von {0,...,n-1} in Punkt umwandeln:
(defun perm-als-punkt (perm)
(let* ((n (length perm))
(n-1 (- n 1))
(A (make-array `(,n-1 ,n-1) :initial-element 0))
(p (make-array (* n-1 n-1) :displaced-to A)))
(dotimes (i n)
(let ((j (elt perm i)))
(when (array-in-bounds-p A i j)
(setf (aref A i j) 1)
) ) )
p
) )
; Liste aller Permutationen von {0,...,n-1} produzieren:
(defun alle-perm (n)
(if (zerop n)
(list '())
(let ((perm_n-1 (alle-perm (- n 1)))
(perm_n nil))
; produziere die Permutationen von {0,...,n-1} aus denen von {0,...,n-2}:
; (n-1)-Liste unverändert. Vorne n-1 anfügen.
; In der (n-1)-Liste alle n-2 in n-1 umwandeln. Vorne n-2 anfügen.
; In der (n-1)-Liste alle n-3 in n-2 umwandeln. Vorne n-3 anfügen.
; ...
; In der (n-1)-Liste alle 0 in 1 umwandeln. Vorne 0 anfügen.
(let ((i (- n 1)))
(loop
(setq perm_n
(append (mapcar #'(lambda (p_n-1) (cons i p_n-1)) perm_n-1)
perm_n
) )
(when (zerop i) (return))
(decf i)
(setq perm_n-1 (subst (+ i 1) i perm_n-1))
) )
perm_n
) ) )
(defun H-ecken (n) (mapcar #'perm-als-punkt (alle-perm n)))
; n=1
(setq H1 (conv (H-ecken 1)))
; n=2
(setq H2 (conv (H-ecken 2)))
; n=3
(setq H3 (conv (H-ecken 3)))