Quali sono i principi matematici / computazionali alla base di questo gioco?


196

I miei bambini hanno questo divertente gioco chiamato Spot It! I vincoli del gioco (come meglio posso descrivere) sono:

  • È un mazzo di 55 carte
  • Su ogni carta ci sono 8 immagini uniche (cioè una carta non può avere 2 della stessa immagine)
  • Date 2 carte scelte dal mazzo, c'è 1 e solo 1 foto corrispondente .
  • Le foto corrispondenti possono essere ridimensionate in modo diverso su carte diverse, ma ciò serve solo a rendere il gioco più difficile (ovvero un albero piccolo corrisponde ancora a un albero più grande)

Il principio del gioco è: capovolgere 2 carte e chi sceglie per primo l'immagine corrispondente ottiene un punto.

Ecco un'immagine per chiarimenti:

individualo

(Esempio: puoi vedere dalle 2 carte in basso sopra che l'immagine corrispondente è il dinosauro verde. Tra l'immagine in basso a destra e quella in mezzo a destra, è la testa di un pagliaccio.)

Sto cercando di capire quanto segue:

  1. Qual è il numero minimo di immagini diverse richieste per soddisfare questi criteri e come lo determineresti?

  2. Usando lo pseudocodice (o Ruby), come genereresti 55 carte da un array di N immagini (dove N è il numero minimo dalla domanda 1)?

Aggiornare:

Le immagini si verificano più di due volte per mazzo (contrariamente a quanto alcuni hanno ipotizzato). Vedi questa immagine di 3 carte, ognuna con un fulmine:3 carte


64
+1 per aver trasformato un gioco in qualcosa che mi fa male al cervello.
cabaret,

3
Numero minimo di immagini per scheda o numero minimo di immagini dato che ce ne sono 8 per scheda? Inoltre, ogni immagine deve essere abbinabile?
verità

7
Penso che sia necessario aggiungere più vincoli. Altrimenti, potresti mettere una mela su ogni carta, quindi aggiungere un numero qualsiasi di immagini uniche a ogni carta. Ogni coppia di carte corrisponderà solo all'immagine della mela.
mbeckish,

8
@cabaret: in tal caso ti piacerà set . Incredibilmente divertente e aggravante.
dmckee --- ex gattino moderatore

4
Mentre questa è una grande domanda, è già stata posta sul sito di matematica (da me). Sembra un po 'fuori tema qui. - math.stackexchange.com/questions/36798/…
Javid Jamae

Risposte:


148

Geometrie proiettive finite

