Was sind die mathematischen / rechnerischen Prinzipien hinter diesem Spiel?


196

Meine Kinder haben dieses lustige Spiel namens Spot It! Die Spielbeschränkungen (so gut ich beschreiben kann) sind:

  • Es ist ein Kartenspiel mit 55 Karten
  • Auf jeder Karte befinden sich 8 einzigartige Bilder (dh eine Karte kann nicht 2 gleiche Bilder enthalten).
  • Bei 2 Karten, die aus dem Stapel ausgewählt wurden, gibt es 1 und nur 1 passendes Bild .
  • Übereinstimmende Bilder können auf verschiedenen Karten unterschiedlich skaliert werden, was das Spiel jedoch nur erschwert (dh ein kleiner Baum passt immer noch zu einem größeren Baum).

Das Prinzip des Spiels ist: Wirf 2 Karten um und wer zuerst das passende Bild auswählt, bekommt einen Punkt.

Hier ist ein Bild zur Verdeutlichung:

entdecke es

(Beispiel: Auf den beiden unteren Karten oben sehen Sie, dass das passende Bild der grüne Dinosaurier ist. Zwischen dem Bild unten rechts und der Mitte rechts befindet sich der Kopf eines Clowns.)

Ich versuche Folgendes zu verstehen:

  1. Wie viele verschiedene Bilder sind mindestens erforderlich, um diese Kriterien zu erfüllen, und wie würden Sie dies bestimmen?

  2. Wie würden Sie mit Pseudocode (oder Ruby) 55 Spielkarten aus einem Array von N Bildern generieren (wobei N die Mindestanzahl aus Frage 1 ist)?

Aktualisieren:

Bilder kommen mehr als zweimal pro Deck vor (im Gegensatz zu dem, was einige vermutet haben). Sehen Sie dieses Bild von 3 Karten mit jeweils einem Blitz:3 Karten


64
+1 für das Verwandeln eines Spiels in etwas, das mein Gehirn verletzt.
Kabarett

3
Mindestanzahl von Bildern pro Karte oder Mindestanzahl von Bildern bei 8 pro Karte? Muss auch jedes Bild übereinstimmen?
Wahrhaftigkeit

7
Ich denke, Sie müssen mehr Einschränkungen hinzufügen. Andernfalls können Sie auf jede Karte einen Apfel legen und dann jeder Karte eine beliebige Anzahl eindeutiger Bilder hinzufügen. Jedes Kartenpaar stimmt nur mit dem Bild des Apfels überein.
mbeckish

8
@cabaret: In diesem Fall möchten Sie set . Unglaublich lustig und ärgerlich.
dmckee --- Ex-Moderator Kätzchen

4
Dies ist zwar eine großartige Frage, sie wurde jedoch bereits auf der Mathe-Website (von mir) gestellt. Es scheint hier ein wenig vom Thema abzukommen. - math.stackexchange.com/questions/36798/…
Javid Jamae

Antworten:


148

Endliche projektive Geometrien

Die Axiome der projektiven (ebenen) Geometrie unterscheiden sich geringfügig von der euklidischen Geometrie:

  • Alle zwei Punkte werden von genau einer Linie durchzogen (dies ist dieselbe).
  • Alle zwei Linien treffen sich an genau einem Punkt (dies unterscheidet sich ein wenig von Euklid).

Fügen Sie nun "endlich" in die Suppe ein und Sie haben die Frage:

Können wir eine Geometrie mit nur 2 Punkten haben? Mit 3 Punkten? Mit 4? Mit 7?

Es gibt noch offene Fragen zu diesem Problem, aber wir wissen dies:

  • Wenn es Geometrien mit QPunkten gibt, dann Q = n^2 + n + 1und nwird die orderder Geometrie genannt.
  • n+1In jeder Zeile gibt es Punkte.
  • Übergeben Sie von jedem Punkt aus genau n+1Linien.
  • Die Gesamtzahl der Zeilen beträgt ebenfalls Q.

  • Und schließlich, wenn nes Primzahl ist, gibt es eine Geometrie der Ordnung n.


Was hat das mit dem Puzzle zu tun?

Setzen Sie cardstatt pointund picturestatt lineund die Axiome werden:

  • Alle zwei Karten haben genau ein Bild gemeinsam.
  • Für jeweils zwei Bilder gibt es genau eine Karte, die beide enthält.

Nehmen n=7wir nun und wir haben die order-7endliche Geometrie mit Q = 7^2 + 7 + 1. Das macht Q=57Linien (Bilder) und Q=57Punkte (Karten). Ich denke, die Puzzler haben entschieden, dass 55 eine rundere Zahl als 57 ist und 2 Karten ausgelassen.

Wir erhalten auch n+1 = 8, so dass von jedem Punkt (Karte) 8 Linien vergehen (8 Bilder erscheinen) und jede Linie (Bild) 8 Punkte hat (erscheint in 8 Karten).


Hier ist eine Darstellung der berühmtesten endlichen projektiven Ebene (Ordnung 2) (Geometrie) mit 7 Punkten, bekannt als Fano-Ebene , kopiert von Noelle Evans - Finite Geometry Problem Page

