Calculate values of the spectrum with FFT - c#

I have to calculate the spectrum values of an audio.
I used aForge's FFT in Sources/Math/FourierTransform.cs and I used an example of sampling with 16 samples as used in this video to check the results with excel (I tested the results in a spreadsheet like in the video).
FFT:
public enum Direction
{
Forward = 1,
Backward = -1
};
private const int minLength = 2;
private const int maxLength = 16384;
private const int minBits = 1;
private const int maxBits = 14;
private static int[][] reversedBits = new int[maxBits][];
private static Complex[,][] complexRotation = new Complex[maxBits, 2][];
static void Main(string[] args)
{
var Data = new Complex[16];
Data[0] = new Complex(0, 0);
Data[1] = new Complex((float)0.998027, 0);
Data[2] = new Complex((float)0.125333, 0);
Data[3] = new Complex((float)-0.98229, 0);
Data[4] = new Complex((float)-0.24869, 0);
Data[5] = new Complex((float)0.951057, 0);
Data[6] = new Complex((float)0.368125, 0);
Data[7] = new Complex((float)-0.90483, 0);
Data[8] = new Complex((float)-0.48175, 0);
Data[9] = new Complex((float)0.844328, 0);
Data[10] = new Complex((float)0.587785, 0);
Data[11] = new Complex((float)-0.77051, 0);
Data[12] = new Complex((float)-0.68455, 0);
Data[13] = new Complex((float)0.684547, 0);
Data[14] = new Complex((float)0.770513, 0);
Data[15] = new Complex((float)-0.58779, 0);
FFT(Data, Direction.Forward);
for (int a = 0; a <= Data.Length - 1; a++)
{
Console.WriteLine(Data[a].Re.ToString());
}
Console.ReadLine();
}
public static void FFT(Complex[] data, Direction direction)
{
int n = data.Length;
int m = Tools.Log2(n);
// reorder data first
ReorderData(data);
// compute FFT
int tn = 1, tm;
for (int k = 1; k <= m; k++)
{
Complex[] rotation = GetComplexRotation(k, direction);
tm = tn;
tn <<= 1;
for (int i = 0; i < tm; i++)
{
Complex t = rotation[i];
for (int even = i; even < n; even += tn)
{
int odd = even + tm;
Complex ce = data[even];
Complex co = data[odd];
double tr = co.Re * t.Re - co.Im * t.Im;
double ti = co.Re * t.Im + co.Im * t.Re;
data[even].Re += tr;
data[even].Im += ti;
data[odd].Re = ce.Re - tr;
data[odd].Im = ce.Im - ti;
}
}
}
if (direction == Direction.Forward)
{
for (int i = 0; i < n; i++)
{
data[i].Re /= (double)n;
data[i].Im /= (double)n;
}
}
}
private static int[] GetReversedBits(int numberOfBits)
{
if ((numberOfBits < minBits) || (numberOfBits > maxBits))
throw new ArgumentOutOfRangeException();
// check if the array is already calculated
if (reversedBits[numberOfBits - 1] == null)
{
int n = Tools.Pow2(numberOfBits);
int[] rBits = new int[n];
// calculate the array
for (int i = 0; i < n; i++)
{
int oldBits = i;
int newBits = 0;
for (int j = 0; j < numberOfBits; j++)
{
newBits = (newBits << 1) | (oldBits & 1);
oldBits = (oldBits >> 1);
}
rBits[i] = newBits;
}
reversedBits[numberOfBits - 1] = rBits;
}
return reversedBits[numberOfBits - 1];
}
private static Complex[] GetComplexRotation(int numberOfBits, Direction direction)
{
int directionIndex = (direction == Direction.Forward) ? 0 : 1;
// check if the array is already calculated
if (complexRotation[numberOfBits - 1, directionIndex] == null)
{
int n = 1 << (numberOfBits - 1);
double uR = 1.0;
double uI = 0.0;
double angle = System.Math.PI / n * (int)direction;
double wR = System.Math.Cos(angle);
double wI = System.Math.Sin(angle);
double t;
Complex[] rotation = new Complex[n];
for (int i = 0; i < n; i++)
{
rotation[i] = new Complex(uR, uI);
t = uR * wI + uI * wR;
uR = uR * wR - uI * wI;
uI = t;
}
complexRotation[numberOfBits - 1, directionIndex] = rotation;
}
return complexRotation[numberOfBits - 1, directionIndex];
}
// Reorder data for FFT using
private static void ReorderData(Complex[] data)
{
int len = data.Length;
// check data length
if ((len < minLength) || (len > maxLength) || (!Tools.IsPowerOf2(len)))
throw new ArgumentException("Incorrect data length.");
int[] rBits = GetReversedBits(Tools.Log2(len));
for (int i = 0; i < len; i++)
{
int s = rBits[i];
if (s > i)
{
Complex t = data[i];
data[i] = data[s];
data[s] = t;
}
}
}
These are the results after the transformation:
Output FFT results: Excel FFT results:
0,0418315622955561 0,669305
0,0533257974328085 0,716163407
0,137615673627316 0,908647001
0,114642731070279 1,673453043
0,234673940537634 7,474988602
0,0811255020953362 0,880988382
0,138088891589122 0,406276784
0,0623766891658306 0,248854492
0,0272978749126196 0,204227
0,0124250144575261 0,248854492
0,053787064184711 0,406276784
0,00783331226557493 0,880988382
0,0884368745610118 7,474988602
0,0155431246384978 1,673453043
0,0301093757152557 0,908647001
0 0,716163407
The results are not at all similar. Where is it wrong?
Is the implementation of complex (Data) wrong or is the FFT method wrong or other?
Thanks in advance!

