How to generate a number from 3 inputs - c#

I am trying to create a system in unity where a number is generated (with a range from 0 to 9) from 3 float inputs. This is to create a procedural game where being in the same location always gives the same output. Because of this, I don't just want to use an in-built random method, as it would give different results each time. However, I am struggling to create a method that can achieve this.
To summarise:
I want to be able to put in 3 values, e.g.:
[3.2344234, -44.33030, 0.22222]
And have the method come up with a number like:
[2]
But I don't want to use an in-built method.
This is what I have tried so far - It's a bit of a mess:
public float randomNumber(float x, float y, float z)
{
float stage1 = (float)((Mathf.Sign(x + 512) / Mathf.Cos(z + 42) + Mathf.SmoothDampAngle(z,y,ref(z),x) * Mathf.Atan2(y - 15,z)) / 24 + 5 * 6)%7.2f;
string stage2 = stage1.ToString();
int stage3 = (int)stage2[5];
float result = stage3;
return result;
}
Any suggestions to properly generate random numbers are most welcome.

If distribution is not that important, the following will do:
static int GetRandomNumber(float x, float y, float z)
{
// convert the floats to byte arrays
var b1 = BitConverter.GetBytes(x);
var b2 = BitConverter.GetBytes(y);
var b3 = BitConverter.GetBytes(z);
// turn the byte arrays into integers
var i1 = BitConverter.ToInt32(b1);
var i2 = BitConverter.ToInt32(b2);
var i3 = BitConverter.ToInt32(b3);
// XOR the 3 integers
var merged = i1 ^ i2 ^ i3;
// get the positive value of the integer and do a modulo by 10
// that will give you a value between 0 and 9
var result = Math.Abs(merged) % 10;
return result;
}
Same using unsafe:
static unsafe int GetRandomNumberUnsafe(float x, float y, float z)
{
var i1 = *(int*)(&x);
var i2 = *(int*)(&y);
var i3 = *(int*)(&z);
var merged = i1 ^ i2 ^ i3;
var result = Math.Abs(merged) % 10;
return result;
}
----- EDIT -----
Starting from .NET Core 2.2, you can also use HashCode.Combine:
static unsafe int GetRandomNumber(float x, float y, float z)
{
var hash = HashCode.Combine(x, y, z);
var result = Math.Abs(hash) % 10;
return result;
}

Basically you can use
whatever hash algorithm you want that generates distributed values from the given input
use modulo on the hash in order to map it into your required range
since hashes might be negative values get the absolute value of the result
You could e.g. just do
public int randomNumber(float x, float y, float z)
{
var hash = x.GetHashCode();
hash = (hash * 397) ^ y.GetHashCode();
hash = (hash * 397) ^ z.GetHashCode();
return Mathf.Abs(hash % 10);
}
Or in newer version of Unity using .NET Framework 4.7.X you can actually use tuples and simply do e.g.
public int randomNumber(float x, float y, float z)
{
return Mathf.Abs((x, y, z).GetHashCode() % 10);
}
Or as alternative assuming your values come from a Vector3 since you speak of a location you could also use your given Vector3 directly
public int randomNumber(Vector3 vector)
{
return Mathf.Abs(vector.GetHashCode() % 10);
}

Limit you map area for x, y and z axes and after that divide them to 10 as incremental numbers: For example x axis -100:100 freedom of movement. (30-(-30))/3=20 will give incremental number of x axis. Make an array of 10 imaginary boxes where different x, y and z location values will give different numbers according to your array. Your imaginary 3D object with cubes must include 10 members so 3x3x3 will give 9. Add one box(it is up to you) to one dimension and after that every increments step in one axis will change the number.
If you want certain numbers will give always same result make as mod4(input_1)+mod4(input_2)+mod4(input_3)+mod2(input_1+input_2+input_3) may work. But I didn't check last formula, you should check it.

Related

Combine Aggregate and Select Linq

