Bu oyunun arkasındaki matematiksel / hesaplama ilkeleri nelerdir?


196

Çocuklarım Spot It adlı bu eğlenceli oyun var ! Oyun kısıtlamaları (en iyi tarif edebileceğim gibi):

  • 55 kartlık bir destedir
  • Her kartta 8 benzersiz resim vardır (yani bir kart aynı resmin 2'sine sahip olamaz)
  • Desteden seçilen 2 kart verildiğinde, 1 ve sadece 1 eşleşen resim vardır .
  • Eşleşen resimler farklı kartlarda farklı ölçeklendirilebilir, ancak bu sadece oyunu zorlaştırmak içindir (yani küçük bir ağaç hala daha büyük bir ağaçla eşleşir)

Oyunun prensibi şöyledir: 2 kartı ters çevirin ve eşleşen resmi ilk kim seçerse puan kazanır.

İşte açıklama için bir resim:

tespit et

(Örnek: Yukarıdaki alt 2 karttan eşleşen resmin yeşil dinozor olduğunu görebilirsiniz. Sağ alt ve sağ ortadaki resim arasında bir palyaçanın başı.)

Aşağıdakileri anlamaya çalışıyorum:

  1. Bu kriterleri karşılamak için gereken minimum sayıda farklı resim sayısı nedir ve bunu nasıl belirlersiniz?

  2. Sahte kod (veya Ruby) kullanarak, bir dizi N resimden nasıl 55 oyun kartı üretersiniz (burada N, 1. sorudaki minimum sayıdır)?

Güncelleme:

Resimler güverte başına iki kattan fazla ortaya çıkar (bazılarının tahmin ettiklerinin aksine). Şimşek çakması olan 3 kartın bu resmine bakın:3 kart


64
Bir oyunu beynime zarar veren bir şeye dönüştürmek için +1.
kabare

3
Kart başına minimum resim sayısı veya kart başına 8 adet olduğu düşünüldüğünde minimum resim sayısı? Ayrıca, her resim eşleşebilir mi?
doğruluk

7
Daha fazla kısıtlama eklemeniz gerektiğini düşünüyorum. Aksi takdirde, her karta bir elma koyabilir ve ardından her karta istediğiniz sayıda benzersiz görüntü ekleyebilirsiniz. Her kart çifti sadece elmanın görüntüsüyle eşleşir.
mbeckish

8
@cabaret: Bu durumda ayarlamayı seveceksiniz . İnanılmaz derecede eğlenceli ve can sıkıcı.
dmckee --- eski moderatör kedi yavrusu

4
Bu harika bir soru olsa da, matematik sitesinde zaten soruldu (benim tarafımdan). Burada biraz konu dışı görünüyor. - math.stackexchange.com/questions/36798/…
Javid Jamae

Yanıtlar:


148

Sonlu Projektif Geometriler

Aksiyonları ve yansıtmalı (düzlemsel) geometrisi Öklid geometrisi biraz daha farklıdır:

  • Her iki noktanın içinden geçen bir çizgi vardır (bu aynıdır).
  • Her iki çizgi tam olarak bir noktada buluşuyor (bu Öklid'den biraz farklı).

Şimdi, çorbaya "sonlu" ekleyin ve şu soruyu sorun:

Sadece 2 noktadan oluşan bir geometrimiz olabilir mi? 3 puanla mı? 4 ile mi? 7 ile mi?

Bu sorunla ilgili hala açık sorular var, ancak bunu biliyoruz:

  • Noktalı geometriler varsa Q, o zaman Q = n^2 + n + 1ve geometri ndenir order.
  • n+1Her satırda puan var .
  • Her noktadan tam olarak n+1çizgileri geçin.
  • Toplam satır sayısı da Q.

  • Ve son olarak, eğer nasal ise, o zaman bir düzen geometrisi vardır n.


Bunun yapbozla ne ilgisi var, diye sorabilir.

Put cardyerine pointve pictureyerine lineve aksiyomlar hale gelir:

  • Her iki kartın ortak bir resmi vardır.
  • Her iki resim için her ikisine de sahip olan bir kart var.

Şimdi alalım n=7ve order-7sonlu geometriye sahibiz Q = 7^2 + 7 + 1. Bu Q=57çizgiler (resimler) ve Q=57noktalar (kartlar) yapar. Sanırım bulmaca yapımcıları 55'in 57'den fazla yuvarlak sayı olduğuna karar verdiler ve 2 kart bıraktılar.

Ayrıca n+1 = 8, her noktadan (kart) 8 satır geçer (8 resim görünür) ve her satırın (resim) 8 puanı vardır (8 kartta görünür).


İşte Noelle Evans - Sonlu Geometri Sorunu'ndan kopyalanan Fano Düzlemi olarak bilinen, 7 nokta ile en ünlü sonlu projektif (sipariş-2) düzleminin (geometri) bir temsili.

resim açıklamasını buraya girin

Yukarıdaki sipariş-2 düzleminin 7 kart ve 7 resim ile nasıl benzer bir bulmaca yapılabileceğini açıklayan bir görüntü oluşturmayı düşünüyordum, ancak math.exchange ikiz sorusundan bir bağlantı tam olarak böyle bir diyagrama sahip: Dobble-et- la-geometrie-finie

Fano Düzlemi


9
Yani bu oyun Öklidyen olmayan geometri mi sergiliyor? Kartların Doğru olduğunu söylemek doğru olur mu?
RMorrisey

2
Bu kulağa harika geliyor, ama aslında sorunu iyi modellediğinden emin değilim. @ ypercube, kart / resim ile nokta / çizgi arasındaki benzetmenin neden geçerli olduğunu düşündüğünüzü biraz daha açıklayabilir misiniz?
Nate Kohl

@Doğa: Birinci benzetme every two cards have exactly one picture in common, soruda belirtilmiştir. İkincisi for every two pictures there is exactly one card that has both of them, OP bize oyun setinin tatmin edip etmediğini söyleyebilir.
ypercubeᵀᴹ

4
Müthiş cevap! Oyunun bir Order-7 Projektif Düzleminin özellikleriyle ve gördüğüm katmanlar için Projektif Düzlemlerin en iyi açıklamalarından biri ile eşleştiğini fark eden harika bir fikir.
RBarryYoung

3
Parlak. Python'da kart setlerinin nasıl oluşturulacağını anlamaya çalışmak için bu 100 kez daha okumam gerekecek
Jared

22

Projektif düzlem geometrisini 57 puanla hayal etmekte zorlananlar için, oyunu 57 kart ve 57 sembolle inşa etmenin gerçekten güzel, sezgisel bir yolu var ( bu soru için Yuval Filmus'un cevabına dayanarak ):

  1. 8 sembollü kartlar için 7x7 benzersiz sembol ızgarası oluşturun.
  2. 0'dan 6'ya kadar "eğimler" için ek 8 sembol, sonsuzluk eğimi için bir sembol ekleyin.
  3. Her kart, ızgaradaki bir çizgidir (7 sembol) artı çizginin eğimi için ayarlanan eğimden bir semboldür. Çizgiler bir ofset (yani soldaki başlangıç ​​noktası) ve bir eğime (yani her bir adım için kaç sembol yukarı çıkacak) sahiptir. Çizgi ızgarayı en üstte bıraktığında, en alttaki tekrar girin. Bu tür iki kart için bu örnek şekle ( boardgamegeek'ten resimler ) bakın:

Izgaradan satır olarak alınan iki örnek kart (kırmızı ve yeşil)

Örnekte, eğim sıfır (kırmızı) ile bir çizgi ve eğim 1 (yeşil) ile bir çizgi alıyorum. Tam bir ortak noktada kesişiyorlar (baykuş).

Bu yöntem, herhangi iki kartın tam olarak bir ortak sembolü olmasını sağlar, çünkü

  1. Eğimler farklıysa, çizgiler her zaman tam olarak bir noktada kesişir.
  2. Eğimler aynı ise, çizgiler kesişmeyecek ve ızgarada ortak bir sembol olmayacaktır. Bu durumda, eğim sembolü aynı olacaktır.

Bu şekilde 7x7 kart (7 ofset ve 7 eğim) oluşturabiliriz.

Ayrıca dikey çizgilerden ızgara boyunca yedi ek kart oluşturabiliriz (yani her sütunu alarak). Bunlar için sonsuz eğim simgesi kullanılır.

Her kart ızgaradaki yedi sembolden ve tam olarak bir "eğim" sembolünden oluştuğu için, sadece 8 eğim sembolünden oluşan bir ek kart oluşturabiliriz.

Bu bize 7x8 + 1 = 57 olası kart ve 7 x 7 + 8 = 57 gerekli semboller bırakıyor.

(Doğal olarak, bu yalnızca asal sayı büyüklüğünde bir ızgara ile çalışır (örneğin n = 7). Aksi takdirde, eğim ızgara boyutunun bir böleniyse, farklı eğim çizgilerinin sıfır veya birden fazla kesişimi olabilir.)


18

Yani toplamda n resim havuzundan m = 8 resim içeren k = 55 kart var . Biz 'Kaç resim soruyu yeniden yazabiliriz n biz bir dizi oluşturmak, böylece ihtiyacımız var k kartlarının herhangi bir çifti arasında tek bir ortak resim ile kartların?' eşit olarak şunu sorarak:

Bir göz önüne alındığında n boyutlu vektör uzayı ve tam olarak içeren tüm vektörlerin, kümesini m elemanları biri ve diğer tüm sıfıra eşit, büyük vardır ne n biz bir dizi bulmak, böylece, olmaya k olan ikili nokta ürünlerdir vektörler, hepsi 1'e eşit mi?

Çiftleri oluşturmak için tam olarak ( n, m'yi seçer ) olası vektörler vardır. Bu yüzden en azından yeterince büyük bir n'ye ihtiyacımız var, böylece ( n, m'yi seçer )> = k olur . Bu sadece bir alt sınırdır, bu nedenle ikili uyumluluk kısıtlamasını yerine getirmek için muhtemelen çok daha yüksek bir n'ye ihtiyacımız var .

Sadece biraz denemek için geçerli kart setleri hesaplamak için küçük bir Haskell programı yazdım:

Düzenleme: Neil ve Gajet'in çözümünü gördükten sonra farkettim, kullandığım algoritma her zaman mümkün olan en iyi çözümü bulamıyor, bu yüzden aşağıdaki her şey mutlaka geçerli değil. Kodumu yakında güncellemeye çalışacağım.

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

İlk birkaç n için n arasından seçim yapabileceğiniz farklı sayıda resim için kart başına m = 8 resim için maksimum uyumlu kart sayısı şöyle görünür:

Bu kaba kuvvet yöntemi, kombinatoryal patlama nedeniyle çok fazla ilerlemiyor. Ama yine de ilginç olabileceğini düşündüm.

İlginçtir, bu verilen yönelik gibi görünüyor m , k artar n sadece yukarı belli etmek n bu sabit kalır, bundan sonra,.

Bu, kart başına her resim sayısı için seçilebilecek belirli sayıda resim olduğu anlamına gelir, bu da mümkün olan en fazla sayıda yasal kartla sonuçlanır. Geçmişten seçim yapmak için daha fazla resim eklemek, en uygun numarayı yasal kartların sayısını artırmaz.

İlk birkaç optimal k :

optimal k masası


Bu sadece bir başlangıç ​​denemesi, değil mi? "1'e eşit ikili nokta ürünleri" gereksinimini dahil etmediniz ...
Nemo

Görünüşe göre buradaki sözdizimi vurgulayıcı henüz Haskell'i gerçekten desteklemiyor ( meta.stackexchange.com/questions/78363/… ), ancak gelecekte olması durumunda ipucunu atacağım .
BoltClock

@BoltClock Düzenlediğiniz için teşekkürler! dile özgü sözdizimi vurgulama için ipuçları verebilir bilmiyordum.
Thies Heidecke

Henüz çok iyi tanınmıyor :)
BoltClock

9

Diğerleri, tasarım için genel çerçeveyi (sonlu projektif düzlem) tarif etmiş ve asal düzenin sonlu projektif düzlemlerinin nasıl üretileceğini göstermişlerdir. Sadece bazı boşlukları doldurmak istiyorum.

Birçok farklı sipariş için sonlu projektif düzlemler oluşturulabilir, ancak bunlar birinci dereceden sipariş için en açık olanlardır p. Daha sonra tamsayılar modüleri p, düzlemdeki noktalar ve çizgiler için koordinatları tanımlamak için kullanılabilen sonlu bir alan oluşturur. Orada noktaların koordinat 3 farklı türü vardır: (1,x,y), (0,1,x), ve (0,0,1), nerede xve ygelen değerler alabilir 0için p-1. 3 farklı nokta türü p^2+p+1, sistemdeki nokta sayısı formülünü açıklar . : Aynı zamanda koordinat aynı 3 farklı türde çizgiler tanımlayan [1,x,y], [0,1,x]ve [0,0,1].

Bir nokta ve çizginin olay olup olmadığını, koordinatlarının nokta çarpımının 0 moda eşit olup olmadığını hesaplıyoruz p. Örneğin, nokta (1,2,5)ve çizgi [0,1,1]o p=7zamandan beri olaydır 1*0+2*1+5*1 = 7 == 0 mod 7, ancak nokta (1,3,3)ve çizgi [1,2,6]o zamandan beri olay değildir 1*1+3*2+3*6 = 25 != 0 mod 7.

Koordinatlarla kart demektir kartları ve resimlerin dili haline çevirmek (1,2,5)koordinatlarla resim içeriyor [0,1,1], ancak koordinatlarla kartıyla (1,3,3)koordinatlarla resmi içermiyor [1,2,6]. Bu prosedürü, kartların ve içerdikleri resimlerin tam bir listesini geliştirmek için kullanabiliriz.

Bu arada, resimleri noktalar ve kartlar olarak çizgiler olarak düşünmek daha kolay, ancak noktalar ve çizgiler arasında projektif geometride bir dualite var, bu yüzden gerçekten önemli değil. Ancak, bundan sonra resimler için puan ve kartlar için çizgiler kullanacağım.

Aynı yapı her sonlu alan için de geçerlidir. Biliyoruz ki, qsadece ve eğer q=p^kbirinci güç ise sınırlı bir düzen alanı vardır . GF(p^k)"Galois alanı" anlamına gelen alan denir . Alanların ana güç durumunda yapılandırılması, ana durumda olduğu kadar kolay değildir.

Neyse ki, sıkı çalışma zaten özgür yazılımda, yani Sage'de yapılmış ve uygulanmıştır . Örneğin, 4. sıradaki bir projektif düzlem tasarımı elde etmek için,

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

ve şuna benzer bir çıktı elde edersiniz

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

Yukarıdakileri şu şekilde yorumluyorum: 0 ila 20 arasında etiketlenmiş 21 resim var. Blokların her biri (projektif geometride çizgi) bana bir kartta hangi resimlerin göründüğünü söyler. Örneğin, ilk kartta 0, 1, 2, 3 ve 20 resimleri bulunur; ikinci kartta 0, 4, 8, 12 ve 16 resimleri bulunur; ve bunun gibi.

Sipariş 7 sistemi aşağıdakiler tarafından oluşturulabilir

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

çıktı üreten

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

Sadece 57 ya da 58 fotoğrafla bunu yapmanın bir yolunu buldum ama şimdi çok kötü bir baş ağrım var, iyi uyuduktan sonra yakut kodunu 8-10 saat içinde göndereceğim! Sadece bir ipucu benim çözüm her 7 kart aynı işareti paylaşır ve benim çözüm kullanılarak toplam 56 kart inşa edilebilir.

İşte ypercube'un bahsettiği 57 kartın tümünü oluşturan kod. tam 57 resim kullanır ve üzgünüm gerçek C ++ kodu yazdım ama bu vector <something>tür değerleri içeren bir dizi olduğunu bilmek somethingbu kodun ne olduğunu anlamak kolaydır. ve bu kod , her bir ana P değeri için, her biri resim içeren resimleri P^2+P+1kullanarak ve yalnızca 1 resmi paylaşan kartlar kullanarak kartlar oluşturur . yani her biri 3 resim (7 = 2) olan 7 kart, 13 resim (13 = 3) kullanan 13 kart, 31 resim (31 = 5) için 31 kart, 57 resim için 57 kart olabilir (p = 7 için) vb.P^2+P+1P+1

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

gecikmeli kod için tekrar özür dilerim.


37
Bunun zarif bir kanıtı var, ama ne yazık ki bu yorum kutusu onu içermek için çok küçük.
sarnold

@Gajet: Koştunuz p=4mu? (ve 21 kartlar / resimler)
ypercubeᵀᴹ

4 asal sayı olmadığından algoritmamda 4 çalışmıyor, algoritmamda p asal olması önemlidir.
Ali1S232

@ ypercube tekrar kontrol ettikten sonra algoritmamda bazı minior hatalar vardı ama 2, 3,5,7 için kontrol ettim ve çalışacak başka bir asal sayı için kanıtlayabilirim, bu yüzden tam kodum (ama c ++)
Ali1S232

1
@Gajet: harika bir çözüm! Şimdi açgözlü algoritmamın neden her zaman en iyi çözümü üretmediğini anlıyorum.
Thies Heidecke

6

Python'u daha okunabilir bulduğum için Gajet'in Python'daki çözümü. Asal olmayan sayılarla da çalışacak şekilde değiştirdim. Bazı daha kolay anlaşılır ekran kodu oluşturmak için Thies içgörü kullandım.

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)

Çıktı ile:

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

2
bence örneğinizdeki son üç kart geçerli değil çünkü beşinci kartla resim paylaşmıyorlar. Sadece fark etmeden önce bir saatten fazla kodumu kontrol etti :) İlginç bir şekilde, yasal bir kart setinin maksimum boyutu kart başına 4 resim için 5 görünüyor ve seçim için daha fazla resim ile bile artmıyor.
Thies Heidecke

1
@ Gajet kodunu kullanarak oluşturduğum diyagramla, neden tam olarak (p) + (p * p) + (1)yapılandırmaların olduğunu görmek çok daha kolay .
Neil G

1
@Neil: Güncellenen diyagram için teşekkürler, Gajet çözümünün nasıl çalıştığını görmeyi çok daha kolay hale getiriyor!
Thies Heidecke

1
@Gajet: Sanırım yanlış düşünüyorsun all p except 4 and 6. p*p+p+1Noktaların ve çizgilerin (kartlar ve resimler) olduğu sonlu bir düzlem üretmek istiyorsanız , o ile ilgilidir finite fieldsve değil rings. pP primeveya a olduğunda sonlu düzen alanları vardır prime power. Kodunuz asal sayılar için doğru şekilde çalışır, çünkü gibi k * p + (j + i * k) % pifadeler k*p + j + i*kçarpma ve sonlu düzen alanına ekleme açısından ifade edilir p.
ypercubeᵀᴹ

1
Siparişin sonlu alanlarda bu işlemleri (. Mult ve ekleme) ifade edebilir eğer, çok asal güçler için doğru şekilde çalışacaktır p^2, p^3vb Yani, için çalışacak4, 8, 9, 16, 25, 27, ...
ypercubeᵀᴹ

4

z3Teorem kanıtlayıcıyı kullanma

Izin vermek Pkart başına sembol sayısı. Bu makaleye ve ypercubeᵀᴹcevabına göre N = P**2 - P + 1sırasıyla kartlar ve semboller var. Bir kart destesi, her kart için bir sıraya ve olası her sembol için bir sütuna sahip olan insidans matrisi ile temsil edilebilir. Onun (i,j)unsurdur 1kart ise isembolü vardır jüzerinde. Bu matrisi sadece şu kısıtlamaları göz önünde bulundurarak doldurmamız gerekir:

  • her eleman sıfır veya birdir
  • her satırın toplamı P
  • her sütunun toplamı tam olarak P
  • herhangi iki satırın ortak olarak tam bir sembolü olmalıdır

Bu, N**2değişkenler ve N**2 + 2*N + (N choose 2)kısıtlamalar anlamına gelir . z3Küçük girdiler için çok uzun olmayan bir sürede yönetilebilir gibi görünüyor .

edit : Ne yazık ki P = 8 bu yöntem için çok büyük gibi görünüyor. İşlemi 14 saatlik hesaplama süresinden sonra öldürdüm.

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)

Sonuçlar

$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

Perl kodu ile bu tür güverte oluşturmak hakkında bir makale yazdım . Kod optimize edilmemiştir, ancak en azından "makul" siparişlerin destelerini ve daha fazlasını oluşturabilir.

Aşağıda, biraz daha karmaşık bir temel matematiği dikkate alması gereken 8. derece ile ilgili bir örnek var, çünkü 8, bu tür güverteler oluşturmak için geçerli bir düzen olmasına rağmen asal değildir. Biraz daha zor bir Spot-It oluşturmak istiyorsanız, yukarıya veya daha ayrıntılı bir açıklama için makaleye bakın :-)

$ 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

Dan Her tanımlayıcı 0için 72bir kart belirteci olarak ve bir resim tanımlayıcı olarak hem okunabilir. Örneğin, son satır şu anlama gelir:

  • kart 72içeren resimler 2, 13, 22, ..., 59, 68, VE
  • Resmi 72kartları görünür 2, 13, 22, ..., 59ve 68.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.