Geben Sie hier die Bildbeschreibung ein

Ich dachte daran, ein Bild zu erstellen, das erklärt, wie die obige Ebene der Ordnung 2 mit 7 Karten und 7 Bildern zu einem ähnlichen Puzzle gemacht werden kann, aber dann hat ein Link aus der Zwillingsfrage math.exchange genau ein solches Diagramm: Dobble-et- la-geometrie-finie

Fano Flugzeug


9
Dieses Spiel weist also eine nichteuklidische Geometrie auf? Wäre es richtig zu sagen, dass die Karten richtig sind?
RMorrisey

2
Das klingt großartig, aber ich bin mir nicht sicher, ob es das Problem tatsächlich gut modelliert. @ypercube, kannst du etwas mehr erklären, warum du denkst, dass die Analogie zwischen Karte / Bild und Punkt / Linie gültig ist?
Nate Kohl

@Nate: Die 1. Analogie every two cards have exactly one picture in commonwird in der Frage angegeben. Das 2. for every two pictures there is exactly one card that has both of them, das OP kann uns sagen, ob das Spielset es erfüllt.
Ypercubeᵀᴹ

4
Super Antwort! Großartige Einblicke, die erkennen, dass das Spiel den Eigenschaften einer Order-7-Projektivebene entspricht, sowie eine der besten Erklärungen für Projektivebenen für Laien, die ich gesehen habe.
RBarryYoung

3
Brillant. Ich muss dies noch 100 Mal lesen, um herauszufinden, wie Kartensätze in Python generiert werden ...
Jared

22

Für diejenigen, die Probleme haben, sich die Geometrie der projektiven Ebene mit 57 Punkten vorzustellen, gibt es eine wirklich schöne, intuitive Möglichkeit, das Spiel mit 57 Karten und 57 Symbolen zu konstruieren (basierend auf der Antwort von Yuval Filmus auf diese Frage ):

  1. Erstellen Sie für Karten mit 8 Symbolen ein 7x7-Raster mit eindeutigen Symbolen.
  2. Fügen Sie zusätzliche 8 Symbole für die "Steigungen" von 0 bis 6 sowie eines für die Unendlichkeitssteigung hinzu.
  3. Jede Karte ist eine Linie im Raster (7 Symbole) plus ein Symbol aus der Steigung, die für die Steigung der Linie festgelegt wurde. Linien haben einen Versatz (dh den Startpunkt links) und eine Steigung (dh wie viele Symbole für jeden Schritt nach rechts nach oben gehen sollen). Wenn die Linie das Gitter oben verlässt, geben Sie es unten erneut ein. In dieser Beispielfigur (Bilder von Boardgamegeek ) sind zwei solcher Karten dargestellt:

Zwei Beispielkarten (rot und grün) als Linien aus dem Raster

Im Beispiel nehme ich eine Linie mit Steigung Null (rot) und eine mit Steigung 1 (grün). Sie schneiden sich an genau einem gemeinsamen Punkt (der Eule).

Diese Methode stellt sicher, dass zwei beliebige Karten genau ein gemeinsames Symbol haben, weil

  1. Wenn die Steigungen unterschiedlich sind, schneiden sich die Linien immer genau an einem Punkt.
  2. Wenn die Steigungen gleich sind, schneiden sich die Linien nicht und es gibt kein gemeinsames Symbol im Raster. In diesem Fall ist das Steigungssymbol dasselbe.

Auf diese Weise können wir 7x7-Karten (7 Offsets und 7 Steigungen) konstruieren.

Wir können auch sieben zusätzliche Karten aus vertikalen Linien durch das Gitter konstruieren (dh jede Spalte nehmen). Für diese wird das Unendlichkeitssteigungssymbol verwendet.

Da jede Karte aus sieben Symbolen aus dem Raster und genau einem "Steigungssymbol" besteht, können wir eine zusätzliche Karte erstellen, die einfach aus allen 8 Steigungssymbolen besteht.

Dies lässt uns 7x8 + 1 = 57 mögliche Karten und 7 x 7 + 8 = 57 erforderliche Symbole.

(Dies funktioniert natürlich nur mit einem Raster mit Primzahlgröße (z. B. n = 7). Andernfalls können Linien mit unterschiedlicher Steigung null oder mehr als einen Schnittpunkt haben, wenn die Steigung ein Teiler der Rastergröße ist.)


18

Es gibt also k = 55 Karten mit jeweils m = 8 Bildern aus einem Pool von insgesamt n Bildern. Wir können die Frage neu formulieren : Wie viele Bilder n brauchen wir, damit wir einen Satz konstruieren k Karten mit nur einem gemeinsamen Bild zwischen zwei beliebigen Karten? " gleichwertig mit der Frage:

Bei einem n- dimensionalen Vektorraum und der Menge aller Vektoren, die genau m Elemente enthalten, die gleich eins und allen anderen Nullen sind, wie groß muss n sein, damit wir eine Menge von k Vektoren finden können, deren paarweise Punktprodukte sind alle gleich 1 ?