I have a Listof points and I want to calculate the remaining distance to the end using Linq (given an index):
double remainingToEnd = Points.Skip(CurrentIndex).Aggregate((x, y) => x.DistanceTo(y));
This doesn't compile:
Cannot convert lambda expression to intended delegate type because
some of the return types in the block are not implicitly convertible
to the delegate return type
I normally solve this situation projecting by using the Select extension, but that would prevent me from calculating the distance afterwards.
This is easily achieved by using a loop but I want to know if it is possible with some simple Linq. I would like to avoid anonymous types too.
Point is defined like:
public class Point
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
public float DistanceTo(Point p2)
{
float x = this.X - p2.X;
float y = this.Y - p2.Y;
float z = this.Z - p2.Z;
return (float)Math.Sqrt((x * x) + (y * y) + (z * z));
}
}
Assume you want to calculate total distance between points in collection (starting from some index). You need previous point on each step. You can get it by zipping points collection with itself:
double remainingToEnd = Points.Skip(CurrentIndex)
.Zip(Points.Skip(CurrentIndex + 1), (x,y) => x.DistanceTo(y))
.Sum();
Zip will produce pairs of starting and ending points. Result selector function will select distance between points for each pair. And then you just calculate sum or distances.
You can solve this task with aggregation as will, but you need to store last point on each step. So you need accumulator which will keep both current distance and last point:
var remainingToEnd = Points.Skip(CurrentIndex).Aggregate(
new { total = 0.0, x = Points.Skip(CurrentIndex).FirstOrDefault() },
(a, y) => new { total = a.total + a.x.DistanceTo(y), x = y },
a => a.total);
And keep in mind, that Skip means just iterating your sequence item by item without doing anything. If you have a lot of points, skipping twice can hurt your performance. So if you have list of points, and performance matters, then simple for loop will do the job:
double remainingToEnd = 0.0;
for(int i = CurrentIndex; i < Points.Count - 1; i++)
remainingToEnd += Points[i].DistanceTo(Points[i+1]);
Try this:
double remainingToEnd = Points.Skip(CurrentIndex).Sum(point => point.DistanceTo(Points[Points.Findindex(p => p == point) - 1]));

How could I optimize this algorithm for approximating pi?

I am quite inexperienced in coding, but I have managed to write this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PiApprox
{
public class PiApprox
{
//side of the rectangle
public static int s;
//radius of the circle
public static int r;
//indexers of the coordinates, points
public static int ix;
public static int iy;
//current y
public static decimal cury;
//without rounding
public static decimal wcury;
//amounts of points relative to the circle
public static decimal inAmount;
public static decimal onAmount;
public static decimal outAmount;
//amount of all points
public static decimal allAmount;
//short for inAmount and on onAmount,
//used to make the calculations clearer in the final part
public static decimal inanon;
//final result, crude approximation of pi
public static decimal piApprox;
public static void Main()
{
while (true)
{
Calculate();
}
}
public static void Calculate ()
{
s = Convert.ToInt32(Console.ReadLine());
//calculate the radius of the circle
r = s / 2;
//calculate the total amount of points in the grid
//rectangle area
allAmount = (decimal) Math.Pow(s, 2);
//reset values
inAmount = 0;
onAmount = 0;
outAmount = 0;
//main loop
//iterate for y, from up to down
for (ix = -r; ix <= 0; ix++)
{
wcury = (decimal) Math.Sqrt(Math.Pow(r, 2) - Math.Pow(ix, 2));
cury = Math.Floor(wcury);
outAmount += r - (int)cury;
if (wcury == cury)
{
onAmount++;
}
if (wcury == cury)
{
inAmount += (int)cury;
}
else
{
inAmount += (int)cury + 1;
}
Result();
}
Result();
}
public static void Result()
{
//total amount of points
inanon = 4 * (onAmount + inAmount - (r + 1)) + 1;
//proportion
piApprox = 4 * (inanon / allAmount);
Console.SetCursorPosition(1, 0);
Console.WriteLine(piApprox);
}
}
}
The Monte Carlo principle is simple; I calculate the y values
for the plot f(x) = sqrt(r^2 - ix^2) which represent the first quarter of a circle. I then calculate points within the circle and output it at the end.
The multiplication on the line piApprox = 4 * (inanon / allAmount);
comes from the proportions of the square and the circle:
(pi * r^2) / ( (2r) ^ 2 ) ->
(pi * r ^ 2) / (4 * r ^ 2) -> pi / 4
Is there something I could do to speed up the computing?
I assume you're new to C# so I'll just give you a couple hints here.
Several things have potential for improvement:
decimal is slow: it uses software computations. On the other hand, calculations on int, double and similar are implemented in hardware. Use int here, you don't use the decimal part anyway.
Math.Pow is slow. Don't use it for squaring: Replace Math.Pow(x, 2) with x * x
Math.Sqrt is slow. Instead of comparing Math.Sqrt(x) to y, compare x to y * y instead. Or just call it once at the end.
Math.Floor is slow :)
You could use parallelism to leverage multicore CPUs
You should use local variables as they're more prone for optimization
Bear in mind that when I mean slow it's relative. All of these operations are extremely fast in an absolute sense - I just mean you could use an even faster alternative.
But there's one thing which is painfully slow (such that it's noticeable for a human): Console. It got much better on Windows 10, but it's still slow and you're using the console in the hot path of your code. Get rid of these intermediate results.
One more thing, if you use int in a division, you'll get an int in C#. You need to cast one operand to, say, a double before dividing if you want to get the fractional part (as in (double)x / y).

