このゲームの背後にある数学/計算の原理は何ですか?


196

私の子供たちには、Spot Itという楽しいゲームがあります。ゲームの制約(私が説明できる限り)は次のとおりです。

  • 55枚のデッキです
  • 各カードには8枚の一意の画像があります(つまり、カードに同じ画像を2枚含めることはできません)
  • デッキから選んだカードが2枚ある場合、一致する画像は1枚だけです。
  • 一致する画像は、カードによって異なる縮尺で表示される場合がありますが、これはゲームを難しくするためです(つまり、小さな木は大きな木と一致します)。

ゲームの原則は次のとおりです。2枚のカードを裏返し、最初に一致する画像を選んだ人がポイントを獲得します。

明確にするための画像は次のとおりです。

それを見つける

(例:上の2枚のカードから、一致する画像が緑の恐竜であることがわかります。右下の画像と中央右の画像の間は、ピエロの頭です。)

私は次のことを理解しようとしています:

  1. これらの基準を満たすために必要なさまざまな画像の最小数はいくつですか?これをどのように決定しますか?

  2. 疑似コード(またはRuby)を使用して、N個の画像の配列(Nは質問1の最小数)から55個のゲームカードをどのように生成しますか?

更新:

写真はデッキごとに2回以上発生します(一部の人が推測したものとは異なります)。3つのカードの写真を参照してください。それぞれに稲妻が付いています。カード3枚


64
ゲームを私の脳を痛めるものに変えるための+1。
キャバレー2011年

3
カードあたりの画像の最小数、またはカードあたり8枚の場合の画像の最小数?また、すべての画像が一致している必要がありますか?
真実性2011年

7
さらに制約を追加する必要があると思います。それ以外の場合は、すべてのカードにリンゴを置き、各カードに任意の数の固有の画像を追加できます。カードの各ペアは、リンゴのイメージでのみ一致します。
mbeckish

8
@cabaret:その場合はsetが好きです。信じられないほど楽しく、悪化します。
dmckee ---元モデレーターの子猫

4
これはすばらしい質問ですが、数学のサイトですでに質問されています(私が)。ここでは少し外れているようです。- math.stackexchange.com/questions/36798/...
Javid Jamae

回答:


148

有限射影幾何学

射影(平面)幾何学公理は、ユークリッド幾何学とは少し異なります。

  • 2つのポイントごとに、それらを通る線が1つだけあります(これは同じです)。
  • 2本の線が1つの点で交わる(これはEuclidとは少し異なります)。

さて、スープに「有限」を追加すると、質問があります:

2点だけのジオメトリを作成できますか?3点で?4で?7で?

この問題に関する未解決の質問はまだありますが、私たちはこれを知っています:

  • ジオメトリがある場合Qのポイントは、その後Q = n^2 + n + 1n呼ばれるorder幾何学を。
  • n+1すべての行にポイントがあります。
  • すべての点から、正確にn+1線を渡します。
  • 総行数もQです。

  • そして最後に、もしn素数ならば、秩序の幾何学が存在しnます。


それがパズルとどう関係しているのかと尋ねる人もいるかもしれません。

入れcardの代わりに、pointpictureの代わりlineと公理は次のようになります。

  • 2枚のカードごとに1枚の画像が共通しています。
  • 2つの画像ごとに、両方のカードが1枚だけあります。

今、取りましょうn=7、と私たちはorder-7有限幾何学を持っていQ = 7^2 + 7 + 1ます。それはQ=57線(絵)とQ=57点(カード)を作ります。パズルメーカーは、55は57よりもラウンド数が多いと判断し、2枚のカードを残したと思います。

またn+1 = 8、すべてのポイント(カード)から8行が通過し(8つの画像が表示されます)、すべての行(画像)には8ポイントがあります(8つのカードに表示されます)。


これは、Noelle Evans-Finite Geometry Problem Pageからコピーされた、Fano Planeとして知られる7点の最も有名な有限射影(次数2)平面(ジオメトリ)の表現です