Es gibt genau ( n wähle m ) mögliche Vektoren, aus denen Paare gebildet werden können. Wir brauchen also mindestens ein ausreichend großes n, damit ( n wähle m )> = k . Dies ist nur eine Untergrenze. Um die paarweise Kompatibilitätsbedingung zu erfüllen, benötigen wir möglicherweise ein viel höheres n .

Nur um ein bisschen zu experimentieren, habe ich ein kleines Haskell-Programm geschrieben, um gültige Kartensätze zu berechnen:

Bearbeiten: Ich habe gerade festgestellt, nachdem ich Neils und Gajets Lösung gesehen habe, dass der von mir verwendete Algorithmus nicht immer die bestmögliche Lösung findet, daher ist nicht unbedingt alles unten gültig. Ich werde versuchen, meinen Code bald zu aktualisieren.

module Main where

cardCandidates n m = cardCandidates' [] (n-m) m
cardCandidates' buildup  0  0 = [buildup]
cardCandidates' buildup zc oc
    | zc>0 && oc>0 = zerorec ++ onerec
    | zc>0         = zerorec
    | otherwise    = onerec
    where zerorec = cardCandidates' (0:buildup) (zc-1) oc
          onerec  = cardCandidates' (1:buildup) zc (oc-1)

dot x y = sum $ zipWith (*) x y
compatible x y = dot x y == 1

compatibleCards = compatibleCards' []
compatibleCards' valid     [] = valid
compatibleCards' valid (c:cs)
  | all (compatible c) valid = compatibleCards' (c:valid) cs
  |                otherwise = compatibleCards'    valid  cs

legalCardSet n m = compatibleCards $ cardCandidates n m

main = mapM_ print [(n, length $ legalCardSet n m) | n<-[m..]]
  where m = 8

Die resultierende maximale Anzahl kompatibler Karten für m = 8 Bilder pro Karte für eine unterschiedliche Anzahl von Bildern zur Auswahl aus n für die ersten n sieht folgendermaßen aus:

Diese Brute-Force-Methode kommt jedoch aufgrund der kombinatorischen Explosion nicht sehr weit. Aber ich dachte, es könnte immer noch interessant sein.

Interessanterweise scheint es , dass für gegebenen m , k steigt mit n nur bis zu einem bestimmten n , wonach er konstant bleibt.

Dies bedeutet, dass für jede Anzahl von Bildern pro Karte eine bestimmte Anzahl von Bildern zur Auswahl steht, was zu einer maximal möglichen Anzahl von legalen Karten führt. Durch Hinzufügen weiterer Bilder zur Auswahl dieser optimalen Anzahl wird die Anzahl der legalen Karten nicht weiter erhöht.

Die ersten optimalen k sind:

optimale k Tabelle


Das ist nur ein erster Versuch einer Grenze, oder? Sie haben die Anforderung "paarweise Punktprodukte gleich 1" nicht berücksichtigt ...
Nemo

Anscheinend unterstützt der Syntax- Textmarker hier Haskell noch nicht wirklich ( meta.stackexchange.com/questions/78363/… ), aber ich werde den Hinweis nur für den Fall einwerfen , dass dies in Zukunft der Fall ist.
BoltClock

@ BoltClock danke für deine Bearbeitung! Ich wusste nicht, dass Sie Hinweise für die sprachspezifische Syntaxhervorhebung geben können.
Thies Heidecke

Es ist noch nicht sehr bekannt :)
BoltClock

9

Andere haben den allgemeinen Rahmen für das Design (endliche projektive Ebene) beschrieben und gezeigt, wie endliche projektive Ebenen erster Ordnung erzeugt werden können. Ich möchte nur einige Lücken füllen.

Endliche projektive Ebenen können für viele verschiedene Ordnungen erzeugt werden, sind jedoch im Fall der Primordnung am einfachsten p. Dann bilden die ganzen Zahlen modulo pein endliches Feld, mit dem Koordinaten für die Punkte und Linien in der Ebene beschrieben werden können. Es gibt 3 verschiedene Arten von Koordinaten für Punkte: (1,x,y),, (0,1,x)und (0,0,1), wo xund ykönnen Werte von 0bis annehmen p-1. Die 3 verschiedenen Arten von Punkten erklären die Formel p^2+p+1für die Anzahl der Punkte im System. Wir können auch Linien mit den gleichen drei verschiedenen Arten von Koordinaten beschreiben: [1,x,y], [0,1,x], und [0,0,1].

Wir berechnen, ob ein Punkt und eine Linie einfallen, indem wir feststellen, ob das Punktprodukt ihrer Koordinaten gleich 0 mod ist p. So fallen zum Beispiel der Punkt (1,2,5)und die Linie ein, [0,1,1]wenn p=7seitdem 1*0+2*1+5*1 = 7 == 0 mod 7, aber der Punkt (1,3,3)und die Linie fallen [1,2,6]seitdem nicht ein 1*1+3*2+3*6 = 25 != 0 mod 7.