Why is my implementation of the parking lot test for random number generators producing bad results?

I'm trying to write an implementation of the parking lot test for random number generators. Here are the sources that I'm getting my information about the test from: Intel math library documentation and Page 4 of this paper along with the phi function for probability density listed here.
I wrote an implementation of the test in C#. It uses a 100x100 grid whose values are initially set to null. I then use the random number generator to generate random integers for x and y. If that index of the grid and it's neighbors are empty, that index gets set to 1. Otherwise, nothing happens because there was a "crash".
I ran it using C# System.Random generator. I don't believe the results are correct because I always get very near 3079 points parked, which is about 500 short of the average I'm supposed to get. It's also yields a p-value of 2.21829146215425E-90.
My code is below. Does anyone have any experience with this or can anyone see something that I might be doing incorrectly in my implementation? Any help would be greatly appreciated.
private void RunParkingLotTest()
{
points = new int?[100,100];
int parked = 0;
for (int i = 0; i < 12000; i++)
{
int x = random.Next(100);
int y = random.Next(100);
if (IsSafeToPark(x, y))
{
points[x, y] = 1;
parked++;
}
}
Console.WriteLine("Parked: " + parked + "\nP value: " + PhiFunction((parked-3523)/21.9));
}
private bool IsSafeToPark(int x, int y)
{
return PointIsEmpty(x, y)
&& LeftOfPointIsEmpty(x, y)
&& RightOfPointIsEmpty(x, y)
&& BelowPointIsEmpty(x, y)
&& AbovePointIsEmpty(x, y);
}
private bool AbovePointIsEmpty(int x, int y)
{
if (y == 99)
{
return true;
}
else
return points[x, y + 1] == null;
}
private bool BelowPointIsEmpty(int x, int y)
{
if (y == 0)
{
return true;
}
else
return points[x, y - 1] == null;
}
private bool RightOfPointIsEmpty(int x, int y)
{
if (x == 99)
{
return true;
}
else
return points[x + 1, y] == null;
}
private bool LeftOfPointIsEmpty(int x, int y)
{
if (x == 0)
{
return true;
}
else
return points[x - 1, y] == null;
}
private bool PointIsEmpty(int x, int y)
{
return points[x, y] == null;
}
private double PhiFunction(double x)
{
//ϕ(x) = (2π)−½e−x2/2
return ((1 / Math.Sqrt(2 * Math.PI)) * Math.Exp(-(Math.Pow(x, 2)) / 2));
}
edit - The problems with my original implementation were
I was plotting squares instead of disks
I only plotted points at integer values. I should have used decimal values instead.
As a result of the above two, I needed to change my distance check
Thanks to Chris Sinclair and mine z for help in figuring this out. The final code is posted below.
I'm going to take a stab at this, and admittedly, I have not attempted any such test, so forgive me if I'm way off. In general though, the .NET Random implementation is pretty good and I've never had issues with it, so I wouldn't suspect that initially especially since you're properly reusing the same instance instead of creating new ones.
Reading from the parking.pdf, and from the Intel documentation, it seems that they're using discs, and compute the distance from their centre points. Your implementation is using squares (array of 1 distance between spots) and thus ignoring diagonals.
From the pdf:
If disks were being used, the distance between the particles r =
p(x(i) − z)2 + (y(i) − z)2 would need to be less than or equal to one.
Does it matter whether one uses disks or squares? An indication of the
importance of which geometric figure is parked can be obtained by
comparing the area occupied by a square of side 1.0 to the area of a
disk of diameter 1.0. The ratio of the areas, disk to square, is π/4.
Therefore, it would be anticipated that more disks could be placed in
a box than squares in the same number of tries.
And the Intel doc:
The test assumes a next random point (x, y) successfully ”parked”, if
it is far enough from every previous successfully ”parked” point. The
sufficient distance between the points (x1, y1) and (x2, y2) is
min(|x1 - x2|,|y1 - y2|) > 1.
I'm guessing that the π/4 disk to square ratio and the differences between how many discs can fit vs squares might be why you're seeing a different number. (although right now I fail to see a directly relationship between 3523 and 3070 and π/4. 3523 * π/4 = 2767, which is close, but I'm sure if there's a relationship it's slightly more complex than just simple multiplication.)
Not a great answer, but my best guess.
EDIT: Interestingly enough, I did a quick implementation using discs with 1 unit diameter and getting results around 4000 parked. So maybe there's a bit more to this than my untrained self can grasp (or maybe .NET's Random doesn't pass the test?) Anyway, here's my disc implementation:
List<Point> parkedCars = new List<Point>();
Random random = new Random();
void Main()
{
int parked = 0;
for (int i = 0; i < 12000; i++)
{
double x = random.NextDouble() * 100;
double y = random.NextDouble() * 100;
Point pointToPark = new Point(x, y);
if (IsSafeToPark(pointToPark))
{
parkedCars.Add(pointToPark);
parked++;
}
}
Console.WriteLine("Parked: " + parked);
}
private bool IsSafeToPark(Point pointToPark)
{
//make sure it's "inside" the box
if (pointToPark.X < 0.5 || pointToPark.X > 99.5
|| pointToPark.Y < 0.5 || pointToPark.Y > 99.5)
return false;
if (parkedCars.Any(p => Distance(pointToPark, p) <= 1))
return false;
return true;
}
private double Distance(Point p1, Point p2)
{
return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}
Using my likely too simple application of the π/4 ratio yields about 3142. A bit closer, but it seems very incorrect.
EDIT: As #mike z pointed out, my test using directly distance is incorrect. According to the parameters of the test, which I forgot about, just checks that the X and Y distance are greater than 1. Changing my Distance check to:
Math.Max(Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y))
Yields a much closer result around 3450, which is pretty close. If I take out my "//make sure it's "inside" the box" check, averaged over 10 tries gets 3531!
So my final, "working" code is:
public struct Point
{
public double X,Y;
public Point(double x, double y)
{
this.X = x;
this.Y = y;
}
}
List<Point> parkedCars = new List<Point>();
Random random = new Random();
void Main()
{
int parked = 0;
for (int i = 0; i < 12000; i++)
{
double x = random.NextDouble() * 100;
double y = random.NextDouble() * 100;
Point pointToPark = new Point(x, y);
if (IsSafeToPark(pointToPark))
{
parkedCars.Add(pointToPark);
parked++;
}
}
Console.WriteLine("Parked: " + parked);
}
private bool IsSafeToPark(Point pointToPark)
{
if (parkedCars.Any(p => Distance(pointToPark, p) <= 1))
return false;
return true;
}
private double Distance(Point p1, Point p2)
{
return Math.Max(Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y));
}
EDIT: I ran the test 100 times twice, and averaged the results to 3521.29 and 3526.74 respectively. Not sure if this means there still something slightly more to this, but perhaps this is just indicative of rounding or floating point precision differences between .NET and Fortran.

