Schläfli Convex Regular Polytope Interpreter


15

Hintergrund

Das Schläfli-Symbol ist eine Notation der Form {p, q, r, ...}, die reguläre Polytope und Tessellationen definiert.

Das Schläfli-Symbol ist eine rekursive Beschreibung, die mit einem p-seitigen regelmäßigen Polygon als {p} beginnt. Zum Beispiel ist {3} ein gleichseitiges Dreieck, {4} ein Quadrat und so weiter.

Ein reguläres Polyeder mit q regelmäßigen p-seitigen Polygonflächen um jeden Scheitelpunkt wird durch {p, q} dargestellt. Zum Beispiel hat der Würfel 3 Quadrate um jeden Eckpunkt und wird durch {4,3} dargestellt.

Ein reguläres 4-dimensionales Polytop mit r {p, q} regulären polyedrischen Zellen um jede Kante wird durch {p, q, r} dargestellt. Zum Beispiel hat ein Tesserakt {4,3,3} drei Würfel {4,3} um eine Kante.

Im Allgemeinen hat ein reguläres Polytop {p, q, r, ..., y, z} z {p, q, r, ..., y} -Facetten um jeden Peak, wobei ein Peak ein Eckpunkt in einem Polyeder ist. Eine Kante in einem 4-Polytop, eine Fläche in einem 5-Polytop, eine Zelle in einem 6-Polytop und eine (n-3) -Fläche in einem n-Polytop.

Ein reguläres Polytop hat eine reguläre Scheitelpunktzahl. Die Scheitelpunktzahl eines regulären Polytops {p, q, r, ... y, z} ist {q, r, ... y, z}.

Normale Polytope können Sternpolygonelemente wie das Pentagramm mit dem Symbol {5/2} enthalten, das durch die Eckpunkte eines Fünfecks dargestellt wird, jedoch abwechselnd verbunden ist.

Das Schläfli-Symbol kann je nach Winkeldefekt der Konstruktion ein endliches konvexes Polyeder, eine unendliche Tessellation des euklidischen Raums oder eine unendliche Tessellation des hyperbolischen Raums darstellen. Ein positiver Winkeldefekt ermöglicht es der Scheitelpunktfigur, sich in eine höhere Dimension zu falten und als Polytop in sich selbst zurückzukehren. Ein Nullwinkelfehler tesselliert einen Raum mit der gleichen Dimension wie die Facetten. Ein negativer Winkeldefekt kann nicht im gewöhnlichen Raum existieren, sondern kann im hyperbolischen Raum konstruiert werden.

Wettbewerb

Ihr Ziel ist es, ein Programm zu erstellen, das bei Übergabe eines Schläfli-Symbols eine vollständige Beschreibung eines konvexen Polytops zurückgibt. Dies ist nur eine Teilmenge der Schläfli-Symbole, aber die einfachste, glaube ich, auch ohne die anderen Möglichkeiten wird dies eine sehr schwierige Aufgabe sein, und Polytope sind der Ausgangspunkt für Tessellationen. Die Regeln dieser Frage wurden mit der Idee entworfen, dass dieses Ergebnis eine API ist, und ich war nicht in der Lage, ein solches Programm im Internet zu finden.

Ihr Programm muss alle folgenden Aufgaben ausführen.

  • Das Programm muss in der Lage sein, ein beliebiges endliches reguläres konvexes Polytop zu erzeugen. In 2 Dimensionen sind dies n-Gone. In 3 Dimensionen sind dies die platonischen Körper, in 4 Dimensionen sind dies der Tesserakt, der Orthoplex und einige andere.
  • Das Programm muss entweder (a) einen Punkt auf dem Ursprung platzieren oder (b) sicherstellen, dass der Durchschnitt aller Punkte der Ursprung ist. Orientierung spielt keine Rolle. Gesamtgröße spielt keine Rolle.
  • Das Programm muss eine vollständige Beschreibung bereitstellen, dh, für ein 4-dimensionales Objekt gibt das Programm die Eckpunkte, Kanten, Flächen und Polyeder zurück bzw. druckt sie aus. Die Reihenfolge, in der diese gemeldet werden, spielt keine Rolle. Für Polyeder sind dies die Informationen, die Sie zum Rendern des Objekts benötigen würden.

Sie müssen nicht behandeln:

  • Tesselationen
  • Hyperbolische Geometrie
  • Bruchschläfli-Symbole (nicht konvex)
  • Eingebettete Schläfli-Symbole

Wenn Sie dazu aufgefordert werden, können Sie einen Fehler zurückgeben.

Beispiel: Würfel

Eingang:

4 3

Ausgabe:

Vertices
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1    

Edges (These are the vertex pairs that make up the edges)
0 1
0 2
0 4
1 3
1 5
2 3
2 6
3 7
4 5
4 6
5 7
6 7

Faces (These are the squares which are the faces of the cube)
0 1 3 2
0 1 5 4
0 2 6 4
6 7 5 4
7 6 2 3
7 5 1 3

Ich hatte einige Ideen, wie dieser Algorithmus funktionieren und sehr rekursiv sein könnte, aber bisher habe ich versagt, aber wenn Sie Inspiration suchen, lesen Sie: https://en.wikipedia.org/wiki/Euler_characteristic

Als Beispiel für die Ermittlung der Anzahl der Eckpunkte, Kanten und Flächen betrachten wir den Würfel {4,3}. Wenn wir uns die ersten 4 ansehen, dann hat sie 4 Kanten und 4 Eckpunkte. Wenn wir uns nun die nächsten 3 ansehen, wissen wir, dass 3 Kanten an jedem Scheitelpunkt aufeinander treffen, jede Kante mit 2 Scheitelpunkten verbunden ist, 2 Flächen an jeder Kante aufeinander treffen, jede Fläche mit 4 Kanten verbunden ist (aufgrund der quadratischen Seiten), und wir haben die Euler-Charakteristikformel.

E = 3/2 V

E = 4/2 F

V - E + F = 2

Was ergibt E = 12, V = 8, F = 6.

Wertung

Um die Frage zum Thema zu halten, wurde dies zu Code Golf überarbeitet. Kürzester Code gewinnt.

Für diese Frage wurde ein Github erstellt


1
Googeln zeigt, dass es nur drei Familien von regulären Polytopen gibt, die sich über vier Dimensionen hinaus erstrecken: Analog zu Würfel, Oktaeder und Tetraeder. Es scheint einfacher zu sein, für diese Familien zu schreiben und den Rest fest zu codieren (zwei 3D-Polytope, drei 4D-Polytope und die unendliche Familie von 2D-Polytopen). Soweit ich sehe, entspricht dies der Spezifikation, ist aber nicht verallgemeinerbar. Wäre das eine gültige Antwort? Es mag möglich sein, einen rekursiven Algorithmus zu schreiben, um topologische Graphen zu generieren, die über den Umfang der Spezifikation hinausgehen, aber der Killer mit diesem Ansatz berechnet sogar innerhalb der Spezifikation die Koordinaten.
Level River St