First, the resulting FFT is a complex function in general. You're only displaying the real parts in your code but the thing you're comparing to is displaying the magnitudes, so of course they're going to be different: you're comparing apples to oranges.
When you use magnitudes and compare apples to apples, you should get this:
for (int a = 0; a <= Data.Length - 1; a++)
{
Console.WriteLine(Data[a].Magnitude.ToString());
}
...
0.0418315622955561
0.0447602132472683
0.0567904388057513
0.104590813761862
0.46718679147454
0.0550617784710375
0.025392294285886
0.0155534081359397
0.0127641875296831
0.0155534081359397
0.025392294285886
0.0550617784710375
0.46718679147454
0.104590813761862
0.0567904388057513
0.0447602132472683
That looks a little better -- it has the same symmetry property as the Excel output and there appear to be peaks in the same locations.
It almost looks like the scale is off. If I divide each element by the corresponding element from the Excel output, I get:
16
16
16
16
16
16
16
16
16
16
16
16
16
16
16
16
So your results are pretty much correct, just off by a scaling factor.
You're dividing everything by n in the last step of your FFT:
if (direction == Direction.Forward)
{
for (int i = 0; i < n; i++)
{
data[i].Re /= (double)n;
data[i].Im /= (double)n;
}
}
This is conventionally done for the inverse transform, not the forward transform.
In summary, changing the output from Data[a].Re to Data[a].Magnitude and changing the condition at the end of FFT from if (direction == Direction.Forward) to if (direction == Direction.Backward), I get this output:
0.669304996728897
0.716163411956293
0.908647020892022
1.67345302018979
7.47498866359264
0.880988455536601
0.406276708574176
0.248854530175035
0.20422700047493
0.248854530175035
0.406276708574176
0.880988455536601
7.47498866359264
1.67345302018979
0.908647020892022
0.716163411956293
which matches the Excel output.

Related

FFT on an image, using butterfly notation with decimation in spatial domain

