Zwei Zeilen einer Matrix sind orthogonal, wenn ihr inneres Produkt gleich Null ist. Nennen Sie eine Matrix mit allen Zeilen paarweise orthogonal eine orthogonale Matrix . Eine zirkulierende Matrix ist eine Matrix, bei der jeder Zeilenvektor relativ zum vorhergehenden Zeilenvektor um ein Element nach rechts gedreht wird. Wir werden nur an Matrizen interessiert sein, bei denen die Einträge entweder -1
oder sind 1
.
Aufgabe
Schreiben Sie Code, um in 2 Minuten (für gerade ) so viele verschiedene n/2
durch n
orthogonale, zirkulierende Matrizen wie möglich zu zählen n
.
Eingang
Der Code hat keine Eingabe. Es kann alle gleichmäßigen Werte ausprobieren, die n
es mag. Zum Beispiel könnte der Code alles versuchen n
, was Multiplikationen 4
von der kleinsten sind, und auch versuchen n = 2
.
Ausgabe
Die Anzahl der gefundenen orthogonalen Zirkulationsmatrizen. Ihr Code sollte auch einen einfachen Schalter enthalten, damit er die Matrizen selbst ausgeben kann.
Ergebnis
Die Anzahl der gefundenen zirkulierenden Matrizen.
Hinweise
Orthogonal n/2
durch n
zirkulierende Matrizen existieren nur, wenn n
ein Vielfaches von 4
oder n
kleiner als ist 4
.
Eine beispielhafte orthogonale Zirkulationsmatrix ist:
-1 1 -1 -1
-1 -1 1 -1
Tipps für einen naiven Ansatz
Der naivste Ansatz besteht darin, alle möglichen Matrizen zu durchlaufen. Dies kann anhand der folgenden beiden Beobachtungen beschleunigt werden.
Um die Orthogonalität einer zirkulierenden Matrix zu testen, müssen wir nur jede Zeile mit der ersten vergleichen. Dies ist im Beispielcode implementiert.
Wir können über Lyndon-Wörter iterieren und dann, wenn wir eine orthogonale Matrix finden, mit der Anzahl möglicher Umdrehungen multiplizieren. Diese Idee ist noch nicht getestet und kann daher fehlerhaft sein.
Beispielcode
Dies ist eine sehr einfache und naive Python-Antwort. Ich habe es mit ausgeführt timeout 120
.
import itertools
def check_orthogonal(row):
for i in xrange(1,int(n/2)):
if (sum(row[j]*row[(j+i) % n] for j in xrange(n)) != 0):
return False
return True
counter = 0
for n in xrange(4,33,4):
for row in itertools.product([-1,1],repeat = n):
if check_orthogonal(row):
counter +=1
print "Counter is ", counter, ". n = ", n
Richtigkeitsprüfungen
Für n = 4,8,12,16,20,24,28
sollte die Anzahl der unterschiedlichen Matrices auf Sie erhalten 12,40,144,128,80,192,560
sind.
Ebenen der Großartigkeit
Nach dem Beispielcode zu urteilen, präsentiere ich hiermit zwei Ebenen der Attraktivität, die jede Antwort erreichen kann.
Die Attraktivität des Silberlevels wird durch eine Punktzahl oder 1156 erreicht .
Das Goldniveau der Attraktivität ist es, höher zu werden.
Sprachen und Bibliotheken
Sie können jede Sprache oder Bibliothek verwenden, die Sie mögen (die nicht für diese Herausforderung entwickelt wurde). Zum Zwecke der Bewertung werde ich Ihren Code jedoch auf meinem Computer ausführen. Geben Sie daher bitte klare Anweisungen zur Ausführung unter Ubuntu.
Meine Maschine Die Timings werden auf meiner Maschine ausgeführt. Dies ist eine Standard-Ubuntu-Installation auf einem 8 GB AMD FX-8350 Eight-Core-Prozessor. Dies bedeutet auch, dass ich Ihren Code ausführen kann.
Führende Antworten
332 von errorr in Octave
404 von RT in Python
744 durch Probenlösung unter Verwendung von Pypy
1156 von Thomas Kwa mit Java . Silberlevel-Attraktivität!
1588 von Reimer Behrends in OCaml . Goldlevel-Attraktivität!
n
, das ein Vielfaches von vier ist?Antworten:
OCaml, 1588 (n = 36)
Diese Lösung verwendet den üblichen Bitmusteransatz, um Vektoren von -1s und 1s darzustellen. Das Skalarprodukt wird wie üblich berechnet, indem das xor von zwei Bitvektoren genommen und n / 2 subtrahiert wird. Vektoren sind orthogonal, wenn für ihr xor genau n / 2 Bits gesetzt sind.
Lyndon-Wörter sind an sich nicht als normalisierte Darstellung nützlich, da sie jedes Muster ausschließen, das eine Rotation von sich selbst ist. Sie sind auch relativ teuer zu berechnen. Daher verwendet dieser Code eine etwas einfachere Normalform, die erfordert, dass die längste aufeinanderfolgende Folge von Nullen nach der Drehung (oder eine von ihnen, wenn es mehrere gibt) die höchstwertigen Bits belegen muss. Daraus folgt, dass das niedrigstwertige Bit immer 1 ist.
Beachten Sie auch, dass jeder Kandidatenvektor mindestens n / 4 (und höchstens 3n / 4) haben muss. Wir betrachten daher nur Vektoren mit n / 4 ... n / 2 gesetzten Bits, da wir andere über Komplement und Rotation ableiten können (in der Praxis scheinen alle diese Vektoren zwischen n / 2-2 und n / 2 + 2 zu liegen , aber das scheint auch schwer zu beweisen zu sein).
Wir erstellen diese Normalformen vom niedrigstwertigen Bit an und beachten dabei die Einschränkung, dass alle verbleibenden Nullenläufe (im Code als "Lücken" bezeichnet) unseren normalen Formularanforderungen entsprechen müssen. Insbesondere muss, solange mindestens ein weiteres 1-Bit platziert werden muss, Platz für die aktuelle Lücke vorhanden sein und eine andere, die mindestens so groß ist wie die aktuelle Lücke oder eine andere bisher beobachtete Lücke.
Wir stellen auch fest, dass die Liste der Ergebnisse klein ist. Daher versuchen wir nicht, Duplikate während des Erkennungsprozesses zu vermeiden, sondern zeichnen die Ergebnisse einfach in Worker-Sets auf und berechnen die Vereinigung dieser Sets am Ende.
Es ist erwähnenswert, dass die Laufzeitkosten des Algorithmus immer noch exponentiell und mit einer Geschwindigkeit steigen, die mit der der Brute-Force-Version vergleichbar ist. Dies bringt uns im Wesentlichen eine Reduzierung um einen konstanten Faktor und geht zu Lasten eines Algorithmus, der schwieriger zu parallelisieren ist als die Brute-Force-Version.
Ausgabe für n bis 40:
Das Programm ist in OCaml geschrieben und kann kompiliert werden mit:
Führen Sie aus, um
./orthcirc -help
zu sehen, welche Optionen das Programm unterstützt.Bei Architekturen, die dies unterstützen,
-fno-PIC
kann dies zu einem kleinen zusätzlichen Leistungsgewinn führen.Dies ist für OCaml 4.02.3 geschrieben, funktioniert aber möglicherweise auch mit älteren Versionen (sofern diese nicht zu alt sind).
UPDATE: Diese neue Version bietet eine bessere Parallelisierung. Beachten Sie, dass
p * (n/4 + 1)
pro Instanz des Problems Arbeitsthreads verwendet werden und einige von ihnen immer noch erheblich kürzer als andere ausgeführt werden. Der Wert vonp
muss eine Potenz von 2 sein. Die Beschleunigung bei 4-8 Kernen ist minimal (möglicherweise um 10%), lässt sich jedoch bei großen Kernen besser auf eine große Anzahl von Kernen skalierenn
.quelle
Java, 1156 Matrizen
Dies verwendet eine ziemlich naive Bitmaske und dauert auf meinem Computer weniger als 15 Sekunden für n = 28.
Zirkulierende Matrizen werden durch ihre ersten Reihen bestimmt. Daher stelle ich die ersten Zeilen der Matrizen als Bitvektoren dar: 1 und 0 stellen -1 und 1 dar. Zwei Zeilen sind orthogonal, wenn die Anzahl der gesetzten Bits, wenn sie zusammen xor'd sind, n / 2 beträgt.
Ich kann Eclipse momentan nicht zum Laufen bringen, daher wurde dies auf repl.it getestet.
Hier ist die Anzahl der ersten Zeilen, die orthogonal zu den ersten r Zeilen danach für n = 28 sind:
Optimierungen:
long
und verwende dann eine einzelneand
mit den unteren N-Bits, um die benötigten zu extrahieren.Mögliche weitere Optimierungen:
00
und ihre Rotationen (und Rotationen des NOT) verwenden, wenn wir eine orthogonale Matrix finden. Wir können dies tun, weil0101...01
und1010...10
sind keine ersten Zeilen möglich, und alle anderen enthalten entweder a00
oder a11
.quelle
n=36
.Python (404 Matrizen auf i5-5300U)
Dies wird hauptsächlich als Ausgangspunkt für Verbesserungen anderer verwendet. Dies kann viel aufgeräumt, parallelisiert usw. werden.
quelle
Matlab / Octave, 381/328 Matrizen
Auch nur der naive Ansatz, jede mögliche Kombination auszuprobieren.
quelle
n
und einez
, diese beiden können mit einer einzigen auskommentiert werden%
. Und dann können Sie ein;
nach demcounter = counter+1
und das hinzufügen,k/N
das die Ausgabe unterdrückt.