SchläfliConvex Regular Polytope Interpreter


15

バックグラウンド

シュレーフリ記号は正規ポリトープとテッセレーションを定義するフォーム{P、Q、R、...}の表記です。

Schläfliシンボルは再帰的な記述であり、p側の正多角形から{p}として始まります。たとえば、{3}は正三角形、{4}は正方形などです。

各頂点の周りにq個の正多角形の多角形面を持つ正多面体は、{p、q}で表されます。たとえば、キュ​​ーブには各頂点の周りに3つの正方形があり、{4,3}で表されます。

各エッジの周りにr {p、q}個の正多面体セルがある通常の4次元ポリトープは、{p、q、r}で表されます。たとえば、テッセラクト{4,3,3}には、エッジの周りに3つのキューブ{4,3}があります。

一般に、通常のポリトープ{p、q、r、...、y、z}は、すべてのピークの周りにz {p、q、r、...、y}ファセットを持ちます。ここで、ピークは多面体の頂点です。 4ポリトープのエッジ、5ポリトープの面、6ポリトープのセル、nポリトープの(n-3)面。

通常のポリトープには、通常の頂点図形があります。通常のポリトープ{p、q、r、... y、z}の頂点の図は{q、r、... y、z}です。

通常のポリトープは、五角形のように五角形の頂点で表されるが交互に接続されたシンボル{5/2}の星形の多角形要素を持つことができます。

Schläfliシンボルは、構造の角度欠陥に応じて、有限凸多面体、ユークリッド空間の無限テッセレーション、または双曲線空間の無限テッセレーションを表すことができます。正の角度の欠陥により、頂点の図形がより高い次元に折り畳まれ、ポリトープとしてループ自体に戻ります。ゼロ角欠陥は、ファセットと同じ次元の空間をテッセレーションします。負の角度の欠陥は通常の空間には存在できませんが、双曲線空間に構築できます。

コンペ

あなたの目標は、Schläfliシンボルを渡すと、凸ポリトープの完全な記述を返すプログラムを作成することです。これはSchläfliシンボルのサブセットにすぎませんが、これは最も単純なシンボルです。他の可能性がなくてもこれは非常に難しい作業であり、ポリトープがテッセレーションの出発点だと思います。この質問のルールは、この結果がAPIであるという考えで設計されたもので、インターネット上でそのようなプログラムを見つけることができませんでした。

プログラムは、次のすべてを達成する必要があります。

  • プログラムは、有限次元の正規凸ポリトープを生成できなければなりません。2次元では、nゴンが含まれます。3次元ではこれらはプラトニックな固体であり、4次元ではこれにはtesseract、orthoplex、および他のいくつかが含まれます)
  • プログラムは、(a)原点に点を配置するか、(b)すべての点の平均が原点であることを確認する必要があります。向きは関係ありません。全体のサイズは重要ではありません。
  • プログラムは、4次元オブジェクトの場合、頂点、エッジ、面、および多面体を返す/印刷することを意味する完全な説明を提供する必要があります。これらが報告される順序は重要ではありません。多面体の場合、これはオブジェクトをレンダリングするために必要な情報です。

以下を処理する必要ありませ

  • テセレーション
  • 双曲線幾何
  • フラクショナルシュレーフリ記号(非凸)
  • 埋め込みシュレーフリ記号(不均一なタイル)

これらのいずれかを行うように求められた場合、エラーを返すことができます。

例:キューブ

入力:

4 3

出力:

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

このアルゴリズムがどのように機能し、非常に再帰的であるかについていくつかのアイデアがありましたが、これまで失敗しましたが、インスピレーションを探している場合はチェックアウトしてくださいhttps : //en.wikipedia.org/wiki/Euler_characteristic

頂点、エッジ、および面の数を計算する例として、{4,3}である立方体を考えます。最初の4を見ると、4つのエッジと4つの頂点があります。次の3を見ると、各頂点で3つのエッジが交わり、各エッジが2つの頂点に接続し、2つの面が各エッジで交わり、各面が4つのエッジに接続していることがわかります(正方形の側面のため)オイラー特性式。

E = 3/2 V

E = 4/2 F

V-E + F = 2

E = 12、V = 8、F = 6になります。

得点

トピックに関する質問を保持するために、これはCode Golfに改訂されました。最短のコードが優先されます。

この質問に対してgithubが作成されました


1
グーグルは、立方体、八面体、四面体に類似する、4次元を超えて伸びる通常のポリトープのファミリが3つしかないことを示しています。これらのファミリに対して記述し、残りをハードコードする方が簡単だと思われます(2つの3Dポリトープ、3つの4Dポリトープ、および2Dポリトープの無限のファミリ)。それは有効な答えでしょうか?再帰アルゴリズムを記述して、仕様の範囲を超えたトポロジーグラフを生成することは可能かもしれませんが、仕様内であってもそのアプローチを採用しているキラーは座標を計算しています。
レベルリバーセント

