Row sum in PIVOT query - oracle

Please Help me i am trying to create pivot but not success.
query
select
t.g_no,
t.sz,
t.DT,
t.Qty
from gp123 t
Result
4480 46 4/24/2017 30
4480 42 4/24/2017 28
4480 40 4/24/2017 37
4480 44 4/24/2017 26
4480 50 4/24/2017 17
4480 48 4/24/2017 2
Required result
Gate Pass No. Date 40 42 44 46 48 50 Total
4480 24-Apr-17 37 28 26 30 2 17 140
4500 25-Apr-17 187 140 155 127 99 85 793
4537 25-Apr-17 141 97 139 172 141 159 849
4538 26-Apr-17 90 141 122 148 172 151 824
4542 26-Apr-17 1 60 118 63 32 3 277

Use the SUM() OVER () analytic function to get the total and then use PIVOT to convert the rows to columns:
SELECT *
FROM ( SELECT g_no AS "Gate Pass No.",
sz,
DT AS "Date",
Qty,
SUM( qty ) OVER ( PARTITION BY g_no, DT ) AS Total
FROM gp123
)
PIVOT ( SUM( qty ) FOR sz IN ( 40, 42, 44, 46, 48, 50 ) )

... or simply:
select g_no, dt, "40", "42", "44", "46", "48", "50",
"40" + "42" + "44" + "46" + "48" + "50" total
from gp123 t
pivot (sum(qty) for sz in (40, 42, 44, 46, 48, 50));

Related

An allocation problem of people into groups getting them to meet as little as possible

I have been trying to solve this for quite a while without success.
I have not found (I searched though) theory that could help me on wikipedia.
Here is the problem.
I have a group of n players (more than 7)
I have a game (diplomacy for those who know !) that requires 7 players, one for these roles : E,F,G,I,A,R and T (countries in fact)
I want to set up a tournament (many games).
There will be n games.
(*) Every player gets into 7 different games, with different role each time
(**) Every game gets 7 different players
=> That is very easy to do.
However, when things get tough, is when you want to limit interactions between players.
What I want is any player to interact (interact = play in same game) at most with one other player.
(In other words, I want to prevent players from making such deals : "I help you in game A, you help me in game B")
So:
Question 1 : For which n is this possible ? (obviously at least 50)
Question 2 : When it is possible, how do you do it ?
Question 3 : What is the algo to minimize these interactions when it is not possible ?
For the record, I did implement a try-and-error program in python (using recursion), working quite well, but I never can get maximum intearctions between players limited to 1 (endless calculations)
thanks for any help !
PS This is no homework, it is for actually designing game tournaments :-)
I did some doodling and I think; If I understand you correctly, that you can do the following:
28 people to do the 7 roles/7 games meeting other players only once.
If the position of a person in the following games is allocated to distinct roles then no person plays the same role in the games they play.
Python
# -*- coding: utf-8 -*-
"""
https://stackoverflow.com/questions/71143133/an-allocation-problem-of-people-into-groups-getting-them-to-meet-as-little-as-po
Created on Sat Feb 19 21:15:02 2022
#author: paddy3118
By hand to get the algo:
0 roles, 0 people for 0 interactions
1 role, 1 person
2 roles, 3 people: p0+p1, p1+p2
3 roles, 012, 134, 235 = 6 people
4 roles, 0123, 1456, 2478, 3579 = 10 people
"""
from itertools import count
def new_person():
yield from count()
def people_allocation(roles: int=4):
games, new_p = [], count()
for i in range(roles):
game = []
games.append(game)
for j in range(i):
game.append(games[j][i])
for _ in range(i, roles):
game.append(next(new_p))
return games, next(new_p)
for roles in range(8):
print(f"Roles = {roles}:")
games, people = people_allocation(roles)
print(f" Takes {people} people in the following games")
print(' ',
', '.join('+'.join(f"P{x}" for x in game) for game in games))
Output
Roles = 0:
Takes 0 people in the following games
Roles = 1:
Takes 1 people in the following games
P0
Roles = 2:
Takes 3 people in the following games
P0+P1, P1+P2
Roles = 3:
Takes 6 people in the following games
P0+P1+P2, P1+P3+P4, P2+P4+P5
Roles = 4:
Takes 10 people in the following games
P0+P1+P2+P3, P1+P4+P5+P6, P2+P5+P7+P8, P3+P6+P8+P9
Roles = 5:
Takes 15 people in the following games
P0+P1+P2+P3+P4, P1+P5+P6+P7+P8, P2+P6+P9+P10+P11, P3+P7+P10+P12+P13, P4+P8+P11+P13+P14
Roles = 6:
Takes 21 people in the following games
P0+P1+P2+P3+P4+P5, P1+P6+P7+P8+P9+P10, P2+P7+P11+P12+P13+P14, P3+P8+P12+P15+P16+P17, P4+P9+P13+P16+P18+P19, P5+P10+P14+P17+P19+P20
Roles = 7:
Takes 28 people in the following games
P0+P1+P2+P3+P4+P5+P6, P1+P7+P8+P9+P10+P11+P12, P2+P8+P13+P14+P15+P16+P17, P3+P9+P14+P18+P19+P20+P21, P4+P10+P15+P19+P22+P23+P24, P5+P11+P16+P20+P23+P25+P26, P6+P12+P17+P21+P24+P26+P27
Assuming that n ≥ 78 or so, the following simple hill climbing algorithm with periodic restarts will return a solution.
The algorithmic idea is to initialize games where each player plays each role exactly once, then drive the number of conflicts to zero (where a conflict is a player playing two roles in a single game, or two players meeting each other more than once) by choosing two random games and a random role and swapping the players involved. We restart every 107 steps because that seems to work well in practice.
Doubtless we could do a little better with constraint programming.
#include <algorithm>
#include <array>
#include <cstdlib>
#include <iostream>
#include <random>
#include <vector>
constexpr int r = 7;
int main() {
int n;
std::cin >> n;
if (n <= r * (r - 1)) {
return EXIT_FAILURE;
}
std::uniform_int_distribution<int> uniform_game(0, n - 1);
std::uniform_int_distribution<int> uniform_role(0, r - 1);
std::random_device device;
std::default_random_engine engine(device());
while (true) {
std::vector<std::array<int, r>> games(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < r; j++) {
games[i][j] = (i + j) % n;
}
}
int badness = 0;
std::vector<std::vector<int>> pair_counts(n, std::vector<int>(n, 0));
auto count = [&badness, &games, &pair_counts](int i, int j, int increment) {
for (int k = 0; k < r; k++) {
if (k == j) continue;
auto [a, b] = std::minmax(games[i][j], games[i][k]);
badness -= pair_counts[a][b] > (a != b ? 2 : 0);
pair_counts[a][b] += increment;
badness += pair_counts[a][b] > (a != b ? 2 : 0);
}
};
for (int i = 0; i < n; i++) {
for (int j = 0; j < r; j++) {
count(i, j, 1);
}
}
for (long t = 0; t < 10000000; t++) {
int i1;
int i2;
do {
i1 = uniform_game(engine);
i2 = uniform_game(engine);
} while (i1 == i2);
int j = uniform_role(engine);
auto swap_players = [&]() {
count(i2, j, -2);
count(i1, j, -2);
std::swap(games[i1][j], games[i2][j]);
count(i1, j, 2);
count(i2, j, 2);
};
int old_badness = badness;
swap_players();
if (old_badness < badness) {
swap_players();
} else if (badness < old_badness) {
std::cerr << badness << '\n';
}
if (badness <= 0) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < r; j++) {
if (j) std::cout << ' ';
std::cout << games[i][j];
}
std::cout << '\n';
}
return EXIT_SUCCESS;
}
}
}
}
Sample output:
21 38 61 75 77 2 22
70 31 75 7 15 59 69
28 52 29 73 59 23 40
61 45 16 65 35 15 55
12 72 44 45 46 14 10
57 1 3 38 11 6 49
20 6 7 26 0 74 18
54 73 67 58 6 55 75
73 77 63 36 3 0 45
37 57 55 28 34 43 7
17 46 36 66 16 7 48
74 9 24 22 17 73 15
36 50 4 69 28 65 6
59 62 12 32 24 20 51
38 8 59 17 10 19 39
6 53 21 70 13 71 56
55 33 49 59 5 27 36
15 71 33 54 43 18 29
60 36 8 40 71 51 67
19 49 9 34 45 53 60
41 26 73 21 72 35 19
14 64 42 15 57 63 62
44 11 23 27 9 16 21
2 7 68 9 63 52 54
35 18 2 3 60 64 17
29 17 50 41 31 61 57
47 10 32 25 75 28 35
30 48 64 6 32 39 44
46 22 71 35 20 31 11
43 76 41 11 47 60 14
56 2 1 33 74 10 37
51 41 39 0 33 70 34
32 5 18 31 23 76 68
65 43 53 8 27 46 73
63 44 26 5 70 24 28
62 75 66 71 44 3 41
16 69 54 13 22 41 5
58 19 52 57 36 22 70
42 25 22 44 55 56 76
4 30 35 51 76 13 9
72 13 17 62 40 77 43
53 16 10 68 50 58 64
64 47 70 55 38 40 20
50 59 14 48 1 9 71
40 63 19 76 69 1 12
24 35 69 56 68 57 27
67 34 15 72 56 66 32
68 3 46 37 25 21 59
77 54 47 19 4 44 31
76 67 38 46 52 50 33
25 15 77 1 51 26 23
3 61 40 4 53 5 74
45 51 74 29 48 68 47
75 0 57 23 30 8 72
13 27 28 14 19 67 2
9 20 72 42 65 33 3
49 58 48 20 41 37 77
22 12 37 67 39 47 53
26 42 11 61 37 36 13
1 60 5 39 29 75 46
48 40 65 10 54 34 26
23 66 60 50 42 54 24
8 74 13 63 49 32 50
27 29 58 12 7 38 42
7 4 45 24 64 25 8
71 24 30 47 2 49 16
31 28 56 60 12 48 0
10 23 62 49 67 69 61
34 21 31 30 58 62 1
66 70 27 18 61 30 25
0 37 76 64 66 29 65
69 39 43 2 26 45 58
39 65 25 74 62 11 52
5 56 20 52 14 17 30
33 68 6 77 8 12 66
11 55 51 53 18 72 63
52 32 0 43 21 42 4
18 14 34 16 73 4 38
Eventually I think I solved this problem.
I explain it here to help any other person facing such a problem.
I used two "classical" algorithms.
try-and-error to get a first configuration of low quality, with iterations that tries first those players with fewest intearctions and which are already in more games
hill-climibing to improve quality of configuration (making swaps between a conflicting and not conflicting player, or two conflicting players if all players are conflicting) and selecting randomly, keeping the result of swap only if it increases quality - quality is worst number of conflicts (2 usually) and number of occurences)
I reach the following conclusion :
always a solution above 100
never a solution below 90
Thanks for all support you provided !