In die Sprache der Karten und Bilder übersetzen bedeutet, dass die Karte mit den Koordinaten (1,2,5)das Bild mit den Koordinaten enthält [0,1,1], die Karte mit den Koordinaten (1,3,3)jedoch nicht das Bild mit den Koordinaten [1,2,6]. Mit diesem Verfahren können wir eine vollständige Liste der Karten und der darin enthaltenen Bilder erstellen.

Ich denke übrigens, es ist einfacher, sich Bilder als Punkte und Karten als Linien vorzustellen, aber die projektive Geometrie zwischen Punkten und Linien ist dual, so dass es wirklich egal ist. Im Folgenden werde ich jedoch Punkte für Bilder und Linien für Karten verwenden.

Die gleiche Konstruktion funktioniert für jedes endliche Feld. Wir wissen, dass es genau dann ein endliches Ordnungsfeld gibt, qwenn es sich um q=p^keine Hauptmacht handelt. Das Feld heißt GF(p^k)"Galois-Feld". Die Felder sind im Primzahlfall nicht so einfach zu konstruieren wie im Primzahlfall.

Glücklicherweise wurde die harte Arbeit bereits in freier Software, nämlich Sage, geleistet und implementiert . Um beispielsweise ein projektives Flugzeugdesign der Ordnung 4 zu erhalten, geben Sie einfach ein

print designs.ProjectiveGeometryDesign(2,1,GF(4,'z'))

und Sie erhalten eine Ausgabe, die aussieht wie

ProjectiveGeometryDesign<points=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20], blocks=[[0, 1, 2, 3, 20], [0,
4, 8, 12, 16], [0, 5, 10, 15, 19], [0, 6, 11, 13, 17], [0, 7, 9, 14,
18], [1, 4, 11, 14, 19], [1, 5, 9, 13, 16], [1, 6, 8, 15, 18], [1, 7,
10, 12, 17], [2, 4, 9, 15, 17], [2, 5, 11, 12, 18], [2, 6, 10, 14, 16],
[2, 7, 8, 13, 19], [3, 4, 10, 13, 18], [3, 5, 8, 14, 17], [3, 6, 9, 12,
19], [3, 7, 11, 15, 16], [4, 5, 6, 7, 20], [8, 9, 10, 11, 20], [12, 13,
14, 15, 20], [16, 17, 18, 19, 20]]>

Ich interpretiere das Obige wie folgt: Es gibt 21 Bilder mit den Bezeichnungen 0 bis 20. Jeder der Blöcke (Linie in projektiver Geometrie) sagt mir, welche Bilder auf einer Karte erscheinen. Beispielsweise enthält die erste Karte die Bilder 0, 1, 2, 3 und 20. Die zweite Karte enthält die Bilder 0, 4, 8, 12 und 16. und so weiter.

Das System der Ordnung 7 kann erzeugt werden durch

print designs.ProjectiveGeometryDesign(2,1,GF(7)) 

welches die Ausgabe erzeugt

ProjectiveGeometryDesign<points=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56], blocks=[[0, 1, 2, 3, 4, 5, 6,
56], [0, 7, 14, 21, 28, 35, 42, 49], [0, 8, 16, 24, 32, 40, 48, 50], [0,
9, 18, 27, 29, 38, 47, 51], [0, 10, 20, 23, 33, 36, 46, 52], [0, 11, 15,
26, 30, 41, 45, 53], [0, 12, 17, 22, 34, 39, 44, 54], [0, 13, 19, 25,
31, 37, 43, 55], [1, 7, 20, 26, 32, 38, 44, 55], [1, 8, 15, 22, 29, 36,
43, 49], [1, 9, 17, 25, 33, 41, 42, 50], [1, 10, 19, 21, 30, 39, 48,
51], [1, 11, 14, 24, 34, 37, 47, 52], [1, 12, 16, 27, 31, 35, 46, 53],
[1, 13, 18, 23, 28, 40, 45, 54], [2, 7, 19, 24, 29, 41, 46, 54], [2, 8,
14, 27, 33, 39, 45, 55], [2, 9, 16, 23, 30, 37, 44, 49], [2, 10, 18, 26,
34, 35, 43, 50], [2, 11, 20, 22, 31, 40, 42, 51], [2, 12, 15, 25, 28,
38, 48, 52], [2, 13, 17, 21, 32, 36, 47, 53], [3, 7, 18, 22, 33, 37, 48,
53], [3, 8, 20, 25, 30, 35, 47, 54], [3, 9, 15, 21, 34, 40, 46, 55], [3,
10, 17, 24, 31, 38, 45, 49], [3, 11, 19, 27, 28, 36, 44, 50], [3, 12,
14, 23, 32, 41, 43, 51], [3, 13, 16, 26, 29, 39, 42, 52], [4, 7, 17, 27,
30, 40, 43, 52], [4, 8, 19, 23, 34, 38, 42, 53], [4, 9, 14, 26, 31, 36,
48, 54], [4, 10, 16, 22, 28, 41, 47, 55], [4, 11, 18, 25, 32, 39, 46,
49], [4, 12, 20, 21, 29, 37, 45, 50], [4, 13, 15, 24, 33, 35, 44, 51],
[5, 7, 16, 25, 34, 36, 45, 51], [5, 8, 18, 21, 31, 41, 44, 52], [5, 9,
20, 24, 28, 39, 43, 53], [5, 10, 15, 27, 32, 37, 42, 54], [5, 11, 17,
23, 29, 35, 48, 55], [5, 12, 19, 26, 33, 40, 47, 49], [5, 13, 14, 22,
30, 38, 46, 50], [6, 7, 15, 23, 31, 39, 47, 50], [6, 8, 17, 26, 28, 37,
46, 51], [6, 9, 19, 22, 32, 35, 45, 52], [6, 10, 14, 25, 29, 40, 44,
53], [6, 11, 16, 21, 33, 38, 43, 54], [6, 12, 18, 24, 30, 36, 42, 55],
[6, 13, 20, 27, 34, 41, 48, 49], [7, 8, 9, 10, 11, 12, 13, 56], [14, 15,
16, 17, 18, 19, 20, 56], [21, 22, 23, 24, 25, 26, 27, 56], [28, 29, 30,
31, 32, 33, 34, 56], [35, 36, 37, 38, 39, 40, 41, 56], [42, 43, 44, 45,
46, 47, 48, 56], [49, 50, 51, 52, 53, 54, 55, 56]]>

