이 게임의 수학적 / 계산 원칙은 무엇입니까?


196

내 아이들은 Spot It 이라는 재미있는 게임을 가지고 있습니다 ! 게임 제약은 (내가 설명 할 수있는 최선) :

  • 55 장의 덱
  • 각 카드에는 8 개의 고유 한 사진이 있습니다 (예 : 카드에 동일한 사진 중 2 개를 가질 수 없음)
  • 갑판에서 2 장의 카드를 선택하면 1 장의 일치하는 그림 만 있습니다.
  • 일치하는 그림은 다른 카드에서 다르게 크기가 조절 될 수 있지만 게임을 더 어렵게 만드는 것입니다 (예 : 작은 나무는 여전히 큰 나무와 일치합니다)

게임의 원칙은 2 장의 카드를 뒤집어 놓고 먼저 일치하는 그림을 선택한 사람이 점수를 얻습니다.

다음은 설명을위한 그림입니다.

그것을 발견

(예 : 위의 아래 두 카드에서 일치하는 그림이 녹색 공룡임을 알 수 있습니다. 오른쪽 아래 그림과 오른쪽 가운데 그림 사이에는 광대 머리입니다.)

다음을 이해하려고합니다.

  1. 이러한 기준을 충족하는 데 필요한 최소 그림 수는 얼마이며 어떻게 결정합니까?

  2. 의사 코드 (또는 루비)를 사용하여 N 개의 그림 배열에서 55 개의 게임 카드를 생성하는 방법은 무엇입니까 (N은 질문 1의 최소 숫자)?

최신 정보:

사진은 덱당 두 번 이상 발생합니다 (일부는 예상 한 것과 반대). 각각 번개 모양의 3 장의 카드 그림을보십시오.3 장


64
내 두뇌를 상하게하는 무언가로 게임을 바꾸어 +1.
카바레

3
카드 당 최소 사진 수 또는 카드 당 8 개가있는 경우 최소 사진 수는? 또한 모든 사진이 일치해야합니까?
진실성

7
더 많은 제약 조건을 추가해야한다고 생각합니다. 그렇지 않으면 모든 카드에 사과를 넣은 다음 각 카드에 원하는 수의 고유 이미지를 추가 할 수 있습니다. 각 카드 쌍은 사과 이미지에서만 일치합니다.
mbeckish

8
@cabaret :이 경우 set을 좋아 합니다 . 놀랍도록 재미 있고 가혹한.
dmckee --- 전 운영자 고양이

4
이것은 훌륭한 질문이지만 이미 수학 사이트에서 나에게 묻습니다. 여기서 조금 벗어난 주제로 보입니다. - math.stackexchange.com/questions/36798/...
Javid Jamae

답변:


148

유한 한 투영 기하학

공리투영 (평면) 기하학은 유클리드 기하학보다 약간 다릅니다 :

  • 두 점마다 정확하게 하나의 선이 있습니다 (이것은 동일합니다).
  • 모든 두 줄은 정확히 한 지점에서 만나게됩니다 (유클리드와는 조금 다릅니다).

이제 수프 에 "finite" 를 추가 하면 질문이 있습니다.

우리는 단지 2 점으로 지오메트리를 가질 수 있습니까? 3 점? 4로? 7로?

이 문제와 관련하여 여전히 공개적인 질문이 있지만 우리는 이것을 알고 있습니다 :

  • 와 형상이있는 경우 Q포인트는 다음 Q = n^2 + n + 1n호출되는 order형상의합니다.
  • n+1모든 라인에 포인트 가 있습니다 .
  • 모든 지점에서 정확하게 n+1선을 전달하십시오 .
  • 총 줄 수는 또한 Q입니다.

  • 그리고 마지막으로 n소수라면 order의 지오메트리가 존재합니다 n.


그것이 퍼즐과 어떤 관련이 있는지 묻습니다.

넣어 card대신 pointpicture대신 line과 공리가 될 :

  • 두 장의 카드마다 정확히 하나의 공통점이 있습니다.
  • 두 장의 사진마다 정확히 두 장의 카드가 있습니다.

이제을 사용 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 개의 점이있는 가장 유명한 유한 투영법 (order-2) 평면 (형상)의 표현입니다.

여기에 이미지 설명을 입력하십시오