ここに画像の説明を入力してください

上記のorder-2平面を7枚のカードと7枚の写真で同様のパズルにする方法を説明する画像を作成することを考えていましたが、math.exchangeツインの質問からのリンクはまさに​​そのような図です:Dobble-et- la-geometrie-finie

ファーノ飛行機


9
それで、このゲームは非ユークリッド幾何学を示していますか?カードは正しいと言うのは正しいでしょうか?
RMorrisey

2
これは素晴らしいことのように聞こえますが、実際に問題をうまくモデル化していることは確かです。@ypercube、カード/画像と点/線の間のアナロジーが有効であると考える理由についてもう少し説明していただけますか?
ネイトコール

@ネイト:最初のアナロジーevery two cards have exactly one picture in commonは、質問で述べられています。2つ目for every two pictures there is exactly one card that has both of themは、OPは、ゲームセットがそれを満たしているかどうかを教えてくれます。
ypercubeᵀᴹ

4
素晴らしい答えです!素晴らしい洞察、ゲームがOrder-7射影平面のプロパティに一致することに気づき、さらに、私が見た一般の人のための射影平面の最も良い説明の1つ。
RBarryYoung

3
鮮やかさ。Pythonでカードセットを生成する方法を理解するには、この記事をさらに100回読む必要があります...
Jared

22

57ポイントで射影平面形状を描いて困っている人のために、本当に素晴らしい、(での回答に基づいて、57枚のカードと57個のシンボルでゲームを構築するための直感的な方法があるのYuval Filmusのため、この質問は):

  1. シンボルが8つあるカードの場合、一意のシンボルの7x7グリッドを作成します。
  2. 0から6までの「スロープ」に8つのシンボルを追加し、さらに1つの無限スロープを追加します。
  3. 各カードは、グリッド上のライン(7シンボル)とラインのスロープに設定されたスロープからの1つのシンボルです。線には、オフセット(つまり、左側の開始点)と勾配(つまり、右のステップごとに上がるシンボルの数)があります。ラインがグリッドの上部を離れたら、下部から再入力します。このような2つのカードについては、次の図の例(boardgamegeekの写真)を参照してください。

グリッドから線として取った2つのサンプルカード(赤と緑)

この例では、勾配がゼロ(赤)の線と、勾配1(緑)の線を1つとります。それらはちょうど1つの共通点(フクロウ)で交差します。

この方法により、2枚のカードに共通のシンボルが1つだけ含まれることが保証されます。

  1. 勾配が異なる場合、線は常に正確に1点で交差します。
  2. 勾配が同じ場合、線は交差せず、グリッドからの共通記号はありません。この場合、勾配記号は同じになります。

このようにして、7x7カード(7つのオフセットと7つのスロープ)を構築できます。

縦の線からグリッドまで(つまり、各列を取りながら)7枚の追加のカードを作成することもできます。それらの場合、無限スロープアイコンが使用されます。

各カードはグリッドからの7つのシンボルと正確に1つの「スロープ」シンボルで構成されるため、8つのスロープシンボルすべてで構成される1つの追加カードを作成できます。

これにより、7x8 + 1 = 57の可能なカードと7 x 7 + 8 = 57の必要なシンボルが残ります。

(当然、これは素数サイズのグリッド(例n = 7)でのみ機能します。それ以外の場合、勾配がグリッドサイズの約数である場合、異なる勾配の線は0または1つ以上の交点を持つ可能性があります。)


18

したがって、合計n枚の画像のプールからそれぞれm = 8枚の画像を含むk = 55枚のカードがあります。「何枚の写真を nは、我々は一連の構築することができるように、我々は必要なのですk個のカードの任意のペアの間に一つだけの共有絵でカードを?」同等に尋ねることによって:

n次元のベクトル空間とすべてのベクトルのセットが与えられ、それらは1つと他のすべてのゼロに等しいm個の要素を含み、nの大きさはどれくらいであるかで、 k個のペアごとの製品に点在しているベクトルを、すべて1に等しい