Woher kennen wir die tatsächlichen Eckpunkte, indem wir nur wissen, dass sie gleichseitig sind?
Matthew Roh

@SIGSEGV Die einzige angegebene Anforderung ist, dass der Ursprung entweder der Mitte oder einem der Punkte entsprechen muss. Das gibt viel Spielraum, um die Form nach Belieben zu drehen. en.wikipedia.org/wiki/Simplex gibt einen Algorithmus zur Berechnung der Koordinaten der Hypertetraeder an (der vielleicht auf das Ikosaeder und sein 4d-Analogon ausgedehnt werden könnte, aber das ist zu viel für mich, daher meine Frage.) Die Hyperwürfel und Hyperoktaeder haben nette ganzzahlige Koordinaten (und die Hypertetraeder auch tatsächlich, aber oft nur in mehr Dimensionen als die Form selbst, die unordentlich ist.)
Level River St

@LevelRiverSt, ja, da die einzigen regulären Polytope, die existieren, in Ihren Vorschlägen behandelt würden, könnten Sie sie ja fest codieren.
Tony Ruth

Ich habe die Schlussabstimmung über diese Frage abgegeben, da es sich um eine Herausforderung im Stil der Schnellsten im Westen handelt , bei der die erste gültige Antwort gewinnt. Dies wird im Allgemeinen nicht als gültiges Gewinnkriterium angesehen. Ich weiß nicht, wie das so lange offen war, es hätte geschlossen werden sollen.
Weizen-Zauberer

Antworten:


2

Python

Hier ist ein rekursives Programm ohne Sonderfälle. Ohne Berücksichtigung von Leerzeilen und Kommentaren sind es weniger als 100 bis 90 Zeilen, einschließlich einer kostenlosen Überprüfung der Euler-Formel am Ende. Ohne die Definitionen von Ad-hoc-Mathematikfunktionen (die wahrscheinlich von einer Bibliothek bereitgestellt werden könnten) und I / O beträgt die Polytop-Generierung 50 Codezeilen. Und es macht sogar die Sternpolytope!

Das Ausgabepolytop hat die Kantenlänge 1 und befindet sich in kanonischer Position und Ausrichtung in folgendem Sinne:

  • der erste Scheitelpunkt ist der Ursprung,
  • die erste Kante liegt entlang der + x-Achse,
  • Die erste Fläche befindet sich in der + y-Halbebene der xy-Ebene.
  • Die erste 3-Zelle befindet sich im + z-Halbraum des xyz-Raums usw.

Ansonsten sind die Ausgabelisten in keiner bestimmten Reihenfolge. (Nun, eigentlich ist das nicht ganz wahr - sie werden tatsächlich grob herauskommen, beginnend mit dem ersten Element und sich nach außen ausdehnend.)

Es wird nicht nach ungültigen Schlafli-Symbolen gesucht. Wenn Sie es geben, wird das Programm wahrscheinlich von den Gleisen verschwinden (Endlosschleife, Stapelüberlauf oder einfach Müll raus).

Wenn Sie nach einer unendlichen planaren Kachelung wie {4,4} oder {3,6} oder {6,3} fragen, wird das Programm die Kachelung tatsächlich generieren, sie wird jedoch für immer fortgesetzt, bis der Speicherplatz erschöpft ist, niemals Finishing noch Ausgabe zu produzieren. Dies ist nicht allzu schwer zu beheben (beschränken Sie einfach die Anzahl der zu generierenden Elemente; das Ergebnis sollte ein ziemlich zusammenhängender Bereich des unendlichen Bilds sein, da die Elemente in der Reihenfolge der ersten Suche ungefähr in der Breite generiert werden).

Der Code

#!/usr/bin/python3
# (works with python2 or python3)

#
# schlafli_interpreter.py
# Author: Don Hatch
# For: /codegolf/114280/schl%C3%A4fli-convex-regular-polytope-interpreter
#
# Print the vertex coords and per-element (edges, faces, etc.) vertex index
# lists of a regular polytope, given by its schlafli symbol {p,q,r,...}.
# The output polytope will have edge length 1 and will be in canonical position
# and orientation, in the following sense:
#  - the first vertex is the origin,
#  - the first edge lies along the +x axis,
#  - the first face is in the +y half-plane of the xy plane,
#  - the first 3-cell is in the +z half-space of the xyz space, etc.
# Other than that, the output lists are in no particular order.
#

import sys
from math import *

# vector minus vector.
def vmv(a,b): return [x-y for x,y in zip(a,b)]
# matrix minus matrix.
def mmm(m0,m1): return [vmv(row0,row1) for row0,row1 in zip(m0,m1)]
# scalar times vector.
def sxv(s,v): return [s*x for x in v]
# scalar times matrix.
def sxm(s,m): return [sxv(s,row) for row in m]
# vector dot product.
def dot(a,b): return sum(x*y for x,y in zip(a,b))
# matrix outer product of two vectors; that is, if a,b are column vectors: a*b^T
def outer(a,b): return [sxv(x,b) for x in a]
# vector length squared.
def length2(v): return dot(v,v)
# distance between two vectors, squared.
def dist2(a,b): return length2(vmv(a,b))
# matrix times vector, homogeneous (i.e. input vector ends with an implicit 1).
def mxvhomo(m,v): return [dot(row,v+[1]) for row in m]
# Pad a square matrix (rotation/reflection) with an extra column of 0's on the
# right (translation).
def makehomo(m): return [row+[0] for row in m]
# Expand dimensionality of homogeneous transform matrix by 1.
def expandhomo(m): return ([row[:-1]+[0,row[-1]] for row in m]
                         + [[0]*len(m)+[1,0]])
# identity matrix
def identity(dim): return [[(1 if i==j else 0) for j in range(dim)]
                                               for i in range(dim)]
# https://en.wikipedia.org/wiki/Householder_transformation. v must be unit.
# Not homogeneous (makehomo the result if you want that).
def householderReflection(v): return mmm(identity(len(v)), sxm(2, outer(v,v)))

def sinAndCosHalfDihedralAngle(schlafli):
  # note, cos(pi/q)**2 generally has a nicer expression with no trig and often
  # no radicals, see http://www.maths.manchester.ac.uk/~cds/articles/trig.pdf
  ss = 0
  for q in schlafli: ss = cos(pi/q)**2 / (1 - ss)
  if abs(1-ss) < 1e-9: ss = 1  # prevent glitch in planar tiling cases
  return sqrt(ss), sqrt(1 - ss)