それらが等辺であることを知るだけで、実際の頂点をどのように知ることができますか?
マシュー盧

@SIGSEGVに指定される唯一の要件は、原点が中心またはポイントの1つに対応することです。これにより、シェイプを自由に回転させることができます。en.wikipedia.org/wiki/Simplexは、ハイパー四面体の座標を計算するためのアルゴリズムを提供します(おそらく、20面体とその4dアナログに拡張できますが、それを行うのは私にとってはやりすぎです。したがって、私の質問です)。素敵な整数座標(および実際にはハイパー四面体でも、形状自体よりも多くの次元でのみであり、乱雑です。)
Level River St

@LevelRiverSt、はい、存在する唯一の通常のポリトープが提案内でカバーされるため、はい、はい、それらをハードコーディングできます。
トニールース

最初の有効な答えが勝つ、最速の西側の銃スタイルの挑戦であるので、私はこの質問に最後の票を投じました。通常、これは有効な勝ち基準とはみなされません。私はこれが長い間開いていた方法がわからない、それは閉じられるべきだった。
ポストロックガーフハンター

回答:


2

Python

これは特別なケースのない再帰プログラムです。空白行とコメントを無視すると、最後にオイラーの式の無償チェックを含めて、100行未満です。アドホック数学関数(おそらくライブラリによって提供される可能性があります)とI / Oの定義を除くと、ポリトープ生成は50行のコードです。そして、それは星のポリトープもします!

出力ポリトープのエッジの長さは1で、次の意味で標準的な位置と方向になります。

  • 最初の頂点が原点であり、
  • 最初のエッジは+ x軸に沿ってあり、
  • 最初の面は、xy平面の+ y半平面にあります。
  • 最初の3セルはxyz空間の+ z半空間などにあります。

それ以外は、出力リストは特定の順序ではありません。(まあ、実際には、それは完全に真実ではありません。実際には、最初の要素から始まって外側に向かって大まかに順番に出てきます。)

無効なschlafliシンボルのチェックはありません。あなたがそれを与えた場合、プログラムはおそらくレールから外れます(無限ループ、スタックオーバーフロー、または単にガベージアウト)。

{4,4}または{3,6}または{6,3}などの無限平面タイルを要求すると、プログラムは実際にタイルの生成を開始しますが、スペースがなくなるまで永久に続きます。仕上げも出力もしません。これを修正するのはそれほど難しいことではありません(生成する要素の数に制限を設けるだけです。要素はほぼ幅優先の検索順序で生成されるため、結果は無限の画像のかなり一貫した領域になるはずです)。

コード

#!/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)

いくつかのケースで試してみる

入力(cube):

4 3

出力:

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

UNIXコマンドシェルからの入力(120セルポリコロン):

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

出力:

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

入力(10次元のクロスポリトープ):

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

出力:

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):

入力(15次元シンプレックス):

$ 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):

スターポリトープ

ハ、そしてそれは当然、星のポリトープも当然します!私は試してみる必要さえありませんでした:-)最後のオイラーの公式についてのビットが失敗することを除いて、その公式は星の多面体に対して有効ではないからです。

入力(小さな星形十二面体):

5/2 5

出力:

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

入力(星型120セル):

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

出力:

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

この質問を復活させてくれてありがとう、そしてあなたの答えはとても印象的だ。私は再帰的な性質と星の数字が好きです。ポリトープを描画するために、コードをopenglに接続しました(上記のgithubリンクを参照)。
トニールース

14

ルビー

バックグラウンド

無限次元に拡張する通常のポリトープには3つのファミリーがあります。

  • 四面体がメンバーであるシンプレックス(ここでは、シンプレックスという用語はより正確ですが、ここではしばしばハイパー四面体と呼びます)。それらのシュラフィ記号の形式は {3,3,...,3,3}

  • キューブがメンバーであるnキューブ。彼らのシュラフィ記号は{4,3,...,3,3}

  • 正八面体がメンバーである正則(ここではしばしば、それらを超八面体と呼びます)シュラフ記号は次の形式です {3,3,...,3,4}

規則的なポリトープのもう1つの無限のファミリー、シンボルがあります {m}、任意の数のエッジmを持つ2次元ポリゴンのそれです。

これに加えて、通常のポリトープの他の5つの特別なケースがあります。3次元の20面体{3,5}と12面体{5,3}です。4次元の類似物である600セル{3,3,5}と120セル{5,3,3}。もう1つの4次元ポリトープ、24セル{3,4,3}(3次元で最も近い類似体は立方八面体であり、その双対は菱形十二面体です。)

メイン機能