Oscillate or "ping pong" between two values?

I have a path that is evaluate at time 't' and returns an orientation and position based on the path type.
The value for time is affected by the path type:
switch (type)
{
case PathType.Closed:
time = ToolBox.Wrap(time, StartTime, EndTime);
break; // Wrap time around the path time to loop
case PathType.Open:
time = ToolBox.Min(time, EndTime);
break; // Clamp the time value to the max path time range
case PathType.Oscillating:
break;
}
The missing link is oscillating.
My question is what is a good, efficient way for oscillating between two values?
For example (2, 7). If time reaches 7 it reverses and decrements towards to 2 and once it reaches 2 it reverses and increases towards 7.
The algorithm should know whether to increase/decrease the value based on the original value so if the value is 9 it knows the answer is 7 - (Abs(7 - 9). If the value is 14 the value has wrapped around so it will result in an increase of 1.
Higher values will also increase or decrease the value depending on the number of times it wraps around the original range.
I hope that makes sense as I'm finding it difficult to explain.
EDIT:
Doesn't oscillate with floating point values:
for (float i = 0; i < 100; i += 0.1f)
{
Console.WriteLine("{0} {1}", i, Oscillate(2.5f, 7.5f, i));
}
private float Oscillate(float min, float max, float value)
{
float range = max - min;
float multiple = value / range;
bool ascending = multiple % 2 == 0;
float modulus = value % range;
return ascending ? modulus + min : max - modulus;
}
Here is what I came up with:
public static int Oscillate(int input, int min, int max)
{
int range = max - min ;
return min + Math.Abs(((input + range) % (range * 2)) - range);
}
I'm assuming input will be a counter starting at 0.
Ideally, you should be abstracting this functionality into some kind of a class and not be concerned about how the implementation actually works when you're using it. Here's an initial take on what that would look like in C++ (my C# is a little rusty). I think you can work it into C# with only little difficulty.
class oscillator
{
private:
float min;
float max;
static float mod(float num, float div)
{
float ratio = num / div;
return div * (ratio - std::floor(ratio));
}
public:
oscillator(float a, float b)
: min(a < b ? a : b), max(a > b ? a : b) {}
float range() ( return max-min; }
float cycle_length() { return 2*range(); }
float normalize(float val)
{
float state = mod(val-min, cycle_length());
if (state > range())
state = cycle_length()-state;
return state + min;
}
};
This will oscillate your numbers between 2 & 7, in this example, time is an int:
bool isIncreasing = time <= 7;
for (int i = 0; i < 20; i++) //some random loop
{
time = time + (isIncreasing ? 1 : -1);
if (time >= 7 || time <= 2) isIncreasing = !isIncreasing;
}
New answer to account for float values:
// Note: Increase FACTOR depending on how many decimal places of accuracy you need.
private const float FACTOR = 10;
public void Test()
{
for (float i = 0; i < 1000; i += 0.1F)
{
Console.WriteLine("{0} {1}", i, Oscillate(2.5F, 7.5F, i));
}
}
private float Oscillate(float min, float max, float time)
{
return (float)(Oscillate_Aux(Upscale(min), Upscale(max), Upscale(time))) / FACTOR;
}
private int Upscale(float value)
{
return (int)(value * FACTOR);
}
private int Oscillate_Aux(int min, int max, int time)
{
int range = max - min;
int multiple = time / range;
bool ascending = multiple % 2 == 0;
int modulus = time % range;
return ascending ? modulus + min : max - modulus;
}
What you're describing sounds a lot like periodic linear interpolation between two values. Consider using XNA's MathHelper.Lerp function as the basis for your oscillation.
Note that it uses a percentage to control the oscillation as its third parameter. You'll have to figure out how to translate your time t value into a percentile, but you could start with ex. sin(t) to see how things work.
If you're reluctant to import XNA into your project, the core equation is very simple:
value1 + (value2 - value1) * amount
Edit: If your question, at its heart, really is "What is a good, efficient way for oscillating between two values?", then Math.Sin(t) (or Cos) can provide you with regular oscillation between 0 and 1.

How to "flatten" or "index" 3D-array in 1D array?

I am trying to flatten 3D array into 1D array for "chunk" system in my game. It's a 3D-block game and basically I want the chunk system to be almost identical to Minecraft's system (however, this isn't Minecraft clone by any measure). In my previous 2D-games I have accessed the flattened array with following algorithm:
Tiles[x + y * WIDTH]
However, this obviously doesn't work with 3D since it's missing the Z-axis. I have no idea how to implement this sort of algorithm in 3D-space. Width, height and depth are all constants (and width is just as large as height).
Is it just x + y*WIDTH + Z*DEPTH ? I am pretty bad with math and I am just beginning 3D-programming so I am pretty lost :|
PS. The reason for this is that I am looping and getting stuff by index from it quite a lot. I know that 1D arrays are faster than multi-dimensional arrays (for reasons I cant remember :P ). Even though this may not be necessary, I want as good performance as possible :)
Here is a solution in Java that gives you both:
from 3D to 1D
from 1D to 3D
Below is a graphical illustration of the path I chose to traverse the 3D matrix, the cells are numbered in their traversal order:
Conversion functions:
public int to1D( int x, int y, int z ) {
return (z * xMax * yMax) + (y * xMax) + x;
}
public int[] to3D( int idx ) {
final int z = idx / (xMax * yMax);
idx -= (z * xMax * yMax);
final int y = idx / xMax;
final int x = idx % xMax;
return new int[]{ x, y, z };
}
The algorithm is mostly the same. If you have a 3D array Original[HEIGHT, WIDTH, DEPTH] then you could turn it into Flat[HEIGHT * WIDTH * DEPTH] by
Flat[x + WIDTH * (y + DEPTH * z)] = Original[x, y, z]
As an aside, you should prefer arrays of arrays over multi-dimensional arrays in .NET. The performance differences are significant
I think the above needs a little correction. Lets say you have a HEIGHT of 10, and a WIDTH of 90, single dimensional array will be 900. By the above logic, if you are at the last element on the array 9 + 89*89, obviously this is greater than 900. The correct algorithm is:
Flat[x + HEIGHT* (y + WIDTH* z)] = Original[x, y, z], assuming Original[HEIGHT,WIDTH,DEPTH]
Ironically if you the HEIGHT>WIDTH you will not experience an overflow, just complete bonkers results ;)
x + y*WIDTH + Z*WIDTH*DEPTH. Visualize it as a rectangular solid: first you traverse along x, then each y is a "line" width steps long, and each z is a "plane" WIDTH*DEPTH steps in area.
You're almost there. You need to multiply Z by WIDTH and DEPTH:
Tiles[x + y*WIDTH + Z*WIDTH*DEPTH] = elements[x][y][z]; // or elements[x,y,z]
TL;DR
The correct answer can be written various ways, but I like it best when it can be written in a way that is very easy to understand and visualize. Here is the exact answer:
(width * height * z) + (width * y) + x
TS;DR
Visualize it:
someNumberToRepresentZ + someNumberToRepresentY + someNumberToRepresentX
someNumberToRepresentZ indicates which matrix we are on (depth). To know which matrix we are on, we have to know how big each matrix is. A matrix is 2d sized as width * height, simple. The question to ask is "how many matrices are before the matrix I'm on?" The answer is z:
someNumberToRepresentZ = width * height * z
someNumberToRepresentY indicates which row we are on (height). To know which row we are on, we have to know how big each row is: Each row is 1d, sized as width. The question to ask is "how many rows are before the row I'm on?". The answer is y:
someNumberToRepresentY = width * y
someNumberToRepresentX indicates which column we are on (width). To know which column we are on we simply use x:
someNumberToRepresentX = x
Our visualization then of
someNumberToRepresentZ + someNumberToRepresentY + someNumberToRepresentX
Becomes
(width * height * z) + (width * y) + x
The forward and reverse transforms of Samuel Kerrien above are almost correct. A more concise (R-based) transformation maps are included below with an example (the "a %% b" is the modulo operator representing the remainder of the division of a by b):
dx=5; dy=6; dz=7 # dimensions
x1=1; y1=2; z1=3 # 3D point example
I = dx*dy*z1+dx*y1+x1; I # corresponding 2D index
# [1] 101
x= I %% dx; x # inverse transform recovering the x index
# [1] 1
y = ((I - x)/dx) %% dy; y # inverse transform recovering the y index
# [1] 2
z= (I-x -dx*y)/(dx*dy); z # inverse transform recovering the z index
# [1] 3
Mind the division (/) and module (%%) operators.
The correct Algorithm is:
Flat[ x * height * depth + y * depth + z ] = elements[x][y][z]
where [WIDTH][HEIGHT][DEPTH]
To better understand description of 3D array in 1D array would be ( I guess Depth in best answer is meant Y size)
IndexArray = x + y * InSizeX + z * InSizeX * InSizeY;
IndexArray = x + InSizeX * (y + z * InSizeY);
m[x][y][z] = data[xYZ + yZ + z]
x-picture:
0-YZ
.
.
x-YZ
y-picture
0-Z
.
.
.
y-Z
summing up, it should be : targetX*YZ + targetY*Z + targetZ
In case, somebody is interested to flatten an nD (2D, 3D, 4D, ...) array to 1D, I wrote the below code. For example, if the size of the array in different dimensions is stored in the sizes array:
# pseudo code
sizes = {size_x, size_y, size_z,...};
This recursive function gives you the series of {1, size_x, size_x*size_y, size_x*size_y*size_z, ...}
// i: number of the term
public int GetCoeff(int i){
if (i==0)
return 1;
return sizes[i-1]*GetCoeff(i-1);
}
So, we have to multiply nD indexes by their corresponding series term and sum them to get {ix + iy*size_x + iz*size_x*size_y, ...}:
// indexNd: {ix, iy, iz, ...}
public int GetIndex1d(int[] indexNd){
int sum =0;
for (var i=0; i<indexNd.Length;i++)
sum += indexNd[i]*GetCoeff(i);
return sum;
}
In this code I assumed, the nD array is contiguous in memory along firstly x, then y, z, ... . So probably you call your array-like arr[z,y,x]. But, if you call them the other way, arr[x,y,z] then z is the fastest index and we like to calculate iz + iy*size_z + ix* size_z*size_y. In this case, the below function gives us the series {1, size_z, size_z*size_y, ...}:
// Dims is dimension of array, like 3 for 3D
public int GetReverseCoeff(int i){
if (i==0)
return 1;
return sizes[Dims-i]*GetReverseCoeff(i-1);
}
The coefficients are stored in the right order:
public void SetCoeffs(){
for (int i=0;i<Dims;i++)
coeffs[Dims-i-1] = GetReverseCoeff(i);
}
The 1D index is calculated the same as before except coeffs array is used:
// indexNd: {ix, iy, iz, ...}
public int GetIndex1d(int[] indexNd){
int sum =0;
for (var i=0; i<indexNd.Length;i++)
sum += indexNd[i]*coeffs[i];
return sum;
}
Samuel Kerrien's answer to python :
def to1D(crds,dims):
x,y,z=crds
xMax,yMax,zMax=dims
return (z * xMax * yMax) + (y * xMax) + x
def to3D(idx,dims):
xMax,yMax,zMax=dims
z = idx // (xMax * yMax)
idx -= (z * xMax * yMax)
y = idx // xMax
x = idx % xMax
return x, y, z

Categories