# Calculate a set of generators of the symmetry group of a {p,q,r,...} with
# edge length 1.
# Each generator is a dim x (dim+1) matrix where the square part is the initial
# orthogonal rotation/reflection and the final column is the final translation.
def calcSymmetryGenerators(schlafli):
  dim = len(schlafli) + 1
  if dim == 1: return [[[-1,1]]]  # one generator: reflect about x=.5
  facetGenerators = calcSymmetryGenerators(schlafli[:-1])
  # Start with facet generators, expanding each homogeneous matrix to full
  # dimensionality (i.e. from its previous size dim-1 x dim to dim x dim+1).
  generators = [expandhomo(gen) for gen in facetGenerators]
  # Final generator will reflect the first facet across the hyperplane
  # spanned by the first ridge and the entire polytope's center,
  # taking the first facet to a second facet also containing that ridge.
  # v = unit vector normal to that bisecting hyperplane
  #   = [0,...,0,-sin(dihedralAngle/2),cos(dihedralAngle/2)]
  s,c = sinAndCosHalfDihedralAngle(schlafli)
  v = [0]*(dim-2) + [-s,c]
  generators.append(makehomo(householderReflection(v)))
  return generators

# Key for comparing coords with roundoff error.  Makes sure the formatted
# numbers are not very close to 0, to avoid them coming out as "-0" or "1e-16".
# This isn't reliable in general, but it suffices for this application
# (except for very large {p}, no doubt).
def vert2key(vert): return ' '.join(['%.9g'%(x+.123) for x in vert])

# Returns a pair verts,edgesEtc where edgesEtc is [edges,faces,...]
def regular_polytope(schlafli):
  dim = len(schlafli) + 1
  if dim == 1: return [[0],[1]],[]

  gens = calcSymmetryGenerators(schlafli)

  facetVerts,facetEdgesEtc = regular_polytope(schlafli[:-1])

  # First get all the verts, and make a multiplication table.
  # Start with the verts of the first facet (padded to full dimensionality),
  # so indices will match up.
  verts = [facetVert+[0] for facetVert in facetVerts]
  vert2index = dict([[vert2key(vert),i] for i,vert in enumerate(verts)])
  multiplicationTable = []
  iVert = 0
  while iVert < len(verts):  # while verts is growing
    multiplicationTable.append([None] * len(gens))
    for iGen in range(len(gens)):
      newVert = mxvhomo(gens[iGen], verts[iVert])
      newVertKey = vert2key(newVert)
      if newVertKey not in vert2index:
        vert2index[newVertKey] = len(verts)
        verts.append(newVert)
      multiplicationTable[iVert][iGen] = vert2index[newVertKey]
    iVert += 1

  # The higher-level elements of each dimension are found by transforming
  # the facet's elements of that dimension.  Start by augmenting facetEdgesEtc
  # by adding one more list representing the entire facet.
  facetEdgesEtc.append([tuple(range(len(facetVerts)))])
  edgesEtc = []
  for facetElementsOfSomeDimension in facetEdgesEtc:
    elts = facetElementsOfSomeDimension[:]
    elt2index = dict([[elt,i] for i,elt in enumerate(elts)])
    iElt = 0
    while iElt < len(elts):  # while elts is growing
      for iGen in range(len(gens)):
        newElt = tuple(sorted([multiplicationTable[iVert][iGen]
                               for iVert in elts[iElt]]))
        if newElt not in elt2index:
          elt2index[newElt] = len(elts)
          elts.append(newElt)
      iElt += 1
    edgesEtc.append(elts)

  return verts,edgesEtc

# So input numbers can be like any of "8", "2.5", "7/3"
def parseNumberOrFraction(s):
  tokens = s.split('/')
  return float(tokens[0])/float(tokens[1]) if len(tokens)==2 else float(s)

if sys.stdin.isatty():
  sys.stderr.write("Enter schlafli symbol (space-separated numbers or fractions): ")
  sys.stderr.flush()
schlafli = [parseNumberOrFraction(token) for token in sys.stdin.readline().split()]
verts,edgesEtc = regular_polytope(schlafli)

# Hacky polishing of any integers or half-integers give or take rounding error.
def fudge(x): return round(2*x)/2 if abs(2*x-round(2*x))<1e-9 else x

print(repr(len(verts))+' Vertices:')
for v in verts: print(' '.join([repr(fudge(x)) for x in v]))
for eltDim in range(1,len(edgesEtc)+1):
  print("")
  elts = edgesEtc[eltDim-1]
  print(repr(len(elts))+' '+('Edges' if eltDim==1
                        else 'Faces' if eltDim==2
                        else repr(eltDim)+'-cells')+" ("+repr(len(elts[0]))+" vertices each):")
  for elt in elts: print(' '.join([repr(i) for i in elt]))

# Assert the generalization of Euler's formula: N0-N1+N2-... = 1+(-1)**(dim-1).
N = [len(elts) for elts in [verts]+edgesEtc]
eulerCharacteristic = sum((-1)**i * N[i] for i in range(len(N)))
print("Euler characteristic: "+repr(eulerCharacteristic))
if 2.5 not in schlafli: assert eulerCharacteristic == 1 + (-1)**len(schlafli)

Probieren Sie es in einigen Fällen aus

Eingabe ( Würfel ):

4 3

Ausgabe:

8 Vertices:
0.0 0.0 0.0
1.0 0.0 0.0
0.0 1.0 0.0
1.0 1.0 0.0
0.0 0.0 1.0
1.0 0.0 1.0
0.0 1.0 1.0
1.0 1.0 1.0

12 Edges (2 vertices each):
0 1
0 2
1 3
2 3
0 4
1 5
4 5
2 6
4 6
3 7
5 7
6 7

6 Faces (4 vertices each):
0 1 2 3
0 1 4 5
0 2 4 6
1 3 5 7
2 3 6 7
4 5 6 7

Eingabe von einer Unix-Befehlsshell ( 120-Zellen-Polychoron ):

$ echo "5 3 3" | ./schlafli_interpreter.py | grep ":"

Ausgabe:

600 Vertices:
1200 Edges (2 vertices each):
720 Faces (5 vertices each):
120 3-cells (20 vertices each):

Eingabe (10-dimensionales Kreuzpolytop ):

$ echo "3 3 3 3 3 3 3 3 4" | ./schlafli_interpreter.py | grep ":"

Ausgabe:

20 Vertices:
180 Edges (2 vertices each):
960 Faces (3 vertices each):
3360 3-cells (4 vertices each):
8064 4-cells (5 vertices each):
13440 5-cells (6 vertices each):
15360 6-cells (7 vertices each):
11520 7-cells (8 vertices each):
5120 8-cells (9 vertices each):
1024 9-cells (10 vertices each):

Eingabe (15-dimensionaler Simplex ):

$ echo "3 3 3 3 3 3 3 3 3 3 3 3 3 3" | ./schlafli_interpreter.py | grep ":"