8

Ich habe gerade einen Weg gefunden, dies mit 57 oder 58 Bildern zu tun, aber jetzt habe ich sehr starke Kopfschmerzen. Ich werde den Ruby-Code in 8-10 Stunden veröffentlichen, nachdem ich gut geschlafen habe! Nur ein Hinweis, dass meine Lösung alle 7 Karten die gleiche Note hat und mit meiner Lösung insgesamt 56 Karten erstellt werden können.

Hier ist der Code, der alle 57 Karten generiert, über die ypercube gesprochen hat. Es werden genau 57 Bilder verwendet, und leider habe ich tatsächlichen C ++ - Code geschrieben, aber da ich weiß, dass vector <something>es sich um ein Array handelt, das Werte vom Typ enthält, ist somethinges leicht zu verstehen, was dieser Code bewirkt. und dieser Code erzeugt P^2+P+1Karten unter Verwendung von P^2+P+1Bildern, die jeweils ein P+1Bild enthalten und nur 1 gemeinsames Bild für jeden primären P-Wert teilen. Das heißt, wir können 7 Karten mit 7 Bildern mit jeweils 3 Bildern (für p = 2), 13 Karten mit 13 Bildern (für p = 3), 31 Karten mit 31 Bildern (für p = 5) und 57 Karten mit 57 Bildern haben (für p = 7) und so weiter ...

#include <iostream>
#include <vector>

using namespace std;

vector <vector<int> > cards;

void createcards(int p)
{
    cards.resize(0);
    for (int i=0;i<p;i++)
    {
        cards.resize(cards.size()+1);
        for(int j=0;j<p;j++)
        {
            cards.back().push_back(i*p+j);
        }
        cards.back().push_back(p*p+1);
    }

    for (int i=0;i<p;i++)
    {
        for(int j=0;j<p;j++)
        {
            cards.resize(cards.size()+1);
            for(int k=0;k<p;k++)
            {
                cards.back().push_back(k*p+(j+i*k)%p);
            }
            cards.back().push_back(p*p+2+i);
        }
    }

    cards.resize(cards.size()+1);

    for (int i=0;i<p+1;i++)
        cards.back().push_back(p*p+1+i);
}

void checkCards()
{
    cout << "---------------------\n";
    for(unsigned i=0;i<cards.size();i++)
    {
        for(unsigned j=0;j<cards[i].size();j++)
        {
            printf("%3d",cards[i][j]);
        }
        cout << "\n";
    }
    cout << "---------------------\n";
    for(unsigned i=0;i<cards.size();i++)
    {
        for(unsigned j=i+1;j<cards.size();j++)
        {
            int sim = 0;
            for(unsigned k=0;k<cards[i].size();k++)
                for(unsigned l=0;l<cards[j].size();l++)
                    if (cards[i][k] == cards[j][l])
                        sim ++;
            if (sim != 1)
                cout << "there is a problem between cards : " << i << " " << j << "\n";

        }
    }
}

int main()
{
    int p;
    for(cin >> p; p!=0;cin>> p)
    {
        createcards(p);
        checkCards();
    }
}

Nochmals Entschuldigung für den verspäteten Code.


37
Ich habe einen eleganten Beweis dafür, aber leider ist dieses Kommentarfeld zu klein, um es zu enthalten.
Sarnold

@ Gajet: Hast du es ausgeführt p=4? (und 21 Karten / Bilder)
ypercubeᵀᴹ

4 funktioniert in meinem Algorithmus nicht, da 4 keine Primzahl ist. In meinem Algorithmus ist es wichtig, dass p eine Primzahl ist.
Ali1S232

