Каковы математические/вычислительные принципы этой игры?

У моих детей есть эта забавная игра под названием Spot It! Ограничения игры (насколько я могу описать):

  • Это колода из 55 карт.
  • На каждой карте 8 уникальных изображений (т.е. на карте не может быть 2 одинакового изображения)
  • Для любых двух карт, выбранных из колоды, есть 1 и только 1 соответствующее изображение.
  • Соответствующие изображения могут быть различны по разному на разных картах, но это только для того, чтобы сделать игру более сложной (т.е. небольшое дерево по-прежнему соответствует более крупному дереву).

Принцип игры: перевернуть две карты, и тот, кто первым выбирает подходящее изображение, получает очко.

Вот изображение для уточнения:

spot it

(Пример: вы можете видеть из нижних 2 карт выше, что совпадающее изображение - это зеленый динозавр. Между правым и нижним правым снимками это голова клоуна.)

Я пытаюсь понять следующее:

  • Какое минимальное количество разных изображений требуется для соответствия этим критериям и как вы это определяете?

  • Используя псевдокод (или Ruby), как бы вы создали 55 игровых карт из массива из N изображений (где N - минимальное число из вопроса 1)?

Update:

Изображения случаются более двух раз на колоду (вопреки тому, что некоторые догадывались). Посмотрите эту картинку из 3-х карт, каждая из которых имеет молнию: 3 cards

Ответ 1

Конечные проективные геометрии

axioms проективная (плоская) геометрия несколько отличаются от евклидовой геометрии:

  • Каждые две точки имеют ровно одну строку, проходящую через них (это то же самое).
  • Каждые две строки встречаются ровно в одной точке (это немного отличается от Евклида).

Теперь добавьте "конечный" в суп, и у вас возник вопрос:

Можем ли мы иметь геометрию всего с двумя точками? С 3 очками? С 4? С 7?

У этой проблемы еще есть открытые вопросы, но мы это знаем:

  • Если есть геометрия с Q точками, то Q = n^2 + n + 1 и n называется геометрией order.
  • В каждой строке есть n+1 точек.
  • От каждой точки передайте ровно строки n+1.
  • Общее количество строк также Q.

  • И, наконец, если n является простым, то существует геометрия порядка n.


Какое это имеет отношение к головоломке, можно спросить.

Поместите card вместо point и picture вместо line, и аксиомы станут:

  • Каждые две карты имеют ровно одну общую картину.
  • Для каждых двух картин есть ровно одна карта, в которой есть обе.

Теперь давайте возьмем n=7 и имеем конечную геометрию order-7 с Q = 7^2 + 7 + 1. Это делает строки Q=57 (картинки) и Q=57 точек (карт). Я думаю, разработчики головоломок решили, что 55 больше круглых, чем 57, и оставили 2 карты.

Мы также получаем n+1 = 8, поэтому из каждой точки (карты) проходит 8 строк (появляется 8 изображений), и каждая строка (изображение) имеет 8 точек (появляется в 8 картах).


Здесь представлено представление самой известной конечной проективной (порядок-2) плоскости (геометрии) с 7 точками, известной как Fano Plane, скопирован из Noelle Evans - Страница проблем конечной геометрии

enter image description here

Я думал создать изображение, объясняющее, как вышеупомянутый самолет порядка-2 мог быть похож на головоломку с 7 картами и 7 картинками, но тогда ссылка из вопроса math.exchange twin имеет именно такую ​​диаграмму: Dobble-et-la-geometrie-finie

Fano Plane

Ответ 2

Таким образом, есть k = 55 карт, содержащих m = 8 изображений из пула из n изображений. Мы можем повторить вопрос "Сколько рисунков нам нужно, чтобы мы могли построить набор k-карточек с одним общим изображением между любыми парами карт?" равнозначно, спрашивая:

Для n-мерного векторного пространства и множества всех векторов, которые содержат ровно m элементов, равных одному и всем остальным нулям, насколько велика n, чтобы найти множество k векторов, попарно точечные продукты равны 1?