16 Vertices:
120 Edges (2 vertices each):
560 Faces (3 vertices each):
1820 3-cells (4 vertices each):
4368 4-cells (5 vertices each):
8008 5-cells (6 vertices each):
11440 6-cells (7 vertices each):
12870 7-cells (8 vertices each):
11440 8-cells (9 vertices each):
8008 9-cells (10 vertices each):
4368 10-cells (11 vertices each):
1820 11-cells (12 vertices each):
560 12-cells (13 vertices each):
120 13-cells (14 vertices each):
16 14-cells (15 vertices each):

Sternpolytope

Ha, und natürlich auch Sternpolytope! Ich musste es nicht einmal versuchen :-) Außer, dass das bisschen über Eulers Formel am Ende fehlschlägt, da diese Formel für Sternpolytope nicht gültig ist.

Eingabe ( kleines Dodekaeder ):

5/2 5

Ausgabe:

12 Vertices:
0.0 0.0 0.0
1.0 0.0 0.0
0.8090169943749473 0.5877852522924732 0.0
0.19098300562505266 0.5877852522924732 0.0
0.5 -0.36327126400268034 0.0
0.8090169943749473 -0.2628655560595667 0.5257311121191336
0.19098300562505266 -0.2628655560595667 0.5257311121191336
0.5 0.162459848116453 -0.3249196962329062
0.5 0.6881909602355867 0.5257311121191336
0.0 0.32491969623290623 0.5257311121191336
0.5 0.1624598481164533 0.8506508083520398
1.0 0.32491969623290623 0.5257311121191336

30 Edges (2 vertices each):
0 1
0 2
1 3
2 4
3 4
0 5
1 6
5 7
6 7
0 8
2 9
7 8
7 9
1 8
0 10
3 11
5 9
4 10
7 11
4 9
2 5
1 10
4 11
6 11
6 8
3 10
3 6
2 10
9 11
5 8

12 Faces (5 vertices each):
0 1 2 3 4
0 1 5 6 7
0 2 7 8 9
1 3 7 8 11
0 4 5 9 10
2 4 5 7 11
1 4 6 10 11
0 3 6 8 10
3 4 6 7 9
2 3 9 10 11
1 2 5 8 10
5 6 8 9 11
Traceback (most recent call last):
  File "./schlafli_interpreter.py", line 185, in <module>
    assert sum((-1)**i * N[i] for i in range(len(N))) == 1 + (-1)**len(schlafli)
AssertionError

Eingang ( großartige 120-Zellen-Anzeige ):

$ echo "5/2 3 5" | ./schlafli_interpreter.py | grep ":"

Ausgabe:

120 Vertices:
720 Edges (2 vertices each):
720 Faces (5 vertices each):
120 3-cells (20 vertices each):

Vielen Dank für die Wiederbelebung dieser Frage, und Ihre Antwort sieht ziemlich beeindruckend aus. Ich mag die rekursive Natur und die Sternfiguren. Ich habe Ihren Code mit einem OpenGL-Code zum Zeichnen von Polytopen verbunden (siehe Github-Link oben).
Tony Ruth

14

Rubin

Hintergrund

Es gibt drei Familien regelmäßiger Polytope, die sich in unendliche Dimensionen erstrecken:

  • Die Simplexe, zu denen das Tetraeder gehört (ich werde sie hier oft als Hypertetraeder bezeichnen, obwohl der Begriff Simplex korrekter ist.) Ihre schlafi-Symbole haben die Form {3,3,...,3,3}

  • die n-Würfel, zu denen der Würfel gehört. Ihre schlafi Symbole sind von der Form{4,3,...,3,3}

  • Die Orthoplexe, zu denen das Oktaeder gehört (ich werde sie hier oft als Hyperoktaeder bezeichnen). Ihre Schlafi-Symbole haben die Form {3,3,...,3,4}

Es gibt eine weitere unendliche Familie regelmäßiger Polytope, Symbol {m}, die der zweidimensionalen Polygone, die eine beliebige Anzahl von Kanten m haben können.

Darüber hinaus gibt es nur fünf weitere Spezialfälle für reguläre Polytope: das dreidimensionale Ikosaeder {3,5}und das Dodekaeder {5,3}; ihre 4-dimensionalen Analoga sind die 600-Zellen {3,3,5}und 120-Zellen {5,3,3}; und ein weiteres 4-dimensionales Polytop, das 24-Zellen-Polytop {3,4,3}(dessen nächstliegende Analoga in 3 Dimensionen das Kuboktaeder und sein duales das rhombische Dodekaeder sind.)

Hauptfunktion

Unten ist die polytopeHauptfunktion, die das Schlafzimmer-Symbol interpretiert. Es erwartet ein Array mit Zahlen und gibt ein Array mit einer Reihe von Arrays wie folgt zurück:

  • Ein Array aller Scheitelpunkte, jeweils ausgedrückt als n-Element-Koordinatenarray (wobei n die Anzahl der Dimensionen ist)

  • Ein Array aller Kanten, die jeweils als 2-Element der Scheitelpunktindizes ausgedrückt werden

  • Ein Array aller Flächen, jeweils ausgedrückt als m-Element der Eckpunktindizes (wobei m die Anzahl der Eckpunkte pro Fläche ist).

und so weiter, je nach Anzahl der Dimensionen.

Es berechnet 2D-Polytope selbst, ruft Funktionen für die dreidimensionalen Familien auf und verwendet Nachschlagetabellen für die fünf Sonderfälle. Es erwartet, die darüber deklarierten Funktionen und Tabellen zu finden.

include Math

#code in subsequent sections of this answer should be inserted here 

polytope=->schl{
  if schl.size==1                                #if a single digit calculate and return a polygon
    return [(1..schl[0]).map{|i|[sin(PI*2*i/schl[0]),cos(PI*2*i/schl[0])]},(1..schl[0]).map{|i|[i%schl[0],(i+1)%schl[0]]}]  
  elsif  i=[[3,5],[5,3]].index(schl)             #if a 3d special, lookup from tables
    return [[vv,ee,ff],[uu,aa,bb]][i]
  elsif i=[[3,3,5],[5,3,3],[3,4,3]].index(schl)  #if a 4d special. lookup fromm tables
    return [[v,e,f,g],[u,x,y,z],[o,p,q,r]][i]
  elsif schl.size==schl.count(3)                 #if all threes, call tetr for a hypertetrahedron
    return tetr[schl.size+1]
  elsif schl.size-1==schl.count(3)               #if all except one number 3
    return cube[schl.size+1] if schl[0]==4       #and the 1st digit is 4, call cube for a hypercube
    return octa[schl.size+1] if schl[-1]==4      #and the last digit is 4, call octa for a hyperoctahedron
  end
  return "error"                                 #in any other case return an error
}

Funktionen für die Tetraeder-, Würfel- und Oktaederfamilie

https://en.wikipedia.org/wiki/Simplex

https://en.wikipedia.org/wiki/5-cell (4d simplex)

http://mathworld.wolfram.com/Simplex.html

Erklärung der Tetraederfamilie - Koordinaten