how to sort the numbers 1, ... n lexicographically without converting the numbers to string?

Assume I have input n how I can print this sequence without convert the number to string?
i.e:-
n = 100
Output :- 1 10 100 11 12 13 14 15 16 17 18 19 2 20 ...
n = 15 Output :- 1 10 11 12 13 14 15 2 3 4 5 6 7 8 9
n = 20 Output :- 1 10 11 12 13 15 15 16 17 18 19 2 20 3 4 5 6 7 8 9
What is the main factor here?
My initial solution that will print the 1's followed by 0's or 2's followed by 0's
int n = 100;
for (int i = 1; i <= n; i++) {
int x = i;
while (x <= n) {
System.out.println(x);
x *= 10;
}
}
I figured out the solution:-
You can call it with initial k = 1
for example printnum(15, 1
void printnums(int n, int k) {
if (k > n) {
return;
}
for (int i = 0; i < 10; i++) {
if (k <= n) {
System.out.println(k);
k *= 10;
printnums(n, k);
k /= 10;
k++;
if (k % 10 == 0) return;
}
}
}
I don't know if there are more optimized one?
Here a condensed solution in Python based on the OP's own answer:
def genRangeLexiSorted(n, k=1):
for i in range(k, min(k+10-k%10, n+1)):
yield i
for j in genRangeLexiSorted(n, 10*i):
yield j
def printnums(n):
print(*list(genRangeLexiSorted(n)))
Then the calls
printnums(1)
printnums(9)
printnums(11)
printnums(20)
printnums(100)
give the following outputs:
1
1 2 3 4 5 6 7 8 9
1 10 11 2 3 4 5 6 7 8 9
1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9
1 10 100 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 26 27 28 29 3 30 31 32 33 34 35 36 37 38 39 4 40 41 42 43 44 45 46 47 48 49 5 50 51 52 53 54 55 56 57 58 59 6 60 61 62 63 64 65 66 67 68 69 7 70 71 72 73 74 75 76 77 78 79 8 80 81 82 83 84 85 86 87 88 89 9 90 91 92 93 94 95 96 97 98 99

How i can improve the code?

I'm beginner in programming. Could i use two elements in golang for-loops? If you know answer, or material which i should read, please help me.
package main
import (
"fmt"
)
func main() {
x := []int{
48, 96, 86, 68,
57, 82, 63, 70,
37, 34, 83, 27,
19, 97, 9, 17,
}
for a := 0, b := 1; a++, b++ {
if x[a] > x[b] {
x = append(x[:1], x[1+1:]...)
fmt.Println("x[1+1:]x)", x)
} else {
x = append(x[:0], x[0+1:]...)
fmt.Println("x[0+1:]x)", x)
}
}
}
Yes, you can, although it's unnecessary in this case.
The syntax function fixes the syntax and other errors in your code so that your code runs and produces output. The idiom function rewrites your code in a more idiomatic form.
package main
import (
"fmt"
)
func syntax() {
x := []int{
48, 96, 86, 68,
57, 82, 63, 70,
37, 34, 83, 27,
19, 97, 9, 17,
}
for a, b := 0, 1; a < len(x) && b < len(x); a, b = a+1, b+1 {
if x[a] > x[b] {
x = append(x[:1], x[1+1:]...)
fmt.Println("x[1+1:]x)", x)
} else {
x = append(x[:0], x[0+1:]...)
fmt.Println("x[0+1:]x)", x)
}
}
}
func idiom() {
x := []int{
48, 96, 86, 68,
57, 82, 63, 70,
37, 34, 83, 27,
19, 97, 9, 17,
}
for i := 1; i < len(x); i++ {
if x[i-1] > x[i] {
x = append(x[:1], x[1+1:]...)
fmt.Println("x[1+1:]x)", x)
} else {
x = append(x[:0], x[0+1:]...)
fmt.Println("x[0+1:]x)", x)
}
}
}
func main() {
syntax()
fmt.Println()
idiom()
}
Output:
$ go run beginner.go
x[0+1:]x) [96 86 68 57 82 63 70 37 34 83 27 19 97 9 17]
x[1+1:]x) [96 68 57 82 63 70 37 34 83 27 19 97 9 17]
x[0+1:]x) [68 57 82 63 70 37 34 83 27 19 97 9 17]
x[0+1:]x) [57 82 63 70 37 34 83 27 19 97 9 17]
x[1+1:]x) [57 63 70 37 34 83 27 19 97 9 17]
x[1+1:]x) [57 70 37 34 83 27 19 97 9 17]
x[0+1:]x) [70 37 34 83 27 19 97 9 17]
x[0+1:]x) [37 34 83 27 19 97 9 17]
x[0+1:]x) [96 86 68 57 82 63 70 37 34 83 27 19 97 9 17]
x[1+1:]x) [96 68 57 82 63 70 37 34 83 27 19 97 9 17]
x[0+1:]x) [68 57 82 63 70 37 34 83 27 19 97 9 17]
x[0+1:]x) [57 82 63 70 37 34 83 27 19 97 9 17]
x[1+1:]x) [57 63 70 37 34 83 27 19 97 9 17]
x[1+1:]x) [57 70 37 34 83 27 19 97 9 17]
x[0+1:]x) [70 37 34 83 27 19 97 9 17]
x[0+1:]x) [37 34 83 27 19 97 9 17]
Follow the link to take A Tour of Go.