Есть точно (n выбрать m) возможные векторы для создания пар из. Поэтому нам, по крайней мере, нужно достаточно большое n, чтобы (n выбрать m) >= k. Это просто нижняя граница, поэтому для выполнения парного ограничения совместимости нам может понадобиться гораздо больше n.

Просто для экспериментов немного написал небольшую программу Haskell для расчета действительных наборов карт:

Изменить: Я понял, увидев решение Neil и Gajet, что алгоритм, который я использую, не всегда находит наилучшее возможное решение, поэтому все ниже не обязательно. Я скоро попытаюсь обновить свой код.

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

Полученное максимальное количество совместимых карт для m = 8 снимков на карту для различного количества изображений для выбора из n для первых нескольких n выглядит следующим образом:

Этот метод грубой силы не очень далеко, хотя из-за комбинаторного взрыва. Но я думал, что это может быть интересно.

Интересно, что при заданных m k увеличивается с n только до некоторого n, после чего оно остается постоянным.

Это означает, что для каждого количества изображений на карту есть определенное количество фотографий на выбор, что приводит к максимально возможному количеству юридических карт. Добавляя больше фотографий, чтобы выбрать из прошлого, оптимальное число больше не увеличивает количество юридических карт.

Первые несколько оптимальных k:

optimal k table

Ответ 3

Я только нашел способ сделать это с 57 или 58 картинами, но теперь у меня очень плохая головная боль, я отправлю код ruby ​​через 8-10 часов после того, как я спал хорошо! просто подсказка, мое решение, каждые 7 карт имеют одну и ту же марку, и все 56 карт могут быть построены с использованием моего решения.

вот код, который генерирует все 57 карт, о которых говорил ypercube. он использует ровно 57 изображений, и извините, что я написал реальный код С++, но зная, что vector <something> - это массив, содержащий значения типа something, легко понять, что делает этот код. и этот код генерирует карты P^2+P+1, используя P^2+P+1 изображения, каждый из которых содержит P+1 изображение и общий общий 1 общий рисунок для каждого простого значения P. это означает, что мы можем иметь 7 карт с 7 картинками, каждая из которых имеет 3 изображения (для p = 2), 13 карт с 13 картинками (для p = 3), 31 карточку с 31 изображением (для p = 5), 57 карт для 57 изображений (для р = 7) и т.д....

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

снова извините за задержанный код.

Ответ 4

Здесь решение Gajet в Python, так как я нахожу Python более удобочитаемым. Я изменил его так, чтобы он работал и с не-простыми числами. Я использовал Thies для создания более понятного отображаемого кода.

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)

С выходом:

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

Ответ 5

Другие описали общую схему проектирования (конечную проективную плоскость) и показали, как создавать конечные проективные плоскости простого порядка. Я просто хотел бы заполнить некоторые пробелы.

Конечные проективные плоскости могут быть сгенерированы для многих разных порядков, но они наиболее просты в случае простого порядка p. Тогда целые числа по модулю p образуют конечное поле, которое можно использовать для описания координат точек и прямых на плоскости. Для точек есть 3 разных вида координат: (1,x,y), (0,1,x) и (0,0,1), где x и y могут принимать значения от 0 до p-1. 3 разных типа точек объясняют формулу p^2+p+1 для количества точек в системе. Мы также можем описать линии с теми же тремя различными типами координат: [1,x,y], [0,1,x] и [0,0,1].

Мы вычислим, падает ли точка и прямая, является ли точечное произведение их координат равным 0 mod p. Так, например, точка (1,2,5) и строка [0,1,1] падают, когда p=7 с 1*0+2*1+5*1 = 7 == 0 mod 7, но точка (1,3,3) и строка [1,2,6] не падают, так как 1*1+3*2+3*6 = 25 != 0 mod 7.

Перевод на язык карточек и изображений, то есть карта с координатами (1,2,5) содержит изображение с координатами [0,1,1], но карта с координатами (1,3,3) не содержит изображения с координатами [1,2,6]. Мы можем использовать эту процедуру для разработки полного списка карточек и изображений, которые они содержат.