以下は、polytopeシュラフィ記号を解釈する主な機能です。数値の配列を予期し、次のように一連の配列を含む配列を返します。

  • すべてが頂点の配列で、それぞれが座標のn要素配列として表されます(nは次元数です)

  • それぞれが頂点インデックスの2要素として表される、すべてのエッジの配列

  • それぞれが頂点インデックスのm要素として表されるすべての面の配列(mは面ごとの頂点の数)

など、次元の数に応じて。

2Dポリトープ自体を計算し、3つの無限次元ファミリの関数を呼び出し、5つの特殊なケースのルックアップテーブルを使用します。それは、その上で宣言された関数とテーブルを見つけることを期待しています。

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
}

四面体、立方体、八面体ファミリーの関数

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

https://en.wikipedia.org/wiki/5-cellシンプレックス)

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

四面体ファミリーの説明-座標

n次元のシンプレックス/超四面体にはn + 1個のポイントがあります。n + 1次元のn次元シンプレックスの頂点を与えるのは非常に簡単です。

したがって(1,0,0),(0,1,0),(0,0,1)、3次元に埋め込まれた2次元の三角形を(1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)記述し、4次元に埋め込まれた3次元の四面体を記述します。これは、頂点間のすべての距離がsqrt(2)であることを確認することで簡単に検証できます。

n次元空間でn次元シンプレックスの頂点を見つけるためのさまざまな複雑なアルゴリズムがインターネット上で提供されています。この答え/mathpro//a/38725に対するウィルジャギーのコメントで、非常にシンプルなものを見つけました。最後の点はp=q=...=x=y=z、他の点からsqrt(2)の距離にある線上にあります。したがって、上記の三角形は、(-1/3,-1/3,-1/3)またはのいずれかに点を追加することにより、四面体に変換できます(1,1,1)。最後の点の座標のこれら2つの可能な値は、(1-(1+n)**0.5)/n(1+(1+n)**0.5)/n

n-topeのサイズは重要ではないという質問にあるように、nを乗算し、t =の最後のポイント(n,0,0..0)までの座標を使用するのが簡単です。(0..0,0,n)(t,t,..,t,t)1-(1+n)**0.5

この四面体の中心は原点にないので、すべての座標の修正s.map!{|j|j-((1-(1+n)**0.5)+n)/(1+n)}は、中心が原点からどれだけ離れているかを見つけ、それを差し引く線によって行われなければなりません。これを別の操作として保持しました。ただし、配列を初期化するときに正しいオフセットをここに入れて、最後ではなく最初にセンタリング補正を行うことができるという事実を暗示するために、s[i]+=nどこでs[i]=n行うかを使用しましたs=[0]*n

四面体ファミリーの説明-グラフトポロジ

シンプレックスのグラフは完全なグラフです。すべての頂点は、他のすべての頂点に1回だけ接続されます。n個のシンプレックスがある場合、頂点を削除してn-1シンプレックスを作成し、三角形またはエッジさえも持つことができます。

したがって、カタログに合計2 **(n + 1)個のアイテムがあり、それぞれが2進数で表されます。これは、0無の場合のすべてのs から1、頂点の場合は1つ1、エッジの場合は2つの1sから、完全なポリトープのすべてのs までの範囲です。

空の配列の配列を設定して、各サイズの要素を保存します。次に、ゼロから(2 ** n + 1)までループして、頂点の可能なサブセットのそれぞれを生成し、各サブセットのサイズに従って配列に格納します。

エッジ(頂点またはゼロ)より小さいものや完全なポリトープ(問題の例では完全な立方体が示されていないため)に関心がないtg[2..n]ため、これらの不要な要素を削除するために戻ります。戻る前に、頂点座標を含む[tv]を先頭に追加します。

コード

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]}

立方体と八面体のファミリーの説明-座標

nキューブには2**n頂点があり、各頂点はn 1および-1sの配列で表されます(すべての可能性が許可されます)。すべての頂点のリスト02**n-1のインデックスを反復処理し、インデックスと配列への追加-1または1配列(最下位ビットから最上位ビット)。したがって、バイナリ1101は4dポイントになり[1,-1,1,1]ます。

n-8面体またはn-orthoplexには2n頂点があり、1つを除くすべての座標が0で、be 1または-1です。生成された配列の頂点の順序は[[1,0,0..],[-1,0,0..],[0,1,0..],[0,-1,0..],[0,0,1..],[0,0,-1..]...]です。八面体は立方体の双対であるため、八面体の頂点はそれを囲む立方体の面の中心によって定義されることに注意してください。

立方体および八面体ファミリの説明-グラフトポロジ

いくつかのインスピレーションは、Hypercube側から得られたものであり、hyperoctahedronはhypercubeの双対です。