ペアを構築するための正確に(nを選ぶm)の可能なベクトルがあります。私たちは、少なくとも十分に大きい必要があるので、n個(ようにn個選びメートルを)> = kとです。これは下限に過ぎないため、ペアごとの互換性制約を満たすためには、はるかに高いnが必要になる可能性がありますます。

ちょっと実験するためだけに、有効なカードセットを計算する小さなHaskellプログラムを書きました。

編集:私はニールとガジェットのソリューションを見てすぐに気づきました、私が使用するアルゴリズムは常に最良の可能なソリューションを見つけるとは限らないため、以下のすべてが必ずしも有効であるとは限りません。すぐにコードを更新します。

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

最初の数nに対してnから選択するさまざまな画像の数について、カードあたりのm = 8画像の互換性のあるカードの最大数次のようになります。

この総当たりの方法は、組み合わせ爆発のためにそれほど遠くまでは行きません。しかし、それでもまだ面白いかもしれないと思いました。

興味深いことに、mが一定の場合knとともに増加し、 n、その後一定のままです。

これは、カードごとの写真の数ごとに、選択できる特定の数の写真があることを意味し、その結果、有効なカードの最大数が得られます。過去から選択する写真をさらに追加して、その最適な数を指定しても、法的カードの数はそれ以上増えません。

最初のいくつかの最適なkは次のとおりです。

最適なkテーブル


それは限界での最初の試みにすぎませんよね?「1に等しいペアワイズドット積」要件を組み込んでいません...
Nemo

どうやらここの構文強調表示機能はまだ実際にHaskellをサポートしていませんmeta.stackexchange.com/questions/78363/…)が、将来に備えてヒントを投げます。
BoltClock

@BoltClock編集ありがとうございます!言語固有の構文強調表示のヒントを提供できることを知りませんでした。
Thies Heidecke

まだあまり知られていません:)
BoltClock

9

また、設計の一般的なフレームワーク(有限射影平面)について説明し、基本次数の有限射影平面を生成する方法を示しています。いくつかのギャップを埋めたいと思います。

有限射影平面はさまざまな次数に対して生成できますが、主次数の場合は最も簡単pです。次に、整数を法としてp、有限フィールドを形成します。このフィールドを使用して、平面内の点と線の座標を記述できます。:3つの異なる点の座標の種類があり(1,x,y)(0,1,x)、および(0,0,1)xyの値を取ることができる0のはp-1。3種類のポイントはp^2+p+1、システム内のポイント数の式を説明しています。:我々はまた、座標の同じ3つの異なる種類の行を記述することができ[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]]>

上記を次のように解釈します。0から20までのラベルが付いた21の画像があります。各ブロック(射影ジオメトリの線)は、カードに表示される画像を示します。たとえば、最初のカードには写真0、1、2、3、20があります。2枚目のカードには写真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]]>

8

57枚または58枚の写真でそれを行う方法を見つけましたが、頭痛がひどいので、よく眠ってから8〜10時間後にルビーコードを投稿します。7枚のカードごとに同じマークを共有し、私のソリューションを使用して合計56枚のカードを構築できます。

以下は、ypercubeが話していた57枚すべてのカードを生成するコードです。正確に57枚の画像を使用しています。申し訳ありませんが、実際のC ++コードを記述しましたvector <something>が、somethingそれが型の値を含む配列であることを知っているので、このコードの機能は簡単に理解できます。そしてこのコードは、すべての素数P値に対して、それぞれが画像を含み、共通の1つの画像のみを共有する画像P^2+P+1を使用してカードを生成します。つまり、7枚のカードで7枚の写真に3枚の写真(p = 2の場合)、13枚のカードで13枚の写真(p = 3の場合)、31枚のカードで31枚の写真(p = 5の場合)、57枚のカードで57枚の写真を使用できます。 (p = 7の場合)など...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();
    }
}

再び遅延コードをごめんなさい。


37
私はこれのエレガントな証拠を持っていますが、残念ながらこのコメントボックスは小さすぎてそれを含めることができません。
sarnold

