tags:

views:

267

answers:

1

Hello,

I wished I paid more attention to the math classes back in Uni. :)

How do I implement this math formula for naked triples?

Naked Triples
Take three cells C = {c1, c2, c3} that share a unit U. Take three numbers N = {n1, n2, n3}. If each cell in C has as its candidates ci ⊆ N then we can remove all ni ∈ N from the other cells in U.**

I have a method that takes a Unit (e.g. a Box, a row or a column) as parameter. The unit contains 9 cells, therefore I need to compare all combinations of 3 cells at a time that from the box, perhaps put them into a stack or collection for further calculation.

Next step would be taking these 3-cell-combinations one by one and compare their candidates against 3 numbers. Again these 3 numbers can be any possible combination from 1 to 9. Thats all I need.

But how would I do that? How many combinations would I get? Do I get 3 x 9 = 27 combinations for cells and then the same for numbers (N)?

How would you solve this in classic C# loops? No Lambda expression please I am already confused enough :)

Code: I had to cut the classes short in order to represent them here.

public class Cell : INotifyPropertyChanged
    {

public ObservableCollection<ObservableCollection<Candidate>> CandidateActual {...}

public int Id { ... }

//Position of the Cell inside a box if applicable
public int CellBoxPositionX { get; private set; }  
public int CellBoxPositionY { get; private set; }

//Position of the Cell inside the game board
public int CellBoardPositionX { get; private set; }
public int CellBoardPositionY { get; private set; }

//Position of the Box inside the game board
public int BoxPositionX { get; private set; }
public int BoxPositionY { get; private set; }

public int CountCandidates { ... }    
public int? Value { ...}

public Candidate this[int number]
        {
            get
            {
                if (number < 1 || number > PossibleValues.Count)
                {
                    throw new ArgumentOutOfRangeException("number", number, "Invalid Number Index");
                }

                switch (number)
                {
                    case 1:
                        return CandidateActual[0][0];
                    case 2:
                        return CandidateActual[0][1];
                    case 3:
                        return CandidateActual[0][2];
                    case 4:
                        return CandidateActual[1][0];
                    case 5:
                        return CandidateActual[1][1];
                    case 6:
                        return CandidateActual[1][2];
                    case 7:
                        return CandidateActual[2][0];
                    case 8:
                        return CandidateActual[2][1];
                    case 9:
                        return CandidateActual[2][2];
                    default:
                        return null;
                }
            }
        }
}

Candidate

public class Candidate : INotifyPropertyChanged
    {

        private int? _value;

        public int? Value { ... }

    }

Box:

public class Box : INotifyPropertyChanged
    {

public ObservableCollection<ObservableCollection<Cell>> BoxActual { ... }

public Cell this[int row, int column]
        {
            get
            {
                if(row < 0 || row >= BoxActual.Count)
                {
                    throw new ArgumentOutOfRangeException("row", row, "Invalid Row Index");
                }
                if(column < 0 || column >= BoxActual.Count)
                {
                    throw new ArgumentOutOfRangeException("column", column, "Invalid Column Index");
                }
                return BoxActual[row][column];
            }
        }
}

Board

public class Board : INotifyPropertyChanged 
    {

 public ObservableCollection<ObservableCollection<Box>> GameBoard {...}

public Cell this[int boardRowPosition, int boardColumnPosition]
        {
            get
            {
                int totalSize = GameBoard.Count*GameBoard.Count();
                if (boardRowPosition < 0 || boardRowPosition >= totalSize) 
                    throw new ArgumentOutOfRangeException("boardRowPosition", boardRowPosition, "Invalid boardRowPosition index");
                if (boardColumnPosition < 0 || boardColumnPosition >= totalSize)
                    throw new ArgumentOutOfRangeException("boardColumnPosition", boardColumnPosition, "Invalid boardColumnPosition index");
                return
                    GameBoard[boardRowPosition/GameBoard.Count][boardColumnPosition/GameBoard.Count][
                        boardRowPosition%GameBoard.Count, boardColumnPosition%GameBoard.Count];
            }
        }



        public Box this[int boardRowPosition, int boardColumnPosition, bool b]
        {
            get
            {
                int totalSize = GameBoard.Count * GameBoard.Count();
                if (boardRowPosition < 0 || boardRowPosition >= totalSize)
                    throw new ArgumentOutOfRangeException("boardRowPosition", boardRowPosition, "Invalid boardRowPosition index");
                if (boardColumnPosition < 0 || boardColumnPosition >= totalSize)
                    throw new ArgumentOutOfRangeException("boardColumnPosition", boardColumnPosition, "Invalid boardColumnPosition index");
                return
                    GameBoard[boardRowPosition / GameBoard.Count][boardColumnPosition / GameBoard.Count];
            }
        }
}