nキューブの場合、3**nカタログ化するアイテムがあります。たとえば、3つのキューブの3**3要素数は27です。これは、中心が1つ、面が6つ、端が12つ、頂点が8つある合計27のrubikの立方体を調べることで確認できます。 ..そして、立方体の反対側にないすべての頂点を返します。したがって、立方体の中心点はすべての2 ** n頂点を返し、任意の軸に沿って中心から1単位離れると、頂点の数が半分に減少します。

四面体ファミリーと同様に、配列の空の配列を生成することから始め、要素ごとの頂点の数に応じてそれを設定します。頂点、エッジ、面、立方体などを通過すると頂点の数が2 ** nになるためlog2(s.size)+1、単にではなくを使用することに注意してくださいs.size。繰り返しますが、関数から戻る前に、ハイパーキューブ自体と2つ未満の頂点を持つすべての要素を削除する必要があります。

八面体/オルソプレックスファミリはキューブファミリの双対であるため、ここでも3**nカタログ化するアイテムがあります。ここで-1,0,1、すべての次元について反復処理を行い、頂点の非ゼロ座標がポイントの対応する座標と等しい場合、頂点はそのポイントに対応するリストに追加されます。したがって、エッジは2つの非ゼロ座標を持つポイントに対応し、三角形は3つの非ゼロ座標を持つポイントに対応し、四面体は4つの非ゼロ接触を持つポイントに対応します(4D空間内)。

結果の各ポイントの頂点の配列は、他の場合と同様に大きな配列に格納され、戻る前に2未満の頂点を持つ要素を削除する必要があります。ただし、この場合、アルゴリズムが記録しないため、完全な親n-topeを削除する必要はありません。

キューブのコードの実装は、可能な限り類似するように設計されました。これにはある程度の優雅さがありますが、同じ原理に基づいたより効率的なアルゴリズムを考案できる可能性があります。

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

3D特殊ケース用のテーブルを生成するためのコード

パーツの最も一貫したラベリングを実現するために、最後の次元に平行な5重対称軸で方向付けられた20面体/ 12面体の方向が使用されました。二十面体の頂点と面の番号付けは、コードコメントの図に基づいており、十二面体の場合は逆になっています。

https://en.wikipedia.org/wiki/Regular_icosahedronによると、正二十面体の10個の非極性頂点の緯度は+/- arctan(1/2)であり、二十面体の最初の10個の頂点の座標はこれは、xy平面から+/- 2の距離にある半径2の2つの円上にあります。これにより、外圏の半径が全体的にsqrt(5)になり、最後の2つの頂点が(0,0、+ /-sqrt(2))になります。

12面体の頂点の座標は、それらを囲む3つの20面体の頂点の座標を合計することによって計算されます。

=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]]

4dの特殊なケースのテーブルを生成するためのコード

これはちょっとしたハックです。このコードの実行には数秒かかります。出力をファイルに保存し、必要に応じてロードする方が良いでしょう。

600セルの120の頂点座標のリストは、http: //mathworld.wolfram.com/600-Cell.htmlから入手できます。黄金比を特徴としない24の頂点座標は、24セルの頂点を形成します。ウィキペディアにも同じスキームがありますが、これらの24座標と他の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

使用例と出力

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
うわー、これは素晴らしい答えです!! 200行以内でこれを実行できたことに非常に驚きました。立方体、四面体、600セル、その他いくつかを実行しましたが、見栄えはよかったです。出力が非常に多いため、出力を検証するのは困難です。出力がプログラムよりも長くなるのは非常に簡単ですが、あなたの言葉を受け入れます。これをopenGLにロードして、すべての面がリストされているので簡単なはずのプラトンのソリッドを表示してみます。平らな空間にテッセレーションを追加するのは簡単だと思うし、それも試してみるかもしれない。
トニールース

@TonyRuthキーは最高のアルゴリズムを見つけることでした。少ないライン=エラーの余地が少なくなります。私が最初にしたことは、3つの無限次元ファミリー以外に存在するものをチェックすることでした。Will Jagyのコメントは天の恵みだったので(ウィキペディアの方法が難しく見えたので、このタイプのソリューションを考えていました)、整数以外の座標は最小限に抑えられます。賞金の期限が切れる前に完了させたかったので、チェックは徹底的に行われておらず、プロットもしていません。エラーをお知らせください。数時間前に24セルを修正しました。
レベルリバーセント

@TonyRuthの顔の頂点には特定の順序はありません(時計回りの意味で顔の周りを移動することはありません)。高次元の場合、標準の順序はありません。ハイパーキューブには番号順にリストされた面があるため、2番目と3番目の頂点は対角線上にあります(時計回り/反時計回りの意味で1番目と2番目、または3番目と4番目の頂点を交換する必要があります)。時計回り/反時計回りの順序ですが、120セルにはすべての順序で面の頂点があります。
レベルリバーセント
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.