I'm trying to implement FFT, using butterfly notation with decimation in spatial domain. I have two separated functions, one to calculate the FFT on 1D array, and the other that applies the first function over columns of the image, puts these columns in a temporary array, and then computes the FFT of rows in the temporary array.
Here is the code
public static Complex[] FFT(Complex[] input)
{
// Get the length of the input array
int n = input.Length;
Complex I = new Complex(0, 1);
// Check if the input has a length of 1
if (n == 1)
{
return input;
}
// Split the input into even and odd elements
Complex[] even = new Complex[n / 2];
Complex[] odd = new Complex[n / 2];
for (int i = 0; i < n; i++)
{
if (i % 2 == 0)
{
even[i / 2] = input[i];
}
else
{
odd[(i - 1) / 2] = input[i];
}
}
// Compute the FFT of the even and odd elements
Complex[] evenFFT = FFT(even);
Complex[] oddFFT = FFT(odd);
// Combine the FFT of the even and odd elements using the butterfly notation
Complex[] output = new Complex[n];
for (int i = 0; i < n / 2; i++)
{
Complex w = Complex.Exp(-2 * I * Math.PI * i / n);
output[i] = evenFFT[i] + w * oddFFT[i];
output[i + n / 2] = evenFFT[i] - w * oddFFT[i];
}
return output;
}
public static Complex[,] FFT2D(Complex[,] input)
{
int N = input.GetLength(0);
int M = input.GetLength(1);
Complex[,] output = new Complex[128, 128];
Complex[,] columnsFFT = new Complex[128, 128];
//Perform FFT over the columns of the input
for(int i = 0; i < M; i++)
{
//Put all the values from i'th column in the tempColumn variable
var tempColumn = new Complex[128];
for(int j = 0; j < N; j++)
{
tempColumn[j] = input[j, i];
}
//Calculate the FFT of i'th column
tempColumn = FFT(tempColumn);
//Assign the column to columnsFFT, after calculating its FFT
for(int z = 0; z < 128; z++)
{
columnsFFT[z, i] = tempColumn[z];
}
}
//Perform FFT over the rows of the columnsFFT
for(int i = 0; i < N; i++)
{
//Put the values from i'th row in tempRow, so we can perform the FFT on its entirety
var tempRow = new Complex[128];
for(int j = 0; j < M; j++)
{
tempRow[j] = columnsFFT[i, j];
}
//Calculate the FFT on tempRow
tempRow = FFT(tempRow);
//Assign the tempRow to the output
for(int z = 0; z < M; z++)
{
if (i == 0)
Console.WriteLine(tempRow[z].Real);
output[i, z] = tempRow[z];
}
}
return output;
}
The problem that i have is that it simply doesn't work, or i think it doesn't, based on the image output i get.
Input image
Output
What is it that i'm doing wrong here?

Float array resampling

I want to "stretch" a one-dimensional float array into a bigger array.
//expected behaviour
float[] initialArray = {2.0, 6.5, 2.0}
float[] biggerArray = resample(initialArray, 7 /*new size*/)
//output: {2.0, 3.5, 5.0, 6.5, 5.0, 3.5, 2.0}
The new values should propobaly be calculated from linear interpolation of the previous array values but i can't figure out how to achieve that.
Any hint ?
Lets the length of a source array is N, and the length of a destination array is M where N < M and N > 1.
You can calculate the new index of the source i-th element by the formula:
j = i * (M - 1)/(N - 1);
When i == 0 then j == 0; and when i == N - 1 then j == M - 1. The external loop can looks like this:
float[] source = ...;
float[] destination = ...;
destination[0] = source[0];
for (int i = 1; i < source.Length; i++)
{
int j = i * (destination.Length - 1)/(source.Length - 1);
destination[j] = source[i];
// interpolation
}
To interpolation you should calculate intermediate values for each pair (source[i - 1], source[i]). You'll need to store previous value of j:
destination[0] = source[0];
int jPrevious = 0;
for (int i = 1; i < source.Length; i++)
{
int j = i * (destination.Length - 1)/(source.Length - 1);
Interpolate(destination, jPrevious, j, source[i - 1], source[i]);
jPrevious = j;
}
private static void Interpolate(float[] destination, int destFrom, int destTo, float valueFrom, float valueTo)
{
int destLength = destTo - destFrom;
float valueLength = valueTo - valueFrom;
for (int i = 0; i <= destLength; i++)
destination[destFrom + i] = valueFrom + (valueLength * i)/destLength;
}
This one works for both if source size is larger than destination and vice versa.
private double[] Resample(double[] source, int n)
{
//n destination length
int m = source.Length; //source length
double[] destination = new double[n];
destination[0] = source[0];
destination[n-1] = source[m-1];
for (int i = 1; i < n-1; i++)
{
double jd = ((double)i * (double)(m - 1) / (double)(n - 1));
int j = (int)jd;
destination[i] = source[j] + (source[j + 1] - source[j]) * (jd - (double)j);
}
return destination;
}
You can use List<float>:
float[] initialArray = { 2.0f, 6.5f, 2.0f };
List<float> initialArrayTemp = ToListFloat(initialArray);
private List<float> ToListFloat(float[] array)
{
List<float> list = new List<float>();
for (int i = 0; i < array.Length; i++)
{
list.Add(array[i]);
}
return list;
}
Now your array is a dynamic array and you can add your new nodes anywhere of your Array by using Insert() method.
As soon as you need a new static array, use below:
float[] newInitialArray = initialArrayTemp.ToArray();