@ypercube Nach erneuter Überprüfung gab es einige geringfügige Fehler in meinem Algorithmus, aber ich habe ihn auf 2, 3,5,7 überprüft und ich kann für jede andere Primzahl beweisen, dass er funktioniert. Hier ist also mein vollständiger Code (aber in c ++)
Ali1S232

1
@ Gajet: coole Lösung! Jetzt verstehe ich, warum mein gieriger Algorithmus nicht immer die beste Lösung hervorgebracht hat.
Thies Heidecke

6

Hier ist Gajets Lösung in Python, da ich Python besser lesbar finde. Ich habe es so modifiziert, dass es auch mit Nicht-Primzahlen funktioniert. Ich habe Thies Insight verwendet, um einen leichter verständlichen Anzeigecode zu generieren.

from __future__ import print_function
from itertools import *

def create_cards(p):
    for min_factor in range(2, 1 + int(p ** 0.5)):
        if p % min_factor == 0:
            break
    else:
        min_factor = p
    cards = []
    for i in range(p):
        cards.append(set([i * p + j for j in range(p)] + [p * p]))
    for i in range(min_factor):
        for j in range(p):
            cards.append(set([k * p + (j + i * k) % p
                              for k in range(p)] + [p * p + 1 + i]))

    cards.append(set([p * p + i for i in range(min_factor + 1)]))
    return cards, p * p + p + 1

def display_using_stars(cards, num_pictures):
    for pictures_for_card in cards:
        print("".join('*' if picture in pictures_for_card else ' '
                      for picture in range(num_pictures)))

def check_cards(cards):
    for card, other_card in combinations(cards, 2):
        if len(card & other_card) != 1:
            print("Cards", sorted(card), "and", sorted(other_card),
                  "have intersection", sorted(card & other_card))

cards, num_pictures = create_cards(7)
display_using_stars(cards, num_pictures)
check_cards(cards)

Mit Ausgabe:

***      *   
   ***   *   
      ****   
*  *  *   *  
 *  *  *  *  
  *  *  * *  
*   *   *  * 
 *   **    * 
  **   *   * 
*    * *    *
 * *    *   *
  * * *     *
         ****

2
Ich denke, die letzten drei Karten in Ihrem Beispiel sind nicht gültig, weil sie kein Bild mit der fünften Karte teilen. Ich habe meinen Code über eine Stunde lang überprüft, bevor ich ihn bemerkte :) Interessanterweise scheint die maximale Größe eines legalen Kartensatzes 5 für 4 Bilder pro Karte zu sein und erhöht sich nicht, selbst wenn mehr Bilder zur Auswahl stehen.
Thies Heidecke

1
@Thies mit dem Diagramm, das ich mit Gajets Code erstellt habe, ist es viel einfacher zu erkennen, warum es genau (p) + (p * p) + (1)Konfigurationen gibt.
Neil G

1
@Neil: Vielen Dank für das aktualisierte Diagramm, das es viel einfacher macht zu sehen, wie die Lösung von Gajet funktioniert!
Thies Heidecke

1
@ Gajet: Ich denke du liegst falsch all p except 4 and 6. Wenn Sie eine endliche Ebene erzeugen möchten, in der es p*p+p+1Punkte und Linien (Karten und Bilder) gibt, dann ist dies verwandt finite fieldsund nicht rings. Es gibt endliche Ordnungsfelder, pwenn p primeoder a ist prime power. Ihr Code funktioniert korrekt für Primzahlen, da Ausdrücke wie die Multiplikation und Addition im endlichen Ordnungsfeld k * p + (j + i * k) % pausdrücken . k*p + j + i*kp
Ypercubeᵀᴹ

1
Es wird auch richtig für die Prime - Kräfte arbeiten, wenn Sie diese Operationen zum Ausdruck bringen können (. Mult und Addition) in den endlichen Körpern der Ordnung p^2, p^3usw. So wird es für Arbeit4, 8, 9, 16, 25, 27, ...
ypercubeᵀᴹ

4

Verwendung des z3Theorembeweisers

Sei Pdie Anzahl der Symbole pro Karte. Nach diesem Artikel und seiner ypercubeᵀᴹAntwort gibt es N = P**2 - P + 1Karten bzw. Symbole. Ein Kartenspiel kann mit seiner Inzidenzmatrix dargestellt werden, die eine Zeile für jede Karte und eine Spalte für jedes mögliche Symbol enthält. Sein (i,j)Element ist, 1wenn die Karte iein Symbol enthält j. Wir müssen diese Matrix nur unter Berücksichtigung dieser Einschränkungen füllen:

  • Jedes Element ist entweder Null oder Eins
  • Die Summe jeder Zeile ist genau P
  • Die Summe jeder Spalte ist genau P
  • Zwei beliebige Zeilen müssen genau ein Symbol gemeinsam haben

Das bedeutet N**2Variablen und N**2 + 2*N + (N choose 2)Einschränkungen. Es scheint in nicht allzu langer Zeit mit z3kleinen Eingaben überschaubar zu sein .