return resultset from function

I need to return a resultset from function and work with this resultset just like with an ordinary table.
So I need something like following:
select * from table(querydb('select * from dual'))
The querydb function should return a resultset of a query passed to it.
Can it be implemented in oracle?
Would be grateful for any information.
If you need a result set and a ref cursor won't do with a datatype called sys.anydataset. i.e what you seem to want is a pipelined function, but of course with a regular pipelined function you need to define the output structure, which in your case isn't static.
Enter anydataset. this type allows us to dynamically generate types on the fly (at hard parse time only) to allow us to define pipelined functions with varying outputs.
The coding is a bit complex unfortunately.
To start with, we define a type that will do the processing of the passed in SQL statement.
SQL> create type dyn_pipeline as object
2 (
3 atype anytype,
4
5 static function ODCITableDescribe(rtype out anytype,
6 stmt in varchar2)
7 return number,
8
9 static function ODCITablePrepare(sctx out dyn_pipeline,
10 tf_info in sys.ODCITabfuncinfo,
11 stmt in varchar2)
12 return number,
13
14 static function ODCITableStart(sctx in out dyn_pipeline,
15 stmt in varchar2)
16 return number,
17
18 member function ODCITablefetch(self in out dyn_pipeline,
19 nrows in number,
20 rws out anydataset)
21 return number,
22
23 member function ODCITableClose(self in dyn_pipeline)
24 return number
25 );
26 /
Next up, we create a package spec that will be basically your querydb function call:
SQL> create package pkg_pipeline
2 as
3
4 /*
5 * Global Types
6 */
7 -- Describe array.
8 type dynamic_sql_rec is record(cursor integer,
9 column_cnt pls_integer,
10 description dbms_sql.desc_tab2,
11 execute integer);
12 -- Meta data for the ANYTYPE.
13 type anytype_metadata_rec is record(precision pls_integer,
14 scale pls_integer,
15 length pls_integer,
16 csid pls_integer,
17 csfrm pls_integer,
18 schema varchar2(30),
19 type anytype,
20 name varchar2(30),
21 version varchar2(30),
22 attr_cnt pls_integer,
23 attr_type anytype,
24 attr_name varchar2(128),
25 typecode pls_integer);
26
27
28 /*
29 * Global Variables
30 */
31 -- SQL descriptor.
32 r_sql dynamic_sql_rec;
33
34 /*
35 * function will run the given SQL
36 */
37 function querydb(p_stmt in varchar2)
38 return anydataset pipelined using dyn_pipeline;
39
40 end pkg_pipeline;
41 /
Package created.
the types there will just hold some info about the SQL structure itself (we will be using DBMS_SQL to describe the input SQL as it has functions to get the number of columns, data types etc out of any given SQL statement.
The main type body is where the processing occurs:
SQL> create type body dyn_pipeline
2 as
3
4 /*
5 * DESC step. this will be called at hard parse and will create
6 * a physical type in the DB Schema based on the select columns.
7 */
8 static function ODCITableDescribe(rtype out anytype,
9 stmt in varchar2)
10 return number
11 is
12
13 /* Variables */
14 -- Type to hold the dbms_sql info (description)
15 r_sql pkg_pipeline.dynamic_sql_rec;
16 -- Type to create (has all the columns) of the sql query.
17 t_anyt anytype;
18 -- SQL query that will be made up from the 2 passed in queries.
19 v_sql varchar2(32767);
20
21 begin
22
23 /*
24 * Parse the SQL and describe its format and structure.
25 */
26 v_sql := replace(stmt, ';', null);
27
28 -- open, parse and discover all info about this SQL.
29 r_sql.cursor := dbms_sql.open_cursor;
30 dbms_sql.parse( r_sql.cursor, v_sql, dbms_sql.native );
31 dbms_sql.describe_columns2( r_sql.cursor, r_sql.column_cnt, r_sql.description );
32 dbms_sql.close_cursor( r_sql.cursor );
33
34 -- Start to create the physical type.
35 anytype.BeginCreate( DBMS_TYPES.TYPECODE_OBJECT, t_anyt );
36
37 -- Loop through each attribute and add to the type.
38 for i in 1 .. r_sql.column_cnt
39 loop
40
41 t_anyt.AddAttr(r_sql.description(i).col_name,
42 case
43 when r_sql.description(i).col_type in (1,96,11,208)
44 then dbms_types.typecode_varchar2
45 when r_sql.description(i).col_type = 2
46 then dbms_types.typecode_number
47 when r_sql.description(i).col_type in (8,112)
48 then dbms_types.typecode_clob
49 when r_sql.description(i).col_type = 12
50 then dbms_types.typecode_date
51 when r_sql.description(i).col_type = 23
52 then dbms_types.typecode_raw
53 when r_sql.description(i).col_type = 180
54 then dbms_types.typecode_timestamp
55 when r_sql.description(i).col_type = 181
56 then dbms_types.typecode_timestamp_tz
57 when r_sql.description(i).col_type = 182
58 then dbms_types.typecode_interval_ym
59 when r_sql.description(i).col_type = 183
60 then dbms_types.typecode_interval_ds
61 when r_sql.description(i).col_type = 231
62 then dbms_types.typecode_timestamp_ltz
63 end,
64 r_sql.description(i).col_precision,
65 r_sql.description(i).col_scale,
66 r_sql.description(i).col_max_len,
67 r_sql.description(i).col_charsetid,
68 r_sql.description(i).col_charsetform );
69 end loop;
70
71 t_anyt.EndCreate;
72
73 -- set the output type to our built type.
74 ANYTYPE.BeginCreate(dbms_types.TYPECODE_TABLE, rtype);
75 rtype.SetInfo(null, null, null, null, null, t_anyt,
76 dbms_types.TYPECODE_OBJECT, 0);
77 rtype.EndCreate();
78
79 return ODCIConst.Success;
80
81 end ODCITableDescribe;
82
83
84 /*
85 * PREPARE step. Initialise our type.
86 */
87 static function ODCITableprepare(sctx out dyn_pipeline,
88 tf_info in sys.ODCITabfuncinfo,
89 stmt in varchar2)
90 return number
91 is
92
93 /* Variables */
94 -- Meta data.
95 r_meta pkg_pipeline.anytype_metadata_rec;
96
97 begin
98
99 r_meta.typecode := tf_info.rettype.getattreleminfo(
100 1, r_meta.precision, r_meta.scale, r_meta.length,
101 r_meta.csid, r_meta.csfrm, r_meta.type, r_meta.name
102 );
103
104 sctx := dyn_pipeline(r_meta.type);
105 return odciconst.success;
106
107 end;
108
109
110 /*
111 * START step. this is where we execute the cursor prior to fetching from it.
112 */
113 static function ODCITablestart(sctx in out dyn_pipeline,
114 stmt in varchar2)
115 return number
116 is
117
118 /* Variables */
119 r_meta pkg_pipeline.anytype_metadata_rec;
120 v_sql varchar2(32767);
121 begin
122
123 v_sql := replace(stmt, ';', null);
124 pkg_pipeline.r_sql.cursor := dbms_sql.open_cursor;
125 dbms_sql.parse(pkg_pipeline.r_sql.cursor, v_sql, dbms_sql.native);
126 dbms_sql.describe_columns2(pkg_pipeline.r_sql.cursor,
127 pkg_pipeline.r_sql.column_cnt,
128 pkg_pipeline.r_sql.description);
129
130 -- define all the columns found to let Oracle know the datatypes.
131 for i in 1..pkg_pipeline.r_sql.column_cnt
132 loop
133
134 r_meta.typecode := sctx.atype.GetAttrElemInfo(
135 i, r_meta.precision, r_meta.scale, r_meta.length,
136 r_meta.csid, r_meta.csfrm, r_meta.type, r_meta.name
137 );
138
139 case r_meta.typecode
140 when dbms_types.typecode_varchar2
141 then
142 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, '', 32767);
143 when dbms_types.typecode_number
144 then
145 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as number));
146 when dbms_types.typecode_date
147 then
148 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as date));
149 when dbms_types.typecode_raw
150 then
151 dbms_sql.define_column_raw(pkg_pipeline.r_sql.cursor, i, cast(null as raw), r_meta.length);
152 when dbms_types.typecode_timestamp
153 then
154 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as timestamp));
155 when dbms_types.typecode_timestamp_tz
156 then
157 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as timestamp with time zone));
158 when dbms_types.typecode_timestamp_ltz
159 then
160 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as timestamp with local time zone));
161 when dbms_types.typecode_interval_ym
162 then
163 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as interval year to month));
164 when dbms_types.typecode_interval_ds
165 then
166 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as interval day to second));
167 when dbms_types.typecode_clob
168 then
169 case pkg_pipeline.r_sql.description(i).col_type
170 when 8
171 then
172 dbms_sql.define_column_long(pkg_pipeline.r_sql.cursor, i);
173 else
174 dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as clob));
175 end case;
176 end case;
177 end loop;
178
179 -- execute the SQL.
180 pkg_pipeline.r_sql.execute := dbms_sql.execute(pkg_pipeline.r_sql.cursor);
181
182 return odciconst.success;
183
184 end ODCITablestart;
185
186
187 /*
188 * FETCH step.
189 */
190 member function ODCITablefetch(self in out dyn_pipeline,
191 nrows in number,
192 rws out anydataset)
193 return number
194 is
195
196 /* Variables */
197 -- Buffers to hold values.
198 v_vc_col varchar2(32767);
199 v_num_col number;
200 v_date_col date;
201 v_raw_col raw(32767);
202 v_raw_error number;
203 v_raw_len integer;
204 v_int_ds_col interval day to second;
205 v_int_ym_col interval year to month;
206 v_ts_col timestamp;
207 v_tstz_col timestamp with time zone;
208 v_tsltz_col timestamp with local time zone;
209 v_clob_col clob;
210 v_clob_offset integer := 0;
211 v_clob_len integer;
212 -- Metadata
213 r_meta pkg_pipeline.anytype_metadata_rec;
214
215 begin
216
217 if dbms_sql.fetch_rows( pkg_pipeline.r_sql.cursor ) > 0
218 then
219
220 -- Describe to get number and types of columns.
221 r_meta.typecode := self.atype.getinfo(
222 r_meta.precision, r_meta.scale, r_meta.length,
223 r_meta.csid, r_meta.csfrm, r_meta.schema,
224 r_meta.name, r_meta.version, r_meta.attr_cnt
225 );
226
227 anydataset.begincreate(dbms_types.typecode_object, self.atype, rws);
228 rws.addinstance();
229 rws.piecewise();
230
231 -- loop through each column extracting value.
232 for i in 1..pkg_pipeline.r_sql.column_cnt
233 loop
234
235 r_meta.typecode := self.atype.getattreleminfo(
236 i, r_meta.precision, r_meta.scale, r_meta.length,
237 r_meta.csid, r_meta.csfrm, r_meta.attr_type,
238 r_meta.attr_name
239 );
240
241 case r_meta.typecode
242 when dbms_types.typecode_varchar2
243 then
244 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_vc_col);
245 rws.setvarchar2(v_vc_col);
246 when dbms_types.typecode_number
247 then
248 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_num_col);
249 rws.setnumber(v_num_col);
250 when dbms_types.typecode_date
251 then
252 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_date_col);
253 rws.setdate(v_date_col);
254 when dbms_types.typecode_raw
255 then
256 dbms_sql.column_value_raw(pkg_pipeline.r_sql.cursor, i, v_raw_col,
257 v_raw_error, v_raw_len);
258 rws.setraw(v_raw_col);
259 when dbms_types.typecode_interval_ds
260 then
261 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_int_ds_col);
262 rws.setintervalds(v_int_ds_col);
263 when dbms_types.typecode_interval_ym
264 then
265 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_int_ym_col);
266 rws.setintervalym(v_int_ym_col);
267 when dbms_types.typecode_timestamp
268 then
269 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_ts_col);
270 rws.settimestamp(v_ts_col);
271 when dbms_types.typecode_timestamp_tz
272 then
273 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_tstz_col);
274 rws.settimestamptz(v_tstz_col);
275 when dbms_types.typecode_timestamp_ltz
276 then
277 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_tsltz_col);
278 rws.settimestampltz(v_tsltz_col);
279 when dbms_types.typecode_clob
280 then
281 case pkg_pipeline.r_sql.description(i).col_type
282 when 8
283 then
284 loop
285 dbms_sql.column_value_long(pkg_pipeline.r_sql.cursor, i, 32767, v_clob_offset,
286 v_vc_col, v_clob_len);
287 v_clob_col := v_clob_col || v_vc_col;
288 v_clob_offset := v_clob_offset + 32767;
289 exit when v_clob_len < 32767;
290 end loop;
291 else
292 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_clob_col);
293 end case;
294 rws.setclob(v_clob_col);
295 end case;
296 end loop;
297
298 rws.endcreate();
299
300 end if;
301
302 return ODCIConst.Success;
303
304 end;
305
306 /*
307 * CLOSE step. close the cursor.
308 */
309 member function ODCITableClose(self in dyn_pipeline)
310 return number
311 is
312
313
314 begin
315 dbms_sql.close_cursor( pkg_pipeline.r_sql.cursor );
316 pkg_pipeline.r_sql := null;
317 return odciconst.success;
318 end ODCITableClose;
319
320 end;
321 /
Type body created.
once this is done, you can query like:
SQL> select * from table(pkg_pipeline.querydb('select * from dual'));
D
-
X
SQL> select * from table(pkg_pipeline.querydb('select * from v$mystat where rownum <= 2'));
SID STATISTIC# VALUE
---------- ---------- ----------
230 0 1
230 1 1
If I understand you correctly, you want to do this:
select * from (select * from dual)
...with the caveat that the subquery is in some way dynamic? You can do this using a PL/SQL block, using a reference cursor:
declare
subQuery varchar2(1000);
mainQuery varchar2(1000) := 'select * from (';
type myRefCursor is ref cursor;
myResultset myRefCursor;
myField1 FIELDTYPE;
...
myFieldN FIELDTYPE;
begin
-- Generate this dynamically
subQuery := 'select * from dual';
-- Create main query and open cursor
mainQuery := mainQuery || subQuery || ')';
open myResultset for mainQuery;
-- Loop through records
loop
fetch myResultset into myField1, ..., myFieldN;
exit when myResultset%NOTFOUND;
-- Do something with the record data
dbms_output.put_line(myField1 || ' ... ' || myFieldN);
end loop;
close myResultset;
end;
/
Note that rather than using fetch into with individual variables, you can populate an entire record at once, provided you can define the record's field types. That is, if you have created a custom type or your record's type matches a table you already have in your schema. For the latter, you can use:
myRecord someTable%ROWTYPE;
...in the declaration block, then change the fetch into statement to:
fetch myResultset into myRecord;
...and access record fields using dot notation (e.g., myRecord.some_field_name).
You say in your comments that the dynamic SQL bit is from the results of some other query. Therefore, in my example code, you could use a regular cursor to loop over these data to create the subQuery variable in each instance... Although, incidentally, is there any reason why what you're trying to achieve can't be done with a simple join?