IndexOutOfRangeException in my attempt to implement the FFT - fixed, edit: Output are only zeros, why?

I am a beginner in C# and I wanted to implement the following Pseudo code of the FFT algorithm:
function fft(n, f):
if (n = 1)
return f
else
g = fft(n/2, (f_0, f_2, ..., f_{n-2}))
u = fft(n/2, (f_1, f_3, ..., f_{n-1}))
for k = 0 to n/2 - 1
c_k = g_k + u_k*exp(-2*pi*i*k/n)
c_{k+n/2} = g_k-u_k*exp(-2*pi*i*k/n)
return c
I tried to implement that in C# as you can see:
public static class FFT
{
public static Complex[] fft(Complex[] f)
{
if (f.Length == 1)
{
return f;
}
else
{
Complex[] g = fft(even_indices(f));
Complex[] u = fft(odd_indices(f));
Complex[] c = new Complex[f.Length];
for (int k = 0; k < f.Length / 2 - 1; k++)
{
Complex w_k = u[k] * Complex.FromPolarCoordinates(1.0, -2 * Math.PI * k / f.Length);
c[k] = g[k] + w_k;
c[k + f.Length / 2] = g[k] - w_k;
}
return c;
}
}
private static Complex[] even_indices(Complex[] f)
{
Complex[] f_even = new Complex[f.Length / 2];
for (int i = 0; i < f.Length; i++)
{
if (i % 2 == 0)
{
f_even[i / 2] = f[i];
}
}
return f_even;
}
private static Complex[] odd_indices(Complex[] f)
{
Complex[] f_odd = new Complex[f.Length / 2];
for (int i = 0; i < f.Length; i++)
{
if (i % 2 == 1)
{
f_odd[(i-1)/2] = f[i];
}
}
return f_odd;
}
}
class Program
{
static void Main()
{
Complex[] test = { 1.0, 2.454167, 8.4567, 9.4564 };
var data = FFT.fft(test);
Console.WriteLine("FFT");
for (int i = 0; i < data.Length; i++)
{
Console.WriteLine(data[i]);
}
Console.ReadKey();
}
}
Now there is no error message and it gets completely compiled. However, the output is
FFT
(0, 0)
(0, 0)
(0, 0)
(0, 0)
which is not what I want. What is wrong here?
Thanks again
Complex[] c = new Complex[f.Length / 2 - 1];
I would round up, so + 1..
f.Length == 2 would return 0 for array length.
I'd expect the error actually resulting from the next line:
c[k + f.Length / 2] = g[k] - u[k] * Complex.Exp(-w_k);
where you add len/2 to the k-index - which will eventually result in a index > length/2-1. (dev of 'c').
If the rest is correct you should declare c as:
Complex[] c = new Complex[f.Length];
Add:
in your code, the loop should iterate uo to len/2-1, so change to:
for (int k = 0; k <= f.Length / 2 - 1; k++)

Get longest substring between two strings