Il assiomi della geometria proiettiva (piana) sono leggermente diversi dalla geometria euclidea:

  • Ogni due punti hanno esattamente una linea che li attraversa (questa è la stessa).
  • Ogni due righe si incontrano esattamente in un punto (questo è un po 'diverso da Euclide).

Ora, aggiungi "finito" nella zuppa e hai la domanda:

Possiamo avere una geometria con solo 2 punti? Con 3 punti? Con 4? Con 7?

Ci sono ancora domande aperte su questo problema, ma lo sappiamo:

  • Se ci sono geometrie con Qpunti, allora Q = n^2 + n + 1ed nè chiamato il orderdella geometria.
  • Ci sono n+1punti in ogni riga.
  • Da ogni punto, passa esattamente le n+1linee.
  • Anche il numero totale di righe Q.

  • E infine, se nè primo, esiste una geometria dell'ordine n.


Che cosa ha a che fare con il puzzle, ci si potrebbe chiedere.

Metti cardinvece di pointe pictureinvece di linee gli assiomi diventano:

  • Ogni due carte hanno esattamente un'immagine in comune.
  • Per ogni due immagini c'è esattamente una carta che ha entrambe.

Ora, prendiamo n=7e abbiamo la order-7geometria finita con Q = 7^2 + 7 + 1. Ciò crea Q=57linee (immagini) e Q=57punti (carte). Suppongo che i creatori di puzzle abbiano deciso che il numero 55 è più rotondo di 57 e hanno lasciato fuori 2 carte.

Otteniamo anche n+1 = 8, quindi da ogni punto (carta), passano 8 linee (appaiono 8 foto) e ogni linea (immagine) ha 8 punti (appare in 8 carte).


Ecco una rappresentazione del più famoso piano proiettivo finito (ordine-2) (geometria) con 7 punti, noto come Fano Plane , copiato da Noelle Evans - Pagina dei problemi di geometria finita

inserisci qui la descrizione dell'immagine

Stavo pensando di creare un'immagine che spiegasse come il suddetto piano di ordine 2 potesse essere realizzato un puzzle simile con 7 carte e 7 immagini, ma poi un collegamento dalla domanda gemella math.exchange ha esattamente un diagramma simile: Dobble-et- la-Geometrie-finie

Aereo Fano


9
Quindi questo gioco mostra una geometria non euclidea? Sarebbe corretto dire che le carte sono giuste?
RMorrisey,

2
Sembra fantastico, ma non ho la certezza che modella effettivamente il problema. @ypercube, potresti spiegarci un po 'di più perché pensi che l'analogia tra carta / immagine e punto / linea sia valida?
Nate Kohl,

@Nata: la prima analogia every two cards have exactly one picture in commonè indicata nella domanda. Il 2 ° for every two pictures there is exactly one card that has both of them, l'OP può dirci se il set di gioco lo soddisfa.
ypercubeᵀᴹ

4
Risposta fantastica! Ottima intuizione, rendendosi conto che il gioco corrisponde alle proprietà di un Piano proiettivo Ordine 7, oltre a una delle migliori spiegazioni di Piani proiettivi per i laici che ho visto.
RBarryYoung,

3
Brillante. Ho bisogno di leggerlo altre 100 volte per cercare di capire come generare set di carte in Python ...
Jared

22

Per coloro che hanno difficoltà a immaginare la geometria del piano proiettivo con 57 punti, esiste un modo davvero piacevole e intuitivo per costruire il gioco con 57 carte e 57 simboli (basato sulla risposta di Yuval Filmus per questa domanda ):

  1. Per le carte con 8 simboli, crea una griglia 7x7 di simboli unici.
  2. Aggiungi altri 8 simboli per le "pendenze" da 0 a 6, più uno per la pendenza infinita.
  3. Ogni carta è una linea sulla griglia (7 simboli) più un simbolo dalla pendenza impostata per la pendenza della linea. Le linee hanno un offset (cioè il punto di partenza a sinistra) e una pendenza (cioè quanti simboli salire per ogni passo a destra). Quando la linea lascia la griglia in alto, rientra in basso. Vedi questa figura di esempio (immagini da boardgamegeek ) per due di queste carte:

Due carte di esempio (rosso e verde) prese come linee dalla griglia

Nell'esempio, prendo una linea con pendenza zero (rossa) e una con pendenza 1 (verde). Si intersecano esattamente in un punto comune (il gufo).

Questo metodo garantisce che due carte qualsiasi abbiano esattamente un simbolo comune, perché

  1. Se le pendenze sono diverse, le linee si intersecano sempre esattamente in un punto.
  2. Se le pendenze sono uguali, le linee non si intersecano e non ci sarà alcun simbolo comune dalla griglia. In questo caso, il simbolo della pendenza sarà lo stesso.

In questo modo, possiamo costruire carte 7x7 (7 offset e 7 pendenze).

Possiamo anche costruire sette carte aggiuntive da linee verticali attraverso la griglia (cioè prendendo ogni colonna). Per quelli, viene utilizzata l'icona della pendenza infinita.

Poiché ogni carta è composta da sette simboli della griglia e esattamente un simbolo di "pendenza", possiamo creare una carta aggiuntiva, che consiste semplicemente di tutti gli 8 simboli di pendenza.

Questo ci lascia con 7x8 + 1 = 57 carte possibili e 7 x 7 + 8 = 57 simboli richiesti.

(Naturalmente, funziona solo con una griglia di dimensioni in numero primo (ad es. N = 7). Altrimenti, le linee di diversa pendenza potrebbero avere zero o più di un'intersezione se la pendenza è un divisore della dimensione della griglia.)


18

Quindi ci sono k = 55 carte contenenti m = 8 immagini ciascuna da un pool di n immagini totali. Siamo in grado di riformulare la domanda 'quante immagini n abbiamo bisogno, in modo da poter costruire un insieme di k carte con una sola immagine condiviso tra una qualsiasi coppia di carte?' equivalentemente chiedendo:

Dato uno spazio vettoriale n- dimensionale e l'insieme di tutti i vettori, che contengono esattamente m elementi uguali a uno e tutti gli altri zero, quanto grande deve essere n , in modo che possiamo trovare un insieme di k vettori, i cui prodotti punto a coppie sono tutti uguali a 1 ?

Esistono esattamente ( n scegli m ) possibili vettori per costruire coppie. Quindi almeno abbiamo bisogno di una n abbastanza grande in modo che ( n scelga m )> = k . Questo è solo un limite inferiore, quindi per soddisfare il vincolo di compatibilità a coppie potremmo aver bisogno di un n molto più alto .

Solo per sperimentare un po 'ho scritto un piccolo programma Haskell per calcolare set di carte validi:

Modifica: mi sono appena reso conto dopo aver visto la soluzione di Neil e Gajet, che l'algoritmo che utilizzo non trova sempre la migliore soluzione possibile, quindi tutto ciò che segue non è necessariamente valido. Proverò ad aggiornare il mio codice presto.

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

Il numero massimo risultante di schede compatibili per m = 8 immagini per scheda per un diverso numero di immagini tra cui scegliere n per le prime n è simile al seguente:

Questo metodo della forza bruta non va molto lontano, però, a causa dell'esplosione combinatoria. Ma ho pensato che potrebbe essere ancora interessante.

È interessante notare che, per un dato m , k aumenta con n solo fino a un certo n , dopo di che rimane costante.

Ciò significa che per ogni numero di immagini per scheda esiste un certo numero di immagini tra cui scegliere, che si traduce nel numero massimo possibile di carte legali. L'aggiunta di più foto tra cui scegliere quel numero ottimale non aumenta ulteriormente il numero di carte legali.

I primi pochi k ottimali sono:

tabella k ottimale


Questo è solo un tentativo iniziale di un limite, giusto? Non hai incorporato il requisito "prodotti dot pairwise pari a 1" ...
Nemo

Apparentemente l'evidenziatore della sintassi qui non supporta ancora Haskell ( meta.stackexchange.com/questions/78363/… ), ma aggiungerò il suggerimento nel caso in cui lo faccia in futuro.
BoltClock

@BoltClock grazie per la tua modifica! non sapevo che potessi dare suggerimenti per l'evidenziazione della sintassi specifica della lingua.
Thies Heidecke,

Non è ancora molto noto :)
BoltClock

9

Altri hanno descritto il quadro generale per la progettazione (piano proiettivo finito) e hanno mostrato come generare piani proiettivi finiti di ordine primo. Vorrei solo colmare alcune lacune.

I piani proiettivi finiti possono essere generati per molti ordini diversi, ma sono più semplici nel caso dell'ordine primario p. Quindi gli interi modulo pformano un campo finito che può essere usato per descrivere le coordinate per i punti e le linee nel piano. Ci sono 3 diversi tipi di coordinate per punti: (1,x,y), (0,1,x), e (0,0,1), dove xe ypossono assumere valori da 0a p-1. I 3 diversi tipi di punti spiegano la formula p^2+p+1per il numero di punti nel sistema. Possiamo anche descrivere linee con le stesse 3 diversi tipi di coordinate [1,x,y], [0,1,x]e[0,0,1] .

Calcoliamo se un punto e una linea sono incidenti se il prodotto punto delle loro coordinate è uguale a 0 mod p. Quindi, ad esempio, il punto (1,2,5)e la linea [0,1,1]sono incidenti quando da p=7allora 1*0+2*1+5*1 = 7 == 0 mod 7, ma il punto (1,3,3)e la linea [1,2,6]non sono incidenti da allora 1*1+3*2+3*6 = 25 != 0 mod 7.

Traducendo nella lingua delle carte e delle immagini, ciò significa che la carta con le coordinate (1,2,5)contiene l'immagine con le coordinate [0,1,1], ma la carta con le coordinate (1,3,3)non contiene l'immagine con le coordinate [1,2,6]. Possiamo usare questa procedura per sviluppare un elenco completo di carte e le immagini che contengono.

A proposito, penso che sia più facile pensare alle immagini come punti e carte come linee, ma c'è una dualità nella geometria proiettiva tra punti e linee, quindi non importa davvero. Tuttavia, in quanto segue userò punti per le immagini e linee per le carte.

La stessa costruzione funziona per qualsiasi campo finito. Sappiamo che esiste un campo di ordine finito qse e solo se q=p^k, una potenza primaria. Il campo si chiama GF(p^k)che sta per "campo di Galois". I campi non sono così facili da costruire nel caso del potere principale come lo sono nel caso principale.

Fortunatamente, il duro lavoro è già stato fatto e implementato nel software libero, vale a dire Sage . Per ottenere un progetto di piano proiettivo dell'ordine 4, ad esempio, basta digitare

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

e otterrai un output simile

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

Interpreto quanto sopra come segue: ci sono 21 immagini etichettate da 0 a 20. Ciascuno dei blocchi (linea nella geometria proiettiva) mi dice quali immagini appaiono su una carta. Ad esempio, la prima carta avrà le immagini 0, 1, 2, 3 e 20; la seconda carta avrà le immagini 0, 4, 8, 12 e 16; e così via.

Il sistema dell'ordine 7 può essere generato da

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

che genera l'output

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

Ho appena trovato un modo per farlo con 57 o 58 foto ma ora ho un brutto mal di testa, pubblicherò il codice ruby ​​in 8-10 ore dopo aver dormito bene! solo un suggerimento per la mia soluzione ogni 7 carte condividono lo stesso segno e in totale 56 carte possono essere costruite usando la mia soluzione.

ecco il codice che genera tutte le 57 carte di cui parlava ypercube. usa esattamente 57 immagini e mi dispiace che io abbia scritto il vero codice C ++ ma sapendo che vector <something>è un array che contiene valori di tipo somethingè facile capire cosa fa questo codice. e questo codice genera P^2+P+1carte utilizzando P^2+P+1immagini contenenti ciascuna P+1un'immagine e condividendo solo 1 immagine in comune, per ogni valore P principale. ciò significa che possiamo avere 7 carte usando 7 immagini ognuna con 3 immagini (per p = 2), 13 carte usando 13 immagini (per p = 3), 31 carte usando 31 immagini (per p = 5), 57 carte per 57 immagini (per p = 7) e così via ...

#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();
    }
}