Ein n-dimensionales Simplex / Hypertetraeder hat n + 1 Punkte. Es ist sehr einfach, die Eckpunkte des n-dimensionalen Simplex in n + 1 Dimensionen anzugeben.

So (1,0,0),(0,1,0),(0,0,1)beschreibt ein 2D - Dreieck in 3 Dimensionen eingebettet und (1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)beschreibt einen 3D Tetraeders in 4 Dimensionen eingebettet. Dies lässt sich leicht überprüfen, indem bestätigt wird, dass alle Abstände zwischen Scheitelpunkten sqrt (2) sind.

Im Internet werden verschiedene komplizierte Algorithmen angegeben, um die Eckpunkte für den n-dimensionalen Simplex im n-dimensionalen Raum zu finden. Ich fand einen bemerkenswert einfachen in Will Jagys Kommentaren zu dieser Antwort /mathpro//a/38725 . Der letzte Punkt liegt auf der Linie p=q=...=x=y=zin einem Abstand von sqrt (2) von den anderen. Somit kann das obige Dreieck durch Hinzufügen eines Punktes an entweder (-1/3,-1/3,-1/3)oder in ein Tetraeder umgewandelt werden (1,1,1). Diese 2 möglichen Werte der Koordinaten für den letzten Punkt sind durch (1-(1+n)**0.5)/nund gegeben(1+(1+n)**0.5)/n

Da die Frage besagt, dass die Größe des n-Topes keine Rolle spielt, ziehe ich es vor, mit n zu multiplizieren und der Einfachheit halber die Koordinaten (n,0,0..0)bis zum (0..0,0,n)Endpunkt bei (t,t,..,t,t)t = 1-(1+n)**0.5zu verwenden.

Da der Mittelpunkt dieses Tetraeders nicht am Ursprung liegt, muss eine Korrektur aller Koordinaten durch die Linie vorgenommen werden, s.map!{|j|j-((1-(1+n)**0.5)+n)/(1+n)}die den Abstand des Mittelpunkts vom Ursprung ermittelt und diesen subtrahiert. Ich habe dies als separate Operation behalten. Allerdings habe ich verwendet , s[i]+=nwo s[i]=ntun würde, auf die Tatsache hinweisen , dass , wenn die Anordnung von initialisiert s=[0]*nuns stattdessen hier Offset korrekt setzen könnte und tun , um die Zentrierung Korrektur am Anfang und nicht am Ende.

Erklärung der Tetraederfamilie - Graphentopologie

Die Grafik des Simplex ist die vollständige Grafik: Jeder Scheitelpunkt ist genau einmal mit jedem anderen Scheitelpunkt verbunden. Wenn wir einen n-Simplex haben, können wir jeden Scheitelpunkt entfernen, um einen n-1-Simplex zu erhalten, bis zu dem Punkt, an dem wir ein Dreieck oder sogar eine Kante haben.

Daher müssen wir insgesamt 2 ** (n + 1) Elemente katalogisieren, die jeweils durch eine Binärzahl dargestellt werden. Dies reicht von allen 0s für Nichts über eins 1für einen Scheitelpunkt und zwei 1s für eine Kante bis zu allen 1s für das gesamte Polytop.

Wir richten ein Array mit leeren Arrays ein, um die Elemente jeder Größe zu speichern. Dann schleifen wir von Null bis (2 ** n + 1), um jede der möglichen Untergruppen von Scheitelpunkten zu erzeugen, und speichern sie in dem Array entsprechend der Größe jeder Untergruppe.

Wir interessieren uns weder für etwas Kleineres als eine Kante (einen Scheitelpunkt oder eine Null) noch für das gesamte Polytop (da im Beispiel in der Frage nicht der gesamte Würfel angegeben ist), also kehren wir zurück tg[2..n], um diese unerwünschten Elemente zu entfernen. Bevor wir zurückkehren, heften wir [tv] mit den Scheitelpunktkoordinaten an den Anfang.

Code

tetr=->n{

  #Tetrahedron Family Vertices
  tv=(0..n).map{|i|
    s=[0]*n
    if i==n
      s.map!{(1-(1+n)**0.5)}
    else
      s[i]+=n
    end
    s.map!{|j|j-((1-(1+n)**0.5)+n)/(1+n)}
  s}

  #Tetrahedron Family Graph
  tg=(0..n+1).map{[]}
  (2**(n+1)).times{|i|
    s=[]
    (n+1).times{|j|s<<j if i>>j&1==1}
    tg[s.size]<<s
  }

return [tv]+tg[2..n]}

cube=->n{

  #Cube Family Vertices
  cv=(0..2**n-1).map{|i|s=[];n.times{|j|s<<(i>>j&1)*2-1};s}

  #Cube Family Graph
  cg=(0..n+1).map{[]}
  (3**n).times{|i|                         #for each point
    s=[]
    cv.size.times{|j|                      #and each vertex
      t=true                               #assume vertex goes with point
      n.times{|k|                          #and each pair of opposite sides
        t&&= (i/(3**k)%3-1)*cv[j][k]!=-1   #if the vertex has kingsmove distance >1 from point it does not belong      
      }
      s<<j if t                            #add the vertex if it belongs
    }
    cg[log2(s.size)+1]<<s if s.size > 0
  } 

return [cv]+cg[2..n]}

octa=->n{

  #Octahedron Family Vertices
  ov=(0..n*2-1).map{|i|s=[0]*n;s[i/2]=(-1)**i;s}

  #Octahedron Family Graph
  og=(0..n).map{[]}
  (3**n).times{|i|                         #for each point
    s=[]
    ov.size.times{|j|                      #and each vertex
      n.times{|k|                          #and each pair of opposite sides
        s<<j if (i/(3**k)%3-1)*ov[j][k]==1 #if the vertex is located in the side corresponding to the point, add the vertex to the list      
      }    
    }
    og[s.size]<<s
  } 

return [ov]+og[2..n]}

Erklärung der Würfel- und Oktaederfamilien - Koordinaten

Der n-Würfel hat 2**nScheitelpunkte , die jeweils durch eine Anordnung von n repräsentiert 1s und -1s (alle Möglichkeiten erlaubt sind.) Wir iterieren Indizes 0auf 2**n-1der Liste aller Knoten und für jeden Vertex durch Iterieren durch die Bits der ein Array aufbauen indexieren und -1oder 1zum Array hinzufügen (niedrigstwertiges Bit bis höchstwertiges Bit). Somit wird Binary 1101zum 4d-Punkt [1,-1,1,1].

Das n-Oktaeder oder der n-Orthoplex hat 2nEckpunkte mit allen Koordinaten null bis auf einen, die ein sein 1oder sind -1. Die Reihenfolge der Eckpunkte in dem generierten Array ist [[1,0,0..],[-1,0,0..],[0,1,0..],[0,-1,0..],[0,0,1..],[0,0,-1..]...]. Beachten Sie, dass, da das Oktaeder das Dual des Würfels ist, die Scheitelpunkte des Oktaeders durch die Zentren der Flächen des Würfels definiert sind, der es umgibt.

