I am trying to create an application to record the time elapsed per machine using simple arithmetic operations.
Using console application, with parameters of number of loop and the threads to use with the code below:
public static Int64 IterationCount { get; set; }
static void Main(string[] args)
{
int iterations = int.Parse(args[0]);
int threads = int.Parse(args[1]);
IterationCount = iterations * 1000000000;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < threads; i++)
{
Task.Factory.StartNew(() => Calculate());
Task.WaitAll();
}
sw.Stop();
Console.WriteLine("Elapsed={0}", sw.Elapsed);
}
And my Calculate method:
private static void Calculate()
{
for (int i = 0; i < IterationCount; i++)
{
a = 1 + 2;
b = 1 - 2;
c = 1 * 2;
a = 1 / 2;
}
}
Now I think this is not working because the result of my elapsed time when I entered 10 iterations (I am multiplying the first parameter to 1 billion: 10 * 1,000,000,000) and 4 threads is:
00:00:00:0119747
Any thing I missed?
Your call to Task.WaitAll() has no effect as the signature of the function is
public static void WaitAll(params Task[] tasks).
You see, you can supply a variable count of Tasks to wait for and you call this function with no task; so it will not wait at all.
If you replace your code by the following, you will see the effect.
Task[] tasks = new Task[threads];
for (int i = 0; i < threads; i++)
{
tasks[i] = Task.Factory.StartNew(() => Calculate());
}
Task.WaitAll(tasks);
Turns out my comment is accurate. If I copy the contents of your Calculate method into Visual Studio:
private static void Calculate()
{
for (int i = 0; i < IterationCount; i++)
{
a = 1 + 2;
b = 1 - 2;
c = 1 * 2;
d = 1 / 2;
}
}
after compilation, the generated C# code looks like this:
private static void Calculate()
{
for (int i = 0; i < Program.IterationCount; i++)
{
Program.a = 3;
Program.b = -1;
Program.c = 2;
Program.d = 0;
}
}
Instead, you're going to have to make one of the constants into a variable:
private static void Calculate()
{
int x = 1;
for (int i = 0; i < IterationCount; i++)
{
a = x + 2;
b = x - 2;
c = x * 2;
d = x / 2;
}
}
This code becomes:
private static void Calculate()
{
int x = 1;
for (int i = 0; i < Program.IterationCount; i++)
{
Program.a = x + 2;
Program.b = x - 2;
Program.c = x * 2;
Program.d = x / 2;
}
}
I am solving this problem, in which they ask for the index of the first Fibonacci number of 1000 digits, and my first idea was something similar to:
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (x.NoOfDigits < 1000)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
However, as far as I can tell, there is no method for counting the number of digits of a BigInteger. Is this true? One way of circumventing it is to use the .ToString().Length method of a BigInteger, but I'm told that string processing is slow.
A BigInteger also has a .ToByteArray(), and I thought of converting a BigInteger to a byte array and checking the length of that array - but I don't think that this uniquely determines the number of digits in the BigInteger.
For what it's worth, I implemented another way of solving it, which is manually storing the Fibonacci numbers in array, and which stops as soon as the array is full, and I compared this to the .ToString-based method, which is about 2.5 times slower, but the first method takes 0.1 second, which also seems like a long time.
Edit: I've tested the two suggestions in the answers below (the one with BigInteger.Log and the one with MaxLimitMethod). I get the following run times:
Original method: 00:00:00.0961957
StringMethod: 00:00:00.1535350
BigIntegerLogMethod: 00:00:00.0387479
MaxLimitMethod: 00:00:00.0019509
Program
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
Stopwatch clock = new Stopwatch();
clock.Start();
int index1 = Algorithms.IndexOfNDigits(1000);
clock.Stop();
var elapsedTime1 = clock.Elapsed;
Console.WriteLine(index1);
Console.WriteLine("Original method: {0}",elapsedTime1);
Console.ReadKey();
clock.Reset();
clock.Start();
int index2 = Algorithms.StringMethod(1000);
clock.Stop();
var elapsedTime2 = clock.Elapsed;
Console.WriteLine(index2);
Console.WriteLine("StringMethod: {0}", elapsedTime2);
Console.ReadKey();
clock.Reset();
clock.Start();
int index3 = Algorithms.BigIntegerLogMethod(1000);
clock.Stop();
var elapsedTime3 = clock.Elapsed;
Console.WriteLine(index3);
Console.WriteLine("BigIntegerLogMethod: {0}", elapsedTime3);
Console.ReadKey();
clock.Reset();
clock.Start();
int index4 = Algorithms.MaxLimitMethod(1000);
clock.Stop();
var elapsedTime4 = clock.Elapsed;
Console.WriteLine(index4);
Console.WriteLine("MaxLimitMethod: {0}", elapsedTime4);
Console.ReadKey();
}
}
static class Algorithms
{
//Find the index of the first Fibonacci number of n digits
public static int IndexOfNDigits(int n)
{
if (n == 1) return 1;
int[] firstNumber = new int[n];
int[] secondNumber = new int[n];
firstNumber[0] = 1;
secondNumber[0] = 1;
int currentIndex = 2;
while (firstNumber[n-1] == 0)
{
int carry = 0, singleSum = 0;
int[] tmp = new int[n]; //Placeholder for the sum
for (int i = 0; i<n; i++)
{
singleSum = firstNumber[i] + secondNumber[i];
if (singleSum >= 10) carry = 1;
else carry = 0;
tmp[i] += singleSum % 10;
if (tmp[i] >= 10)
{
tmp[i] = 0;
carry = 1;
}
int countCarries = 0;
while (carry == 1)
{
countCarries++;
if (tmp[i + countCarries] == 9)
{
tmp[i + countCarries] = 0;
tmp[i + countCarries + 1] += 1;
carry = 1;
}
else
{
tmp[i + countCarries] += 1;
carry = 0;
}
}
}
for (int i = 0; i < n; i++ )
{
secondNumber[i] = firstNumber[i];
firstNumber[i] = tmp[i];
}
currentIndex++;
}
return currentIndex;
}
public static int StringMethod(int n)
{
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (x.ToString().Length < n)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
}
public static int BigIntegerLogMethod(int n)
{
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (Math.Floor(BigInteger.Log10(x) + 1) < n)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
}
public static int MaxLimitMethod(int n)
{
BigInteger maxLimit = BigInteger.Pow(10, n - 1);
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (x.CompareTo(maxLimit) < 0)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
}
}
Provided that x > 0
int digits = (int)Math.Floor(BigInteger.Log10(x) + 1);
will get the number of digits.
Out of curiosity, I tested the
int digits = x.ToString().Length;
approach. For 100 000 000 iterations, it's 3 times slower than the Log10 solution.
Expanding on my comment--instead of testing based on number of digits, test based on exceeding a constant that has the upper limit of the problem:
public static int MaxLimitMethod(int n)
{
BigInteger maxLimit = BigInteger.Pow(10, n);
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (x.CompareTo(maxLimit) < 0)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
}
This should result in a significant performance increase.
UPDATE:
This is an even quicker method on .NET 5 (since GetBitLength() is required):
private static readonly double exponentConvert = Math.Log10(2);
private static readonly BigInteger _ten = 10;
public static int CountDigits(BigInteger value)
{
if (value.IsZero)
return 1;
value = BigInteger.Abs(value);
if (value.IsOne)
return 1;
long numBits = value.GetBitLength();
int base10Digits = (int)(numBits * exponentConvert).Dump();
var reference = BigInteger.Pow(_ten, base10Digits);
if (value >= reference)
base10Digits++;
return base10Digits;
}
The slowest part of this algorithm for large values is the BigInteger.Pow() operation. I have optimized the CountDigits() method in Singulink.Numerics.BigIntegerExtensions with a cache that holds powers of 10, so check that out if you are interested in the fastest possible implementation. It caches powers up to exponents of 1023 by default but if you want to trade memory usage for faster performance on even larger values you can increase the max cached exponent by calling BigIntegerPowCache.GetCache(10, maxSize) where maxSize = maxExponent + 1.
On an i7-3770 CPU, this library takes 350ms to get the digit count for 10 million BigInteger values (single-threaded) when the digit count <= the max cached exponent.
ORIGINAL ANSWER:
The accepted answer is unreliable, as indicated in the comments. This method works for all numbers:
private static int CountDigits(BigInteger value)
{
if (value.IsZero)
return 1;
value = BigInteger.Abs(value);
if (value.IsOne)
return 1;
int exp = (int)Math.Ceiling(BigInteger.Log10(value));
var test = BigInteger.Pow(10, exp);
return value >= test ? exp + 1 : exp;
}
I used to think if a method is inlined, then theoretically it is identical to the merge of the method and the calling method, but the benchmark showed there is slightly difference in performance
e.g. this takes 100ms
public long TestA()
{
long ret = 0;
for (int n = 0; n < testSize; n++)
{
for (int i = 0; i < a; i++)
for (int j = 0; j < b; j++)
{
ret += myArray[i][j];
}
}
return ret;
}
this takes 110ms (if I force MethodImplOptions.NoInlining on GetIt then it will be 400ms, so I assume it is auto inlined)
public long TestB()
{
long ret = 0;
for (int n = 0; n < testSize; n++)
{
for (int i = 0; i < a; i++)
for (int j = 0; j < b; j++)
{
ret += GetIt(i, j);
}
}
return ret;
}
int GetIt(int x, int y)
{
return myArray[x][y];
}
OK, I attach a snippet of benchmark function i used
public static void RunTests(Func<long> myTest)
{
const int numTrials = 100;
Stopwatch sw = new Stopwatch();
double[] sample = new double[numTrials];
Console.WriteLine("Checksum is {0:N0}", myTest());
sw.Start();
myTest();
sw.Stop();
Console.WriteLine("Estimated time per test is {0:N0} ticks\n", sw.ElapsedTicks);
if (sw.ElapsedTicks < 100)
{
Console.WriteLine("Ticks per test is less than 100 ticks. Suggest increase testSize.");
return;
}
if (sw.ElapsedTicks > 10000)
{
Console.WriteLine("Ticks per test is more than 10,000 ticks. Suggest decrease testSize.");
return;
}
for (int i = 0; i < numTrials / 3; i++)
{
myTest();
}
string testName = myTest.Method.Name;
Console.WriteLine("----> Starting benchmark {0}\n", myTest.Method.Name);
for (int i = 0; i < numTrials; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
sw.Restart();
myTest();
sw.Stop();
sample[i] = sw.ElapsedTicks;
}
double testResult = DataSetAnalysis.Report(sample);
DataSetAnalysis.ConsistencyAnalyze(sample, 0.1);
Console.WriteLine();
for (int j = 0; j < numTrials; j = j + 5)
Console.WriteLine("{0,8:N0} {1,8:N0} {2,8:N0} {3,8:N0} {4,8:N0}", sample[j], sample[j + 1], sample[j + 2], sample[j + 3], sample[j + 4]);
Console.WriteLine("\n----> End of benchmark");
}
The resulting number of IL instructions differs slightly, the maxstack differs significantly:
TestA:
// Code size 78 (0x4e)
.maxstack 2
TestB:
// Code size 88 (0x58)
.maxstack 4
GetIt:
// Code size 7 (0x7)
.maxstack 1
C# does inline at JIT, so whether inline or not IL doesn't change.
MethodImplOptions.NoInlining is not the same as inline keyword in F#
I have a one assignment
I have to make one dimension array with 20 numbers - first 10 numbers are from 1 do 10. others 10 numbers I have to get in method called Dopolni - where I have to sum together array with one another like - p11 = p0+p1, p12 = p1+p2, p14 = p2+p3 and so on - I dont know how to arrange it to get those other 10 numbers - help please
my code till now is
static void Dopolni(int[] p)
{
for (int i = 11; i < 20; i++)
{
p[i] = p[i] + 1;
}
}
static void Main(string[] args)
{
int[] p = new int[20];
for (int i = 1; i < 20; i++)
{
if (i <= 10)
{
p[i] += i;
}
Console.WriteLine("{0}", p[i]);
}
Dopolni(p);
Console.WriteLine(p);
Console.ReadKey(true);
}
All numbers I have to write out in main window. Hope someone can help out
The indices of the first 10 numbers range from 0 to 9, the others from 10 to 19. But since you always sum two consecutive numbers, you will only get 9 sums! In order to get 10 sums, you could start by summing 0 with p[0]:
int previous = 0;
for (int i = 0; i < 10; i++) {
p[i + 10] = previous + p[i];
previous = p[i];
}
public static void Main()
{
int[] p = new int[20];
for (int i = 0; i < 10; i++)
{
p[i] = i + 1;
¨
Console.WriteLine(p[i]);
}
Dopolni(p);
}
static void Dopolni(int[] p)
{
for (int i = 10; i < 20; i++)
{
p[i] = p[i - 10] + p[i - 9];
Console.WriteLine(p[i]);
}
}
This looks like trouble:
int[] p = new int[20];
Console.WriteLine(p);
What you want is to loop through p and print each element, not rely on the array implementation of ToString().
Try:
foreach (var n in p)
Console.WriteLine(n);
Do you need to have it in a function? Its really quite simple...
Notice I use 'out int[]', thats what your missing in your code. Out specifies you want in/out param, not just in ;)
static void Main()
{
int[] p = new int[20];
// First 10 numbers
for (int i = 0; i < 10; i++)
p[i] = i + 1;
Dolpini(out p);
foreach (int m in p)
Console.WriteLine(m);
}
static void Dolpini(out int[] numbers)
{
// Next 10 numbers
for (int k = 10; k < 20; k++)
p[k] = p[k-10] + p[k-9];
}
Please see my own answer, I think I did it!
Hi,
An example question for a programming contest was to write a program that finds out how much polyominos are possible with a given number of stones.
So for two stones (n = 2) there is only one polyominos:
XX
You might think this is a second solution:
X
X
But it isn't. The polyominos are not unique if you can rotate them.
So, for 4 stones (n = 4), there are 7 solutions:
X
X XX X X X X
X X XX X XX XX XX
X X X XX X X XX
The application has to be able to find the solution for 1 <= n <=10
PS: Using the list of polyominos on Wikipedia isn't allowed ;)
EDIT: Of course the question is: How to do this in Java, C/C++, C#
I started this project in Java. But then I had to admit I didn't know how to build polyominos using an efficient algorithm.
This is what I had so far:
import java.util.ArrayList;
import java.util.List;
public class Main
{
private int countPolyminos(int n)
{
hashes.clear();
count = 0;
boolean[][] matrix = new boolean[n][n];
createPolyominos(matrix, n);
return count;
}
private List<Integer> hashes = new ArrayList<Integer>();
private int count;
private void createPolyominos(boolean[][] matrix, int n)
{
if (n == 0)
{
boolean[][] cropped = cropMatrix(matrix);
int hash = hashMatrixOrientationIndependent(matrix);
if (!hashes.contains(hash))
{
count++;
hashes.add(hash);
}
return;
}
// Here is the real trouble!!
// Then here something like; createPolyominos(matrix, n-1);
// But, we need to keep in mind that the polyominos can have ramifications
}
public boolean[][] copy(boolean[][] matrix)
{
boolean[][] b = new boolean[matrix.length][matrix[0].length];
for (int i = 0; i < matrix.length; ++i)
{
System.arraycopy(matrix[i], 0, b, 0, matrix[i].length);
}
return b;
}
public boolean[][] cropMatrix(boolean[][] matrix)
{
int l = 0, t = 0, r = 0, b = 0;
// Left
left: for (int x = 0; x < matrix.length; ++x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
break left;
}
}
l++;
}
// Right
right: for (int x = matrix.length - 1; x >= 0; --x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
break right;
}
}
r++;
}
// Top
top: for (int y = 0; y < matrix[0].length; ++y)
{
for (int x = 0; x < matrix.length; ++x)
{
if (matrix[x][y])
{
break top;
}
}
t++;
}
// Bottom
bottom: for (int y = matrix[0].length; y >= 0; --y)
{
for (int x = 0; x < matrix.length; ++x)
{
if (matrix[x][y])
{
break bottom;
}
}
b++;
}
// Perform the real crop
boolean[][] cropped = new boolean[matrix.length - l - r][matrix[0].length - t - b];
for (int x = l; x < matrix.length - r; ++x)
{
System.arraycopy(matrix[x - l], t, cropped, 0, matrix[x].length - t - b);
}
return cropped;
}
public int hashMatrix(boolean[][] matrix)
{
int hash = 0;
for (int x = 0; x < matrix.length; ++x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
hash += matrix[x][y] ? (((x + 7) << 4) * ((y + 3) << 6) * 31) : ((((x+5) << 9) * (((y + x) + 18) << 7) * 53));
}
}
return hash;
}
public int hashMatrixOrientationIndependent(boolean[][] matrix)
{
int hash = 0;
hash += hashMatrix(matrix);
for (int i = 0; i < 3; ++i)
{
matrix = rotateMatrixLeft(matrix);
hash += hashMatrix(matrix);
}
return hash;
}
public boolean[][] rotateMatrixRight(boolean[][] matrix)
{
/* W and H are already swapped */
int w = matrix.length;
int h = matrix[0].length;
boolean[][] ret = new boolean[h][w];
for (int i = 0; i < h; ++i)
{
for (int j = 0; j < w; ++j)
{
ret[i][j] = matrix[w - j - 1][i];
}
}
return ret;
}
public boolean[][] rotateMatrixLeft(boolean[][] matrix)
{
/* W and H are already swapped */
int w = matrix.length;
int h = matrix[0].length;
boolean[][] ret = new boolean[h][w];
for (int i = 0; i < h; ++i)
{
for (int j = 0; j < w; ++j)
{
ret[i][j] = matrix[j][h - i - 1];
}
}
return ret;
}
}
There are only 4,461 polynominoes of size 10, so we can just enumerate them all.
Start with a single stone. To expand it by one stone, try add the new stone in at all empty cells that neighbour an existing stone. Do this recursively until reaching the desired size.
To avoid duplicates, keep a hash table of all polynominoes of each size we've already enumerated. When we put together a new polynomino, we check that its not already in the hash table. We also need to check its 3 rotations (and possibly its mirror image). While duplicate checking at the final size is the only strictly necessary check, checking at each step prunes recursive branches that will yield a new polynomino.
Here's some pseudo-code:
polynomino = array of n hashtables
function find_polynominoes(n, base):
if base.size == n:
return
for stone in base:
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
new_stone.x = stone.x + dx
new_stone.y = stone.y + dy
if new_stone not in base:
new_polynomino = base + new_stone
is_new = true
for rotation in [0, 90, 180, 270]:
if new_polynomino.rotate(rotation) in polynomino[new_polynomino.size]:
is_new = false
break
if is_new:
polynomino[new_polynomino.size].add(new_polynomino)
Just solved this as well in java. Since all here appear to have performance issues. I give you mine as well.
Board reprsentation:
2 arrays of integers. 1 for the rows and 1 for the columns.
Rotation: column[i]=row[size-(i+1)], row[i] = reverse(column[i]) where reverse is the bits reversed according to the size (for size = 4 and first 2 bits are taken: rev(1100) = 0011)
Shifting block: row[i-1] = row[i], col[i]<<=1
Check if bit is set: (row[r] & (1<<c)) > 0
Board uniqueness: The board is unique when the array row is unique.
Board hash: Hashcode of the array row
..
So this makes all operations fast. Many of them would have been O(size²) in the 2D array representation instead of now O(size).
Algorithm:
Start with the block of size 1
For each size start from the blocks with 1 stone less.
If it's possible to add the stone. Check if it was already added to the set.
If it's not yet added. Add it to the solution of this size.
add the block to the set and all its rotations. (3 rotations, 4 in total)
Important, after each rotation shift the block as left/top as possible.
+Special cases: do the same logic for the next 2 cases
shift block one to the right and add stone in first column
shift block one to the bottom and add stone in first row
Performance:
N=5 , time: 3ms
N=10, time: 58ms
N=11, time: 166ms
N=12, time: 538ms
N=13, time: 2893ms
N=14, time:17266ms
N=15, NA (out of heapspace)
Code:
https://github.com/Samjayyy/logicpuzzles/tree/master/polyominos
The most naive solution is to start with a single X, and for each iteration, build the list of unique possible next-states. From that list, build the list of unique states by adding another X. Continue this until the iteration you desire.
I'm not sure if this runs in reasonable time for N=10, however. It might, depending on your requirements.
I think I did it!
EDIT: I'm using the SHA-256 algorithm to hash them, now it works correct.
Here are the results:
numberOfStones -> numberOfPolyominos
1 -> 1
2 -> 1
3 -> 2
4 -> 7
5 -> 18
6 -> 60
7 -> 196
8 -> 704
9 -> 2500
10 -> terminated
Here is the code (Java):
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/* VPW Template */
public class Main
{
/**
* #param args
*/
public static void main(String[] args) throws IOException
{
new Main().start();
}
public void start() throws IOException
{
/* Read the stuff */
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] input = new String[Integer.parseInt(br.readLine())];
for (int i = 0; i < input.length; ++i)
{
input[i] = br.readLine();
}
/* Process each line */
for (int i = 0; i < input.length; ++i)
{
processLine(input[i]);
}
}
public void processLine(String line)
{
int n = Integer.parseInt(line);
System.out.println(countPolyminos(n));
}
private int countPolyminos(int n)
{
hashes.clear();
count = 0;
boolean[][] matrix = new boolean[n][n];
matrix[n / 2][n / 2] = true;
createPolyominos(matrix, n - 1);
return count;
}
private List<BigInteger> hashes = new ArrayList<BigInteger>();
private int count;
private void createPolyominos(boolean[][] matrix, int n)
{
if (n == 0)
{
boolean[][] cropped = cropMatrix(matrix);
BigInteger hash = hashMatrixOrientationIndependent(cropped);
if (!hashes.contains(hash))
{
// System.out.println(count + " Found!");
// printMatrix(cropped);
// System.out.println();
count++;
hashes.add(hash);
}
return;
}
for (int x = 0; x < matrix.length; ++x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
if (x > 0 && !matrix[x - 1][y])
{
boolean[][] clone = copy(matrix);
clone[x - 1][y] = true;
createPolyominos(clone, n - 1);
}
if (x < matrix.length - 1 && !matrix[x + 1][y])
{
boolean[][] clone = copy(matrix);
clone[x + 1][y] = true;
createPolyominos(clone, n - 1);
}
if (y > 0 && !matrix[x][y - 1])
{
boolean[][] clone = copy(matrix);
clone[x][y - 1] = true;
createPolyominos(clone, n - 1);
}
if (y < matrix[x].length - 1 && !matrix[x][y + 1])
{
boolean[][] clone = copy(matrix);
clone[x][y + 1] = true;
createPolyominos(clone, n - 1);
}
}
}
}
}
public boolean[][] copy(boolean[][] matrix)
{
boolean[][] b = new boolean[matrix.length][matrix[0].length];
for (int i = 0; i < matrix.length; ++i)
{
System.arraycopy(matrix[i], 0, b[i], 0, matrix[i].length);
}
return b;
}
public void printMatrix(boolean[][] matrix)
{
for (int y = 0; y < matrix.length; ++y)
{
for (int x = 0; x < matrix[y].length; ++x)
{
System.out.print((matrix[y][x] ? 'X' : ' '));
}
System.out.println();
}
}
public boolean[][] cropMatrix(boolean[][] matrix)
{
int l = 0, t = 0, r = 0, b = 0;
// Left
left: for (int x = 0; x < matrix.length; ++x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
break left;
}
}
l++;
}
// Right
right: for (int x = matrix.length - 1; x >= 0; --x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
break right;
}
}
r++;
}
// Top
top: for (int y = 0; y < matrix[0].length; ++y)
{
for (int x = 0; x < matrix.length; ++x)
{
if (matrix[x][y])
{
break top;
}
}
t++;
}
// Bottom
bottom: for (int y = matrix[0].length - 1; y >= 0; --y)
{
for (int x = 0; x < matrix.length; ++x)
{
if (matrix[x][y])
{
break bottom;
}
}
b++;
}
// Perform the real crop
boolean[][] cropped = new boolean[matrix.length - l - r][matrix[0].length - t - b];
for (int x = l; x < matrix.length - r; ++x)
{
System.arraycopy(matrix[x], t, cropped[x - l], 0, matrix[x].length - t - b);
}
return cropped;
}
public BigInteger hashMatrix(boolean[][] matrix)
{
try
{
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update((byte) matrix.length);
md.update((byte) matrix[0].length);
for (int x = 0; x < matrix.length; ++x)
{
for (int y = 0; y < matrix[x].length; ++y)
{
if (matrix[x][y])
{
md.update((byte) x);
} else
{
md.update((byte) y);
}
}
}
return new BigInteger(1, md.digest());
} catch (NoSuchAlgorithmException e)
{
System.exit(1);
return null;
}
}
public BigInteger hashMatrixOrientationIndependent(boolean[][] matrix)
{
BigInteger hash = hashMatrix(matrix);
for (int i = 0; i < 3; ++i)
{
matrix = rotateMatrixLeft(matrix);
hash = hash.add(hashMatrix(matrix));
}
return hash;
}
public boolean[][] rotateMatrixRight(boolean[][] matrix)
{
/* W and H are already swapped */
int w = matrix.length;
int h = matrix[0].length;
boolean[][] ret = new boolean[h][w];
for (int i = 0; i < h; ++i)
{
for (int j = 0; j < w; ++j)
{
ret[i][j] = matrix[w - j - 1][i];
}
}
return ret;
}
public boolean[][] rotateMatrixLeft(boolean[][] matrix)
{
/* W and H are already swapped */
int w = matrix.length;
int h = matrix[0].length;
boolean[][] ret = new boolean[h][w];
for (int i = 0; i < h; ++i)
{
for (int j = 0; j < w; ++j)
{
ret[i][j] = matrix[j][h - i - 1];
}
}
return ret;
}
Here's my solution in Java to the same problem. I can confirm Martijn's numbers (see below). I've also added in the rough time it takes to compute the results (mid-2012 Macbook Retina Core i7). I suppose substantial performance improvements could be achieved via parallelization.
numberOfStones -> numberOfPolyominos
1 -> 1
2 -> 1
3 -> 2
4 -> 7
5 -> 18
6 -> 60
7 -> 196
8 -> 704 (3 seconds)
9 -> 2500 (46 seconds)
10 -> 9189 (~14 minutes)
.
/*
* This class is a solution to the Tetris unique shapes problem.
* That is, the game of Tetris has 7 unique shapes. These 7 shapes
* are all the possible unique combinations of any 4 adjoining blocks
* (i.e. ignoring rotations).
*
* How many unique shapes are possible with, say, 7 or n blocks?
*
* The solution uses recursive back-tracking to construct all the possible
* shapes. It uses a HashMap to store unique shapes and to ignore rotations.
* It also uses a temporary HashMap so that the program does not needlessly
* waste time checking the same path multiple times.
*
* Even so, this is an exponential run-time solution, with n=10 taking a few
* minutes to complete.
*/
package com.glugabytes.gbjutils;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class TetrisBlocks {
private HashMap uShapes;
private HashMap tempShapes;
/* Get a map of unique shapes for n squares. The keys are string-representations
* of each shape, and values are corresponding boolean[][] arrays.
* #param squares - number of blocks to use for shapes, e.g. n=4 has 7 unique shapes
*/
public Map getUniqueShapes(int squares) {
uShapes = new HashMap();
tempShapes = new HashMap();
boolean[][] data = new boolean[squares*2+1][squares*2+1];
data[squares][squares] = true;
make(squares, data, 1); //start the process with a single square in the center of a boolean[][] matrix
return uShapes;
}
/* Recursivelly keep adding blocks to the data array until number of blocks(squares) = required size (e.g. n=4)
* Make sure to eliminate rotations. Also make sure not to enter infinite backtracking loops, and also not
* needlessly recompute the same path multiple times.
*/
private void make(int squares, boolean[][] data, int size) {
if(size == squares) { //used the required number of squares
//get a trimmed version of the array
boolean[][] trimmed = trimArray(data);
if(!isRotation(trimmed)) { //if a unique piece, add it to unique map
uShapes.put(arrayToString(trimmed), trimmed);
}
} else {
//go through the grid 1 element at a time and add a block next to an existing block
//do this for all possible combinations
for(int iX = 0; iX < data.length; iX++) {
for(int iY = 0; iY < data.length; iY++) {
if(data[iX][iY] == true) { //only add a block next to an existing block
if(data[iX+1][iY] != true) { //if no existing block to the right, add one and recuse
data[iX+1][iY] = true;
if(!isTempRotation(data)) { //only recurse if we haven't already been on this path before
make(squares, data, size+1);
tempShapes.put(arrayToString(data), data); //store this path so we don't repeat it later
}
data[iX+1][iY] = false;
}
if(data[iX-1][iY] != true) { //repeat by adding a block on the left
data[iX-1][iY] = true;
if(!isTempRotation(data)) {
make(squares, data, size+1);
tempShapes.put(arrayToString(data), data);
}
data[iX-1][iY] = false;
}
if(data[iX][iY+1] != true) { //repeat by adding a block down
data[iX][iY+1] = true;
if(!isTempRotation(data)) {
make(squares, data, size+1);
tempShapes.put(arrayToString(data), data);
}
data[iX][iY+1] = false;
}
if(data[iX][iY-1] != true) { //repeat by adding a block up
data[iX][iY-1] = true;
if(!isTempRotation(data)) {
make(squares, data, size+1);
tempShapes.put(arrayToString(data), data);
}
data[iX][iY-1] = false;
}
}
}
}
}
}
/**
* This function basically removes all rows and columns that have no 'true' flags,
* leaving only the portion of the array that contains useful data.
*
* #param data
* #return
*/
private boolean[][] trimArray(boolean[][] data) {
int maxX = 0;
int maxY = 0;
int firstX = data.length;
int firstY = data.length;
for(int iX = 0; iX < data.length; iX++) {
for (int iY = 0; iY < data.length; iY++) {
if(data[iX][iY]) {
if(iY < firstY) firstY = iY;
if(iY > maxY) maxY = iY;
}
}
}
for(int iY = 0; iY < data.length; iY++) {
for (int iX = 0; iX < data.length; iX++) {
if(data[iX][iY]) {
if(iX < firstX) firstX = iX;
if(iX > maxX) maxX = iX;
}
}
}
boolean[][] trimmed = new boolean[maxX-firstX+1][maxY-firstY+1];
for(int iX = firstX; iX <= maxX; iX++) {
for(int iY = firstY; iY <= maxY; iY++) {
trimmed[iX-firstX][iY-firstY] = data[iX][iY];
}
}
return trimmed;
}
/**
* Return a string representation of the 2D array.
*
* #param data
* #return
*/
private String arrayToString(boolean[][] data) {
StringBuilder sb = new StringBuilder();
for(int iX = 0; iX < data.length; iX++) {
for(int iY = 0; iY < data[0].length; iY++) {
sb.append(data[iX][iY] ? '#' : ' ');
}
sb.append('\n');
}
return sb.toString();
}
/**
* Rotate an array clockwise by 90 degrees.
* #param data
* #return
*/
public boolean[][] rotate90(boolean[][] data) {
boolean[][] rotated = new boolean[data[0].length][data.length];
for(int iX = 0; iX < data.length; iX++) {
for(int iY = 0; iY < data[0].length; iY++) {
rotated[iY][iX] = data[data.length - iX - 1][iY];
}
}
return rotated;
}
/**
* Checks to see if two 2d boolean arrays are the same
* #param a
* #param b
* #return
*/
public boolean equal(boolean[][] a, boolean[][] b) {
if(a.length != b.length || a[0].length != b[0].length) {
return false;
} else {
for(int iX = 0; iX < a.length; iX++) {
for(int iY = 0; iY < a[0].length; iY++) {
if(a[iX][iY] != b[iX][iY]) {
return false;
}
}
}
}
return true;
}
public boolean isRotation(boolean[][] data) {
//check to see if it's a rotation of a shape that we already have
data = rotate90(data); //+90*
String str = arrayToString(data);
if(!uShapes.containsKey(str)) {
data = rotate90(data); //180*
str = arrayToString(data);
if(!uShapes.containsKey(str)) {
data = rotate90(data); //270*
str = arrayToString(data);
if(!uShapes.containsKey(str)) {
return false;
}
}
}
return true;
}
public boolean isTempRotation(boolean[][] data) {
//check to see if it's a rotation of a shape that we already have
data = rotate90(data); //+90*
String str = arrayToString(data);
if(!tempShapes.containsKey(str)) {
data = rotate90(data); //180*
str = arrayToString(data);
if(!tempShapes.containsKey(str)) {
data = rotate90(data); //270*
str = arrayToString(data);
if(!tempShapes.containsKey(str)) {
return false;
}
}
}
return true;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
TetrisBlocks tetris = new TetrisBlocks();
long start = System.currentTimeMillis();
Map shapes = tetris.getUniqueShapes(8);
long end = System.currentTimeMillis();
Iterator it = shapes.keySet().iterator();
while(it.hasNext()) {
String shape = (String)it.next();
System.out.println(shape);
}
System.out.println("Unique Shapes: " + shapes.size());
System.out.println("Time: " + (end-start));
}
}
Here's some python that computes the answer. Seems to agree with Wikipedia. It isn't terribly fast because it uses lots of array searches instead of hash tables, but it still takes only a minute or so to complete.
#!/usr/bin/python
# compute the canonical representation of polyomino p.
# (minimum x and y coordinate is zero, sorted)
def canonical(p):
mx = min(map(lambda v: v[0], p))
my = min(map(lambda v: v[1], p))
return sorted(map(lambda v: (v[0]-mx, v[1]-my), p))
# rotate p 90 degrees
def rotate(p):
return canonical(map(lambda v: (v[1], -v[0]), p))
# add one tile to p
def expand(p):
result = []
for (x,y) in p:
for (dx,dy) in ((-1,0),(1,0),(0,-1),(0,1)):
if p.count((x+dx,y+dy)) == 0:
result.append(canonical(p + [(x+dx,y+dy)]))
return result
polyominos = [[(0,0)]]
for i in xrange(1,10):
new_polyominos = []
for p in polyominos:
for q in expand(p):
dup = 0
for r in xrange(4):
if new_polyominos.count(q) != 0:
dup = 1
break
q = rotate(q)
if not dup: new_polyominos.append(q)
polyominos = new_polyominos
print i+1, len(polyominos)
Here is my full Python solution inspired by #marcog's answer. It prints the number of polyominos of sizes 2..10 in about 2s on my laptop.
The algorithm is straightforward:
Size 1: start with one square
Size n + 1: take all pieces of size n and try adding a single square to all possible adjacent positions. This way you find all possible new pieces of size n + 1. Skip duplicates.
The main speedup came from hashing pieces to quickly check if we've already seen a piece.
import itertools
from collections import defaultdict
n = 10
print("Number of Tetris pieces up to size", n)
# Times:
# n is number of blocks
# - Python O(exp(n)^2): 10 blocks 2.5m
# - Python O(exp(n)): 10 blocks 2.5s, 11 blocks 10.9s, 12 block 33s, 13 blocks 141s (800MB memory)
smallest_piece = [(0, 0)] # We represent a piece as a list of block positions
pieces_of_size = {
1: [smallest_piece],
}
# Returns a list of all possible pieces made by adding one block to given piece
def possible_expansions(piece):
# No flatMap in Python 2/3:
# https://stackoverflow.com/questions/21418764/flatmap-or-bind-in-python-3
positions = set(itertools.chain.from_iterable(
[(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] for (x, y) in piece
))
# Time complexity O(n^2) can be improved
# For each valid position, append to piece
expansions = []
for p in positions:
if not p in piece:
expansions.append(piece + [p])
return expansions
def rotate_90_cw(piece):
return [(y, -x) for (x, y) in piece]
def canonical(piece):
min_x = min(x for (x, y) in piece)
min_y = min(y for (x, y) in piece)
res = sorted((x - min_x, y - min_y) for (x, y) in piece)
return res
def hash_piece(piece):
return hash(tuple(piece))
def expand_pieces(pieces):
expanded = []
#[
# 332322396: [[(1,0), (0,-1)], [...]],
# 323200700000: [[(1,0), (0,-2)]]
#]
# Multimap because two different pieces can happen to have the same hash
expanded_hashes = defaultdict(list)
for piece in pieces:
for e in possible_expansions(piece):
exp = canonical(e)
is_new = True
if exp in expanded_hashes[hash_piece(exp)]:
is_new = False
for rotation in range(3):
exp = canonical(rotate_90_cw(exp))
if exp in expanded_hashes[hash_piece(exp)]:
is_new = False
if is_new:
expanded.append(exp)
expanded_hashes[hash_piece(exp)].append(exp)
return expanded
for i in range(2, n + 1):
pieces_of_size[i] = expand_pieces(pieces_of_size[i - 1])
print("Pieces with {} blocks: {}".format(i, len(pieces_of_size[i])))