ancora scusa per il codice ritardato.


37
Ne ho una prova elegante, ma purtroppo questa casella di commento è troppo piccola per contenerla.
Sarnold,

@Gajet: l'hai eseguito per p=4? (e 21 carte / immagini)
ypercubeᵀᴹ

4 non funziona nel mio algoritmo poiché 4 non è un numero primo, nel mio algoritmo è importante che p sia un numero primo.
Ali1S232,

@ypercube dopo aver ricontrollato ci sono stati alcuni piccoli errori nel mio algoritmo ma l'ho verificato per 2, 3,5,7 e posso provare che per qualsiasi altro numero primo funzionerà, quindi ecco il mio codice completo (ma in c ++)
Ali1S232,

1
@Gajet: soluzione interessante! Ora capisco perché il mio avido algoritmo non ha sempre prodotto la soluzione migliore.
Thies Heidecke,

6

Ecco la soluzione di Gajet in Python, poiché trovo Python più leggibile. L'ho modificato in modo che funzioni anche con numeri non primi. Ho usato Thies insight per generare un codice di visualizzazione più facilmente comprensibile.

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)

Con uscita:

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

2
penso che le ultime tre carte nel tuo esempio non siano valide, perché non condividono un'immagine con la quinta carta. Ho appena controllato il mio codice per oltre un'ora prima che me ne rendessi conto :) È interessante notare che la dimensione massima di un set di carte legale è 5 per 4 foto per scheda e non aumenta anche con più immagini tra cui scegliere.
Thies Heidecke,