위의 order-2 평면이 7 장의 카드와 7 장의 그림으로 비슷한 퍼즐을 만드는 방법을 설명하는 이미지를 만들려고 생각했지만 math.exchange 쌍둥이 질문의 링크에는 정확히 같은 다이어그램이 있습니다 .Dobble-et- 라 기하학 피니

파노 비행기


9
이 게임은 비 유클리드 기하학을 보여줍니다. The Cards is 옳다고 말하는 것이 맞습니까?
RMorrisey 2016 년

2
이것은 굉장히 들리지만 실제로 문제를 잘 모델링한다는 확신은 없습니다. @ ypercube, 왜 카드 / 그림과 점 / 선의 비유가 유효하다고 생각하는지 조금 더 설명해 주시겠습니까?
Nate Kohl

@Nate : 첫 번째 유추 every two cards have exactly one picture in common는 질문에 나와 있습니다. 둘째 for every two pictures there is exactly one card that has both of them, OP는 게임 세트가 만족하는지 알려줍니다.
ypercubeᵀᴹ

4
멋진 답변! 게임이 Order-7 Projective Plane의 속성과 내가 본 평신도를위한 Projective Planes의 가장 좋은 설명 중 하나와 일치한다는 것을 깨달은 대단한 통찰력.
RBarryYoung

3
훌륭한. 파이썬에서 카드 세트를 생성하는 방법을 알아 내기 위해이 100 번을 더 읽어야합니다.
Jared

22

57 포인트로 투영 평면 형상을 그리는 데 어려움이있는 사람들에게는 57 카드와 57 기호 로이 게임을 구성하는 정말 훌륭하고 직관적 인 방법 이 있습니다 (이 질문에 대한 Yuval Filmus 의 답변을 기반으로 함 ).

  1. 8 개의 심볼이있는 카드의 경우 고유 한 심볼로 구성된 7x7 격자를 만듭니다.
  2. 0에서 6까지의 "기울기"에 대해 8 개의 기호를 추가하고 무한대 기울기에 대한 기호를 추가하십시오.
  3. 각 카드는 그리드의 선 (7 개 기호)에 선의 경사에 대해 설정된 경사에서 하나의 기호를 더한 것입니다. 선은 오프셋 (즉, 왼쪽의 시작점)과 기울기 (즉, 각 단계 오른쪽에 올라갈 심볼 수)가 있습니다. 선이 맨 위에 그리드를 떠나면 맨 아래에 다시 입력하십시오. 이러한 두 가지 카드에 대해서는이 예제 그림 ( boardgamegeek의 그림)을 참조하십시오 .

그리드에서 선으로 취한 두 개의 예제 카드 (빨간색 및 녹색)

이 예에서는 기울기 0 (빨간색)이있는 선 하나와 기울기 1 (녹색)이있는 선 하나를 사용합니다. 그들은 정확히 하나의 공통점 (올빼미)에서 교차합니다.

이 방법을 사용하면 두 카드에 정확히 하나의 공통 기호가 있어야합니다.

  1. 경사가 다른 경우 선은 항상 정확히 한 지점에서 교차합니다.
  2. 경사가 동일하면 선이 교차하지 않고 그리드에서 공통 기호가 없습니다. 이 경우 경사 기호는 동일합니다.

이런 식으로, 우리는 7x7 카드 (7 개의 오프셋과 7 개의 슬로프)를 구성 할 수 있습니다.

또한 세로선에서 그리드를 통해 7 개의 추가 카드를 구성 할 수도 있습니다 (즉, 각 열을 가져가는 경우). 이를 위해 무한대 기울기 아이콘이 사용됩니다.

각 카드는 그리드의 7 개 심볼과 정확히 하나의 "슬로프"심볼로 구성되므로 8 개의 슬로프 심볼 모두로 구성된 하나의 추가 카드를 만들 수 있습니다.

이것은 우리에게 7x8 + 1 = 57 가능한 카드와 7 x 7 + 8 = 57 필요한 기호를 남깁니다.