edit : Leider scheint P = 8 für diese Methode zu groß zu sein. Ich habe den Prozess nach 14 Stunden Rechenzeit abgebrochen.

from z3 import *
from itertools import combinations

def is_prime_exponent(K):
    return K > 1 and K not in 6  # next non-prime exponent is 10, 
                                 # but that is too big anyway

def transposed(rows):
    return zip(*rows)

def spotit_z3(symbols_per_card):
    K = symbols_per_card - 1
    N = symbols_per_card ** 2 - symbols_per_card + 1
    if not is_prime_exponent(K):
        raise TypeError("Symbols per card must be a prime exponent plus one.")

    constraints = []

    # the rows of the incidence matrix
    s = N.bit_length()
    rows = [[BitVec("r%dc%d" % (r, c), s) for c in range(N)] for r in range(N)]

    # every element must be either 1 or 0
    constraints += [Or([elem == 1, elem == 0]) for row in rows for elem in row]

    # sum of rows and cols must be exactly symbols_per_card
    constraints += [Sum(row) == symbols_per_card for row in rows]
    constraints += [Sum(col) == symbols_per_card for col in transposed(rows)]

    # Any two rows must have exactly one symbol in common, in other words they
    # differ in (symbols_per_card - 1) symbols, so their element-wise XOR will
    # have 2 * (symbols_per_card - 1) ones.
    D = 2 * (symbols_per_card - 1)
    for row_a, row_b in combinations(rows, 2):
        constraints += [Sum([a ^ b for a, b in zip(row_a, row_b)]) == D]

    solver = Solver()
    solver.add(constraints)

    if solver.check() == unsat:
        raise RuntimeError("Could not solve it :(")

    # create the incidence matrix
    model = solver.model()
    return [[model[elem].as_long() for elem in row] for row in rows]


if __name__ == "__main__":
    import sys
    symbols_per_card = int(sys.argv[1])
    incidence_matrix = spotit_z3(symbols_per_card)
    for row in incidence_matrix:
        print(row)

Ergebnisse

$python spotit_z3.py 3
[0, 0, 1, 1, 0, 1, 0]
[0, 0, 0, 0, 1, 1, 1]
[0, 1, 0, 1, 0, 0, 1]
[1, 1, 0, 0, 0, 1, 0]
[0, 1, 1, 0, 1, 0, 0]
[1, 0, 0, 1, 1, 0, 0]
[1, 0, 1, 0, 0, 0, 1]
python spotit_z3.py 3  1.12s user 0.06s system 96% cpu 1.225 total

$ time python3 spotit_z3.py 4
[0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0]
[0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0]
[0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1]
[0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0]        
[0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1]
[0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1]
[0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0]
[0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0]
[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0]
[1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0]
[1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]
python spotit_z3.py 4  664.62s user 0.15s system 99% cpu 11:04.88 total

$ time python3 spotit_z3.py 5
[1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0]
[0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1]
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1]
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0]
[0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0]
[0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1]
[1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0]
python spotit_z3.py 5  1162.72s user 20.34s system 99% cpu 19:43.39 total

$ time python3 spotit_z3.py 8
<I killed it after 14 hours of run time.>


3

Ich habe einen Artikel darüber geschrieben, wie man diese Art von Decks mit Code in Perl generiert. Der Code ist nicht optimiert, aber er kann zumindest Decks mit "vernünftigen" Ordnungen generieren ... und noch mehr.

Hier ist ein Beispiel mit der Reihenfolge 8, bei der eine etwas kompliziertere zugrunde liegende Mathematik berücksichtigt werden muss, da 8 keine Primzahl ist, obwohl es sich um eine gültige Reihenfolge zum Generieren dieser Art von Decks handelt. Siehe oben oder im Artikel für eine detailliertere Erklärung unten, wenn Sie nur einen etwas schwierigeren Spot-It generieren möchten :-)