1
@Con il diagramma che ho prodotto usando il codice di Gajet, è molto più facile capire perché ci sono esattamente (p) + (p * p) + (1)configurazioni.
Neil G,

1
@Neil: grazie per il diagramma aggiornato, rende molto più facile vedere come funziona la soluzione Gajet!
Thies Heidecke,

1
@Gajet: penso che ti sbagli all p except 4 and 6. Se vuoi produrre un piano finito in cui ci sono p*p+p+1punti e linee (carte e immagini), allora è correlato finite fieldse non rings. Ci sono campi finiti di ordine pquando p è primeo a prime power. Il codice funziona correttamente per i numeri primi perché espressioni come k * p + (j + i * k) % pquelle espresse k*p + j + i*kin termini di moltiplicazione e aggiunta nel campo finito dell'ordine p.
ypercubeᵀᴹ

1
Esso funziona correttamente per le potenze principali anche se si può esprimere queste operazioni (. Mult e aggiunta) nel campo finito di ordine p^2, p^3ecc Quindi, si lavorerà per4, 8, 9, 16, 25, 27, ...
ypercubeᵀᴹ

4

Utilizzando il z3teorema prover

Sia Pil numero di simboli per carta. Secondo questo articolo e ypercubeᵀᴹla risposta ci sono rispettivamente N = P**2 - P + 1carte e simboli. Un mazzo di carte può essere rappresentato con la sua matrice di incidenza che ha una riga per ogni carta e una colonna per ogni possibile simbolo. Il suo (i,j)elemento è 1se la carta iha un simbolo jsu di essa. Dobbiamo solo riempire questa matrice con questi vincoli in mente:

  • ogni elemento è zero o uno
  • la somma di ogni riga è esattamente P
  • la somma di ogni colonna è esattamente P
  • ogni due righe deve avere esattamente un simbolo in comune

Ciò significa N**2variabili e N**2 + 2*N + (N choose 2)vincoli. Sembra essere gestibile in un tempo non così lungo con z3piccoli input.

modifica : Sfortunatamente P = 8 sembra essere troppo grande per questo metodo. Ho interrotto il processo dopo 14 ore di tempo di calcolo.

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)

risultati

$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

Ho scritto un articolo su come generare questo tipo di deck, con il codice in Perl. Il codice non è ottimizzato ma è almeno in grado di generare mazzi di ordini "ragionevoli" ... e altro ancora.

Ecco un esempio con l'ordine 8, che deve considerare una matematica sottostante leggermente più complicata, perché 8 non è un numero primo, sebbene sia un ordine valido per generare questo tipo di mazzi. Vedi sopra o l'articolo per una spiegazione più dettagliata, sotto se vuoi solo generare uno Spot-It leggermente più difficile :-)

$ 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

Ogni identificatore da 0a 72può essere letto sia come identificativo della carta che come identificatore dell'immagine. Ad esempio, l'ultima riga indica che:

  • carta 72contiene le immagini 2, 13, 22, ..., 59, 68, E
  • immagine 72compare nelle carte 2, 13, 22, ..., 59, e 68.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.