Erklärung der Würfel- und Oktaederfamilien - Graphentopologie

Die Hyperkubusseiten und die Tatsache, dass das Hyperoktaeder das Dual des Hyperkubus ist, haben einige Inspirationen hervorgerufen .

Für den n-Cube gibt es 3**nArtikel zum Katalogisieren. Zum Beispiel hat der Würfel 3 3**3= 27 Elemente. Dies lässt sich an einem Rubik-Würfel erkennen, der 1 Zentrum, 6 Flächen, 12 Kanten und 8 Eckpunkte für insgesamt 27 hat. Wir durchlaufen -1,0 und -1 in allen Dimensionen, die einen n-Würfel mit der Seitenlänge 2x2x2 definieren .. und gebe alle Eckpunkte zurück, die sich NICHT auf der gegenüberliegenden Seite des Würfels befinden. Der Mittelpunkt des Würfels gibt also alle 2 ** n Scheitelpunkte zurück, und wenn Sie eine Einheit entlang einer beliebigen Achse vom Mittelpunkt entfernen, wird die Anzahl der Scheitelpunkte um die Hälfte verringert.

Wie bei der Tetraederfamilie erzeugen wir zunächst ein leeres Array von Arrays und füllen es entsprechend der Anzahl der Eckpunkte pro Element. Beachten Sie, dass wir log2(s.size)+1anstelle von einfach verwenden , da die Anzahl der Eckpunkte beim Durchlaufen von Kanten, Flächen, Würfeln usw. um 2 ** n variiert s.size. Wieder müssen wir den Hypercube selbst und alle Elemente mit weniger als 2 Eckpunkten entfernen, bevor wir von der Funktion zurückkehren.

Die Oktaeder- / Orthoplex-Familie sind die Dualen der Würfelfamilie, daher gibt es auch hier wieder 3**nArtikel zum Katalogisieren. Hier werden -1,0,1alle Dimensionen durchlaufen. Wenn die Koordinate eines Scheitelpunkts ungleich der entsprechenden Koordinate des Punkts ist, wird der Scheitelpunkt zu der Liste hinzugefügt, die diesem Punkt entspricht. Eine Kante entspricht also einem Punkt mit zwei Nicht-Null-Koordinaten, ein Dreieck einem Punkt mit drei Nicht-Null-Koordinaten und ein Tetraeder einem Punkt mit vier Nicht-Null-Kontakten (im 4d-Raum).

Die resultierenden Vertex-Arrays für jeden Punkt werden wie in den anderen Fällen in einem großen Array gespeichert, und wir müssen alle Elemente mit weniger als 2 Vertices entfernen, bevor wir zurückkehren. In diesem Fall müssen wir jedoch nicht das gesamte übergeordnete N-Top entfernen, da der Algorithmus es nicht aufzeichnet.

Die Implementierungen des Codes für den Cube wurden so ähnlich wie möglich gestaltet. Dies hat zwar eine gewisse Eleganz, es ist jedoch wahrscheinlich, dass effizientere Algorithmen basierend auf denselben Prinzipien entwickelt werden könnten.

https://en.wikipedia.org/wiki/Hypercube

http://mathworld.wolfram.com/Hypercube.html

https://en.wikipedia.org/wiki/Cross-polytope

http://mathworld.wolfram.com/CrossPolytope.html

Code zum Generieren von Tabellen für die 3D-Sonderfälle

Eine Orientierung mit dem Ikosaeder / Dodekaeder, die mit der fünffachen Symmetrieachse parallel zur letzten Dimension orientiert ist, wurde verwendet, da dies für die konsistenteste Kennzeichnung der Teile sorgte. Die Nummerierung der Eckpunkte und Flächen für das Ikosaeder entspricht dem Diagramm in den Codekommentaren und ist für das Dodekaeder umgekehrt.

Laut https://en.wikipedia.org/wiki/Regular_icosahedron beträgt der Breitengrad der 10 unpolaren Eckpunkte des Ikosaeders +/- arctan (1/2). Die Koordinaten der ersten 10 Eckpunkte des Ikosaeders werden aus berechnet dies auf zwei Kreisen mit Radius 2 in einem Abstand +/- 2 von der xy-Ebene. Dies bewirkt, dass der Gesamtradius der Umkreiskugel sqrt (5) so ist, dass die letzten 2 Eckpunkte bei (0,0, + / - sqrt (2)) liegen.

Die Koordinaten der Eckpunkte des Dodekaeders werden berechnet, indem die Koordinaten der drei Ikosaeder-Eckpunkte, die sie umgeben, summiert werden.

=begin
TABLE NAMES      vertices     edges      faces
icosahedron      vv           ee         ff
dodecahedron     uu           aa         bb 

    10
    / \   / \   / \   / \   / \
   /10 \ /12 \ /14 \ /16 \ /18 \
   -----1-----3-----5-----7-----9
   \ 0 / \ 2 / \ 4 / \ 6 / \ 8 / \
    \ / 1 \ / 3 \ / 5 \ / 7 \ / 9 \
     0-----2-----4-----6-----8-----
      \11 / \13 / \15 / \17 / \19 /
       \ /   \ /   \ /   \ /   \ / 
       11
=end

vv=[];ee=[];ff=[]
10.times{|i|
  vv[i]=[2*sin(PI/5*i),2*cos(PI/5*i),(-1)**i]
  ee[i]=[i,(i+1)%10];ee[i+10]=[i,(i+2)%10];ee[i+20]=[i,11-i%2]
  ff[i]=[(i-1)%10,i,(i+1)%10];ff[i+10]=[(i-1)%10,10+i%2,(i+1)%10]

}
vv+=[[0,0,-5**0.5],[0,0,5**0.5]]

uu=[];aa=[];bb=[]
10.times{|i|
  uu[i]=(0..2).map{|j|vv[ff[i][0]][j]+vv[ff[i][1]][j]+vv[ff[i][2]][j]}
  uu[i+10]=(0..2).map{|j|vv[ff[i+10][0]][j]+vv[ff[i+10][1]][j]+vv[ff[i+10][2]][j]}
  aa[i]=[i,(i+1)%10];aa[i+10]=[i,(i+10)%10];aa[i+20]=[(i-1)%10+10,(i+1)%10+10]
  bb[i]=[(i-1)%10+10,(i-1)%10,i,(i+1)%10,(i+1)%10+10] 
}
bb+=[[10,12,14,16,18],[11,13,15,17,19]]

Code zum Generieren der Tabellen für die 4d-Sonderfälle

Das ist ein bisschen hacken. Die Ausführung dieses Codes dauert einige Sekunden. Es ist besser, die Ausgabe in einer Datei zu speichern und nach Bedarf zu laden.