$ time pg2 8
elements in field: 8
  0. (1, 9, 17, 25, 33, 41, 49, 57, 65)
  1. (0, 9, 10, 11, 12, 13, 14, 15, 16)
  2. (2, 9, 18, 27, 36, 45, 54, 63, 72)
  3. (6, 9, 22, 26, 37, 43, 56, 60, 71)
  4. (7, 9, 23, 32, 34, 46, 52, 59, 69)
  5. (8, 9, 24, 30, 35, 42, 55, 61, 68)
  6. (3, 9, 19, 29, 39, 44, 50, 64, 70)
  7. (4, 9, 20, 31, 38, 48, 53, 58, 67)
  8. (5, 9, 21, 28, 40, 47, 51, 62, 66)
  9. (0, 1, 2, 3, 4, 5, 6, 7, 8)
 10. (1, 10, 18, 26, 34, 42, 50, 58, 66)
 11. (1, 14, 22, 30, 38, 46, 54, 62, 70)
 12. (1, 15, 23, 31, 39, 47, 55, 63, 71)
 13. (1, 16, 24, 32, 40, 48, 56, 64, 72)
 14. (1, 11, 19, 27, 35, 43, 51, 59, 67)
 15. (1, 12, 20, 28, 36, 44, 52, 60, 68)
 16. (1, 13, 21, 29, 37, 45, 53, 61, 69)
 17. (0, 17, 18, 19, 20, 21, 22, 23, 24)
 18. (2, 10, 17, 28, 35, 46, 53, 64, 71)
 19. (6, 14, 17, 29, 34, 48, 51, 63, 68)
 20. (7, 15, 17, 26, 40, 44, 54, 61, 67)
 21. (8, 16, 17, 27, 38, 47, 50, 60, 69)
 22. (3, 11, 17, 31, 37, 42, 52, 62, 72)
 23. (4, 12, 17, 30, 39, 45, 56, 59, 66)
 24. (5, 13, 17, 32, 36, 43, 55, 58, 70)
 25. (0, 49, 50, 51, 52, 53, 54, 55, 56)
 26. (3, 10, 20, 30, 40, 43, 49, 63, 69)
 27. (2, 14, 21, 32, 39, 42, 49, 60, 67)
 28. (8, 15, 18, 28, 37, 48, 49, 59, 70)
 29. (6, 16, 19, 31, 36, 46, 49, 61, 66)
 30. (5, 11, 23, 26, 38, 45, 49, 64, 68)
 31. (7, 12, 22, 29, 35, 47, 49, 58, 72)
 32. (4, 13, 24, 27, 34, 44, 49, 62, 71)
 33. (0, 57, 58, 59, 60, 61, 62, 63, 64)
 34. (4, 10, 19, 32, 37, 47, 54, 57, 68)
 35. (5, 14, 18, 31, 35, 44, 56, 57, 69)
 36. (2, 15, 24, 29, 38, 43, 52, 57, 66)
 37. (3, 16, 22, 28, 34, 45, 55, 57, 67)
 38. (7, 11, 21, 30, 36, 48, 50, 57, 71)
 39. (6, 12, 23, 27, 40, 42, 53, 57, 70)
 40. (8, 13, 20, 26, 39, 46, 51, 57, 72)
 41. (0, 65, 66, 67, 68, 69, 70, 71, 72)
 42. (5, 10, 22, 27, 39, 48, 52, 61, 65)
 43. (3, 14, 24, 26, 36, 47, 53, 59, 65)
 44. (6, 15, 20, 32, 35, 45, 50, 62, 65)
 45. (2, 16, 23, 30, 37, 44, 51, 58, 65)
 46. (4, 11, 18, 29, 40, 46, 55, 60, 65)
 47. (8, 12, 21, 31, 34, 43, 54, 64, 65)
 48. (7, 13, 19, 28, 38, 42, 56, 63, 65)
 49. (0, 25, 26, 27, 28, 29, 30, 31, 32)
 50. (6, 10, 21, 25, 38, 44, 55, 59, 72)
 51. (8, 14, 19, 25, 40, 45, 52, 58, 71)
 52. (4, 15, 22, 25, 36, 42, 51, 64, 69)
 53. (7, 16, 18, 25, 39, 43, 53, 62, 68)
 54. (2, 11, 20, 25, 34, 47, 56, 61, 70)
 55. (5, 12, 24, 25, 37, 46, 50, 63, 67)
 56. (3, 13, 23, 25, 35, 48, 54, 60, 66)
 57. (0, 33, 34, 35, 36, 37, 38, 39, 40)
 58. (7, 10, 24, 31, 33, 45, 51, 60, 70)
 59. (4, 14, 23, 28, 33, 43, 50, 61, 72)
 60. (3, 15, 21, 27, 33, 46, 56, 58, 68)
 61. (5, 16, 20, 29, 33, 42, 54, 59, 71)
 62. (8, 11, 22, 32, 33, 44, 53, 63, 66)
 63. (2, 12, 19, 26, 33, 48, 55, 62, 69)
 64. (6, 13, 18, 30, 33, 47, 52, 64, 67)
 65. (0, 41, 42, 43, 44, 45, 46, 47, 48)
 66. (8, 10, 23, 29, 36, 41, 56, 62, 67)
 67. (7, 14, 20, 27, 37, 41, 55, 64, 66)
 68. (5, 15, 19, 30, 34, 41, 53, 60, 72)
 69. (4, 16, 21, 26, 35, 41, 52, 63, 70)
 70. (6, 11, 24, 28, 39, 41, 54, 58, 69)
 71. (3, 12, 18, 32, 38, 41, 51, 61, 71)
 72. (2, 13, 22, 31, 40, 41, 50, 59, 68)
errors in check: 0

real    0m0.303s
user    0m0.200s
sys 0m0.016s

Jede Kennung von 0bis 72kann sowohl als Kartenkennung als auch als Bildkennung gelesen werden. Zum Beispiel bedeutet die letzte Zeile Folgendes:

  • Karte 72enthält Bilder 2, 13, 22, ..., 59, 68, und
  • Bild 72erscheint in Karten 2, 13, 22, ..., 59und 68.
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.