What are the mathematical/computational principles behind this game?

My kids have this fun game called Spot It! The game constraints (as best I can describe) are:
It is a deck of 55 cards
On each card are 8 unique pictures (i.e. a card can't have 2 of the same picture)
Given any 2 cards chosen from the deck, there is 1 and only 1 matching picture.
Matching pictures may be scaled differently on different cards but that is only to make the game harder (i.e. a small tree still matches a larger tree)
The principle of the game is: flip over 2 cards and whoever first picks the matching picture gets a point.
Here's a picture for clarification:
(Example: you can see from the bottom 2 cards above that the matching picture is the green dinosaur. Between the bottom-right and middle-right picture, it's a clown's head.)
I'm trying to understand the following:
What are the minimum number of different pictures required to meet these criteria and how would you determine this?
Using pseudocode (or Ruby), how would you generate 55 game cards from an array of N pictures (where N is the minimum number from question 1)?
Update:
Pictures do occur more than twice per deck (contrary to what some have surmised). See this picture of 3 cards, each with a lightning bolt:
Finite Projective Geometries
The axioms of projective (plane) geometry are slightly different than the Euclidean geometry:
Every two points have exactly one line that passes through them (this is the same).
Every two lines meet in exactly one point (this is a bit different from Euclid).
Now, add "finite" into the soup and you have the question:
Can we have a geometry with just 2 points? With 3 points? With 4? With 7?
There are still open questions regarding this problem but we do know this:
If there are geometries with Q points, then Q = n^2 + n + 1 and n is called the order of the geometry.
There are n+1 points in every line.
From every point, pass exactly n+1 lines.
Total number of lines is also Q.
And finally, if n is prime, then there does exists a geometry of order n.
What does that have to do with the puzzle, one may ask.
Put card instead of point and picture instead of line and the axioms become:
Every two cards have exactly one picture in common.
For every two pictures there is exactly one card that has both of them.
Now, lets take n=7 and we have the order-7 finite geometry with Q = 7^2 + 7 + 1 . That makes Q=57 lines (pictures) and Q=57 points (cards). I guess the puzzle makers decided that 55 is more round number than 57 and left 2 cards out.
We also get n+1 = 8, so from every point (card), 8 lines pass (8 pictures appear) and every line (picture) has 8 points (appears in 8 cards).
Here's a representation of the most famous finite projective (order-2) plane (geometry) with 7 points, known as Fano Plane, copied from Noelle Evans - Finite Geometry Problem Page
I was thinking of creating an image that explain how the above order-2 plane could be made a similar puzzle with 7 cards and 7 pictures, but then a link from the math.exchange twin question has exactly such a diagram: Dobble-et-la-geometrie-finie
For those who have trouble picturing the projective plane geometry with 57 points, there is a really nice, intuitive way to construct the game with 57 cards and 57 symbols (based on the answer by Yuval Filmus for this question):
For cards with 8 symbols, create a 7x7 grid of unique symbols.
Add an additional 8 symbols for the "slopes" from 0 to 6, plus one for infinity slope.
Each card is a line on the grid (7 symbols) plus one symbol from the slope set for the slope of the line. Lines have an offset (i.e. starting point on the left), and a slope (i.e. how many symbols to go up for each step right). When the line leaves the grid at the top, re-enter at the bottom. See this example figure (pictures from boardgamegeek) for two such cards:
In the example, I take one line with slope zero (red), and one with slope 1 (green). They intersect at exactly one common point (the owl).
This method ensures that any two cards have exactly one common symbol, because
If the slopes are different, then the lines will always intersect at exactly one point.
If the slopes are the same, then the lines will not intersect and there will be no common symbol from the grid. In this case, the slope symbol will be the same.
In this way, we can construct 7x7 cards (7 offsets and 7 slopes).
We can also construct seven additional cards from vertical lines through the grid (i.e. taking each column). For those, the infinity slope icon is used.
Because each card consists of seven symbols from the grid and exactly one "slope" symbol, we can create one additional card, which simply consists of all the 8 slope symbols.
This leaves us with 7x8 + 1 = 57 possible cards, and 7 x 7 + 8 = 57 required symbols.
(Naturally, this only works with a prime-number-sized grid (e.g. n=7). Otherwise, lines of different slope could have zero or more than one intersection if the slope is a divisor of the grid size.)
So there are k=55 cards containing m=8 pictures each from a pool of n pictures total.
We can restate the question 'How many pictures n do we need, so that we can construct a set of k cards with only one shared picture between any pair of cards?' equivalently by asking:
Given an n-dimensional vector space and the set of all vectors, which contain exactly m elements equal to one and all other zero, how big has n to be, so that we can find a set of k vectors, whose pairwise dot products are all equal to 1?
There are exactly (n choose m) possible vectors to build pairs from. So we at least need a big enough n so that (n choose m) >= k. This is just a lower bound, so for fulfilling the pairwise compatibility constraint we possibly need a much higher n.
Just for experimenting a bit i wrote a small Haskell program to calculate valid card sets:
Edit: I just realized after seeing Neil's and Gajet's solution, that the algorithm i use doesn't always find the best possible solution, so everything below isn't necessarily valid. I'll try to update my code soon.
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
The resulting maximum number of compatible cards for m=8 pictures per card for different number of pictures to choose from n for the first few n looks like this:
This brute force method doesn't get very far though because of combinatorial explosion. But i thought it might still be interesting.
Interestingly, it seems that for given m, k increases with n only up to a certain n, after which it stays constant.
This means, that for every number of pictures per card there is a certain number of pictures to choose from, that results in maximum possible number of legal cards. Adding more pictures to choose from past that optimal number doesn't increase the number of legal cards any further.
The first few optimal k's are:
Others have described the general framework for the design (finite projective plane) and shown how to generate finite projective planes of prime order. I would just like to fill in some gaps.
Finite projective planes can be generated for many different orders, but they are most straightforward in the case of prime order p. Then the integers modulo p form a finite field which can be used to describe coordinates for the points and lines in the plane. There are 3 different kinds of coordinates for points: (1,x,y), (0,1,x), and (0,0,1), where x and y can take on values from 0 to p-1. The 3 different kinds of points explains the formula p^2+p+1 for the number of points in the system. We can also describe lines with the same 3 different kinds of coordinates: [1,x,y], [0,1,x], and [0,0,1].
We compute whether a point and line are incident by whether the dot product of their coordinates is equal to 0 mod p. So for example the point (1,2,5) and the line [0,1,1] are incident when p=7 since 1*0+2*1+5*1 = 7 == 0 mod 7, but the point (1,3,3) and the line [1,2,6] are not incident since 1*1+3*2+3*6 = 25 != 0 mod 7.
Translating into the language of cards and pictures, that means the picture with coordinates (1,2,5) is contained in the card with coordinates [0,1,1], but the picture with coordinates (1,3,3) is not contained in the card with coordinates [1,2,6]. We can use this procedure to develop a complete list of cards and the pictures that they contain.
I think it's easier to think of pictures as points and cards as lines, but there's a duality in projective geometry between points and lines so it really doesn't matter. However, in what follows I will be using points for pictures and lines for cards.
The same construction works for any finite field. We know that there is a finite field of order q if and only if q=p^k, a prime power. The field is called GF(p^k) which stands for "Galois field". The fields are not as easy to construct in the prime power case as they are in the prime case.
Fortunately, the hard work has already been done and implemented in free software, namely Sage Math. To get a projective plane design of order 4, for example, just type
PG = designs.ProjectiveGeometryDesign(2,1,GF(4),point_coordinates=0)
PG.blocks()
and you'll obtain output that looks like
[[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]]
I interpret the above as follows: there are 21 pictures labelled from 0 to 20. Each of the blocks (lines in projective geometry) tells me which pictures appears on a card. For example, the first card will have pictures 0, 1, 2, 3, and 20; the second card will have pictures 0, 4, 8, 12, and 16; and so on.
The system of order 7 can be generated by
PG = designs.ProjectiveGeometryDesign(2,1,GF(7),point_coordinates=0)
PG.blocks()
which generates the output
[[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]]
If you want up to 57 cards you can use GF(7). If you want 58 cards you'll have to use a larger field. Since 8 is a power of a prime, you could use GF(8). Note that the projective plane based on GF(8) will have 8^2 + 8 + 1 = 73 points and 73 lines. You can make 73 cards, but then just throw away 15 of them if you want a set of exactly 58 cards. If you want between 73 and 91 cards you could use GF(9), etc. There is no GF(10) because 10 is not a power of a prime. GF(11) is next, then GF(13), then GF(16) because 16=2^4, and so on.
By the way, I have a theory that the original Spot It deck uses 55, not 57, because they contracted a playing card manufacturer which was already tooled for decks of 55 cards (52 regular cards in a deck, plus two jokers and a title card).
I just found a way to do it with 57 or 58 pictures but now I have a very bad headache, I'll post the ruby code in 8-10 hours after I slept well! just a hint my my solution every 7 cards share same mark and total 56 cards can be constructed using my solution.
here is the code that generates all 57 cards that ypercube was talking about. it uses exactly 57 pictures, and sorry guy's I've written actual C++ code but knowing that vector <something> is an array containing values of type something it's easy to understand what this code does. and this code generates P^2+P+1 cards using P^2+P+1 pictures each containing P+1 picture and sharing only 1 picture in common, for every prime P value. which means we can have 7 cards using 7 pictures each having 3 pictures(for p=2), 13 cards using 13 pictures(for p=3), 31 cards using 31 pictures(for p=5), 57 cards for 57 pictures(for p=7) and so on...
#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();
}
}
again sorry for the delayed code.
Here's Gajet's solution in Python, since I find Python more readable. I have modified it so that it works with non-prime numbers as well. I have used Thies insight to generate some more easily understood display code.
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)
With output:
*** *
*** *
****
* * * *
* * * *
* * * *
* * * *
* ** *
** * *
* * * *
* * * *
* * * *
****
Using the z3 theorem prover
Let P be the number of symbols per card. According to this article and ypercubeᵀᴹ's answer there are N = P**2 - P + 1 cards and symbols, respectively. A deck of cards can be represented with its incidence matrix which has a row for each card and a column for each possible symbol. Its (i,j) element is 1 if card i has symbol j on it. We only need to fill this matrix with these constraints in mind:
every element is either zero or one
the sum of each row is exactly P
the sum of each column is exactly P
any two rows must have exactly one symbol in common
That means N**2 variables and N**2 + 2*N + (N choose 2) constraints. It seems to be manageable in a not-so-long time with z3 for small inputs.
edit: Unfortunately P=8 seems to be too big for this method. I killed the process after 14 hour of computation time.
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)
Results
$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.>
I very much like this thread. I build this github python project with parts of this code here to draw custom cards as png (so one can order custom card games in the internet).
https://github.com/plagtag/ProjectiveGeometry-Game
I wrote an article about how to generate this kind of decks, with code in Perl. The code is not optimized but it's at least capable of generating decks of "reasonable" orders... and some more.
Here's an example with order 8, which has to consider a slightly more complicated underlying math, because 8 is not prime although a valid order for generating these kind of decks. See above or the article for a more detailed explanation, below if you just want to generate a slightly more difficult 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
Each identifier from 0 to 72 can be read both as a card identifier and as a picture identifier. For example, the last row means that:
card 72 contains pictures 2, 13, 22, ..., 59, 68, AND
picture 72 appears in cards 2, 13, 22, ..., 59, and 68.
I wrote the following code to calculate the cards. The idea is to create the first card with n images on it. If the difference of every pair of image indexes is unique then the rest of the cards can be generated trivially, by increasing each index with the same value modulo m = n * n - n + 1
static public int[] Backtrack(int n)
{
int m = n * n - n + 1;
int[] Check = new int[m];
int C = 1;
int[] T = new int[n];
int _p = 2;
T[1] = 1;
if (n > 2) T[2] = 1;
else return T;
while (_p >= 2)
{
T[_p]++;
if (T[_p] == m)
{
_p--;
continue;
}
bool good = true;
C++;
for (int i = 0; i <= _p; i++)
{
for (int j = 0; j < i; j++)
{
int x = (T[i] - T[j] + m) % m;
if (Check[x] == C || Check[m - x] == C)//x cannot be equal to m-x as m is odd.
good = false;
Check[m - x] = C;
Check[x] = C;
}
}
if (good)
{
_p++;
if (_p == n)
{
_p--;
return T;
}
T[_p] = T[_p - 1];
}
}
return new int[] { };
}
static void Main(string[] args)
{
for (int N = 2; N < 11; N++)
{
var X = Backtrack(N);
if (X.Length > 0)
{
int K = N * N - N + 1;
Console.WriteLine("Cards: {0} Order {1}:", K, N - 1);
int C = 0;
for (int j = 0; j < K; j++)
{
Console.Write("Card {0:000}:", C++);
for (int i = 0; i < N; i++)
{
var t = (X[i] + j) % K;
if (j != 0 && Array.Exists(X, x => (x == t)))
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("\t{0}", t);
Console.ResetColor();
}
Console.WriteLine();
}
}
}
}
output:
Cards: 3 Order 1:
Card 000: 0 1
Card 001: 1 2
Card 002: 2 0
Cards: 7 Order 2:
Card 000: 0 1 3
Card 001: 1 2 4
Card 002: 2 3 5
Card 003: 3 4 6
Card 004: 4 5 0
Card 005: 5 6 1
Card 006: 6 0 2
Cards: 13 Order 3:
Card 000: 0 1 3 9
Card 001: 1 2 4 10
Card 002: 2 3 5 11
Card 003: 3 4 6 12
Card 004: 4 5 7 0
Card 005: 5 6 8 1
Card 006: 6 7 9 2
Card 007: 7 8 10 3
Card 008: 8 9 11 4
Card 009: 9 10 12 5
Card 010: 10 11 0 6
Card 011: 11 12 1 7
Card 012: 12 0 2 8
Cards: 21 Order 4:
Card 000: 0 1 4 14 16
Card 001: 1 2 5 15 17
Card 002: 2 3 6 16 18
Card 003: 3 4 7 17 19
Card 004: 4 5 8 18 20
Card 005: 5 6 9 19 0
Card 006: 6 7 10 20 1
Card 007: 7 8 11 0 2
Card 008: 8 9 12 1 3
Card 009: 9 10 13 2 4
Card 010: 10 11 14 3 5
Card 011: 11 12 15 4 6
Card 012: 12 13 16 5 7
Card 013: 13 14 17 6 8
Card 014: 14 15 18 7 9
Card 015: 15 16 19 8 10
Card 016: 16 17 20 9 11
Card 017: 17 18 0 10 12
Card 018: 18 19 1 11 13
Card 019: 19 20 2 12 14
Card 020: 20 0 3 13 15
Cards: 31 Order 5:
Card 000: 0 1 3 8 12 18
Card 001: 1 2 4 9 13 19
Card 002: 2 3 5 10 14 20
Card 003: 3 4 6 11 15 21
Card 004: 4 5 7 12 16 22
Card 005: 5 6 8 13 17 23
Card 006: 6 7 9 14 18 24
Card 007: 7 8 10 15 19 25
Card 008: 8 9 11 16 20 26
Card 009: 9 10 12 17 21 27
Card 010: 10 11 13 18 22 28
Card 011: 11 12 14 19 23 29
Card 012: 12 13 15 20 24 30
Card 013: 13 14 16 21 25 0
Card 014: 14 15 17 22 26 1
Card 015: 15 16 18 23 27 2
Card 016: 16 17 19 24 28 3
Card 017: 17 18 20 25 29 4
Card 018: 18 19 21 26 30 5
Card 019: 19 20 22 27 0 6
Card 020: 20 21 23 28 1 7
Card 021: 21 22 24 29 2 8
Card 022: 22 23 25 30 3 9
Card 023: 23 24 26 0 4 10
Card 024: 24 25 27 1 5 11
Card 025: 25 26 28 2 6 12
Card 026: 26 27 29 3 7 13
Card 027: 27 28 30 4 8 14
Card 028: 28 29 0 5 9 15
Card 029: 29 30 1 6 10 16
Card 030: 30 0 2 7 11 17
Cards: 57 Order 7:
Card 000: 0 1 3 13 32 36 43 52
Card 001: 1 2 4 14 33 37 44 53
Card 002: 2 3 5 15 34 38 45 54
Card 003: 3 4 6 16 35 39 46 55
Card 004: 4 5 7 17 36 40 47 56
Card 005: 5 6 8 18 37 41 48 0
Card 006: 6 7 9 19 38 42 49 1
Card 007: 7 8 10 20 39 43 50 2
Card 008: 8 9 11 21 40 44 51 3
Card 009: 9 10 12 22 41 45 52 4
Card 010: 10 11 13 23 42 46 53 5
Card 011: 11 12 14 24 43 47 54 6
Card 012: 12 13 15 25 44 48 55 7
Card 013: 13 14 16 26 45 49 56 8
Card 014: 14 15 17 27 46 50 0 9
Card 015: 15 16 18 28 47 51 1 10
Card 016: 16 17 19 29 48 52 2 11
Card 017: 17 18 20 30 49 53 3 12
Card 018: 18 19 21 31 50 54 4 13
Card 019: 19 20 22 32 51 55 5 14
Card 020: 20 21 23 33 52 56 6 15
Card 021: 21 22 24 34 53 0 7 16
Card 022: 22 23 25 35 54 1 8 17
Card 023: 23 24 26 36 55 2 9 18
Card 024: 24 25 27 37 56 3 10 19
Card 025: 25 26 28 38 0 4 11 20
Card 026: 26 27 29 39 1 5 12 21
Card 027: 27 28 30 40 2 6 13 22
Card 028: 28 29 31 41 3 7 14 23
Card 029: 29 30 32 42 4 8 15 24
Card 030: 30 31 33 43 5 9 16 25
Card 031: 31 32 34 44 6 10 17 26
Card 032: 32 33 35 45 7 11 18 27
Card 033: 33 34 36 46 8 12 19 28
Card 034: 34 35 37 47 9 13 20 29
Card 035: 35 36 38 48 10 14 21 30
Card 036: 36 37 39 49 11 15 22 31
Card 037: 37 38 40 50 12 16 23 32
Card 038: 38 39 41 51 13 17 24 33
Card 039: 39 40 42 52 14 18 25 34
Card 040: 40 41 43 53 15 19 26 35
Card 041: 41 42 44 54 16 20 27 36
Card 042: 42 43 45 55 17 21 28 37
Card 043: 43 44 46 56 18 22 29 38
Card 044: 44 45 47 0 19 23 30 39
Card 045: 45 46 48 1 20 24 31 40
Card 046: 46 47 49 2 21 25 32 41
Card 047: 47 48 50 3 22 26 33 42
Card 048: 48 49 51 4 23 27 34 43
Card 049: 49 50 52 5 24 28 35 44
Card 050: 50 51 53 6 25 29 36 45
Card 051: 51 52 54 7 26 30 37 46
Card 052: 52 53 55 8 27 31 38 47
Card 053: 53 54 56 9 28 32 39 48
Card 054: 54 55 0 10 29 33 40 49
Card 055: 55 56 1 11 30 34 41 50
Card 056: 56 0 2 12 31 35 42 51
Cards: 73 Order 8:
Card 000: 0 1 3 7 15 31 36 54 63
Card 001: 1 2 4 8 16 32 37 55 64
Card 002: 2 3 5 9 17 33 38 56 65
Card 003: 3 4 6 10 18 34 39 57 66
Card 004: 4 5 7 11 19 35 40 58 67
Card 005: 5 6 8 12 20 36 41 59 68
Card 006: 6 7 9 13 21 37 42 60 69
Card 007: 7 8 10 14 22 38 43 61 70
Card 008: 8 9 11 15 23 39 44 62 71
Card 009: 9 10 12 16 24 40 45 63 72
Card 010: 10 11 13 17 25 41 46 64 0
Card 011: 11 12 14 18 26 42 47 65 1
Card 012: 12 13 15 19 27 43 48 66 2
Card 013: 13 14 16 20 28 44 49 67 3
Card 014: 14 15 17 21 29 45 50 68 4
Card 015: 15 16 18 22 30 46 51 69 5
Card 016: 16 17 19 23 31 47 52 70 6
Card 017: 17 18 20 24 32 48 53 71 7
Card 018: 18 19 21 25 33 49 54 72 8
Card 019: 19 20 22 26 34 50 55 0 9
Card 020: 20 21 23 27 35 51 56 1 10
Card 021: 21 22 24 28 36 52 57 2 11
Card 022: 22 23 25 29 37 53 58 3 12
Card 023: 23 24 26 30 38 54 59 4 13
Card 024: 24 25 27 31 39 55 60 5 14
Card 025: 25 26 28 32 40 56 61 6 15
Card 026: 26 27 29 33 41 57 62 7 16
Card 027: 27 28 30 34 42 58 63 8 17
Card 028: 28 29 31 35 43 59 64 9 18
Card 029: 29 30 32 36 44 60 65 10 19
Card 030: 30 31 33 37 45 61 66 11 20
Card 031: 31 32 34 38 46 62 67 12 21
Card 032: 32 33 35 39 47 63 68 13 22
Card 033: 33 34 36 40 48 64 69 14 23
Card 034: 34 35 37 41 49 65 70 15 24
Card 035: 35 36 38 42 50 66 71 16 25
Card 036: 36 37 39 43 51 67 72 17 26
Card 037: 37 38 40 44 52 68 0 18 27
Card 038: 38 39 41 45 53 69 1 19 28
Card 039: 39 40 42 46 54 70 2 20 29
Card 040: 40 41 43 47 55 71 3 21 30
Card 041: 41 42 44 48 56 72 4 22 31
Card 042: 42 43 45 49 57 0 5 23 32
Card 043: 43 44 46 50 58 1 6 24 33
Card 044: 44 45 47 51 59 2 7 25 34
Card 045: 45 46 48 52 60 3 8 26 35
Card 046: 46 47 49 53 61 4 9 27 36
Card 047: 47 48 50 54 62 5 10 28 37
Card 048: 48 49 51 55 63 6 11 29 38
Card 049: 49 50 52 56 64 7 12 30 39
Card 050: 50 51 53 57 65 8 13 31 40
Card 051: 51 52 54 58 66 9 14 32 41
Card 052: 52 53 55 59 67 10 15 33 42
Card 053: 53 54 56 60 68 11 16 34 43
Card 054: 54 55 57 61 69 12 17 35 44
Card 055: 55 56 58 62 70 13 18 36 45
Card 056: 56 57 59 63 71 14 19 37 46
Card 057: 57 58 60 64 72 15 20 38 47
Card 058: 58 59 61 65 0 16 21 39 48
Card 059: 59 60 62 66 1 17 22 40 49
Card 060: 60 61 63 67 2 18 23 41 50
Card 061: 61 62 64 68 3 19 24 42 51
Card 062: 62 63 65 69 4 20 25 43 52
Card 063: 63 64 66 70 5 21 26 44 53
Card 064: 64 65 67 71 6 22 27 45 54
Card 065: 65 66 68 72 7 23 28 46 55
Card 066: 66 67 69 0 8 24 29 47 56
Card 067: 67 68 70 1 9 25 30 48 57
Card 068: 68 69 71 2 10 26 31 49 58
Card 069: 69 70 72 3 11 27 32 50 59
Card 070: 70 71 0 4 12 28 33 51 60
Card 071: 71 72 1 5 13 29 34 52 61
Card 072: 72 0 2 6 14 30 35 53 62
Cards: 91 Order 9:
Card 000: 0 1 3 9 27 49 56 61 77 81
Card 001: 1 2 4 10 28 50 57 62 78 82
Card 002: 2 3 5 11 29 51 58 63 79 83
Card 003: 3 4 6 12 30 52 59 64 80 84
Card 004: 4 5 7 13 31 53 60 65 81 85
Card 005: 5 6 8 14 32 54 61 66 82 86
Card 006: 6 7 9 15 33 55 62 67 83 87
Card 007: 7 8 10 16 34 56 63 68 84 88
Card 008: 8 9 11 17 35 57 64 69 85 89
Card 009: 9 10 12 18 36 58 65 70 86 90
Card 010: 10 11 13 19 37 59 66 71 87 0
Card 011: 11 12 14 20 38 60 67 72 88 1
Card 012: 12 13 15 21 39 61 68 73 89 2
Card 013: 13 14 16 22 40 62 69 74 90 3
Card 014: 14 15 17 23 41 63 70 75 0 4
Card 015: 15 16 18 24 42 64 71 76 1 5
Card 016: 16 17 19 25 43 65 72 77 2 6
Card 017: 17 18 20 26 44 66 73 78 3 7
Card 018: 18 19 21 27 45 67 74 79 4 8
Card 019: 19 20 22 28 46 68 75 80 5 9
Card 020: 20 21 23 29 47 69 76 81 6 10
Card 021: 21 22 24 30 48 70 77 82 7 11
Card 022: 22 23 25 31 49 71 78 83 8 12
Card 023: 23 24 26 32 50 72 79 84 9 13
Card 024: 24 25 27 33 51 73 80 85 10 14
Card 025: 25 26 28 34 52 74 81 86 11 15
Card 026: 26 27 29 35 53 75 82 87 12 16
Card 027: 27 28 30 36 54 76 83 88 13 17
Card 028: 28 29 31 37 55 77 84 89 14 18
Card 029: 29 30 32 38 56 78 85 90 15 19
Card 030: 30 31 33 39 57 79 86 0 16 20
Card 031: 31 32 34 40 58 80 87 1 17 21
Card 032: 32 33 35 41 59 81 88 2 18 22
Card 033: 33 34 36 42 60 82 89 3 19 23
Card 034: 34 35 37 43 61 83 90 4 20 24
Card 035: 35 36 38 44 62 84 0 5 21 25
Card 036: 36 37 39 45 63 85 1 6 22 26
Card 037: 37 38 40 46 64 86 2 7 23 27
Card 038: 38 39 41 47 65 87 3 8 24 28
Card 039: 39 40 42 48 66 88 4 9 25 29
Card 040: 40 41 43 49 67 89 5 10 26 30
Card 041: 41 42 44 50 68 90 6 11 27 31
Card 042: 42 43 45 51 69 0 7 12 28 32
Card 043: 43 44 46 52 70 1 8 13 29 33
Card 044: 44 45 47 53 71 2 9 14 30 34
Card 045: 45 46 48 54 72 3 10 15 31 35
Card 046: 46 47 49 55 73 4 11 16 32 36
Card 047: 47 48 50 56 74 5 12 17 33 37
Card 048: 48 49 51 57 75 6 13 18 34 38
Card 049: 49 50 52 58 76 7 14 19 35 39
Card 050: 50 51 53 59 77 8 15 20 36 40
Card 051: 51 52 54 60 78 9 16 21 37 41
Card 052: 52 53 55 61 79 10 17 22 38 42
Card 053: 53 54 56 62 80 11 18 23 39 43
Card 054: 54 55 57 63 81 12 19 24 40 44
Card 055: 55 56 58 64 82 13 20 25 41 45
Card 056: 56 57 59 65 83 14 21 26 42 46
Card 057: 57 58 60 66 84 15 22 27 43 47
Card 058: 58 59 61 67 85 16 23 28 44 48
Card 059: 59 60 62 68 86 17 24 29 45 49
Card 060: 60 61 63 69 87 18 25 30 46 50
Card 061: 61 62 64 70 88 19 26 31 47 51
Card 062: 62 63 65 71 89 20 27 32 48 52
Card 063: 63 64 66 72 90 21 28 33 49 53
Card 064: 64 65 67 73 0 22 29 34 50 54
Card 065: 65 66 68 74 1 23 30 35 51 55
Card 066: 66 67 69 75 2 24 31 36 52 56
Card 067: 67 68 70 76 3 25 32 37 53 57
Card 068: 68 69 71 77 4 26 33 38 54 58
Card 069: 69 70 72 78 5 27 34 39 55 59
Card 070: 70 71 73 79 6 28 35 40 56 60
Card 071: 71 72 74 80 7 29 36 41 57 61
Card 072: 72 73 75 81 8 30 37 42 58 62
Card 073: 73 74 76 82 9 31 38 43 59 63
Card 074: 74 75 77 83 10 32 39 44 60 64
Card 075: 75 76 78 84 11 33 40 45 61 65
Card 076: 76 77 79 85 12 34 41 46 62 66
Card 077: 77 78 80 86 13 35 42 47 63 67
Card 078: 78 79 81 87 14 36 43 48 64 68
Card 079: 79 80 82 88 15 37 44 49 65 69
Card 080: 80 81 83 89 16 38 45 50 66 70
Card 081: 81 82 84 90 17 39 46 51 67 71
Card 082: 82 83 85 0 18 40 47 52 68 72
Card 083: 83 84 86 1 19 41 48 53 69 73
Card 084: 84 85 87 2 20 42 49 54 70 74
Card 085: 85 86 88 3 21 43 50 55 71 75
Card 086: 86 87 89 4 22 44 51 56 72 76
Card 087: 87 88 90 5 23 45 52 57 73 77
Card 088: 88 89 0 6 24 46 53 58 74 78
Card 089: 89 90 1 7 25 47 54 59 75 79
Card 090: 90 0 2 8 26 48 55 60 76 80

Resources