Given the following c# array:
const bool X = true, _ = false;
bool[,] array = new bool[,] {
{ X, X, X, X, _, X, X, X, _, _ },
{ _, _, _, _, _, X, X, X, X, _ },
{ X, X, X, X, X, X, X, X, X, X },
{ X, X, X, X, X, X, X, X, X, X },
{ _, X, X, X, _, X, _, _, X, X },
{ _, X, X, X, _, X, X, _, X, X },
{ _, _, _, X, X, _, _, _, _, _ },
{ X, X, _, X, X, _, X, X, X, _ },
{ _, X, X, _, _, X, X, X, X, X },
{ _, _, X, X, _, _, X, X, X, X },
};
Visually something like this:
Are there any existing methods for converting this to as few rectangular areas as possible?
A possible solution might be something like:
Ideally I'm looking for something that can produce a list of rectangles:
public IEnumerable<Rectangle> ReduceMap(bool[,] map)
{
List<Rectangle> rects = new List<Rectangle>();
int width = map.GetLength(0), height = map.GetLength(1);
// Reduce
// ....?
return rects;
}
The result of which would be something like:
var output = new List<Rectangle>
{
new Rectangle(0, 0, 4, 1),
new Rectangle(5, 0, 3, 2),
new Rectangle(8, 1, 1, 1),
new Rectangle(0, 2, 10, 2),
// ... etc
};
Speed is important too, the fewer rectangles the better, but finding a decent solution should not be overly computationally expensive.
Apologies if this has been answered before, if anyone has any idea where to look that would be great!
This is an interesting problem.
Have you seen the following article it provides good information on this topic and discusses different approaches to this problem (quadtree, morphological, graph):
Rectangular decomposition of binary images
This comes from Ojdo's answer in this question Link
I would start looking there! Hope this helped.
This is the best I can come up with myself, if anyone can improve on this, I'd be happy to mark it as the answer.
This method uses multiple passes to union the array and seems to reduce the array pretty well, roughly in line with my diagrams above, although definitely not optimal.
I've created a fiddle for testing:
https://dotnetfiddle.net/3iuIe6
using System;
using System.Collections.Generic;
using System.Drawing;
public class Program
{
private const string pattern1 = #"
XXXX_XXX__
_____XXXX_
XXXXXXXXXX
XXXXXXXXXX
_XXX_X__XX
_XXX_XX_XX
___XX_____
XX_XX_XXX_
_XX__XXXXX
__XX__XXXX
";
private const string pattern2 = #"
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
";
private const string pattern3 = #"
X_XXXXXXXX
X_XXXXXXXX
X_XXXXXXXX
X_XXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXX_XX
XXXXXXX_XX
XXXXXXX_XX
XXXXXXX_XX
";
private const string pattern4 = #"
X_XXXXXXXX
X_XX__XXXX
X_XXXXXXXX
X_XXXX__XX
XXXXX_XXXX
XXX__XXXXX
XXXX_XX_XX
XXX_XXX_XX
XXXXX_X_XX
XXX_XXX_XX
";
private const string pattern5 = #"
XXXXXXXXXX
__XXXXXXXX
____XXXXXX
_____XXXXX
______XXXX
______XXXX
_____XXXXX
____XXXXXX
__XXXXXXXX
XXXXXXXXXX
";
public static void Main()
{
bool[,] map = PatternTo2DArray(pattern1);
//bool[,] map = PatternTo2DArray(pattern2);
//bool[,] map = PatternTo2DArray(pattern3);
//bool[,] map = PatternTo2DArray(pattern4);
//bool[,] map = PatternTo2DArray(pattern5);
var rects = ReduceMap(map);
int index = 0;
foreach (var rect in rects)
{
Console.WriteLine("{0}: {{ {1}, {2}, {3}, {4} }}", ((index + 1).ToString().PadLeft(1, '0')), rect.X, rect.Y, rect.Width, rect.Height);
index++;
}
var total = DumpMap(map);
Console.WriteLine("\r\nOptimised Map From: {0} to {1}", total, index);
}
public static IEnumerable<Rectangle> ReduceMap(bool[,] map)
{
int width = map.GetLength(0), height = map.GetLength(1);
MapElement[,] bin = new MapElement[width, height];
// Reduce
// Step 1: Convert to map elements:
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
if (map[x, y])
bin[x, y] = new MapElement() { X = x, Y = y, Width = 1, Height = 1, Set = true };
}
// Step 2: Process the bin map and generate a collection of Rectangles horizontally
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// Only care about this if we are set.
if (bin[x, y].Set)
{
// Scan forward and link this tile with its following tiles
int xx = 0;
for (int xForward = x + 1; xForward < width; xForward++)
{
if (!bin[xForward, y].Set)
break;
// We can link this...
bin[xForward, y].Set = false;
bin[x, y].Width++;
xx++; // Skip over these tiles.
}
x += xx;
}
}
}
// Step 3: Process the bin map veritically and join any blocks that have equivalent blocks below them.
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// Only care about this if we are set.
if (bin[x, y].Set)
{
// Scan down and link this tile with its following tiles
for (int yDown = y + 1; yDown < height; yDown++)
{
if (!bin[x, yDown].Set || bin[x, yDown].Width != bin[x, y].Width) // We might be able to link this if it's the same size
break;
bin[x, yDown].Set = false;
bin[x, y].Height++;
}
}
}
}
// Step 4: Convert map to rectangles
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// Only care about this if we are set.
if (bin[x, y].Set)
{
var b = bin[x, y];
yield return new Rectangle(b.X, b.Y, b.Width, b.Height);
}
}
}
}
public static int DumpMap(bool[,] map)
{
Console.WriteLine("");
var #out = 0;
int width = map.GetLength(0), height = map.GetLength(1);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Console.Write(map[x, y] ? "X" : "_");
#out++;
}
Console.WriteLine();
}
return #out;
}
public static int CountDataPoints(bool[,] map)
{
var #out = 0;
int width = map.GetLength(0), height = map.GetLength(1);
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
if (map[x, y])
#out++;
return #out;
}
public static bool[,] PatternTo2DArray(string pattern)
{
var lines = new List<string>(pattern.Split('\n'));
for (int i = lines.Count - 1; i > -1; i--)
{
string line = lines[i].TrimEnd('\r');
if (line.Length == 0)
lines.RemoveAt(i);
else
lines[i] = line;
}
var #out = new bool[lines[0].Length, lines.Count];
for (int y = 0; y < lines.Count; y++)
{
string line = lines[y];
for (int x = 0; x < line.Length; x++)
{
#out[x, y] = !(line[x] == '_');
}
}
return #out;
}
internal struct MapElement
{
public int X;
public int Y;
public int Width;
public int Height;
public bool Set;
}
}
Related
UPDATE: The span issues that were mentioned previously were fixed in the .net core 2.1 release (which is currently in preview.) These actually made the Span Vector *faster* than the array Vector...
NB: Testing this on a "Intel Xeon E5-1660 v4" which CPU-Z tells me has Instructions for "MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, EM64T, VT-x, AES, AVX, AVX2, FMA3, RSX" so it should be OK...
Off the back of answering a Vector based question, I thought I would try to implement some BLAS functions. I found that ones that were reading/summing such as dot product were pretty good, but were I was writing back to an array were bad - better than non-SIMD, but barely.
So am I doing something wrong, or is there more work in the JIT required?
The example (assuming x.Length = y.Length, not null etc. blah, blah):
public static void daxpy(double alpha, double[] x, double[] y)
{
for (var i = 0; i < x.Length; ++i)
y[i] = y[i] + x[i] * alpha;
}
In Vector form becomes:
public static void daxpy(double alpha, double[] x, double[] y)
{
var i = 0;
if (Vector.IsHardwareAccelerated)
{
var length = x.Length + 1 - Vector<double>.Count;
for (; i < length; i += Vector<double>.Count)
{
var valpha = new Vector<double>(alpha);
var vx = new Vector<double>(x, i);
var vy = new Vector<double>(y, i);
(vy + vx * valpha).CopyTo(y, i);
}
}
for (; i < x.Length; ++i)
y[i] = y[i] + x[i] * alpha;
}
And, playing around in .NET Core 2.0, I though I would try Span, both naive and Vector form:
public static void daxpy(double alpha, Span<double> x, Span<double> y)
{
for (var i = 0; i < x.Length; ++i)
y[i] += x[i] * alpha;
}
And Vector
public static void daxpy(double alpha, Span<double> x, Span<double> y)
{
if (Vector.IsHardwareAccelerated)
{
var vx = x.NonPortableCast<double, Vector<double>>();
var vy = y.NonPortableCast<double, Vector<double>>();
var valpha = new Vector<double>(alpha);
for (var i = 0; i < vx.Length; ++i)
vy[i] += vx[i] * valpha;
x = x.Slice(Vector<double>.Count * vx.Length);
y = y.Slice(Vector<double>.Count * vy.Length);
}
for (var i = 0; i < x.Length; ++i)
y[i] += x[i] * alpha;
}
So the relative timings on all these are:
Naive 1.0
Vector 0.8
Span Naive 2.5 ==> Update: Span Naive 1.1
Span Vector 0.9 ==> Update: Span Vector 0.6
So am I doing something wrong? I could hardly think of a simpler example, so I don't think so?
You probably want to test with 2.1 more than 2.0;
on my laptop (which has poor SIMD compared to my desktop), I get:
daxpy_naive x10000: 144ms
daxpy_arr_vector x10000: 77ms
daxpy_span x10000: 173ms
daxpy_vector x10000: 67ms
daxpy_vector_no_slice x10000: 67ms
using code:
using System;
using System.Diagnostics;
using System.Numerics;
class Program
{
static void Main(string[] args)
{
double alpha = 0.5;
double[] x = new double[16 * 1024], y = new double[x.Length];
var rand = new Random(12345);
for (int i = 0; i < x.Length; i++)
x[i] = rand.NextDouble();
RunAll(alpha, x, y, 1, false);
RunAll(alpha, x, y, 10000, true);
}
private static void RunAll(double alpha, double[] x, double[] y, int loop, bool log)
{
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
var watch = Stopwatch.StartNew();
for(int i = 0; i < loop; i++)
{
daxpy_naive(alpha, x, y);
}
watch.Stop();
if (log) Console.WriteLine($"{nameof(daxpy_naive)} x{loop}: {watch.ElapsedMilliseconds}ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
daxpy_arr_vector(alpha, x, y);
}
watch.Stop();
if (log) Console.WriteLine($"{nameof(daxpy_arr_vector)} x{loop}: {watch.ElapsedMilliseconds}ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
daxpy_span(alpha, x, y);
}
watch.Stop();
if (log) Console.WriteLine($"{nameof(daxpy_span)} x{loop}: {watch.ElapsedMilliseconds}ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
daxpy_vector(alpha, x, y);
}
watch.Stop();
if (log) Console.WriteLine($"{nameof(daxpy_vector)} x{loop}: {watch.ElapsedMilliseconds}ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
daxpy_vector_no_slice(alpha, x, y);
}
watch.Stop();
if (log) Console.WriteLine($"{nameof(daxpy_vector_no_slice)} x{loop}: {watch.ElapsedMilliseconds}ms");
}
public static void daxpy_naive(double alpha, double[] x, double[] y)
{
for (var i = 0; i < x.Length; ++i)
y[i] = y[i] + x[i] * alpha;
}
public static void daxpy_arr_vector(double alpha, double[] x, double[] y)
{
var i = 0;
if (Vector.IsHardwareAccelerated)
{
var length = x.Length + 1 - Vector<double>.Count;
for (; i < length; i += Vector<double>.Count)
{
var valpha = new Vector<double>(alpha);
var vx = new Vector<double>(x, i);
var vy = new Vector<double>(y, i);
(vy + vx * valpha).CopyTo(y, i);
}
}
for (; i < x.Length; ++i)
y[i] = y[i] + x[i] * alpha;
}
public static void daxpy_span(double alpha, Span<double> x, Span<double> y)
{
for (var i = 0; i < x.Length; ++i)
y[i] += x[i] * alpha;
}
public static void daxpy_vector(double alpha, Span<double> x, Span<double> y)
{
if (Vector.IsHardwareAccelerated)
{
var vx = x.NonPortableCast<double, Vector<double>>();
var vy = y.NonPortableCast<double, Vector<double>>();
var valpha = new Vector<double>(alpha);
for (var i = 0; i < vx.Length; ++i)
vy[i] += vx[i] * valpha;
x = x.Slice(Vector<double>.Count * vx.Length);
y = y.Slice(Vector<double>.Count * vy.Length);
}
for (var i = 0; i < x.Length; ++i)
y[i] += x[i] * alpha;
}
public static void daxpy_vector_no_slice(double alpha, Span<double> x, Span<double> y)
{
int i = 0;
if (Vector.IsHardwareAccelerated)
{
var vx = x.NonPortableCast<double, Vector<double>>();
var vy = y.NonPortableCast<double, Vector<double>>();
var valpha = new Vector<double>(alpha);
for (i = 0; i < vx.Length; ++i)
vy[i] += vx[i] * valpha;
i = Vector<double>.Count * vx.Length;
}
for (; i < x.Length; ++i)
y[i] += x[i] * alpha;
}
}
which is using dotnet build -c Release and dotnet run -c Release, with dotnet --version reporting "2.2.0-preview1-008000" (a "daily" from a little while ago).
On my desktop, I would expect the difference to be even better.
I'm currently trying to make a code, that will help to count efficient reactors set for game StarMade.
I'm using recursive method to explore the 3d tree of elements and find all related groups. For ex. group - it's a cluster of elements, that stay close to each other.
On picture something like this:
XOX
OOX
XXO
where O is nothing, and X is reactor (element).
On this picture there are 3 groups of elements. [0,0], [2,0]-[2,1], [0,2]-[1,2]
Another variant:
XXX
OOX
XXX
Here is only one group, because all elements stay close to each other.
Here is my code:
void CheckGroup(int x, int y, int z, Group group)
{
if(x >= maxz || x < 0 || y >= maxy || y < 0 || z >= maxz || z < 0)
{
return;
}
if (reactorsChecked[x, y, z])
{
return;
}
reactorsChecked[x, y, z] = true;
if (reactors[x, y, z])
{
if (group == null)
{
group = new Group();
group.MaxX = x;
group.MaxY = y;
group.MaxZ = z;
group.MinX = x;
group.MinY = y;
group.MinZ = z;
group.Blocks = 1;
}
else
{
group.MaxX = Math.Max(group.MaxX, x);
group.MaxY = Math.Max(group.MaxY, y);
group.MaxZ = Math.Max(group.MaxZ, z);
group.MinX = Math.Min(group.MinX, x);
group.MinY = Math.Min(group.MinY, y);
group.MinZ = Math.Min(group.MinZ, z);
group.Blocks += 1;
}
CheckGroup(x + 1, y, z, group);
CheckGroup(x - 1, y, z, group);
CheckGroup(x, y + 1, z, group);
CheckGroup(x, y - 1, z, group);
CheckGroup(x, y, z + 1, group);
CheckGroup(x, y, z - 1, group);
if (!groups.Contains(group))
{
groups.Add(group);
}
}
}
group - is simple class for cluster, that store data about elements count in this cluster and bounding box of this cluster.
reactorsChecked - is simple bool[,,] array, that store information about elements, that we have checked, to avoid doubles
reactor - simple bool[,,] array of random elements.
At first I insert random values to reactors array, and then call CheckGroup(x,y,z,null). If reactors array size less then 25x25x25, then all ok. In single thread size of array could be 100x100x100 and all would be ok. But if I try to use Parallel.For, then I got StackOverflow after near 9000 recursions...
Here is full code:
Parallel.For(0, Environment.ProcessorCount, (i) =>
{
Calculator calc = new Calculator(x, y, z, max, cycles);
calcs.Add(calc);
});
public class Calculator
{
Random rnd = new Random();
//List<Group> groups = new List<Group>();
HashSet<Group> groups = new HashSet<Group>();
bool[, ,] reactors;
public bool[, ,] reactorsMax;
bool[, ,] reactorsChecked;
public double maxEnergy = 0;
public string result = "";
public string resultPic = "";
int maxx, maxy, maxz;
public Calculator(int x, int y, int z, int max, int cycles)
{
maxx = x;
maxy = y;
maxz = z;
maxEnergy = max;
for (int i = 0; i < cycles; i++)//check few variants per thread
{
Calculate(x,y,z);
}
}
private void Calculate(int X, int Y, int Z)
{
//groups = new List<Group>();
groups = new HashSet<Group>();
reactors = new bool[X, Y, Z];
for (int x = 0; x < X; x++)
{
for (int y = 0; y < Y; y++)
{
for (int z = 0; z < Z; z++)
{
reactors[x, y, z] = rnd.Next(2)==1;//fill array with random values
}
}
}
reactorsChecked = new bool[X, Y, Z];
for (int x = 0; x < X; x++)
{
for (int y = 0; y < Y; y++)
{
for (int z = 0; z < Z; z++)
{
CheckGroup(x, y, z, null);//start calculations
}
}
}
double sum = 0;
int blocks = 0;
foreach(Group g in groups)
{
float dims = g.MaxX - g.MinX + g.MaxY - g.MinY + g.MaxZ - g.MinZ + 3;
sum += (2000000.0f / (1.0f + Math.Pow(1.000696f, (-0.333f * Math.Pow((dims / 3.0f), 1.7)))) - 1000000.0f + 25.0f * g.Blocks);
blocks += g.Blocks;
}
if (sum > maxEnergy)
{
maxEnergy = sum;
reactorsMax = reactors;
}
}
void CheckGroup(int x, int y, int z, Group group)
{
if(x >= maxz || x < 0 || y >= maxy || y < 0 || z >= maxz || z < 0)
{
return;
}
if (reactorsChecked[x, y, z])
{
return;
}
reactorsChecked[x, y, z] = true;
if (reactors[x, y, z])
{
if (group == null)
{
group = new Group();
group.MaxX = x;
group.MaxY = y;
group.MaxZ = z;
group.MinX = x;
group.MinY = y;
group.MinZ = z;
group.Blocks = 1;
}
else
{
group.MaxX = Math.Max(group.MaxX, x);
group.MaxY = Math.Max(group.MaxY, y);
group.MaxZ = Math.Max(group.MaxZ, z);
group.MinX = Math.Min(group.MinX, x);
group.MinY = Math.Min(group.MinY, y);
group.MinZ = Math.Min(group.MinZ, z);
group.Blocks += 1;
}
CheckGroup(x + 1, y, z, group);
CheckGroup(x - 1, y, z, group);
CheckGroup(x, y + 1, z, group);
CheckGroup(x, y - 1, z, group);
CheckGroup(x, y, z + 1, group);
CheckGroup(x, y, z - 1, group);
if (!groups.Contains(group))
{
groups.Add(group);
}
}
}
}
So the main question - is it possible to avoid stackOverflow in Parallel.For, or to rewrite it to iteration loop?
Parallel.For using default stackSize value even if you will use
Thread(()=>
{
Parallel.For(...);
},stackSize).Start()
it will use default values...
I don't like variant like this:
for(int i = 0; i < cpuCount; i++)
{
Thread t = new Thread(()=>{calculate();},stackSize).Start()
}
because I have to manage all threads, wait while all finishes, so it makes code very complicated... May be there are easier things?
There are two options:
to use recursion and try to increase the stack size (by using the Thread(ThreadStart, maxStackSize) constructor). The stack in applications is usually set to 1MB (see this link for details). Especially in DEBUG mode without optimizations (no inlining optimization done) this is a very limited value. Having a thread with separate stack for every Paralllel.For() statement
might help.
Use a iteration look instead of recursion to handle the stack depth by yourself.
I personally would go with option 1. (with or without separate stack) only in case I known the maximum depth of my recursion.
My preferred solution in most cases like yours will be the iteration approach.
Edit by #LordXaosa:
I tried this, and all works fine
int stackSize = 1024*1024*1024;//1GB limit
ManualResetEvent[] mre = new ManualResetEvent[Environment.ProcessorCount];
Parallel.For(0, Environment.ProcessorCount, (i) =>
{
mre[i] = new ManualResetEvent(false);
Thread t = new Thread((object reset) =>
{
Calculator calc = new Calculator(x, y, z, max, cycles);
calcs.Add(calc);
ManualResetEvent m = (ManualResetEvent)reset;
m.Set();
}, stackSize / (Environment.ProcessorCount * 4));
t.Start(mre[i]);
});
WaitHandle.WaitAll(mre);
But there also a limit... 50x50x50 array works fine, but more - stack overflow... In original game it can process 1000x1000x1000 sets, so may be there is another algorithm.
Thanks for your help!
I want to split a matrix into quadrants, upper and bottom left and right, according to the current position. Here is an example(to avoid writing T/U, for example, I placed the different characters with top and left priority, but they do overlap):
o T T U o o o
B T X U L L L
B B B L L L L
B B B L L L L
o o L L L L L
o o L L L L L
X is the central element. o is an element not included in any of the quadrants, T is top left, U is top right, B is lower left and L is lower right, I hope you get the idea. My solution includes checking in which quadrant of the matrix the starting position is, and then making the quadrants accordingly, which makes a total of 4 cases each with 4 quadrants = 16. I hope there's a better way, so can you please help me with this? I'm writing the program in C#, but pseudocode is fine.
You can access the different quadrant of cartesian system through their respective property.
public class Matrix<T>
{
public T[,] Elements { get; private set; }
public int Size { get; private set; }
public Point CentralPoint { get; set; }
public T this [int y, int x]
{
get { return Elements[y, x]; }
set { Elements[y, x] = value; }
}
public Matrix(int size)
{
this.Size = size;
this.Elements = new T[Size, Size];
}
public T[,] FirstQuadrant { get { return FromCentralTo(Size - 1, 0); } }
public T[,] SecondQuadrant { get { return FromCentralTo(0, 0); } }
public T[,] ThirdQuadrant { get { return FromCentralTo(0, Size - 1); } }
public T[,] FourthQuadrant { get { return FromCentralTo(Size - 1 , Size - 1); } }
private T[,] FromCentralTo(int x1, int y1, [CallerMemberName]string caller = "")
{
var translate = Math.Min(Math.Abs(CentralPoint.X - x1), Math.Abs(CentralPoint.Y - y1));
//fix the p1, so this results in a square array
if (Math.Abs(CentralPoint.X - x1) > translate)
x1 = CentralPoint.X + translate * Math.Sign(x1 - CentralPoint.X);
if (Math.Abs(CentralPoint.Y - y1) > translate)
y1 = CentralPoint.Y + translate * Math.Sign(y1 - CentralPoint.Y);
var size = translate + 1;
var start = new Point(Math.Min(CentralPoint.Y, y1), Math.Min(CentralPoint.X, x1));
var result = new T[size, size];
for (int x = 0; x < size; x++)
for (int y = 0; y < size; y++)
result[y, x] = this[start.Y + y, start.X + x];
return result;
}
}
public struct Point
{
public int X, Y;
public Point(int y, int x) { this.X = x; this.Y = y; }
}
void Main()
{
var matrix = new Matrix<int>(7);
matrix.CentralPoint = new Point(1, 2);
int i = 0;
for(int x = 0; x < 7; x++)
for (int y = 0; y < 7; y++)
matrix[x, y] = i++;
matrix.Elements.Dump("Matrix");
matrix[matrix.CentralPoint.Y, matrix.CentralPoint.X].Dump("Central");
matrix.FirstQuadrant.Dump("1");
matrix.SecondQuadrant.Dump("2");
matrix.ThirdQuadrant.Dump("3");
matrix.FourthQuadrant.Dump("4");
}
The above code produce the following in LINQPad :
I have a 3d-array double[,,] numbers = new double[x,y,z]; and now if one imagines the 3d-array to look like a cube with numbers I need to find the smallest and biggest value of every slice along all three directions.
It is of course easy to do by simply looping over it, but does C# have any functions to find the smallest and biggest value in a slice?
To explain it a bit further, maybe this "unreal" code will help:
int i;
double[] xmin = new double[x];
double[] xmax = new double[x];
double[] ymin = new double[y];
double[] ymax = new double[y];
double[] zmin = new double[z];
double[] zmax = new double[z];
for(i = 0; i < x; i++)
{
MinOf(numbers[i, y, z]) = xmin[i];
MaxOf(numbers[i, y, z]) = xmax[i];
}
for(i = 0; i < y; i++)
{
MinOf(numbers[x, i, z]) = ymin[i];
MaxOf(numbers[x, i, z]) = ymax[i];
}
for(i = 0; i < z; i++)
{
MinOf(numbers[x, y, i]) = zmin[i];
MaxOf(numbers[x, y, i]) = zmax[i];
}
Hopefully someone can help me with that.
Cheers, Phil13131
You can make methods for enumerating the slices. This is for one dimension, you would need another two, but I think you can manage that:
public static IEnumerable<T> SliceX<T>(T[,,] data, int x) {
for (int y = 0; y < data.GetLength(1); y++) {
for (int z = 0; z < data.GetLength(2); z++) {
yield return data[x, y, z];
}
}
}
Then you can just use the Min and Max methods, but that will of course loop through the data twice:
double min = SliceX(numbers, x).Min();
double max = SliceX(numbers, x).Max();
You can make an extension method that gets both min and max in one iteration:
public static class IEnumerableExtensions {
public static void GetMinMax<T>(this IEnumerable<T> data, out T min, out T max) where T : IComparable<T> {
bool first = true;
min = max = default(T);
foreach (T value in data) {
if (first) {
min = max = value;
first = false;
} else {
if (value.CompareTo(min) < 0) min = value;
if (value.CompareTo(max) > 0) max = value;
}
}
}
}
Usage:
double min, max;
SliceX(numbers, 0).GetMinMax(out min, out max);
Are you looking for something like this?
double[, ,] numbers = new double[2, 2, 2];
numbers[0, 0, 0] = 0;
numbers[0, 0, 1] = 1;
numbers[0, 1, 0] = 2;
numbers[0, 1, 1] = 3;
numbers[1, 0, 0] = 4;
numbers[1, 0, 1] = 5;
numbers[1, 1, 0] = 6;
numbers[1, 1, 1] = 7;
double[] xmax = new double[numbers.GetLength(0)];
double[] ymax = new double[numbers.GetLength(1)];
double[] zmax = new double[numbers.GetLength(2)];
for (int x = 0; x < xmax.Length; x++) xmax[x] = int.MinValue;
for (int y = 0; y < ymax.Length; y++) ymax[y] = int.MinValue;
for (int z = 0; z < zmax.Length; z++) zmax[z] = int.MinValue;
for (int x = 0; x < xmax.Length; x++)
for (int y = 0; y < ymax.Length; y++)
for (int z = 0; z < zmax.Length; z++)
{
xmax[x] = Math.Max(xmax[x], numbers[x, y, z]);
ymax[y] = Math.Max(ymax[y], numbers[x, y, z]);
zmax[z] = Math.Max(zmax[z], numbers[x, y, z]);
}
// xmax == { 3, 7 }
// ymax == { 5, 7 }
// zmax == { 6, 7 }
Hi i just write function in C# that generate coordinate for cube but the problem i want just
generate the coordinate of cube sides not in the depth !!!
static class Util
{
public static List<string> GenerateCubeCoord(double bc,int nt,double stp)
{
List<string> list = new List<string>();
double CoorBase = bc;
int n = nt;
double step = stp;
int id = 1;
for (int x = 0; x < n; x++)
{
for (int y = 0; y < n; y++)
{
for (int z = 0; z < n; z++)
{
list.Add(string.Format("GRID {0} {1}.0 {2}.0 {3}.0 \n",
id, step * x + CoorBase, step * y + CoorBase, step * z + CoorBase));
id++;
}
}
}
return list;
}
}
I wat to generate all this coordinate not the corner coordinates of cube , in the image one
side of may cube
Without changing your code too much (assuming you meant all corner points, it's a little unclear):
for (int x = 0; x <= n; x += n)
for (int y = 0; y <= n; y += n)
for (int z = 0; z <= n; z += n)
Console.WriteLine("{0} {1} {2}", x, y, z);
A little cleaner using LINQ:
int n = 6;
var coords = from x in new[] { 0, n }
from y in new[] { 0, n }
from z in new[] { 0, n }
select new { x, y, z };
foreach(var coord in coords)
Console.WriteLine("{0} {1} {2}", coord.x, coord.y, coord.z);
Edit after updated question:
If you just want the coordinates of the sides, the allowed values for one coordinate (x,y or z) are either 0 or n-1:
var coords = from x in new[] { 0, n-1 }
from y in Enumerable.Range(0, n)
from z in Enumerable.Range(0, n)
select new { x, y, z };
Rinse and repeat for the other two and you have the set of coordinates for all 6 sides.
Edit:
With above solution there are overlaps between the different sides (the edge points), so you'd have to use the union of all 3 collections. A better solution is to query for all coordinates in one go:
var coords = from x in Enumerable.Range(0, n)
from y in Enumerable.Range(0, n)
from z in Enumerable.Range(0, n)
where ( x == 0 || x==n-1 || y == 0 || y== n-1 || z == 0 || z== n-1)
select new { x, y, z };