@Gajet:実行しましたp=4か?(21枚のカード/写真)
ypercubeᵀᴹ

4は素数ではないため、私のアルゴリズムでは4は機能しません。私のアルゴリズムでは、pが素数であることが重要です。
Ali1S232

@ypercubeをもう一度確認した後、アルゴリズムにいくつかの小さな間違いがありましたが、2、3、5、7を確認しました。他の素数が機能することを証明できるので、これが私の完全なコードです(ただしc ++で)
Ali1S232

1
@Gajet:クールなソリューション!ここで、貪欲なアルゴリズムが常に最良のソリューションを生成しなかった理由がわかります。
Thies Heidecke

6

Pythonの方が読みやすいので、ここにPythonでのGajetのソリューションを示します。非素数でも動作するように変更しました。私は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)

出力あり:

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

2
あなたの例の最後の3枚のカードは、5枚目のカードと写真を共有していないため、有効ではないと思います。気が付く前に、コードを1時間以上チェックしたところです:)興味深いことに、合法的なカードセットの最大サイズは、カードごとに4枚の写真で5であり、選択する写真が増えても増えません。
Thies Heidecke

1
@Gajetのコードを使用して作成した図を使用すると、(p) + (p * p) + (1)構成が正確に存在する理由を簡単に確認できます。
ニールG

1
@ニール:更新された図のおかげで、Gajetのソリューションがどのように機能するかを簡単に確認できます!
Thies Heidecke

1
@Gajet:あなたはについて間違っていると思いますall p except 4 and 6p*p+p+1ポイントとライン(カードと写真)がある有限の平面を作成したい場合、それはに関係しfinite fields、には関係しませんringsppがprimeまたはaの場合、次数の有限体がありますprime power。のようなk * p + (j + i * k) % pk*p + j + i*kは有限次数体の乗算と加算の観点から表現しているため、コードは素数に対して正しく動作しpます。
ypercubeᵀᴹ

1
ご注文の有限分野におけるこれらの操作(。MULT及び追加)を発現することができれば、それは、あまりにも首相の権限のために正常に動作するp^2p^3などですから、それがために動作します4, 8, 9, 16, 25, 27, ...
ypercubeᵀᴹ

4

z3定理証明を使用する

Pカードあたりのシンボル数とする。この記事ypercubeᵀᴹの回答によるとN = P**2 - P + 1、それぞれカードとシンボルがあります。カードのデッキは、各カードの行と可能な各シンボルの列を持つ発生率マトリックスで表すことができます。その(i,j)要素は1、カードiにシンボルがあるかどうかjです。これらの制約を念頭に置いて、このマトリックスを埋める必要があるだけです。

  • すべての要素は0または1です
  • 各行の合計は正確に P
  • 各列の合計は正確に P
  • 任意の2つの行に共通の記号が1つだけ必要

それはN**2変数とN**2 + 2*N + (N choose 2)制約を意味します 。z3少量の入力でもそれほど長くは管理できないようです。

編集:残念ながら、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.>

4

私はこのスレッドがとても好きです。私はこのコードの一部を使用してこのgithub pythonプロジェクトをビルドし、カスタムカードをpngとして描画します(インターネットでカスタムカードゲームを注文できます)。

https://github.com/plagtag/ProjectiveGeometry-Game


3

この種のデッキをPerlのコードで生成する方法についての記事を書きました。コードは最適化されていませんが、少なくとも「合理的な」注文のデッキを生成することができます...そしてそれ以上のこともできます。

これは次数8の例です。8は素数ではないため、これらの種類のデッキを生成するための有効な次数です。少し難しいSpot-Itを生成したいだけの場合は、上記または下記の詳細な説明を参照してください:-)

$ 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

各識別子0には72カード識別子として及び画像識別子の両方を読み取ることができます。たとえば、最後の行は次のことを意味します。

  • カードが72含まれている写真21322、...、 、5968
  • 絵は72カードに表示されます21322、...、5968
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.