I recently got interviewed and was asked the following question.
Given an n-ary tree, find the maximum path from root to leaf such that maximum path does not contain values from any two adjacent nodes.
(Another edit: The nodes would only have positive values.)
(Edit from comments: An adjacent node means node that share a direct edge. Because its a tree, it means parent-child. So if I include parent, I can not include child and vice versa.)
For example:
5
/ \
8 10
/ \ / \
1 3 7 9
In the above example, the maximum path without two adjacent would be 14 along the path 5->10->9. I include 5 and 9 in the final sum but not 10 because it would violate the no two adjacent nodes condition.
I suggested the following algorithm. While I was fairly sure about it, my interviewer did not seem confident about it. Hence, I wanted to double check if my algorithm was correct or not. It seemed to work on various test cases I could think of:
For each node X, let F(X) be the maximum sum from root to X without two adjacent values in the maximum sum.
The formula for calculating F(X) = Max(F(parent(X)), val(X) + F(grandParent(X)));
Solution would have been
Solution = Max(F(Leaf Nodes))
This was roughly the code I came up with:
class Node
{
int coins;
List<Node> edges;
public Node(int coins, List<Node> edges)
{
this.coins = coins;
this.edges = edges;
}
}
class Tree
{
int maxPath = Integer.MIN_VALUE;
private boolean isLeafNode(Node node)
{
int size = node.edges.size();
for(int i = 0; i < size; i++)
{
if(node.edges.get(i) != null)
return false;
}
return true;
}
// previous[0] = max value obtained from parent
// previous[1] = max value obtained from grandparent
private void helper(Node node, int[] previous)
{
int max = Math.max(previous[0], max.val + previous[1]);
//leaf node
if(isLeafNode(node))
{
maxPath = Math.max(maxPath, max);
return;
}
int[] temp= new int[2];
temp[0] = max;
temp[1] = prev[0];
for(int i = 0; i < node.edges.size(); i++)
{
if(node.edges.get(i) != null)
{
helper(node.edges.get(i), temp);
}
}
}
public int findMax(Node node)
{
int[] prev = new int[2];
prev[0] = 0;
prev[1] = 0;
if(node == null) return 0;
helper(node, prev);
return maxPath;
}
}
Edit: Forgot to mention that my primary purpose in asking this question is to know if my algorithm was correct rather than ask for a new algorithm.
Edit: I have a reason to believe that my algorithm should also have worked.
I was scouring the internet for similar questions and came across this question:
https://leetcode.com/problems/house-robber/?tab=Description
It is pretty similar to the problem above except that it is now an array instead of the tree.
The formal F(X) = Max(F(X-1), a[x] + F(X-2)) works in this case.
Here is my accepted code:
public class Solution {
public int rob(int[] nums) {
int[] dp = new int[nums.length];
if(nums.length < 1) return 0;
dp[0] = nums[0];
if(nums.length < 2) return nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < nums.length; i++)
{
dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]);
}
return dp[nums.length-1];
}
}
The natural solution would be to compute for each node X two values: max path from X to leaf including X and max path from X to leaf, excluding X, let's call them MaxPath(X) and MaxExcluded(X).
For leaf L MaxPath(L) is Value(L) and MaxExcluded(L) is 0.
For internal node X:
MaxPath(X) = Value(X) + Max over child Y of: MaxExcluded(Y)
MaxExcluded(X) = Max over child Y of : Max(MaxExcluded(Y), MaxPath(Y))
The first line means that if you include X, you have to exclude its children. The second means that if you exclude X, you are free to either include or exclude its children.
It's a simple recursive function on nodes which can be computed going leaves-to-parents in O(size of the tree).
Edit: The recursive relation does also work top-down, and in this case you can indeed eliminate storing two values by the observation that MaxExcluded(Y) is actually MaxPath(Parent(Y)), which gives the solution given in the question.
Implementation of what #RafałDowgird explained.
/* 5
* 8 10
* 1 3 7 9
* 4 5 6 11 13 14 3 4
*
*
*/
public class app1 {
public static void main(String[] args) {
Node root = new Node(5);
root.left = new Node(8);root.right = new Node(10);
root.left.left = new Node(1);root.left.right = new Node(3);
root.right.left = new Node(7);root.right.right = new Node(9);
root.left.left.left = new Node(4);root.left.left.right = new Node(5);
root.left.right.left = new Node(6);root.left.right.right = new Node(11);
root.right.left.left = new Node(13);root.right.left.right = new Node(14);
root.right.right.right = new Node(4);
System.out.println(findMaxPath(root));
}
private static int findMaxPath(Node root) {
if (root == null) return 0;
int maxInclude = root.data + findMaxPathExcluded(root);
int maxExcludeLeft = Math.max(findMaxPath(root.left), findMaxPathExcluded(root.left));
int maxExcludeRight = Math.max(findMaxPath(root.right), findMaxPathExcluded(root.right));
return Math.max(maxInclude, Math.max(maxExcludeLeft, maxExcludeRight));
}
private static int findMaxPathExcluded(Node root) {
if(root == null) return 0;
int left1 = root.left!=null ? findMaxPath(root.left.left) : 0;
int right1 = root.left!=null ? findMaxPath(root.left.right) : 0;
int left2 = root.right!=null ? findMaxPath(root.right.left) : 0;
int right2 = root.right!=null ? findMaxPath(root.right.right) : 0;
return Math.max(left1, Math.max(right1, Math.max(left2, right2)));
}
}
class Node{
int data;
Node left;
Node right;
Node(int data){
this.data=data;
}
}
I am trying to code dijkstra's algorithm, starting at any vertex I need to show the distance and print the path of nodes. It works for vertex 2,4, and 5, but for 1 and 3 it gets messed up. It's probably something stupidly small, but I can't see it.
public static void main(String[] args)
{
int INF = Integer.MAX_VALUE;
int verticies = 5;
int W[][] = {{INF,7,4,6,1},
{0,INF,0,0,0},
{0,2,INF,4,0},
{0,0,0,INF,0},
{0,0,0,1,INF}};
int startNode = 1;
dijkstra(W,verticies,startNode-1);
}
public static void dijkstra(int G[][],int n,int startnode)
{
int INF = Integer.MAX_VALUE, nINF = Integer.MIN_VALUE;
//int cost[MAX][MAX],distance[MAX],pred[MAX];
//int visited[MAX],count,mindistance,nextnode,i,j;
int cost[][] = new int[n][n];
int distance[] = new int[n];
int pred[] = new int[n];
boolean visited[] = new boolean[n];
int count=0, mindistance=0, nextnode=0,i,j;
//pred[] stores the predecessor of each node
//count gives the number of nodes seen so far
//create the cost matrix
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if(G[i][j]==0)
cost[i][j]=INF;
else
cost[i][j]=G[i][j];
//initialize pred[],distance[] and visited[]
for(i=0;i<n;i++)
{
distance[i]=cost[startnode][i];
pred[i]=startnode;
visited[i]=false;
}
distance[startnode]=0;
visited[startnode]=true;
count=1;
while(count<n-1)
{
mindistance=INF;
//nextnode gives the node at minimum distance
for(i=0;i<n;i++)
if(distance[i]<mindistance&&!visited[i])
{
mindistance=distance[i];
nextnode=i;
}
//check if a better path exists through nextnode
visited[nextnode]=true;
for(i=0;i<n;i++)
if(!visited[i])
if(mindistance+cost[nextnode][i]<distance[i])
{
distance[i]=mindistance+cost[nextnode][i];
pred[i]=nextnode;
}
count++;
}
//print the path and distance of each node
for(i=0;i<n;i++)
if(i!=startnode)
{
if(distance[i] == INF || distance[i] < 0){
System.out.print("\nNo edge exists between node "+(startnode+1)+" and node "+(i+1));
} else {
System.out.format("\nDistance of node %d = %d", (i + 1), distance[i]);
System.out.format("\nPath = %d", (i + 1));
j = i;
do {
j = pred[j];
System.out.format("<-%d", (j + 1));
} while (j != startnode);
}
}
}
I don’t know exactly how, but you are somehow getting INF into your calculations. My suspicion goes to the line distance[i]=mindistance+cost[nextnode][i];, but it may not be the only culprit, I have not checked. When mindistance is 1 (or greater) and cost is Integer.MAX_VALUE, you get an arithmetic overflow and the result gets negative. Further behaviour, I have not predicted, but it’s not as expected.
When in the two places you define INF I change the value to 1,000,000, I get the following output from your program:
Distance of node 2 = 6
Path = 2<-3<-1
Distance of node 3 = 4
Path = 3<-1
Distance of node 4 = 2
Path = 4<-5<-1
Distance of node 5 = 1
Path = 5<-1
I believe this is correct.
The way I found out? I stuck this statement into the middle of your outer while loop:
System.out.println("count " + count + " nextnode " + nextnode + " mindistance " + mindistance);
When it printed a large negative number, I started suspecting an arithmetic overflow. Until you learn to use a debugger, System.out.println() is your friend for debugging.
There's an error in the logic of what I've build at the moment.
What should be happening is that my code should display a grid of 0's and 1's.
Like so:
001001
101101
010110
110010
001101
So what has to happen here is that:
For each row there can't be more than 2 numbers of the same type consecutively
the numbers are picked randomly
for each column there can't be more than 2 numbers of the same type consecutively
there can be a maximum of 3 of each type of number going by column or row
edit: to further clarify
ok so I have a row like this:
0 1 0 1 1 0
- As you can see there will always be 3 x 1, and 3 x 0
- the order of numbers is picked randomly (so it might go 0 1, or 1 1, or 0 0 to start etc)
- there can never be more than 2 numbers of the same type consecutively, for instance if it's 001100, you can see that there were 2 0's, then it had to display a 1, but then there were 2 1's, so it had to display an 0. So 011100 couldn't happen (3 1's consecutively) or 000101 (3 0's consecutively)
Based upon this, but for now not essential, the same no 2 numbers consecutively must apply in columns (so in my successful example it goes 001001 across, there are at most 2 0's consecutively. But looking down you get 010101 (that is to say, once again, no more than 2 consecutively)
So my code is as follows:
import java.util.Random;
public class Main {
public static void main(String[] args) {
int l = 6;
int w = 6;
Random rd = new Random();
// Create a grid that is 6 x 6
int[][] grid = new int[l][w];
// for each row
for (int i = 0; i < l; i++) {
int zCount = 0;
int oCount = 0;
int current;
int lastA = 2;
int lastB = 2;
// for each item in the row
for (int j = 0; j < w; j++) {
// set the current item to either 0 or 1
current = rd.nextInt(2);
// make sure there aren't already (e.g. 3 items out of 6)
// items in the row
if (j % 2 == 1) {
// hold every second element
lastA = current;
} else {
// hold every first element
lastB = current;
}
if (current == 1) {
if (oCount != 3) {
if (lastA != lastB) {
// if the two previous items aren't the same
grid[i][j] = current;
// add to the counter
oCount++;
}
}
}
if (current == 0) {
if (zCount != 3) {
if (lastA != lastB) {
// if the two previous items aren't the same
grid[i][j] = current;
// add to the counter
zCount++;
}
}
}
System.out.print(grid[i][j]);
}
System.out.println(" ");
}
}
}
The problem is it generates as follows:
010010
100001
100010
000010
100001
001000
So obviously it doesn't conform to the first, third or fourth points.
I have absolutely no idea why! Except for the columns (third point) which I haven't initialised.
Can anybody work out what the logical failure is in my code?
Thanks for your help!
Here is my procedural solution which tries to keep the amount of required code as small as possible. It is capable of computing 2D-Arrays with arbitrary rows and columns like [6, 6] or [4, 7] or [3, 8] for example. The complexity of the algorithm is O(n) with n = rows * columns.
The program computes an arbitrary 2D-Array (grid) populated with either a 0 or 1. The grid guarantees the following characteristics, formulated mathematically:
∀ r,c ∈ Integer | 0 ≤ r < grid.rows, 0 ≤ c < grid.columns :
r - 2 ≥ 0 ⇒ cardinality( distinct( grid[r][c], grid[r-1][c], grid[r-2][c] )) = 2
r + 2 < grid.rows ⇒ cardinality( distinct( grid[r][c], grid[r+1][c], grid[r+2][c] )) = 2
c - 2 ≥ 0 ⇒ cardinality( distinct( grid[r][c], grid[r][c-1], grid[r][c-2] )) = 2
c + 2 < grid.columns ⇒ cardinality( distinct( grid[r][c], grid[r][c+1], grid[r][c+2] )) = 2
or in other words:
the grid does neither contain a row nor a column which has three or more consecutive 0's or 1's.
Below the Java code I will explain how the algorithm works and why it is designed as it is:
public static void main(String[] args) {
int[][] grid = anyGrid(8, 13);
}
private static int[][] anyGrid(int rows, int cols) {
int[][] grid = new int[rows][cols];
int row = 0;
for (int col = 0; col - row < cols; col++) {
for (int r = row; r >= 0 && col - r < cols;) {
setBit(grid, r, col - r--);
}
if (row < rows - 1) row++;
}
return grid;
}
private static void setBit(int[][] grid, int row, int col) {
int vInd = calcVerticalIndicator(grid, row, col);
int hInd = calcHorizontalIndicator(grid, row, col);
if (isPartiallyRestricted(vInd, hInd)) {
grid[row][col] = flip(vInd);
} else if (isFullyRestricted(vInd, hInd)) {
grid[row][col] = vInd;
grid[row - 1][col] = flip(vInd);
} else {
grid[row][col] = Math.abs(vInd) <= 1
? flip(vInd)
: Math.abs(hInd) <= 1 ? flip(hInd) : anyBit();
}
}
private static boolean isPartiallyRestricted(int vInd, int hInd) {
return vInd == hInd;
}
private static boolean isFullyRestricted(int vInd, int hInd) {
return vInd + hInd == 1;
}
private static int calcVerticalIndicator(int[][] grid, int row, int col) {
return calcIndicator(grid, row - 1, col, row - 2, col, 2);
}
private static int calcHorizontalIndicator(int[][] grid, int row, int col) {
return calcIndicator(grid, row, col - 1, row, col - 2, 4);
}
private static int calcIndicator(int[][] grid, int row1, int col1, int row2, int col2, int unrestricted) {
try {
return grid[row1][col1] * grid[row2][col2] + (grid[row1][col1] - grid[row2][col2]) * unrestricted;
} catch (IndexOutOfBoundsException e) {
return unrestricted;
}
}
private static int anyBit() {
return (int) (Math.random() * 2);
}
private static int flip(int bit) {
return bit == 0 ? 1 : 0;
}
The challenge we face is not to ensure that there are no three consecutive 0's or 1's in a row only or in a column only. The challenge is to ensure that no three consecutive 0's or 1's are neither in a row nor in a column by providing an efficient algorithm.
The tricky situation we may run into looks like this:
Let's consider the situation where all the cells at the top and to the left of the cell outlined in blue are already populated and do not violate the rules define above.
picture a) we want to populate the cell having a blue outline. The two cells at it's top are populated with two 0's while the cells at it's left are populated with two 1's. Which value should we choose? Due to symmetry it doesn't matter if we choose a 0 or a 1. Hence, let's go with a 0.
picture b) populating the cell outlined in blue with a 0 violates one rule defined above: the grid does not contain a column with three or more consecutive 0's or 1's. Hence we have to change the value of one of the two cells above of the blue cell.
picture c) say we change the value of the cell which is immediately above the blue cell, from 0 to 1. This could result in the violation of some rules, caused by the already populated cells to the left of the modified cell.
picture d) but a violation would mean that both cells to the left must have a value of 1.
picture e) this would imply that both cells to their top must have a value of 0 which is a contradiction to a situation we assumed. Therefore, changing the cell immediately at the top of the cell outlined in blue will not cause any violation of the rules.
To address the precondition, that no cells to the right of the modified cell are already populated, the algorithm populates the grid in a diagonal way. The population of cells occur in the order as shown below:
The final thing I like to explain is how the algorithm decides which values are available to choose from for each cell. For each cell it inspects the two top-most and two left-most cells and calculates an indication value. This value is used to determine the possible values for a cell by using arithmetic calculation as follows:
if the two cells inspected are both populated with 0's return an indicator value of 0.
if the two cells inspected are both populated with 1's return an indicator value of 1.
I have selected those two values because they communicate the fact, that this values are not permitted, in an intuitive way.
Then I selected a function to communicate if both, the column cells and the row cells, restrict the cell to populate by the same value. This is the case if both indicator values are equal. Keep this characteristic in mind, because we have to find values for the situation when no restriction applies from the column cells or the row cells.
If both indicators restrict the value to populate the cell with by a different value, the sum of them is 1. This is the second characteristic we have to keep in mind when searching for proper indicator values when no restriction applies.
The last thing the algorithm has to achieve is to find proper values when no restriction applies without compromising the unique indicators defined above.
Preserving the indication when the cell is restricted by the same value can be achieved by selecting values for the row and column indicators which are different from 0 and 1 and different from each other.
Preserving the indication when the cell is restricted by both values can be achieved by selecting values being greater than 1 and having a delta to each other of at least 2.
The algorithm does indicate no restriction for a row by the values 2 and -2 and for a column by the values 4 and -4. This values do not conflict with the operations used to identify the other two cases.
I hope this documentation helps to understand the whole program and how it does solve the problem statement. I am glad to hear your comments.
Many of the solutions given are extremely long and complicated. Here's a solution with very minimal code (Ideone Example here):
int row, col, n = 8;
int[][] grid = new int[n][n], cCount = new int[n][2], rCount = new int[n][2];
Deque<Entry<Integer,Integer>> freeInd = new ArrayDeque<Entry<Integer,Integer>>();
Random rand=new Random();
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
// Calcualte constraints: row, col = {-1, 0, 1}, -1 => no constraint.
row = j > 1 && grid[i][j-2] == grid[i][j-1] ? (grid[i][j-1] == 0 ? 1:0):
(rCount[i][0] >= n/2 ? 1: // too many 0's
(rCount[i][1] >= n/2 ? 0:-1)); // too many 1's
col = i > 1 && grid[i-2][j] == grid[i-1][j] ? (grid[i-1][j] == 0 ? 1:0):
(cCount[j][0] >= n/2 ? 1: // too many 0's
(cCount[j][1] >= n/2 ? 0:-1)); // too many 1's
grid[i][j] = row == -1 && col == -1 ? rand.nextInt(2):(row > -1 ? row:col);
// Handle Constraints
if( row == -1 && col == -1){ // no constraint
freeInd.push(new SimpleEntry<Integer,Integer>(i, j)); // add to free indices
} else if( (row > -1 && col > -1 && row != col) // constraint conflict
|| (row > -1 && rCount[i][row] >= n/2) // count conflict
|| (col > -1 && cCount[j][col] >= n/2)){ // count conflict
Entry<Integer, Integer> last = freeInd.pop(); // grab last free index
while(i > last.getKey() || j > last.getValue()){
j = (j-1+ n)%n; // step indices back
i = (j == n-1) ? i-1:i;
rCount[i][grid[i][j]]--; // reduce counters
cCount[j][grid[i][j]]--;
}
grid[i][j] = grid[i][j] == 0 ? 1:0; // flip value
}
rCount[i][grid[i][j]]++; // increment counters
cCount[j][grid[i][j]]++;
}
}
The idea here is that you walk along each row of the matrix adding 0's and 1's abiding by the following rules:
If the current index is unconstrained (i.e. it can be 0 or 1) we choose a value randomly.
If the current index is constrained we force it to have the constrained value.
If there are multiple constraints that do not agree, we revert back to the last unconstrained index (freeInd) by first incrementally stepping backwards along the rows of the matrix, decrementing the count for the given value (0 or 1). E.g. this is done for rows with rCount[i][grid[i][j]]--. When the unconstrained vertex is finally reached, flip it's value.
Finally, increment the count of the value (0 or 1) for the current row and column. E.g. this is done for rows with rCount[i][grid[i][j]]++
The 1st problem which i found in your solution is it's initializing the value of counter value (ocount and zcount) as zero and the only way grid(array) is assigned a value is when if it's greater than three, and the way i see if i am not mistaken the value of counter is incremented in the loop in which they are checked to be greater than 3, and that condition can never be reached .
To solve this problem use the algo of backtracking by assigning the new value to a different value if the calue
A working code in jsFiddle (for 6x6 grids):
$(function(){
function print(str){
$("body").append(str + "<br/>");
}
function toBin(num, length){
if(!length){
length = 3;
}
var str = num.toString(2);
while(str.length < length){
str = 0 + str;
}
return str;
}
var wrongAnds = [
parseInt('000000111', 2),
parseInt('000111000', 2),
parseInt('111000000', 2),
parseInt('100100100', 2),
parseInt('010010010', 2),
parseInt('001001001', 2),
];
var wrongOrs = [
parseInt('111111000', 2),
parseInt('111000111', 2),
parseInt('000111111', 2),
parseInt('011011011', 2),
parseInt('101101101', 2),
parseInt('110110110', 2),
];
function test(mask){
for (var i = 0; i < 6; i++) {
if((wrongAnds[i] & mask) == wrongAnds[i]){
return false;
}
if((wrongOrs[i] | mask) == wrongOrs[i]){
return false;
}
}
return true;
}
var threeGrid = [];
var toRight = [];
var toBottom = [];
for(var mask = 1<<9-1; mask >= 0; mask--){
if(test(mask)){
threeGrid.push(mask);
}
}
function numberOfSetBits(i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
function getCol(grid, col){
var ret = 0;
for(var i=0; i<3; i++){
ret += (grid & (1 << (i*3+col))) >> (i*2+col);
}
return ret;
}
var wrongAnds6 = [
parseInt('011100', 2),
parseInt('001110', 2)
];
var wrongOrs6 = [
parseInt('100011', 2),
parseInt('110001', 2)
];
for(var i = 0; i < threeGrid.length; i++){
for(var j = 0; j < threeGrid.length; j++){
var grid1 = threeGrid[i];
var grid2 = threeGrid[j];
var toRightOk = true;
var toBottomOk = true;
var printit = (i==0);
for(var k=0;k<3;k++){
var row = ((grid1 & wrongAnds[k]) << 3 >> (k*3)) + ((grid2 & wrongAnds[k]) >> (k*3));
var col = ((getCol(grid1, k)) << 3) + ((getCol(grid2, k)));
if(numberOfSetBits(row) != 3
|| ((wrongAnds6[0] & row) == wrongAnds6[0])
|| ((wrongAnds6[1] & row) == wrongAnds6[1])
|| ((wrongOrs6[0] | row) == wrongOrs6[0])
|| ((wrongOrs6[1] | row) == wrongOrs6[1])
) {
toRightOk = false;
}
if(numberOfSetBits(col) != 3
|| ((wrongAnds6[0] & col) == wrongAnds6[0])
|| ((wrongAnds6[1] & col) == wrongAnds6[1])
|| ((wrongOrs6[0] | col) == wrongOrs6[0])
|| ((wrongOrs6[1] | col) == wrongOrs6[1])
) {
toBottomOk = false;
}
}
if(toRightOk){
if(!toRight[grid1]){
toRight[grid1] = [];
}
toRight[grid1].push(grid2);
}
if(toBottomOk){
if(!toBottom[grid1]){
toBottom[grid1] = [];
}
toBottom[grid1].push(grid2);
}
}
}
function intersect(arr1, arr2){
var results = [];
for (var i = 0; i < arr1.length; i++) {
if (arr2.indexOf(arr1[i]) !== -1) {
results.push(arr1[i]);
}
}
return results;
}
var found = false;
while(!found){
var grid1 = threeGrid[0];
var grid1 = threeGrid[Math.floor(Math.random()*threeGrid.length)];
var grid2 = toRight[grid1][Math.floor(Math.random()*toRight[grid1].length)];
var grid3 = toBottom[grid1][Math.floor(Math.random()*toBottom[grid1].length)];
var arr4 = intersect(toBottom[grid2], toRight[grid3]);
if(arr4.length > 0){
var grid4 = arr4[Math.floor(Math.random()*arr4.length)];
found = true;
}
}
function gridToStrings(grid){
var rowS = [];
for(var i=0; i<3; i++){
rowS.push(toBin(((grid & wrongAnds[i]) >> (i*3))));
}
return rowS;
}
var grid1S = gridToStrings(grid1);
var grid2S = gridToStrings(grid2);
var grid3S = gridToStrings(grid3);
var grid4S = gridToStrings(grid4);
print(grid1S[0] + grid2S[0]);
print(grid1S[1] + grid2S[1]);
print(grid1S[2] + grid2S[2]);
print(grid3S[0] + grid4S[0]);
print(grid3S[1] + grid4S[1]);
print(grid3S[2] + grid4S[2]);
});
Theory:
Find all possible 3x3 grids
Find all possible left-to-right and top-to-bottom pairings
get 4 random grids to form the 6x6 grid
Implementation:
Represent 3x3 grids as 9bit integers. A 3x3 grid is wrong if there are 3 1s or 3 0s in it. This can be easily filtered with a couple bitwise operations.
Test the Cartesian product of these 3x3 grids (Compare every grid with every grid). Check if there are exactly 3 0s and 3 1s in all rows and columns (put the second grid right to the first grid to check 3 rows, and put it below the first grid to check 3 columns), and that there are no consecutive 3 0s or 1s.
get the top-left, top-right and bottom-right grids. Check if there is an available 4th grid that can go below the top-right grid and right to the bottom-left grid. If there is none, restart step 4, otherwise pick one.
A couple outputs:
011010
100101
001011
110100
101100
010011
110010
101100
010011
001101
100110
011001
001101
110010
010011
101100
110100
001011
Edit:
there is only 1120 solutions to this problem (jsFiddle). There are 2^36 ways to fill a 6x6 grid with 0s and 1s. If you used brute force (get a random 6x6 grid, then check if its right), that would mean an average ~61356676 (6.1*10^7) executions to find a correct solution. Even thought your method is somewhat faster (it can fail sooner if its not the last digit thats wrong), it might still be slow.
I think there are two problems with your code:
If oCount or zCount have become 3 there are no more assignments grid[i][j]=current if the random value is not acceptable. You get zeroes at these positions (to which the grid was initialized).
Near the right bottom there might not be any more valid solutions. You would have to undo previous assignments, i.e. you would need to do some kind of backtracking.
I would recommend starting with a valid solution and transforming this solution step by step according to random values for grid positions - but only if this is possible without breaking validity. If have prepared an example implementation:
public static void main(String[] args) {
int l = 6, w = 6;
Grid g = new Grid(l, w);
Random rd = new Random();
// initialize with checkerboard pattern (which is a valid solution)
for (int y = 0; y < l; y++) for (int x = 0; x < w; x++) g.arr[y][x] = (x ^ y) & 1;
// construct a valid grid by transformation of grids while preserving validity
for (int y = 0; y < l; y++) for (int x = 0; x < w; x++) {
int v = rd.nextInt(2), v2 = v ^ 1;
if (g.arr[y][x] == v) continue;
// try to modify current grid by exchanging values: 01/10=>10/01 or 10/01=>01/10
// (keep parts of the grid which have already been adapted to random values)
rotating: for (int y2 = y + 1; y2 < l; y2++) for (int x2 = x; x2 < w; x2++) {
if (g.arr[y2][x] == v && g.arr[y][x2] == v && g.arr[y2][x2] == v2) {
g.rotate(x, y, x2, y2);
// keep result if grid is still valid, undo otherwise
if (g.rotatedOk(x, y, x2, y2)) break rotating;
g.rotate(x, y, x2, y2);
}
}
}
g.printOn(System.out);
}
public static class Grid {
int l, w;
int[][] arr;
Grid(int l, int w) {
this.arr = new int[this.l = l][this.w = w];
}
void rotate(int x, int y, int x2, int y2) {
int v;
v = arr[y][x]; arr[y][x] = arr[y2][x]; arr[y2][x] = v;
v = arr[y][x2]; arr[y][x2] = arr[y2][x2]; arr[y2][x2] = v;
}
boolean rotatedOk(int x, int y, int x2, int y2) { // check after rotation
return okAt(x, y) && okAt(x2, y) && okAt(x, y2) && okAt(x2, y2);
}
private boolean okAt(int x, int y) { // check single position in grid
int v = arr[y][x];
if (count(x, y, -1, 0, v) + count(x, y, 1, 0, v) > 1) return false;
if (count(x, y, 0, -1, v) + count(x, y, 0, 1, v) > 1) return false;
return true;
}
private int count(int x, int y, int dx, int dy, int v) {
for (int n = 0; ; n++) {
x += dx; y += dy;
if (x < 0 || x >= w || y < 0 || y >= l || arr[y][x] != v) return n;
}
}
void printOn(PrintStream s) {
for (int y = 0; y < l; y++) { for (int x = 0; x < w; x++) s.print(arr[y][x]); s.println(); }
}
}
The problem with your approach is that you need a mechanism that handles when a new value can't be used because it follows two similar values, but the other value can't be used because it is under two other values. For example, say your grid has got this far:
101010
011010
00?
You would then need to slowly roll back positions and try different values.
The following code solves that problem using recursion:
import java.util.Random;
public class Main {
final int height = 6;
final int width = 6;
int[][] grid;
Random rd = new Random();
public static void main(final String[] args) {
Main main = new Main();
main.process();
}
private void process() {
// Create a grid that is 6 x 6
grid = new int[height][width];
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
grid[x][y] = -1;
}
}
recurseFillMatrix(0, 0);
}
private boolean recurseFillMatrix(final int x, final int y) {
// first, try putting a random number in the cell
int attempt = 1;
grid[x][y] = Math.abs(rd.nextInt()%2);
do {
if(isGridValid()) {
if(x == (width - 1) && y == (height - 1)) {
printGrid();
return true;
}
boolean problemSolved;
if(x == (width - 1)) {
problemSolved = recurseFillMatrix(0, y + 1);
} else {
problemSolved = recurseFillMatrix(x + 1, y);
}
if(problemSolved) {
return true;
}
}
attempt++;
grid[x][y] = 1 - grid[x][y];
} while(attempt <= 2);
grid[x][y] = -1;
return false;
}
private boolean isGridValid() {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
// if the current item is -1, then we are finished
if(grid[x][y] == -1) {
return true;
}
// if we are after the second column
if(x > 1) {
if(grid[x-2][y] == grid[x-1][y] && grid[x-1][y] == grid[x][y]) {
return false;
}
}
// if we are after the second row
if(y > 1) {
if(grid[x][y-2] == grid[x][y-1] && grid[x][y-1] == grid[x][y]) {
return false;
}
}
// total the values in this column
int total = 0;
for(int i = 0; i <= y; i++) {
total += grid[x][i];
}
if(y == (height - 1)) {
if(total != 3) {
return false;
}
} else {
if(total > 3) {
return false;
}
}
// total the values in this row
total = 0;
for(int i = 0; i <= x; i++) {
total += grid[i][y];
}
if(x == (width - 1)) {
if(total != 3) {
return false;
}
} else {
if(total > 3) {
return false;
}
}
}
}
return true;
}
private void printGrid() {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
System.out.print(grid[x][y]);
}
System.out.println("");
}
}
}
The isGridValid() method uses your defined rules to check if the grid (as it is filled so far) complies with the rules. At the first sign that it does not, it returns false.
If I have to change your solution to achieve the result, here is what it should look like..
Take the incrementors for oCount and zCount in a separate if-else
Take the assignment to grid(i,j) outside the loop
Your if-else block is not taking into account every condition possible, like
What about when last 2 items are same
What about when the zCount or oCount has reached 3
Taking into account these consideration, this code works fine.
import java.util.Random;
public class Main {
public static void main(String[] args) {
int l = 6;
int w = 6;
Random rd = new Random();
// Create a grid that is 6 x 6
int[][] grid = new int[l][w];
// for each row
for (int i = 0; i < l; i++) {
int zCount = 0;
int oCount = 0;
int current;
int lastA = 2;
int lastB = 2;
// for each item in the row
for (int j = 0; j < w; j++) {
// set the current item to either 0 or 1
current = rd.nextInt(2);
// make sure there aren't already (e.g. 3 items out of 6)
// items in the row
if (current == 1) {
if (oCount != 3) {
if (lastA == lastB) {
current = lastA == 1 ? 0 : 1;
}
} else {
current = current == 1 ? 0 : 1;
}
} else if (current == 0) {
if (zCount != 3) {
if (lastA == lastB) {
current = lastA == 1 ? 0 : 1;
}
} else {
current = current == 1 ? 0 : 1;
}
}
grid[i][j] = current;
if (current == 1) {
oCount++;
} else {
zCount++;
}
if (j % 2 == 1) {
// hold every second element
lastA = current;
} else {
// hold every first element
lastB = current;
}
System.out.print(grid[i][j]);
}
System.out.println(" ");
}
}
}
Again, This solution takes care of row conditions only. You would need to do similar checks for columns as well, to achieve the full result
HTH
here I tested you problem and seems that it is what you need.
I used a functional approach using Guava, it is quite simple, readable and has a short code.
#Test
public void test_permutations()
{
List<Integer> binary = Lists.newArrayList(1,0,1,0,1,0); // Domain list
Set<String> flattenSet = Sets.newHashSet(); // Store non-repetitive values
// Create list of possible values
Collection<List<Integer>> permutations = Collections2.permutations(binary);
for (List<Integer> permutation : permutations)
{
String joinString = StringUtils.join(permutation, "");
flattenSet.add(joinString);
}
// Create predicate to filter positive values
Predicate<String> predicate = new Predicate<String>() {
public boolean apply(String input) {
// Discard wrong values
if (input.contains("000") || input.contains("111")) {
return false;
} else {
return true;
}
}
};
// Use predicate to filter values
Collection<String> filteredList = Collections2.filter(flattenSet, predicate);
// Display result
for (String result : filteredList) {
System.out.println(result);
}
}
It is simple, I've commented the code to be clear but you can debug it to understand step by step.
The generated output is:
010011
110010
010101
010110
100110
101001
011010
110100
001011
001101
011001
101010
101100
100101
Hope to help
I think that its a mistake to think of generating it one element at at time. Instead imagine that I generate the entire set of permissible rows {001100,101010,....etc} There are only 6!/(3!3!)=20 ways to arrange three ones and three and some of them will be excluded. Now I am going to generate a game tree by saying that a move is selecting a valid row for the next row. If I discover at some point that there are no more valid moves then i will back track and try a different move.
To generate a move I randomly select a row, if its a valid move, I try to select another move, if that is impossible I backtrack, effectively doing a (random) depth first search of the game tree.
public class gametree {
public static ImmutableList<Row> allValidRows = // create a list of all valid rows.
public static List<Rows> getValidMoves(Move parent){ //Backtracks up the
//tree to the root to find the current state of the board, and returns
//which ever of allValidRows are valid given the game board.
}
public class Move {
public final Move parent;
public List<Rows> validMoves;
public final Row thisMove;
public int depth=0;
Move(Move parent, Row thisMove){
this.thisMove = thisMove;
this.parent = parent;
this.validMoves = getValidMoves(parent);
Move hold=parent;
while(hold!=null){
depth++; hold = parent.parent;
}
}
}
void run {
//pick first move
Move Root = new Move(null, Collections.Shuffle(allValidRows).get(0));
Move FinalMove = search(Root);
//Something to print out the answer here
}
public Move search(Move move){
if(depth==5){ return Move} //If I get to row six I win.
else if(move.validMoves.isEmpty()) { //If there are no valid moves,
//then this move wasnt valid, to strip it from the parent's
//possible moves and try again
move.parent.validMoves.remove(move.thisMove);
search(move.parent);
} else { //pick a random valid move and create a nextMove
Move nextMove = new Move(move, Collection.Shuffle(move.getValidMoves).get(0))
search(nextMove);
}
}
The worst case for this algorithm is that there is only one victory state and it has to try every possible state, but in practice this game does not seem very restrictive so it will probably not take long at all.
This code is strictly illustrative.