Die Liste der 120 Scheitelpunktkoordinaten für die 600-Zelle stammt von http://mathworld.wolfram.com/600-Cell.html . Die 24 Scheitelpunktkoordinaten, die kein goldenes Verhältnis aufweisen, bilden die Scheitelpunkte einer 24-Zelle. Wikipedia hat das gleiche Schema, aber einen Fehler in der relativen Skala dieser 24 Koordinaten und der anderen 96.

#TABLE NAMES                           vertices     edges      faces   cells
#600 cell (analogue of icosahedron)    v            e          f       g
#120 cell (analogue of dodecahedron)   u            x          y       z 
#24 cell                               o            p          q       r

#600-CELL

# 120 vertices of 600cell. First 24 are also vertices of 24-cell

v=[[2,0,0,0],[0,2,0,0],[0,0,2,0],[0,0,0,2],[-2,0,0,0],[0,-2,0,0],[0,0,-2,0],[0,0,0,-2]]+

(0..15).map{|j|[(-1)**(j/8),(-1)**(j/4),(-1)**(j/2),(-1)**j]}+

(0..95).map{|i|j=i/12
   a,b,c,d=1.618*(-1)**(j/4),(-1)**(j/2),0.618*(-1)**j,0
   h=[[a,b,c,d],[b,a,d,c],[c,d,a,b],[d,c,b,a]][i%12/3]
   (i%3).times{h[0],h[1],h[2]=h[1],h[2],h[0]}
h}

#720 edges of 600cell. Identified by minimum distance of 2/phi between them

e=[]
120.times{|i|120.times{|j|
  e<<[i,j]  if i<j && ((v[i][0]-v[j][0])**2+(v[i][1]-v[j][1])**2+(v[i][2]-v[j][2])**2+(v[i][3]-v[j][3])**2)**0.5<1.3  
}}

#1200 faces of 600cell. 
#If 2 edges share a common vertex and the other 2 vertices form an edge in the list, it is a valid triangle.

f=[]
720.times{|i|720.times{|j|
  f<< [e[i][0],e[i][1],e[j][1]] if i<j && e[i][0]==e[j][0] && e.index([e[i][1],e[j][1]])
}}

#600 cells of 600cell.
#If 2 triangles share a common edge and the other 2 vertices form an edge in the list, it is a valid tetrahedron.

g=[]
1200.times{|i|1200.times{|j|
  g<< [f[i][0],f[i][1],f[i][2],f[j][2]] if i<j && f[i][0]==f[j][0] && f[i][1]==f[j][1] && e.index([f[i][2],f[j][2]])

}}

#120 CELL (dual of 600 cell)

#600 vertices of 120cell, correspond to the centres of the cells of the 600cell
u=g.map{|i|s=[0,0,0,0];i.each{|j|4.times{|k|s[k]+=v[j][k]/4.0}};s}

#1200 edges of 120cell at centres of faces of 600-cell. Search for pairs of tetrahedra with common face
x=f.map{|i|s=[];600.times{|j|s<<j if i==(i & g[j])};s}

#720 pentagonal faces, surrounding edges of 600-cell. Search for sets of 5 tetrahedra with common edge
y=e.map{|i|s=[];600.times{|j|s<<j if i==(i & g[j])};s}

#120 dodecahedral cells surrounding vertices of 600-cell. Search for sets of 20 tetrahedra with common vertex
z=(0..119).map{|i|s=[];600.times{|j|s<<j if [i]==([i] & g[j])};s}


#24-CELL
#24 vertices, a subset of the 600cell
o=v[0..23]

#96 edges, length 2, found by minimum distances between vertices
p=[]
24.times{|i|24.times{|j|
  p<<[i,j]  if i<j && ((v[i][0]-v[j][0])**2+(v[i][1]-v[j][1])**2+(v[i][2]-v[j][2])**2+(v[i][3]-v[j][3])**2)**0.5<2.1  
}}

#96 triangles
#If 2 edges share a common vertex and the other 2 vertices form an edge in the list, it is a valid triangle.
q=[]
96.times{|i|96.times{|j|
  q<< [p[i][0],p[i][1],p[j][1]] if i<j && p[i][0]==p[j][0] && p.index([p[i][1],p[j][1]])
}}


#24 cells. Calculates the centre of the cell and the 6 vertices nearest it
r=(0..23).map{|i|a,b=(-1)**i,(-1)**(i/2)
    c=[[a,b,0,0],[a,0,b,0],[a,0,0,b],[0,a,b,0],[0,a,0,b],[0,0,a,b]][i/4]
    s=[]
    24.times{|j|t=v[j]
    s<<j if (c[0]-t[0])**2+(c[1]-t[1])**2+(c[2]-t[2])**2+(c[3]-t[3])**2<=2 
    }
s}

https://en.wikipedia.org/wiki/600-cell

http://mathworld.wolfram.com/600-Cell.html

https://en.wikipedia.org/wiki/120-cell

http://mathworld.wolfram.com/120-Cell.html

https://en.wikipedia.org/wiki/24-cell

http://mathworld.wolfram.com/24-Cell.html

Anwendungsbeispiel und Ausgabe

cell24 = polytope[[3,4,3]]

puts "vertices"
cell24[0].each{|i|p i}
puts "edges"
cell24[1].each{|i|p i}
puts "faces"
cell24[2].each{|i|p i}
puts "cells"
cell24[3].each{|i|p i}