Many Thanks for any help,

+1  A: 

Psuedo-Code algorithm; my C is a bit rusty.

I recommend finding all of the possible three-number combinations from your candidate values. There can be anywhere from 6 to 504 such combinations, depending on how many candidates you have (given by n!/(3!*(n-3)!) ).

For each one, cycle through all of the cells in the unit and see if they match the condition that they don't have any numbers not in your combination. If a certain combination has three or more, then you can apply it.

combos = (array containing 3-long combination of candidates)
for each combo in combos                 # iterate through every combo
  matches = new array                    # initialize a blank array
  for each cell in unit
    if (cell does not contain candidates other than the ones in your current combo)
      matches.add(cell)                  # this is a match!
    end
  end

  if matches.size >= 3                   # naked triple found! (three matches for given combo)
    for each cell in unit
      if (cell is not in matches)
        (delete every candidate in current combo in this cell)
      end
    end
  end
  delete matches                         # clear up memory
end

Hope this helps! I'll C-ify this code if you need it; I've been meaning to brush up on my C anyways.

Also, in case you didn't already know, there's a much simpler way to solve Sudoku puzzles using computers that doesn't involve manually programming in any logic. But I think the way you're trying to do it is quite noble.


Generating an array of all possible combos

There are many ways to do this, and there might be a best one; I haven't done any serious research on it myself. I'd recommend google: combination algorithm... I actually found one solution in C myself.

Be sure to include a check to make sure that your number of candidates is appropriate. For n=3, there is only one possible candidate combination, and your algorithm should find it for you. For n=1 and n=2, Naked Triples doesn't even apply.

Justin L.
Thank you so much for this excellent Pseudo code. It makes sense and helped me to understand. Especially the use of Factorial is great. I still have problems to figure out this step:combos = (array containing 3-long combination of candidates)I don't know yet an efficient way to do this. Can you elaborate a bit more on this? Many Thanks
Kave
Regarding this c!/(c-3)!, does it apply to all possible candidates within the Unit right? But what is c in this case? The highest number of the candidates? I dont understand this...
Kave
c!/(c-3)! is the *count* of the possible combinations of candidates, given n possible candidates. So, with 6 possible candidates, you have 6!/3! = 120. Note that a much more efficient way to calculate this would be n*(n-1)*(n-2), which is equivalent and less expensive.I'll edit my post to include one of many methods to generate your combos array, because it'd be hard to format it in this box.
Justin L.
http://snippets.dzone.com/posts/show/4666 -- I hope this helps
Justin L.
Very interesting. Thanks for the help. The C example you showed me is very good. There I must assume that k is 3 for naked triples and 4 for naked quads etc. e.g. if I have 6 possible candidates appearing within my unit then my n would be 6? In the program comments it says n: The size of the set; for {1, 2, 3, 4} it's 4What if the set is {1,2,5} is it then 5? I dont understand this bit. I set k = 3 and n = 9, I get 84 combinations instead of 504. I must be missing something here...
Kave
n is the size of your candidates set. If you have candidates {1,2,5}, n = 3; if you have candiates {4,7,8,9}, n = 4.Also, I must apologize for an embarrassing error; the n!/(n-3)! equation was not for unique combinations...it counted both 123 and 321. The proper equation is n!/(3!*(n-3)!)
Justin L.
No need to apologize my friend. Without your help I would be biting my nails off on this problem. ;o) Your new formula makes a lot sense. :)One last bit I found problematic with the C example is the fact that this bit of the code: /* Setup comb for the initial combination */ int i; for (i = 0; i < k; ++i) comb[i] = i; Assumes your initial candidate starts always at 0. So in this case it would start with 0,1,2. but what if my candidates should be {4,5,8,9} ? Setting here n = 4 as you suggested and k = 3 wont work. I think the code that I pasted need a change too.
Kave
That change would mean I would have to fill comb[] with my existing candidates within unit instead of that loop. But what worries me is if the method next_comb() would still work this way...
Kave
You would have to set your candidates in an array, for example, c = {1,4,5,7,9}. The combination script would return the indices of your array you would pick. For example, returning {0,2,3}, your candidates would be c[0], c[2], c[3] -- 1, 5, 7.
Justin L.
Ahh very well. I understand now. I think there is nothing I don't understand anymore. ;o) I will give this a go tomorrow and will update you how it went. But it looks very promising now. ;o)
Kave
Hey Justin, I just wanted to let you know it worked!!! :) I was able to refactor my code to match the pseudo code and the unit test passes finally. Its beautiful! I was sitting on this problem for 2 weeks now. I really appreciate your help. This code should also cover naked quads. Now I have to start thinking about Hidden Triples.. :)
Kave
Haha no problem =) Glad to have helped.
Justin L.