I have two words,
britanicaeng and
britanicahin
I need to find out the longest common word between these i.e, britanica.
How can I do this in C# ?
Try this method:
public static string FindLongestCommonSubstring(string s1, string s2)
{
int[,] a = new int[s1.Length + 1, s2.Length + 1];
int row = 0; // s1 index
int col = 0; // s2 index
for (var i = 0; i < s1.Length; i++)
for (var j = 0; j < s2.Length; j++)
if (s1[i] == s2[j])
{
int len = a[i + 1, j + 1] = a[i, j] + 1;
if (len > a[row, col])
{
row = i + 1;
col = j + 1;
}
}
return s1.Substring(row - a[row, col], a[row, col]);
}
Usage example:
Console.WriteLine(FindLongestCommonSubstring("britanicaeng", "britanicahin"));
I refactored the C++ code from Ashutosh Singh at https://iq.opengenus.org/longest-common-substring-using-rolling-hash/ to create a rolling hash approach in C# - this will find the substring in O(N * log(N)^2) time and O(N) space
using System;
using System.Collections.Generic;
public class RollingHash
{
private class RollingHashPowers
{
// _mod = prime modulus of polynomial hashing
// any prime number over a billion should suffice
internal const int _mod = (int)1e9 + 123;
// _hashBase = base (point of hashing)
// this should be a prime number larger than the number of characters used
// in my use case I am only interested in ASCII (256) characters
// for strings in languages using non-latin characters, this should be much larger
internal const long _hashBase = 257;
// _pow1 = powers of base modulo mod
internal readonly List<int> _pow1 = new List<int> { 1 };
// _pow2 = powers of base modulo 2^64
internal readonly List<long> _pow2 = new List<long> { 1L };
internal void EnsureLength(int length)
{
if (_pow1.Capacity < length)
{
_pow1.Capacity = _pow2.Capacity = length;
}
for (int currentIndx = _pow1.Count - 1; currentIndx < length; ++currentIndx)
{
_pow1.Add((int)(_pow1[currentIndx] * _hashBase % _mod));
_pow2.Add(_pow2[currentIndx] * _hashBase);
}
}
}
private class RollingHashedString
{
readonly RollingHashPowers _pows;
readonly int[] _pref1; // Hash on prefix modulo mod
readonly long[] _pref2; // Hash on prefix modulo 2^64
// Constructor from string:
internal RollingHashedString(RollingHashPowers pows, string s, bool caseInsensitive = false)
{
_pows = pows;
_pref1 = new int[s.Length + 1];
_pref2 = new long[s.Length + 1];
const long capAVal = 'A';
const long capZVal = 'Z';
const long aADif = 'a' - 'A';
unsafe
{
fixed (char* c = s)
{
// Fill arrays with polynomial hashes on prefix
for (int i = 0; i < s.Length; ++i)
{
long v = c[i];
if (caseInsensitive && capAVal <= v && v <= capZVal)
{
v += aADif;
}
_pref1[i + 1] = (int)((_pref1[i] + v * _pows._pow1[i]) % RollingHashPowers._mod);
_pref2[i + 1] = _pref2[i] + v * _pows._pow2[i];
}
}
}
}
// Rollingnomial hash of subsequence [pos, pos+len)
// If mxPow != 0, value automatically multiply on base in needed power.
// Finally base ^ mxPow
internal Tuple<int, long> Apply(int pos, int len, int mxPow = 0)
{
int hash1 = _pref1[pos + len] - _pref1[pos];
long hash2 = _pref2[pos + len] - _pref2[pos];
if (hash1 < 0)
{
hash1 += RollingHashPowers._mod;
}
if (mxPow != 0)
{
hash1 = (int)((long)hash1 * _pows._pow1[mxPow - (pos + len - 1)] % RollingHashPowers._mod);
hash2 *= _pows._pow2[mxPow - (pos + len - 1)];
}
return Tuple.Create(hash1, hash2);
}
}
private readonly RollingHashPowers _rhp;
public RollingHash(int longestLength = 0)
{
_rhp = new RollingHashPowers();
if (longestLength > 0)
{
_rhp.EnsureLength(longestLength);
}
}
public string FindCommonSubstring(string a, string b, bool caseInsensitive = false)
{
// Calculate max neede power of base:
int mxPow = Math.Max(a.Length, b.Length);
_rhp.EnsureLength(mxPow);
// Create hashing objects from strings:
RollingHashedString hash_a = new RollingHashedString(_rhp, a, caseInsensitive);
RollingHashedString hash_b = new RollingHashedString(_rhp, b, caseInsensitive);
// Binary search by length of same subsequence:
int pos = -1;
int low = 0;
int minLen = Math.Min(a.Length, b.Length);
int high = minLen + 1;
var tupleCompare = Comparer<Tuple<int, long>>.Default;
while (high - low > 1)
{
int mid = (low + high) / 2;
List<Tuple<int, long>> hashes = new List<Tuple<int, long>>(a.Length - mid + 1);
for (int i = 0; i + mid <= a.Length; ++i)
{
hashes.Add(hash_a.Apply(i, mid, mxPow));
}
hashes.Sort(tupleCompare);
int p = -1;
for (int i = 0; i + mid <= b.Length; ++i)
{
if (hashes.BinarySearch(hash_b.Apply(i, mid, mxPow), tupleCompare) >= 0)
{
p = i;
break;
}
}
if (p >= 0)
{
low = mid;
pos = p;
}
else
{
high = mid;
}
}
// Output answer:
return pos >= 0
? b.Substring(pos, low)
: string.Empty;
}
}

Programming Contest Question: Counting Polyominos

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])))

Categories

Resources