vertices
[2, 0, 0, 0]
[0, 2, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 2]
[-2, 0, 0, 0]
[0, -2, 0, 0]
[0, 0, -2, 0]
[0, 0, 0, -2]
[1, 1, 1, 1]
[1, 1, 1, -1]
[1, 1, -1, 1]
[1, 1, -1, -1]
[1, -1, 1, 1]
[1, -1, 1, -1]
[1, -1, -1, 1]
[1, -1, -1, -1]
[-1, 1, 1, 1]
[-1, 1, 1, -1]
[-1, 1, -1, 1]
[-1, 1, -1, -1]
[-1, -1, 1, 1]
[-1, -1, 1, -1]
[-1, -1, -1, 1]
[-1, -1, -1, -1]
edges
[0, 8]
[0, 9]
[0, 10]
[0, 11]
[0, 12]
[0, 13]
[0, 14]
[0, 15]
[1, 8]
[1, 9]
[1, 10]
[1, 11]
[1, 16]
[1, 17]
[1, 18]
[1, 19]
[2, 8]
[2, 9]
[2, 12]
[2, 13]
[2, 16]
[2, 17]
[2, 20]
[2, 21]
[3, 8]
[3, 10]
[3, 12]
[3, 14]
[3, 16]
[3, 18]
[3, 20]
[3, 22]
[4, 16]
[4, 17]
[4, 18]
[4, 19]
[4, 20]
[4, 21]
[4, 22]
[4, 23]
[5, 12]
[5, 13]
[5, 14]
[5, 15]
[5, 20]
[5, 21]
[5, 22]
[5, 23]
[6, 10]
[6, 11]
[6, 14]
[6, 15]
[6, 18]
[6, 19]
[6, 22]
[6, 23]
[7, 9]
[7, 11]
[7, 13]
[7, 15]
[7, 17]
[7, 19]
[7, 21]
[7, 23]
[8, 9]
[8, 10]
[8, 12]
[8, 16]
[9, 11]
[9, 13]
[9, 17]
[10, 11]
[10, 14]
[10, 18]
[11, 15]
[11, 19]
[12, 13]
[12, 14]
[12, 20]
[13, 15]
[13, 21]
[14, 15]
[14, 22]
[15, 23]
[16, 17]
[16, 18]
[16, 20]
[17, 19]
[17, 21]
[18, 19]
[18, 22]
[19, 23]
[20, 21]
[20, 22]
[21, 23]
[22, 23]
faces
[0, 8, 9]
[0, 8, 10]
[0, 8, 12]
[0, 9, 11]
[0, 9, 13]
[0, 10, 11]
[0, 10, 14]
[0, 11, 15]
[0, 12, 13]
[0, 12, 14]
[0, 13, 15]
[0, 14, 15]
[1, 8, 9]
[1, 8, 10]
[1, 8, 16]
[1, 9, 11]
[1, 9, 17]
[1, 10, 11]
[1, 10, 18]
[1, 11, 19]
[1, 16, 17]
[1, 16, 18]
[1, 17, 19]
[1, 18, 19]
[2, 8, 9]
[2, 8, 12]
[2, 8, 16]
[2, 9, 13]
[2, 9, 17]
[2, 12, 13]
[2, 12, 20]
[2, 13, 21]
[2, 16, 17]
[2, 16, 20]
[2, 17, 21]
[2, 20, 21]
[3, 8, 10]
[3, 8, 12]
[3, 8, 16]
[3, 10, 14]
[3, 10, 18]
[3, 12, 14]
[3, 12, 20]
[3, 14, 22]
[3, 16, 18]
[3, 16, 20]
[3, 18, 22]
[3, 20, 22]
[4, 16, 17]
[4, 16, 18]
[4, 16, 20]
[4, 17, 19]
[4, 17, 21]
[4, 18, 19]
[4, 18, 22]
[4, 19, 23]
[4, 20, 21]
[4, 20, 22]
[4, 21, 23]
[4, 22, 23]
[5, 12, 13]
[5, 12, 14]
[5, 12, 20]
[5, 13, 15]
[5, 13, 21]
[5, 14, 15]
[5, 14, 22]
[5, 15, 23]
[5, 20, 21]
[5, 20, 22]
[5, 21, 23]
[5, 22, 23]
[6, 10, 11]
[6, 10, 14]
[6, 10, 18]
[6, 11, 15]
[6, 11, 19]
[6, 14, 15]
[6, 14, 22]
[6, 15, 23]
[6, 18, 19]
[6, 18, 22]
[6, 19, 23]
[6, 22, 23]
[7, 9, 11]
[7, 9, 13]
[7, 9, 17]
[7, 11, 15]
[7, 11, 19]
[7, 13, 15]
[7, 13, 21]
[7, 15, 23]
[7, 17, 19]
[7, 17, 21]
[7, 19, 23]
[7, 21, 23]
cells
[0, 1, 8, 9, 10, 11]
[1, 4, 16, 17, 18, 19]
[0, 5, 12, 13, 14, 15]
[4, 5, 20, 21, 22, 23]
[0, 2, 8, 9, 12, 13]
[2, 4, 16, 17, 20, 21]
[0, 6, 10, 11, 14, 15]
[4, 6, 18, 19, 22, 23]
[0, 3, 8, 10, 12, 14]
[3, 4, 16, 18, 20, 22]
[0, 7, 9, 11, 13, 15]
[4, 7, 17, 19, 21, 23]
[1, 2, 8, 9, 16, 17]
[2, 5, 12, 13, 20, 21]
[1, 6, 10, 11, 18, 19]
[5, 6, 14, 15, 22, 23]
[1, 3, 8, 10, 16, 18]
[3, 5, 12, 14, 20, 22]
[1, 7, 9, 11, 17, 19]
[5, 7, 13, 15, 21, 23]
[2, 3, 8, 12, 16, 20]
[3, 6, 10, 14, 18, 22]
[2, 7, 9, 13, 17, 21]
[6, 7, 11, 15, 19, 23]

1
Wow das ist eine tolle Antwort !! Ich bin sehr überrascht, dass Sie dies in ~ 200 Zeilen geschafft haben. Ich habe den Würfel, den Tetraeder, die 600-Zellen und einige andere ausgeführt, und sie sahen gut aus. Es ist schwer, die Ausgabe zu überprüfen, da es so viel davon gibt. Es ist ziemlich einfach, dass die Ausgabe länger ist als das Programm, aber ich nehme Ihr Wort dafür. Ich werde versuchen, dies in openGL zu laden und die platonischen Körper anzuzeigen, die einfach sein sollten, da alle Gesichter aufgelistet sind. Ich denke, das Hinzufügen von Tesselationen im flachen Raum wäre einfach, und das könnte ich auch versuchen.
Tony Ruth

@TonyRuth der Schlüssel fand den besten Algorithmus. Weniger Zeilen = weniger Platz für Fehler. Das erste, was ich getan habe, war zu überprüfen, was es neben den dreidimensionalen Familien gibt, und dann habe ich beschlossen, zu antworten. Will Jagys Kommentare waren ein Glücksfall (ich dachte über diese Art von Lösung nach, da die Methode von Wikipedia schwierig aussah), sodass die nicht ganzzahligen Koordinaten auf ein Minimum beschränkt sind. Ich wollte es schaffen, bevor das Kopfgeld abgelaufen ist, also war die Überprüfung nicht besonders gründlich und ich habe sie nicht geplant. Lassen Sie mich alle Fehler wissen - ich habe die 24-Zellen vor ein paar Stunden korrigiert.
Level River St

@TonyRuth-Gesichtsscheitelpunkte sind in keiner bestimmten Reihenfolge angeordnet (sie werden nicht im Uhrzeigersinn oder in irgendeiner Weise um das Gesicht herumgeführt). Für höhere Dimensionen gibt es keine Standardbestellung. Bei den Hyperwürfeln sind die Flächen in numerischer Reihenfolge aufgeführt, sodass der 2. und 3. Scheitelpunkt diagonal gegenüberliegen (Sie müssen den 1. und 2. oder 3. und 4. Scheitelpunkt vertauschen, wenn Sie sie im / gegen den Uhrzeigersinn haben möchten.) Reihenfolge im / gegen den Uhrzeigersinn, aber die 120-Zelle wird die Eckpunkte in jeder und allen Reihenfolgen haben.
Level River St
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.