(기본적으로 이것은 소수 크기의 그리드에서만 작동합니다 (예 : n = 7). 그렇지 않으면 경사가 그리드 크기의 제수 인 경우 경사가 다른 선의 교차점이 0 개 이상일 수 있습니다.


18

따라서 총 n 장의 사진 에서 m = 8 장의 사진이 포함 된 k = 55 장의 카드가 있습니다. 우리는 '얼마나 많은 사진 문제를 다시 언급 할 수 없음 우리는 세트 구성 할 수 있도록, 우리가 필요로 할 k 개의 카드의 쌍 사이에 하나 개의 공유 사진과 함께 카드를?' 다음과 같이 질문하여 동등하게 :

감안할 때 N 차원 벡터 공간 정확하게 포함 된 모든 벡터, 세트 해요 요소가 하나의 다른 모든 제로인를 큰이 얼마나 N 우리가 세트 찾을 수 있도록 할 수 케이 누구의 페어 점 제품입니다 벡터를, 모두 1 과 같 습니까?

쌍을 만들 수 있는 정확히 ( n choose m ) 가능한 벡터가 있습니다. 따라서 ( n은 m을 선택 )> = k가 되도록 충분히 큰 n이 필요합니다 . 이것은 하한에 불과하므로 쌍별 호환성 제약 조건을 충족하려면 훨씬 높은 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

처음 몇 n에 대해 n 에서 선택할 수있는 다른 수의 사진에 대해 카드 당 m = 8 장의 호환 가능한 최대 카드 수는 다음과 같습니다.

이 무차별 강제 방법은 조합 폭발로 인해 멀지 않습니다. 그러나 나는 그것이 여전히 흥미로울 것이라고 생각했다.

흥미롭게도 주어진 m대해 kn 을 특정 n 까지만 증가 시킨 후 일정하게 유지되는 것으로 보입니다 .

즉, 카드 당 모든 사진 수에 대해 선택할 수있는 특정 수의 사진이 있으므로 가능한 최대 수의 법적 카드가됩니다. 최적의 수를 초과하여 선택하기 위해 더 많은 사진을 추가해도 더 이상 유효한 카드 수는 증가하지 않습니다.

처음 몇 최적 k 는 다음과 같습니다.

최적의 k 테이블


그것은 바운드에 대한 초기 시도 일뿐입니다. "pairwise dot products equal to 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에서 값을 취할 수 0p-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이 있습니다. 두 번째 카드에는 사진 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 값에 대해 하나의 그림 만 공통으로 공유 하는 P^2+P+1카드를 생성 합니다. 즉, 3 장의 사진 (p = 2의 경우), 13 장의 사진 (p = 3의 경우), 13 장의 31 장 (p = 5의 경우), 31 장의 57 장, 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

파이썬이 더 읽기 쉽기 때문에 파이썬에서 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 장의 카드는 다섯 번째 카드와 사진을 공유하지 않기 때문에 유효하지 않다고 생각합니다. 내가 깨닫기 전에 한 시간 이상 내 코드를 확인했습니다.
Thies Heidecke

1
@Gajet의 코드를 사용하여 생성 한 다이어그램을 사용하면 (p) + (p * p) + (1)구성 이 정확한 이유를 훨씬 쉽게 알 수 있습니다.
Neil G

1
@Neil : 업데이트 된 다이어그램 덕분에 Gajet 솔루션의 작동 방식을 훨씬 쉽게 확인할 수 있습니다!
Thies Heidecke

1
@Gajet :에 대해 잘못 생각합니다 all p except 4 and 6. 당신은 유한 비행기를 생산하려면 어디에가있는 p*p+p+1점 (카드 및 사진), 다음이에 관련이 라인 finite fields이 아니라 rings. pp가 primea 일 때 유한 순서 필드가 있습니다 prime power. 표정이 좋아하기 때문에 코드는 소수 제대로 작동 k * p + (j + i * k) % p표현하는 k*p + j + i*k순서의 유한 체의 곱셈과 추가의 측면에서 p.
ypercubeᵀᴹ

1
당신이 순서의 유한 분야에서 이러한 작업 (. 멀티 포트 및 추가)를 표현할 수있는 경우는, 너무 소수의 힘에 대해 올바르게 작동 p^2, p^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
  • 두 행은 공통으로 정확히 하나의 기호를 가져야합니다.

그것은 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.>


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포함 사진 2, 13, 22, ..., 59, 68, 및
  • 사진을 72카드에 나타납니다 2, 13, 22, ..., 59하고 68.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.