How do you generate a Sudoku board with a unique solution? What I thought was to initialize a random board and then remove some numbers. But my question is how do I maintain the uniqueness of a solution?
Here is the way my own SuDoKu program does it:
Start with a complete, valid board (filled with 81 numbers).
Make a list of all 81 cell positions and shuffle it randomly.
As long as the list is not empty, take the next position from the list and remove the number from the related cell.
Test uniqueness using a fast backtracking solver. My solver is - in theory - able to count all solutions, but for testing uniqueness, it will stop immediately when it finds more than one solution.
If the current board has still just one solution, goto step 3) and repeat.
If the current board has more than one solution, undo the last removal (step 3), and continue step 3 with the next position from the list
Stop when you have tested all 81 positions.
This gives you not only unique boards, but boards where you cannot remove any more numbers without destroying the uniqueness of the solution.
Of course, this is only the second half of the algorithm. The first half is to find a complete valid board first (randomly filled!) It works very similar, but "in the other direction":
Start with an empty board.
Add a random number at one of the free cells (the cell is chosen randomly, and the number is chosen randomly from the list of numbers valid for this cell according to the SuDoKu rules).
Use the backtracking solver to check if the current board has at least one valid solution. If not, undo step 2 and repeat with another number and cell. Note that this step might produce full valid boards on its own, but those are in no way random.
Repeat until the board is completely filled with numbers.
Easy:
Find all solutions with an efficient backtracking algorithm.
If there is just one solution, you are done. Otherwise if you have more than one solution, find a position at which most of the solutions differ. Add the number at this position.
Go to 1.
I doubt you can find a solution that would be much faster than this.
You can cheat. Start with an existing Sudoku board that can be solved then fiddle with it.
You can swap any row of three 3x3 blocks with any other row. You can swap any column of three 3x3 blocks with another column. Within each block row or block column you can swap single rows and single columns. Finally you can permute the numbers so there are different numbers in the filled positions as long as the permutation is consistent across the whole board.
None of these changes will make a solvable board unsolvable.
Unless P = NP, there is no polynomial-time algorithm for generating general Sudoku problems with exactly one solution.
In his master's thesis, Takayuki Yato defined The Another Solution Problem (ASP), where the goal is, given a problem and some solution, to find a different solution to that problem or to show that none exists. Yato then defined ASP-completeness, problems for which it is difficult to find another solution, and showed that Sudoku is ASP-complete. Since he also proves that ASP-completeness implies NP-hardness, this means that if you allow for arbitrary-sized Sudoku boards, there is no polynomial-time algorithm to check if the puzzle you've generated has a unique solution (unless P = NP).
Sorry to spoil your hopes for a fast algorithm!
The solution is divide in to 2 parts:
A. Generating the number pattern 600 billion
B. Generating the masking pattern ~ 7e23 combinations
A ) For Number pattern the fastest way which can generate unique combinations with NO time spent on backtracing or testing
Step 1. Choose an already exiting matrix, I chose the below one as it can be made easily by human without any help from a computing device or solver:
First row is numbers in ascending order
Second row is also in ascending order but start from 4 & roll around
Third row is also in ascending order but start from 7 & roll around
Row 4,5,6: Replace the three cell column with the top right column - 2 5 8 and roll within the 3x3 cell for last column
Row 7,8,9: Replace the three cell column with the top right column - 3 6 9 and roll within the 3x3 cell for last column
1 2 3 4 5 6 7 8 9
4 5 6 7 8 9 1 2 3
7 8 9 1 2 3 4 5 6
2 3 1 5 6 4 8 9 7
5 6 4 8 9 7 2 3 1
8 9 7 2 3 1 5 6 4
3 1 2 6 4 5 9 7 8
6 4 5 9 7 8 3 1 2
9 7 8 3 1 2 6 4 5
Step 2. Shuffle the the digits and replace in all other cells
Step 3. Randomly rearrange columns 1,2 and 3 within themselves
Step 4. Randomly rearrange columns 4,5 and 6 within themselves
Step 5. Randomly rearrange columns 7,8 and 9 within themselves
Step 6. Randomly rearrange rows 1,2 and 3 within themselves
Step 7. Randomly rearrange rows 4,5 and 6 within themselves
Step 8. Randomly rearrange rows 7,8 and 9 within themselves
Step 9. Randomly rearrange in 3 column groups of size 9x3
Step 10. Randomly rearrange in 3 row groups of size 3x9
voila...
5 8 3 1 6 4 9 7 2
7 2 9 3 5 8 1 4 6
1 4 6 2 7 9 3 8 5
8 5 2 6 9 1 4 3 7
3 1 7 4 2 5 8 6 9
6 9 4 8 3 7 2 5 1
4 6 5 9 1 3 7 2 8
2 3 1 7 8 6 5 9 4
9 7 8 5 4 2 6 1 3
B ) For Masking Pattern we need to have a solver algorithm. As we already have a quite unique number grid (which is also solved!) this gives us faster performance for using solver
Step 1: Start with selecting 15 random locations out of the 81.
Step 2: Check with solver whether it has unique solution
Step 3: If solution not unique select additional location. iterate Steps 2 and 3 until unique solution found
This should give you the very unique and fast Sudoku board.
This way you can generate any possible sudoku board as well as any other nxn sudoku board
as for how efficient this algorithm is , it took 3.6 secs to generate a million boards in java & 3.5 sec in golang
Find any filled board of sudoku. (use trivial ones will not affect final result)
int[][] board = new int[][] {
{1,2,3, 4,5,6, 7,8,9},
{4,5,6, 7,8,9, 1,2,3},
{7,8,9, 1,2,3, 4,5,6},
{2,3,1, 5,6,4, 8,9,7},
{5,6,4, 8,9,7, 2,3,1},
{8,9,7, 2,3,1, 5,6,4},
{3,1,2, 6,4,5, 9,7,8},
{6,4,5, 9,7,8, 3,1,2},
{9,7,8, 3,1,2, 6,4,5}
};
for each number from 1 to 9 (say num), (i.e 1, 2, 3, 5, 6, 7, 8, 9) take a random number from range [1 to 9], traverse the board, swap num with your random number.
void shuffleNumbers() {
for (int i = 0; i < 9; i++) {
int ranNum = random.nextInt(9);
swapNumbers(i, ranNum);
}
}
private void swapNumbers(int n1, int n2) {
for (int y = 0; y<9; y++) {
for (int x = 0; x<9; x++) {
if (board[x][y] == n1) {
board[x][y] = n2;
} else if (board[x][y] == n2) {
board[x][y] = n1;
}
}
}
}
Now shuffle rows. Take the first group of 3 rows , shuffle them , and do it for all rows. (in 9 X 9 sudoku), do it for second group and as well as third.
void shuffleRows() {
int blockNumber;
for (int i = 0; i < 9; i++) {
int ranNum = random.nextInt(3);
blockNumber = i / 3;
swapRows(i, blockNumber * 3 + ranNum);
}
}
void swapRows(int r1, int r2) {
int[] row = board[r1];
board[r1] = board[r2];
board[r2] = row;
}
Swap columns, again take block of 3 columns , shuffle them, and do it for all 3 blocks
void shuffleCols() {
int blockNumber;
for (int i = 0; i < 9; i++) {
int ranNum = random.nextInt(3);
blockNumber = i / 3;
swapCols(i, blockNumber * 3 + ranNum);
}
}
void swapCols(int c1, int c2) {
int colVal;
for (int i = 0; i < 9; i++){
colVal = board[i][c1];
board[i][c1] = board[i][c2];
board[i][c2] = colVal;
}
}
swap the row blocks itself (ie 3X9 blocks)
void shuffle3X3Rows() {
for (int i = 0; i < 3; i++) {
int ranNum = random.nextInt(3);
swap3X3Rows(i, ranNum);
}
}
void swap3X3Rows(int r1, int r2) {
for (int i = 0; i < 3; i++) {
swapRows(r1 * 3 + i, r2 * 3 + i);
}
}
do the same for columns, swap blockwise
void shuffle3X3Cols() {
for (int i = 0; i < 3; i++) {
int ranNum = random.nextInt(3);
swap3X3Cols(i, ranNum);
}
}
private void swap3X3Cols(int c1, int c2) {
for (int i = 0; i < 3; i++) {
swapCols(c1 * 3 + i, c2 * 3 + i);
}
}
Now you are done, your board should be a valid sudoku board
To generate a board with hidden values, this can be done using backtracking sudoku algorithm with it try to remove one element from the board until you have a board that is solvable, remove until it will become unsolvable even if you remove only one more element.
if you want to categorised final generated board by difficulty, just count how many numbers are left in board while removing element one by one. The less the number harder it will be to solve
least possible hints in sudoku can be 17, but all possible sudoku board not necessarily reducible to 17 hint sudoku
SWIFT 5 version
The simply way, here my code:
First, create the function into [[Int]] array
func getNumberSudoku() -> [[Int]] {
// Original number
let originalNum = [1,2,3,4,5,6,7,8,9]
// Create line 1 to 9 and shuffle from original
let line1 = originalNum.shuffled()
let line2 = line1.shift(withDistance: 3)
let line3 = line2.shift(withDistance: 3)
let line4 = line3.shift(withDistance: 1)
let line5 = line4.shift(withDistance: 3)
let line6 = line5.shift(withDistance: 3)
let line7 = line6.shift(withDistance: 1)
let line8 = line7.shift(withDistance: 3)
let line9 = line8.shift(withDistance: 3)
// Final array
let renewRow = [line1,line2,line3,line4,line5,line6,line7,line8,line9]
// Pre-shuffle for column
let colSh1 = [0,1,2].shuffled()
let colSh2 = [3,4,5].shuffled()
let colSh3 = [6,7,8].shuffled()
let rowSh1 = [0,1,2].shuffled()
let rowSh2 = [3,4,5].shuffled()
let rowSh3 = [6,7,8].shuffled()
// Create the let and var
let colResult = colSh1 + colSh2 + colSh3
let rowResult = rowSh1 + rowSh2 + rowSh3
var preCol: [Int] = []
var finalCol: [[Int]] = []
var prerow: [Int] = []
var finalRow: [[Int]] = []
// Shuffle the columns
for x in 0...8 {
preCol.removeAll()
for i in 0...8 {
preCol.append(renewRow[x][colResult[i]])
}
finalCol.append(preCol)
}
// Shuffle the rows
for x in 0...8 {
prerow.removeAll()
for i in 0...8 {
prerow.append(finalCol[x][rowResult[i]])
}
finalRow.append(prerow)
}
// Final, create the array into the [[Int]].
return finalRow
}
Then usage:
var finalArray = [[Int]]
finalArray = getNumberSudoku()
It's not easy to give a generic solution. You need to know a few things to generate a specific kind of Sudoku... for example, you cannot build a Sudoku with more than nine empty 9-number groups (rows, 3x3 blocks or columns). Minimum given numbers (i.e. "clues") in a single-solution Sudoku is believed to be 17, but number positions for this Sudoku are very specific if I'm not wrong. The average number of clues for a Sudoku is about 26, and I'm not sure but if you quit numbers of a completed grid until having 26 and leave those in a symmetric way, you may have a valid Sudoku.
On the other hand, you can just randomly quit numbers from completed grids and testing them with CHECKER or other tools until it comes up with an OK.
Here is a way to make a classic sudoku puzzle (sudoku puzzle with one and only solution; pre-filled squares are symmetrical around the center square R5C5).
1) start with a complete grid (using group filling plus circular shift to get it easily)
2) remove number(s) from two symmetrical squares if the cleared squares can be inferred using the remaining clues.
3) repeat (2) until all the numbers are checked.
Using this method you can create a very easy sudoku puzzle with or without programming. You can also use this method to craft harder Sudoku puzzles. You may want to search "create classic sudoku" on YouTube to have a step by step example.
You can start with any valid (filled) puzzle and modify it to produce a completely different one (again, filled). Instead of permutating groups of numbers, you can swap single cells - there will be no similarity whatsoever between the seed puzzle and the resulting puzzle. I have written a simple program long ago in VB, you can find it here: https://www.charalampakis.com/blog/programming-vb-net/a-simple-algorithm-for-creating-sudoku-puzzles-using-visual-basic. It can be translated to any language easily.
Then, randomly and gradually remove cells and check if the puzzle is solvable and has a unique solution. You can also rate the puzzle in terms of difficulty depending on the rules needed for the solution. Continue until removing any known cell leads to an unsolvable puzzle.
HTH
I also think that you will have to explicitly check uniqueness. If you have less than 17 givens, a unique solution is very unlikely, though: None has yet been found, although it is not clear yet whether it might exist.)
But you can also use a SAT-solver, as opposed to writing an own backtracking algorithm. That way, you can to some extent regulate how difficult it will be to find a solution: If you restrict the inference rules that the SAT-solver uses, you can check whether you can solve the puzzle easily. Just google for "SAT solving sudoku".
One way to generate sudoku faster.
find an exist sudoku.
exchange the value with a random group.
exchange the cell or the column or the row-grid or the column-grid.
You exchange the value will make the value different, if not exchange the rows or the column, the sudoku isn't changed in the essential.
You can flag the sudoku with 9 grids, the rows and column exchanged must do in the same grid. Like you can exchange row1-3, row4-6, row7-9, don't exchange row1-4 or row1-7. You can also exchange the row-grid(exchange row1~3 with the row4~6 or row7~9).
Solve the sudoku: record the empty with all the possible value, then check the value from 1 to 9. If one value is unique, remove it from the loop.
You may need code like this:
#pz is a 9x9 numpy array
def PossibleValueAtPosition(pz:[], row:int, col:int):
r=row//3*3
c=col//3*3
return {1,2,3,4,5,6,7,8,9}.difference(set(pz[r:r+3,c:c+3].flat)).difference(set(pz[row,:])).difference(set(pz[:,col]))
def SolvePuzzle(pz:[], n:int, Nof_solution:int):# init Nof_solution = 0
if Nof_solution>1:
return Nof_solution # no need to further check
if n>=81:
Nof_solution+=1
return Nof_solution
(row,col) = divmod(n,9)
if pz[row][col]>0: # location filled, try next location
Nof_solution = SolvePuzzle(pz, n+1, Nof_solution)
else:
l = PossibleValueAtPosition(pz, row,col)
for v in l: # if l = empty set, bypass all
pz[row][col] = v # try to fill a possible value v
Nof_solution = SolvePuzzle(pz, n+1, Nof_solution)
pz[row][col] = 0
return Nof_solution # resume the value, blacktrack
Quick and Dirty, but works:
import numpy as np
import math
N = 3
# rewrite of https://www.tutorialspoint.com/valid-sudoku-in-python
def isValidSudoku(M):
'''
Check a sudoku matrix:
A 9x9 sudoku matrix is valid iff every:
row contains 1 - 9 and
col contains 1 - 9 and
3x3 contains 1 - 9
0 is used for blank entry
'''
for i in range(9):
row = {}
col = {}
block = {}
row_cube = N * (i//N)
col_cube = N * (i%N)
for j in range(N*N):
if M[i][j] != 0 and M[i][j] in row:
return False
row[M[i][j]] = 1
if M[j][i] != 0 and M[j][i] in col:
return False
col[M[j][i]] = 1
rc = row_cube + j//N
cc = col_cube + j%N
if M[rc][cc] in block and M[rc][cc] != 0:
return False
block[M[rc][cc]]=1
return True
def generate_sudoku_puzzles(run_size, seed):
order = int(math.sqrt(run_size))
count = 0
valid = 0
empty = []
np.random.seed(seed) # for reproducible results
for k in range(order):
for l in range(order):
A = np.fromfunction(lambda i, j: ((k*i + l+j) % (N*N)) + 1, (N*N, N*N), dtype=int)
B = np.random.randint(2, size=(N*N, N*N))
empty.append(np.count_nonzero(B))
C = A*B
count += 1
if isValidSudoku(C):
valid += 1
last = C
# print('C(',k,l,') is valid sudoku:')
# print(C) # Uncomment for puzzle
print('Tried', count, 'valid', valid, 'yield', round(valid/count, 3)*100, '%', 'Average Clues', round(sum(empty)/len(empty)))
return(last)
posTest = np.array([(0, 7, 0, 0, 4, 0, 0, 6, 0), \
(3, 0, 0, 5, 0, 7, 0, 0, 2), \
(0, 0, 5, 0, 0, 0, 3, 0, 0), \
(0, 4, 0, 3, 0, 6, 0, 5, 0), \
(6, 0, 0, 0, 0, 0, 0, 0, 8), \
(0, 1, 0, 2, 0, 8, 0, 3, 0), \
(0, 0, 7, 0, 0, 0, 4, 0, 0), \
(1, 0, 0, 8, 0, 2, 0, 0, 9), \
(0, 6, 0, 0, 9, 0, 0, 1, 0), \
])
negTest = np.array([(0, 7, 0, 0, 4, 0, 0, 6, 2), \
(3, 0, 0, 5, 0, 7, 0, 0, 2), \
(0, 0, 5, 0, 0, 0, 3, 0, 0), \
(0, 4, 0, 3, 0, 6, 0, 5, 0), \
(6, 0, 0, 0, 0, 0, 0, 0, 8), \
(0, 1, 0, 2, 0, 8, 0, 3, 0), \
(0, 0, 7, 0, 0, 0, 4, 0, 0), \
(1, 0, 0, 8, 0, 2, 0, 0, 9), \
(0, 6, 0, 0, 9, 0, 0, 1, 0), \
])
print('Positive Quality Control Test:', isValidSudoku(posTest))
print('Negative Quality Control Test:', isValidSudoku(negTest))
print(generate_sudoku_puzzles(10000, 0))
Output:
Positive Quality Control Test: True
Negative Quality Control Test: False
Tried 10000 valid 31 yield 0.3 % Average Clues 40
[[0 0 2 3 0 0 0 7 8]
[7 8 9 1 2 0 0 0 0]
[5 0 0 0 9 0 0 3 0]
[0 0 0 6 7 8 0 0 2]
[0 2 0 0 0 0 7 8 9]
[8 0 0 2 3 0 0 0 0]
[0 0 0 0 0 2 3 0 5]
[0 5 6 0 8 9 1 2 0]
[0 3 0 5 0 0 0 9 0]]
Uncomment the two lines for puzzle source.
Here is a SQL Server stored procedure to generate Sudoku puzzles. I still need to remove values from the completed grid.
CREATE PROC dbo.sp_sudoku
AS
DROP TABLE IF EXISTS #cg
;
WITH cg
AS ( SELECT 1 AS g, 1 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS g, 2 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS g, 3 AS c, RAND() AS rnd
UNION ALL SELECT 2 AS g, 1 AS c, RAND() AS rnd
UNION ALL SELECT 2 AS g, 2 AS c, RAND() AS rnd
UNION ALL SELECT 2 AS g, 3 AS c, RAND() AS rnd
UNION ALL SELECT 3 AS g, 1 AS c, RAND() AS rnd
UNION ALL SELECT 3 AS g, 2 AS c, RAND() AS rnd
UNION ALL SELECT 3 AS g, 3 AS c, RAND() AS rnd)
, cg_seq
AS (SELECT g
, c
, row_number() over (partition by g
order by rnd) AS n
FROM cg)
SELECT g
, c + ((g - 1) * 3) AS c
, n + ((g - 1) * 3) AS n
INTO #cg
FROM cg_seq
--SELECT *
-- FROM #cg
-- ORDER BY g, c
DROP TABLE IF EXISTS #rg
;
WITH rg
AS ( SELECT 1 AS g, 1 AS r, RAND() AS rnd
UNION ALL SELECT 1 AS g, 2 AS r, RAND() AS rnd
UNION ALL SELECT 1 AS g, 3 AS r, RAND() AS rnd
UNION ALL SELECT 2 AS g, 1 AS r, RAND() AS rnd
UNION ALL SELECT 2 AS g, 2 AS r, RAND() AS rnd
UNION ALL SELECT 2 AS g, 3 AS r, RAND() AS rnd
UNION ALL SELECT 3 AS g, 1 AS r, RAND() AS rnd
UNION ALL SELECT 3 AS g, 2 AS r, RAND() AS rnd
UNION ALL SELECT 3 AS g, 3 AS r, RAND() AS rnd)
, rg_seq
AS (SELECT g
, r
, row_number() over (partition by g
order by rnd) AS n
FROM rg)
SELECT g
, r + ((g - 1) * 3) AS r
, n + ((g - 1) * 3) AS n
INTO #rg
FROM rg_seq
--SELECT *
-- FROM #rg
-- ORDER BY g, r
DROP TABLE IF EXISTS #r1
;
WITH r1
AS ( SELECT 1 AS r, 1 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS r, 2 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS r, 3 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS r, 4 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS r, 5 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS r, 6 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS r, 7 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS r, 8 AS c, RAND() AS rnd
UNION ALL SELECT 1 AS r, 9 AS c, RAND() AS rnd)
, r1_seq
AS (SELECT r
, c
, row_number() over (order by rnd) AS n
FROM r1)
SELECT *
INTO #r1
FROM r1_seq
DROP TABLE IF EXISTS #r_tot
;
WITH r2
AS (SELECT r + 1 AS r
, CASE WHEN c > 6 THEN c - 6
ELSE c + 3
END AS c
, n
FROM #r1)
, r3
AS (SELECT r + 1 AS r
, CASE WHEN c > 6 THEN c - 6
ELSE c + 3
END AS c
, n
FROM r2)
, r4
AS (SELECT r + 3 AS r
, CASE WHEN c = 1 THEN c + 8
ELSE c - 1
END AS c
, n
FROM #r1)
, r5
AS (SELECT r + 1 AS r
, CASE WHEN c > 6 THEN c - 6
ELSE c + 3
END AS c
, n
FROM r4)
, r6
AS (SELECT r + 1 AS r
, CASE WHEN c > 6 THEN c - 6
ELSE c + 3
END AS c
, n
FROM r5)
, r7
AS (SELECT r + 6 AS r
, CASE WHEN c = 1 THEN c + 7
WHEN c = 2 THEN c + 7
ELSE c - 2
END AS c
, n
FROM #r1)
, r8
AS (SELECT r + 1 AS r
, CASE WHEN c > 6 THEN c - 6
ELSE c + 3
END AS c
, n
FROM r7)
, r9
AS (SELECT r + 1 AS r
, CASE WHEN c > 6 THEN c - 6
ELSE c + 3
END AS c
, n
FROM r8)
, r_tot
AS (
SELECT * FROM #r1
UNION ALL SELECT * FROM r2
UNION ALL SELECT * FROM r3
UNION ALL SELECT * FROM r4
UNION ALL SELECT * FROM r5
UNION ALL SELECT * FROM r6
UNION ALL SELECT * FROM r7
UNION ALL SELECT * FROM r8
UNION ALL SELECT * FROM r9
)
SELECT *
INTO #r_tot
FROM r_tot
DROP TABLE IF EXISTS #r_tot2
;
SELECT g.n AS r
, r.c
, r.n
INTO #r_tot2
FROM #r_tot r
, #rg g
WHERE r.r = g.r
DROP TABLE IF EXISTS #c_tot2
;
SELECT r.r
, g.n AS c
, r.n
INTO #c_tot2
FROM #r_tot2 r
, #cg g
WHERE r.c = g.c
;
WITH c1 AS (SELECT r, n FROM #c_tot2 WHERE c = 1)
, c2 AS (SELECT r, n FROM #c_tot2 WHERE c = 2)
, c3 AS (SELECT r, n FROM #c_tot2 WHERE c = 3)
, c4 AS (SELECT r, n FROM #c_tot2 WHERE c = 4)
, c5 AS (SELECT r, n FROM #c_tot2 WHERE c = 5)
, c6 AS (SELECT r, n FROM #c_tot2 WHERE c = 6)
, c7 AS (SELECT r, n FROM #c_tot2 WHERE c = 7)
, c8 AS (SELECT r, n FROM #c_tot2 WHERE c = 8)
, c9 AS (SELECT r, n FROM #c_tot2 WHERE c = 9)
SELECT c1.r
, CAST(c1.n AS CHAR(2))
+ CAST(c2.n AS CHAR(2))
+ CAST(c3.n AS CHAR(2))
+ CAST(c4.n AS CHAR(2))
+ CAST(c5.n AS CHAR(2))
+ CAST(c6.n AS CHAR(2))
+ CAST(c7.n AS CHAR(2))
+ CAST(c8.n AS CHAR(2))
+ CAST(c9.n AS CHAR(2)) AS puzzle
FROM c1, c2, c3, c4, c5, c6, c7, c8, c9 WHERE c1.r = c2.r AND c3.r = c2.r AND c4.r = c3.r AND c5.r = c4.r AND c6.r = c5.r AND c7.r = c6.r AND c8.r = c7.r AND c9.r = c8.r
ORDER BY r
Related
There's an array A[] having n elements. There's another array B[] of the same size n with every element initialized to zero. For every i in range 1 to n, elements of B[] in the range i-A_i to i+A_i (inclusive) need to be increased by 1.
I've already tried an O(n^2) solution using nested loop method. I cannot really figure out an O(n) solution if existent.
i=1;
while(i<=n)
{
start=(i-A[i]<1)?1:i-A[i];
end=(i+A[i]>n)?n:i+A[i];
while(start<=end)
{
B[start]+=1;
start+=1;
}
i+=1;
}
A naive implementation is to to increment each range per item in A, but you do not need to do taht. You can first "prepare" your array by adding 1 where the increment should start, and -1 where the increment should stop. Next you can calculate the cummulative sum of the array. Like:
def fill_list(la):
lb = [0]*len(la)
n1 = len(la)-1
for i, a in enumerate(la, 1):
xf, xt = i-a, i+a+1
lb[max(0, i-a)] += 1
if xt <= n1:
lb[xt] -= 1
c = 0
for i, b in enumerate(lb):
c += b
lb[i] = c
return lb
or if you want to return the range from 1 to n:
def fill_list1(la):
n1 = len(la)
lb = [0]*(n1+1)
for i, a in enumerate(la, 1):
xf, xt = i-a, i+a+1
lb[max(0, i-a)] += 1
if xt <= n1:
lb[xt] -= 1
c = 0
for i, b in enumerate(lb):
c += b
lb[i] = c
return lb[1:]
We can then for example generate a list with:
>>> fill_list([1,4,2,5,1,3,0,2])
[4, 4, 4, 5, 5, 5, 4, 3]
>>> fill_list1([1,2,3,4,5])
[5, 5, 4, 4, 3]
This thus has ranges for:
-3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11
--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
|-----|
|-----------------------|
|-----------|
|-----------------------------|
|-----|
|-----------------|
|
|-----------|
--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
0 1 1 1 1 0 0 1 0 0 -1 -1 -1 -2 -1
--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
0 1 2 3 4 4 4 5 5 5 4 3 3 1 0
The increments that are done before the start of the range (so with an index less than 0) are just placed at index 0 such that we take these into account. The ones that are done after the window (so with an index larger than or equal to n are simply ignored).
In the image the first row shows the indices, next we denote the ranges that arise from the same input, next we show the increments and decrements that would be put on an infinite tape, and next we show the cummulative sum.
The algorithm works in O(n): first we iterate over la in linear time, and increment and decrement the corresponding elements in b. Next we iterate over b, again in O(n) to calcuate the cummulative sum.
I have a vector a = [1 3 4 2 1 5 6 3 2]. Now I want to create a new vector 'b' with the cumsum of a, but after reaching a threshold, let's say 5, cumsum should reset and start again till it reaches the threshold again, so the new vector should look like this:
b = [1 4 4 2 3 5 6 3 5]
Any ideas?
You could build a sparse matrix that, when multiplied by the original vector, returns the cumulative sums. I haven't timed this solution versus others, but I strongly suspect this will be the fastest for large arrays of a.
% Original data
a = [1 3 4 2 1 5 6 3 2];
% Threshold
th = 5;
% Cumulative sum corrected by threshold
b = cumsum(a)/th;
% Group indices to be summed by checking for equality,
% rounded down, between each cumsum value and its next value. We add one to
% prevent NaNs from occuring in the next step.
c = cumsum(floor(b) ~= floor([0,b(1:end-1)]))+1;
% Build the sparse matrix, remove all values that are in the upper
% triangle.
S = tril(sparse(c.'./c == 1));
% In case you use matlab 2016a or older:
% S = tril(sparse(bsxfun(#rdivide,c.',c) == 1));
% Matrix multiplication to create o.
o = S*a.';
By normalizing the arguments of cumsum with the threshold and flooring you can get grouping indizes for accumarray, which then can do the cumsumming groupwise:
t = 5;
a = [1 3 4 2 1 5 6 3 2];
%// cumulative sum of normalized vector a
n = cumsum(a/t);
%// subs for accumarray
subs = floor( n ) + 1;
%// cumsum of every group
aout = accumarray( subs(:), (1:numel(subs)).', [], #(x) {cumsum(a(x))});
%// gather results;
b = [aout{:}]
One way is to use a loop. You create the first cumulative sum cs, and then as long as elements in cs are larger than your threshold th, you replace them with elements from the cumulative sum on the rest of the elements in a.
Because some elements in a might be larger than th, this loop will be infinite unless we also eliminate these elements too.
Here is a simple solution with a while loop:
a = [1 3 4 2 1 5 6 3 2];
th = 5;
cs = cumsum(a);
while any(cs>th & cs~=a) % if 'cs' has values larger that 'th',
% and there are any values smaller than th left in 'a'
% sum all the values in 'a' that are after 'cs' reached 'th',
% excluding values that are larger then 'th'
cs(cs>th & cs~=a) = cumsum(a(cs>th & cs~=a));
end
Calculate the cumulative sum and replace the indices value obeying your condition.
a = [1 3 4 2 1 5 6 3 2] ;
b = [1 4 4 2 3 5 6 3 5] ;
iwant = a ;
a_sum = cumsum(a) ;
iwant(a_sum<5) = a_sum(a_sum<5) ;
I have one array, for example B = [2,5,7], and also have a number C = 10, where C is always larger than or equal to the largest number in B.
and I want to generate an array A according to B and C. In this specific example, I have
A = [1, 2, 2, 2, 3, 4, 5, 5, 5, 6, 7, 7, 7, 8, 9, 10]
that is, I generate an array [1:C], but with each element in B are duplicated 3 times. Is there any good way that does not use for loop to generate array A?
Thank you!
You can use repelem (introduced in Matlab R2015a):
B = [2 5 7]
C = 10;
n = 3;
r = ones(1,C);
r(B) = n;
A = repelem(1:C, r)
How about...
B = [2,5,7];
C = 10;
A = sort([1:C,B,B])
I think the answer of #RPM could be faster. But because you specifically asked for a solution without sort:
B = [2,5,7];
C = 10;
D = setdiff(1:C,B)-1;
A = reshape(repmat(1:C,3,1),1,3*C);
A([3*D+1,3*D+2]) = [];
which will also return the correct result. I'm not to sure about the order setdiff() has. It might be worse than sort() in all cases. Especially with
A = sort([1:C,B,B]) as the input is already almost in order.
Following the same philosophy as this solution to Repeat copies of array elements: Run-length decoding in MATLAB, you can do something similar here, like this -
%// Get increment array (increments used after each index being repeated in B)
inc = zeros(1,C);
inc(B+1) = N-1
%// Calculate ID array (places in output array where shifts occur)
id = zeros(1,C+(N-1)*numel(B));
id(cumsum(inc) + (1:C)) = 1
%// Finally get cumulative summation for final output
A = cumsum(id)
Sample run -
B =
2 5 7
C =
10
N =
3
inc =
0 0 2 0 0 2 0 2 0 0
id =
1 1 0 0 1 1 1 0 0 1 1 0 0 1 1 1
A =
1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10
I am trying to allocate (x, y) points to the cells of a non-uniform rectangular grid. Simply speaking, I have a grid defined as a sorted non-equidistant array
xGrid = [x1, x2, x3, x4];
and an array of numbers x lying between x1 and x4. For each x, I want to find its position in xGrid, i.e. such i that
xGrid(i) <= xi <= xGrid(i+1)
Is there a better (faster/simpler) way to do it than arrayfun(#(x) find(xGrid <= x, 1, 'last'), x)?
You are looking for the second output of histc:
[~,where] = histc(x, xGrid)
This returns the array where such that xGrid(where(i)) <= x(i) < xGrid(where(i)+1) holds.
Example:
xGrid = [2,4,6,8,10];
x = [3,5,6,9,11];
[~,where] = histc(x, xGrid)
Yields the following output:
where =
1 2 3 4 0
If you want xGrid(where(i)) < x(i) <= xGrid(where(i)+1), you need to do some trickery of negating the values:
[~,where] = histc(-x,-flip(xGrid));
where(where~=0) = numel(xGrid)-where(where~=0)
This yields:
where =
1 2 2 4 0
Because x(3)==6 is now counted for the second interval (4,6] instead of [6,8) as before.
Using bsxfun for the comparisons and exploiting find-like capabilities of max's second output:
xGrid = [2 4 6 8]; %// example data
x = [3 6 5.5 10 -10]; %// example data
comp = bsxfun(#gt, xGrid(:), x(:).'); %'// see if "x" > "xGrid"
[~, result] = max(comp, [], 1); %// index of first "xGrid" that exceeds each "x"
result = result-1; %// subtract 1 to find the last "xGrid" that is <= "x"
This approach gives 0 for values of x that lie outside xGrid. With the above example values,
result =
1 3 2 0 0
See if this works for you -
matches = bsxfun(#le,xGrid(1:end-1),x(:)) & bsxfun(#ge,xGrid(2:end),x(:))
[valid,pos] = max(cumsum(matches,2),[],2)
pos = pos.*(valid~=0)
Sample run -
xGrid =
5 2 1 6 8 9 2 1 6
x =
3 7 14
pos =
8
4
0
Explanation on the sample run -
First element of x, 3 occurs last between ...1 6 with the criteria of xGrid(i) <= xi <= xGrid(i+1) at the backend of xGrid and that 1 is at the eight position, so the first element of the output pos is 8. This continues for the second element 7, which is found between 6 and 8 and that 6 is at the fourth place in xGrid, so the second element of the output is 4. For the third element 14 which doesn't find any neighbours to satisfy the criteria xGrid(i) <= xi <= xGrid(i+1) and is therefore outputted as 0.
If x is a column this might help
xg1=meshgrid(xGrid,1:length(x));
xg2=ndgrid(x,1:length(xGrid));
[~,I]=min(floor(abs(xg1-xg2)),[],2);
or a single line implementation
[~,I]=min(floor(abs(meshgrid(xGrid,1:length(x))-ndgrid(x,1:length(xGrid)))),[],2);
Example: xGrid=[1 2 3 4 5], x=[2.5; 1.3; 1.7; 4.8; 3.3]
Result:
I =
2
1
1
4
3
I am having trouble averaging y values based on their x counter parts.
For example
1 5
3 4
1 6
How do I get 5 and 6 to average based on being paired with an x value of 1? For my specific issue I will have 98 values between repeating 1's, and there will be a total of 99 1's in the array.
This is not extremely complicated, but it has been over a year since I have used matlab so being rusty has me scratching my head.
Here's what I got:
x = [1, 5;
3, 4;
1, 6]
col1 = x(:, 1) % extract first row
col1 =
1
3
1
ri = find(col1 == 1) % get row indices where 1 appears
ri =
1
3
mean(x(ri, 2)) % index into the second column of rows with a 1, and take average
ans = 5.5000