Кстати, я думаю, что легче думать о картинах как о точках и картах как о строках, но есть двойственность в проективной геометрии между точками и линиями, поэтому это действительно не имеет значения. Однако в дальнейшем я буду использовать точки для изображений и линий для карт.

Такая же конструкция работает для любого конечного поля. Мы знаем, что существует конечное поле порядка q тогда и только тогда, когда q=p^k - основная степень. Поле называется GF(p^k), что означает "поле Галуа". Поля не так легко построить в случае простой мощности, как они в основном случае.

К счастью, тяжелая работа уже выполнена и реализована в свободном программном обеспечении, а именно Sage. Например, чтобы получить проективный дизайн плоскости порядка 4, просто введите

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

и вы получите результат, который выглядит как

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

Я интерпретирую это следующим образом: есть 21 изображение, помеченное от 0 до 20. Каждый из блоков (строка в проективной геометрии) сообщает мне, какие изображения появляются на карте. Например, первая карта будет иметь фотографии 0, 1, 2, 3 и 20; вторая карта будет иметь изображения 0, 4, 8, 12 и 16; и т.д.

Система порядка 7 может быть сгенерирована с помощью

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

который генерирует выходные данные

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

Ответ 6

Используя z3 доказательство теоремы

Пусть P - количество символов на карту. Согласно эта статья и ypercubeᵀᴹ ответ, есть карты и символы N = P**2 - P + 1, соответственно. Колода карт может быть представлена ​​с матрицей инцидентов, которая имеет строку для каждой карты и столбец для каждого возможного символа. Его элемент (i,j) 1, если на карте i есть символ j. Нам нужно только заполнить эту матрицу этими ограничениями:

  • каждый элемент равен нулю или одному
  • сумма каждой строки точно равна P
  • сумма каждого столбца точно равна P
  • любые две строки должны иметь ровно один символ в общем

Это означает N**2 переменные и N**2 + 2*N + (N choose 2) ограничения. Кажется, он управляется в течение не долгого времени с z3 для небольших входов.

edit: К сожалению, для этого метода P = 8 слишком велика. Я убил процесс после 14 часов времени вычисления.

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)

Результаты

$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.>

Ответ 7

Для тех, у кого проблемы с проективной геометрией плана с 57 точками, есть действительно хороший, интуитивный способ построить игру с 57 картами и 57 символами (на основе ответа Yuval Filmus для этот вопрос):

  • Для карт с 8 символами создайте сетку 7x7 уникальных символов.
  • Добавьте дополнительные 8 символов для "наклонов" от 0 до 6, плюс один для бесконечности.
  • Каждая карта представляет собой линию на сетке (7 символов) плюс один символ из набора наклона для наклона линии. Линии имеют смещение (то есть начальную точку слева) и наклон (т.е. Сколько символов поднимается для каждого шага вправо). Когда линия выходит из сетки вверху, снова введите ее внизу. См. Этот пример рисунка (фотографии из boardgamegeek) для двух таких карт:

Две примерные карты (красный и зеленый), взятые в виде строк из сетки

В примере я беру одну строку с наклоном нуль (красный) и один с наклоном 1 (зеленый). Они пересекаются в одной общей точке (сова).

Этот метод гарантирует, что любые две карты имеют ровно один общий символ, потому что

  • Если наклоны разные, тогда линии всегда будут пересекаться ровно в одну точку.
  • Если наклоны совпадают, то линии не пересекаются, и из сетки не будет общего символа. В этом случае символ наклона будет таким же.

Таким образом, мы можем построить карты 7x7 (7 смещений и 7 наклонов).

Мы также можем построить семь дополнительных карт из вертикальных линий через сетку (т.е. брать каждый столбец). Для них используется значок бесконечности.

Поскольку каждая карта состоит из семи символов из сетки и ровно одного символа "склона", мы можем создать одну дополнительную карту, которая просто состоит из всех 8 символов склона.

Это оставляет нам 7x8 + 1 = 57 возможных карт и 7 x 7 + 8 = 57 обязательных символов.

(Естественно, что это работает только с сеткой размера большого числа (например, n = 7). В противном случае линии с различным наклоном могут иметь ноль или более одного пересечения, если наклон является делителем размера сетки.)