I am trying to generate a random 2D array, from a number of smaller arrays. I plan to use it some day to generate a random game map.
Each smaller array is called "Island". Each of them is manually predefined.
char[,] Island1 = new char[,]
{
{'A', 'A', 'A'},
{'A','B','A'},
{'A','A','A'}
};
char[,] Island2 = new char[,]
{
{'C', 'C'},
{'C','C'}
};
char[,] Island3 = new char[,]
{
{'D', 'D', 'D'},
{'D','D','D'},
{'D','D','D'}
};
I am trying to generate a larger array, with all smaller ones inside, placed randomly.
What's important, is that smaller arrays shouldn't overlap each other.
public static Boolean CanPlaceIsland(int StartX, int StartY, Island thisIsland)
{
Boolean Answer = true;
for (int i = StartX; i<StartX+thisIsland.CellArray.GetLength(0);i++)
{
for (int j = StartX; j<StartY+thisIsland.CellArray.GetLength(1);j++)
{
if (WorldMap[i,j].Terrain!='.')
Answer = false;
}
}
return Answer;
}
I am trying to go through each island, one by one, and only add new one, if it doesn't overlap non-empty squares.
Here's updated method for filling the map with islands (previous version could cause infinite loop).
public static void CreateEmptyMap()
{
WorldMap = new Cell[WorldX, WorldY];
for (int i=0; i<WorldX; i++)
for (int j=0; j<WorldY; j++)
WorldMap[i,j] = new Cell('.');
}
public static void FillMap()
{
int IslandsPlaced=0;
foreach(Island thisIsland in IslandsList)
{
Boolean check = false;
int x = 0;
int y = 0;
Random w = rnd;
int SideA = thisIsland.CellArray.GetLength(0);
int SideB = thisIsland.CellArray.GetLength(1);
int WorldSideA = WorldMap.GetLength(0);
int WorldSideB = WorldMap.GetLength(1);
x = w.Next(2, WorldSideA-SideA-1);
y = w.Next(2,WorldSideB-SideB-1);
check = CanPlaceIsland(x,y,thisIsland);
if (check==true)
{
PlaceIsland(x,y,thisIsland);
IslandsPlaced++;
}
}
if (IslandsPlaced!=IslandsList.Count())
{
CreateEmptyMap();
FillMap();
}
}
The placing:
public static void PlaceIsland(int x, int y, Island thisIsland)
{
int SideA = thisIsland.CellArray.GetLength(0);
int SideB = thisIsland.CellArray.GetLength(1);
for (int i=0; i<SideA;i++)
{
for (int j=0; j<SideB;j++)
{
WorldMap[x+i,y+j] = thisIsland.CellArray[i,j];
}
}
}
However, sometimes islands still overlap, and I can't find why.
..........
..........
..........
..........
....AAA...
..DDDBA...
..DDDAA...
..DDD.....
..........
..........
Your bug is in CanPlaceIsland:
for (int j = StartX; //error here!
j < StartY + thisIsland.CellArray.GetLength(1);
j++) { ... }
Should be:
for (int j = StartY;
j < StartY + thisIsland.CellArray.GetLength(1);
j++) { ... }
Looks like a typical copy and paste bug...
Apart from that, if your maps are rather crowded, you risk entering an infinite loop if there is no solution for a given island.
Computing if one or more valid solutions exist and if the combination you are currently in is one of them can be expensive and somewhat tricky so, unless you really have to deal with crowded maps where a solution must be given, I'd bale out after a predetermined number of failed attempts placing an island; you might get false negatives once in a while but its probably something you can live with.
Related
I am going to have a number of two-dimensional arrays that I would like to summarize into a single two-dimensional array. The first row is composed of doubles that represent mass, while the second row is composed of doubles that represent intensity.
//example 1 two-dimensional array
[145.56, 246.44, 346.55, 204.78]
[14, 30, 58, 49]
//example 2 two-dimensional array
[151.62, 223.18, 389.78, 266.96]
[67, 56, 23, 47]
I would like to summarize the two-dimensional arrays by sorting the mass doubles in the first row into bins of equal length, and then summing the pertinent intensity double up. So, assuming the mass bins had a length of 50, the summarized two-dimensional array using the two examples above would be:
//tentative example summarized two-dimensional array
[100-150, 150-200, 200-250, 250-300, 300-350, 350-400]
[14, 67, 135, 47, 58, 23 ]
I have so far tried to create a for loop that would first check which bin the mass would fall into by iterating over a nested for loop of the , and then add the intensity to previous intensity values.
double binSize = 50;
double[] binArray = new double[someNumberOfBins]
double[] summedIntensities= new double[numberOfTheirSummedIntensities];
for(i=0; i<twoDimensionalArray.GetLength(1); i++){
double currentMass= twoDimensionalArray[0,i];
for(j=0; j<binArray.GetLength(1); j++) {
if(currentMass> (binArray[j] - binSize) && currentMass <= (binArray[j] + binSize)) {
double currentIntensity = twoDimensionalArray[1,i];
summedIntensities[j] += currentIntensity;
}
}
//somehow combine the binArray with summedIntensities array into a two dimensional array
However, there are some problems in the a design of this algorithm. Firstly, I do not know how to set up the binArray so that the numbers in it properly reflect the range of in my two-dimensional arrays. I also am not sure how to combine the binArray with the summedIntensities array into a two-dimensional array, or whether this is even practical for this particular case.
Is there another way of making the binArray that is more fitting with my purpose of creating a summarized two-dimensional array, or are there any other glaring problems with this algorithm?
Here is my algorithm:
Note 1: in result array the first row will be '100', '150', '200'... You can construct a struct or use Dictionary<string,int> if you want it as "100-149", "150-199"...
Note 2: i calculated the interval as [100-149], [150,199], [200,249]... modify last condition to change it if you need to
static void Main(string[] args)
{
double[,] array1 = new double[2, 4] { { 145.56d, 246.44d, 346.55d, 204.78d }, { 14d, 30d, 58d, 49d } };
double[,] array2 = new double[2, 4] { { 151.62d, 223.18d, 389.78d, 266.96d }, { 67d, 56d, 23d, 47d } };
double[,] finalArray = DoWork(array1, array2,50);
}
private static double[,] DoWork(double[,] arrayLeft, double[,] arrayRight, int binLength)
{
//union of arrays
double[,] newArray = new double[2, arrayLeft.GetLength(1) + arrayRight.GetLength(1)]; //2x8 array
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < arrayLeft.GetLength(1); j++)
{
newArray[i, j] = arrayLeft[i, j];
}
for (int j = arrayLeft.GetLength(1); j < arrayLeft.GetLength(1) + arrayRight.GetLength(1); j++)
{
newArray[i, j] = arrayRight[i, j-4];
}
}
//sorting first row
for (int i = 0; i < newArray.GetLength(1)-1; i++)
{
for (int j = i+1; j < newArray.GetLength(1); j++)
{
if (newArray[0, i] > newArray[0, j])
{
double temp0 = newArray[0, j];
double temp1 = newArray[1, j];
newArray[0, j] = newArray[0, i];
newArray[1, j] = newArray[1, i];
newArray[0, i] = temp0;
newArray[1, i] = temp1;
}
}
}
//determine the number of bins and their intervals
double minMass = newArray[0, 0]; //145.56
double maxMass = newArray[0, newArray.GetLength(1) - 1]; //389.78
double minBinLowerValue = 0; //since you're working with mass i ignore the negative values
while (minBinLowerValue + binLength < minMass) minBinLowerValue += binLength; //100
double maxBinHigherValue = minBinLowerValue;
while (maxBinHigherValue < maxMass) maxBinHigherValue += binLength; //400
double numberOfBins = (maxBinHigherValue - minBinLowerValue) / binLength; //6
//creation of result array
double[,] resultArray = new double[2, Convert.ToInt32(numberOfBins)]; //2x6 array
//fill intervals to first row
for (int i = 0; i < resultArray.GetLength(1); i++)
{
resultArray[0, i] = minBinLowerValue + binLength * i;
}
//fill the sums
for (int i = 0; i < resultArray.GetLength(1); i++)
{
double sum = 0;
for (int j = 0; j < newArray.GetLength(1); j++)
{
if(resultArray[0,i] <= newArray[0,j] && resultArray[0, i] + binLength > newArray[0, j]) //modify this condition
{
sum += newArray[1, j];
}
}
resultArray[1, i] = sum;
}
return resultArray;
}
Here you go, I used a dictionary to store the summed values. The key is a bin number.
Note: If you want to make your solution better, I would use a class with 2 props (mass and intensity) I did not make this solution because am not sure if you have the freedom to have a class or if you are stuck with with input you got.
using System;
using System.Collections.Generic;
{
class Program
{
static void Main(string[] args)
{
double[] bins1 = { 145.56, 246.44, 346.55, 204.78 };
double[] values1 = { 14, 30, 58, 49 };
double[] bins2 = { 151.62, 223.18, 389.78, 266.96 };
double[] values2 = { 67, 56, 23, 47 };
int binSize = 50;
Dictionary<int, double> summedBins = new Dictionary<int, double>();
AddValuesToSummedBins(binSize, summedBins, bins1, values1);
AddValuesToSummedBins(binSize, summedBins, bins2, values2);
}
public static void AddValuesToSummedBins(int binSize, Dictionary<int, double> SummedBins, double[] Bins, double[] Values)
{
int i = 0;
foreach (double oneBin in Bins)
{
int binSet = binSize * ((int) oneBin / binSize);
if (!SummedBins.ContainsKey(binSet))
{
SummedBins.Add(binSet, Values[i]);
}
else
{
SummedBins[binSet] += Values[i];
}
i++;
}
}
}
}
If you are flexible about your data format, you can use LINQ to do this in a very small amount of code.
You would need to store your data pairs as a class with Mass and Intensity properties, instead of separate items in a 2D array.
Assuming this class to store your data:
public class DataElement
{
public double Mass { get; set; }
public double Intensity { get; set; }
}
You could use the following function:
public List<DataElement> Summarize(IEnumerable<DataElement> data, int range)
{
return data.GroupBy(de => Math.Floor(de.Mass / range) * range,
(range, g) => new DataElement {
Mass = range,
Intensity = g.Sum(d => d.Intensity)
})
.OrderBy(de => de.Mass)
.ToList();
}
This function takes all of your data as a single IEnumerable, and runs it thought LINQ's GroupBy function.
The first argument is an expression that determines how to group items together. For your purpose, I simply round down to the nearest multiple of the range argument (50 in your example, but anything should work).
The 2nd argument is an expression that returns the final output of a group, given the key of the group (the rounded down Mass), and the elements that were grouped together. I just return a new DataElement with e Mass of the rounded down Mass, and an Intensity equal to the Sum of the Intentities of the grouped item.
The result is then sorted by Mass before returning the result.
This produces the following output from the input you specified in the question (from LINQPad's Dump function):
You could use it like this (available as a LINQPad file):
void Main()
{
var data1 = new List<DataElement>{
new DataElement{Mass = 145.56, Intensity = 14},
new DataElement{Mass = 246.44, Intensity = 30},
new DataElement{Mass = 346.55, Intensity = 58},
new DataElement{Mass = 204.78, Intensity = 49},
};
var data2 = new List<DataElement>{
new DataElement{Mass = 151.62, Intensity = 67},
new DataElement{Mass = 223.18, Intensity = 56},
new DataElement{Mass = 389.78, Intensity = 23},
new DataElement{Mass = 266.96, Intensity = 47},
};
var result = Summarize(data1.Concat(data2), 50);
}
I just wrote a code regarding array rotation while studying for Data Structure. I needed to know how I can improve below program by measuring time and space complexity.
Program for array rotation.
Rotation of the array by 2 will make array
1,2,3,4
Input
3,4,1,2
Output
public class Program
{
public static void Main(string[] args)
{
int arrayCount = 0;
int rotate = 2;
int []answer = new int[4];
for (int i = 0; i < answer.Length; i++)
{
answer[i] = Convert.ToInt32(Console.ReadLine());
}
arrayCount = answer.Count();
ArrayRotation.displayRotatedArray(answer, rotate, arrayCount);
ArrayRotation.printArray(answer, arrayCount);
Console.ReadKey();
}
}
public static class ArrayRotation
{
public static void displayRotatedArray(int []temp, int rotate, int count)
{
int c = rotate;
int d = rotate;
int[] firstOccurenceArray = new int[rotate];
for (int g = 0; g < rotate; g++)
{
int num = g;
firstOccurenceArray[g] = temp[g];
}
for (int i = 0; i < temp.Length - c; i++)
{
temp[i] = temp[rotate];
rotate++;
}
for (int k = 1; k < d + 1; k++)
{
temp[count - k] = firstOccurenceArray[c - 1];
c--;
}
}
/* utility function to print an array */
public static void printArray(int[] temp, int size)
{
for (int i = 0; i < size; i++)
Console.Write( temp[i] + " ");
}
}
Time complexity is calculated: by what factor number of operations changes with respect to the change in size of input params.
For this example operation are changing as mentioned:
(2) + 2*rotate + 2* temp.Length + 2 * rotate
For max it can be 2 + (6 * temp.Length)
so time complexity is O(n).
Space complexity: O(rotate) which can be max to O(n)
You can optimize this problem in O(n) time complexity and O(1) space complexity by in-place swapping(Juggling Algorithm) the array values.
REF:
https://www.geeksforgeeks.org/array-rotation
Time Complexity : O(n) where n = length of array (since there is no nested for loops)
Space Complexity : O(2) i.e. O(1) (since size of this array firstOccurenceArray is constant i.e. 2)
I wanted to implement a simple method to sample from a multinomial distribution in C# (the first argument is an array of integers we want to sample and the second one is the probabilities of selecting each of those integers).
When I do this with numpy in python, the results make sense.
np.random.choice(np.array([1,2,3,4,5,6]),p=np.array([.624,.23,.08,.04, .02, .006]),size=len(b))
I get a lot of 1's (probability 62%), a bunch of 2's, some 3's etc.
However, when I try the implementation below in C# (pretty straightforward inverse transform sampling for multinomial, only relies on a uniform random variable), I get really weird results. For all 1000 samples, I'll often find all 1's. Sometimes, I'll find all 3's (!!??). The results never look like what you would expect (and what you get from the python function - try running it yourself a few times). This is really scary since we rely on these primitives. Does anyone have insight into what might be wrong with the C# version?
static void Main(string[] args)
{
int[] iis = new int[7];
int[] itms = new int[] { 1, 2, 3, 4, 5, 6 };
double[] probs = new double[] { .624, .23, .08, .04, .02, .006 };
for (int i = 0; i < 1000; i++)
{
iis[MultinomialSample(itms, probs)] += 1;
}
foreach (var ii in iis)
{
Console.Write(ii + ",");
}
Console.Read();
}
private static int MultinomialSample(int[] s, double[] ps)
{
double[] cumProbs = new double[ps.Length];
cumProbs[0] = ps[0];
for (int i = 1; i < ps.Length; i++)
{
cumProbs[i] = cumProbs[i - 1] + ps[i];
}
Random random = new Random();
double u = random.NextDouble();
for (int i = 0; i < cumProbs.Length - 1; i++)
{
if (u < cumProbs[i])
{
return s[i];
}
}
return s[s.Length - 1];
}
You're initializing Random each time you call MultinomialSample. If these calls are very close together, Random will be initialized with the same seed (based on the system clock). Try either making Random a private class field: private static Random random = new Random(); or pass it into the method as an argument from Main, where it would be initialized only once:
private static Random random = new Random();
private static int MultinomialSample(IReadOnlyList<int> sample,
IReadOnlyList<double> probabilities)
{
var cumProbs = new double[probabilities.Count];
cumProbs[0] = probabilities[0];
for (var i = 1; i < probabilities.Count; i++)
{
cumProbs[i] = cumProbs[i - 1] + probabilities[i];
}
for (var i = 0; i < cumProbs.Length - 1; i++)
{
if (random.NextDouble() < cumProbs[i])
{
return sample[i];
}
}
return sample[sample.Count - 1];
}
private static void Main()
{
var iis = new int[7];
var items = new[] {1, 2, 3, 4, 5, 6};
var probabilities = new[] {.624, .23, .08, .04, .02, .006};
for (int i = 0; i < 1000; i++)
{
iis[MultinomialSample(items, probabilities)] ++;
}
Console.WriteLine(string.Join(", ", iis));
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
I used Rufus' code in a simulation I was working on and noticed there is still a problem, even after seeding the random number generator just once (which is the correct thing to do). You will notice that as we are iterating, the call to random.NextDouble() generates a new random number each time. This is wrong.
for (var i = 0; i < cumProbs.Length - 1; i++)
{
if (random.NextDouble() < cumProbs[i])
{
return sample[i];
}
}
The random number should be generated outside of the loop, as follows:
var r = random.NextDouble();
for (var i = 0; i < cumProbs.Length - 1; i++)
{
if (r < cumProbs[i])
{
return sample[i];
}
}
You can compare it to the Excel algorithm given on Wikipedia: https://en.wikipedia.org/wiki/Multinomial_distribution. When I made the above change to Rufus' code, I got the desired frequency distribution as specified by the probabilities array.
I'm trying to slice a 3d representation of a image. Following factors is known:
DimensionX
DimensionY
DimensionZ
voxels[]
Voxels[] is an array of ushorts representing the grayscale of the pixel.
Now i need to slice it in all possible directions. With let we say x, y or z.
I have tree implementations for this, but they have one problem, they don't work with dimensions that are not the same. (Except for get Z Slice, this is working perfectly).
These are the methods:
private ushort[] GetZSlice(int z, ushort[] voxels, int DimensionX, int DimensionY)
{
var res = new ushort[DimensionX * DimensionY];
for(int j = 0; j < DimensionY; j++)
{
for(int i = 0; i < DimensionX; i++)
{
res[j*DimensionX + i] = voxels[z*DimensionX*DimensionY + j*DimensionX + i];
}
}
return res;
}
This method is working perfectly, it does not mather wat i choose as the dimension.
The next two methods, with x or y as depth poses as a harder problem.
private ushort[] GetYSlice(int y, ushort[] voxels, int DimensionX, int DimensionY, int DimensionZ)
{
var res = new ushort[DimensionX * DimensionZ];
for( int i = 0; i < DimensionX; i++)
{
for( int j = 0; j < DimensionX; j++)
{
res[j + i*DimensionX] = voxels[j * DimensionZ * DimensionY + Y*DimensionZ + i]
}
}
return res;
}
private ushort[] GetXSlice(int x, ushort[voxels], int DimensionX, int DimensionY, int DimensionZ)
{
var res = new short[DimensionY * DimensionZ];
for(int i = 0; i < DimensionY; i++)
{
for(int j = 0; j < DimensionZ; j++)
{
res[j + i*DimensionZ] = voxels[i*DimensionY + j*DimensionZ*DimensionX + x]
}
}
return res;
}
How could I improve the last 2 methods so it works with dimensions that are not equal?
Why not make universal slice function using basis vectors? It will be far less code the manage.
Basicly you got U,V axises each mapped into X,Y or Z. And the Slice W is the third unused axis. So you just loop the U,V and leave W as is. Each U,V has a basis vector (ux,uy,uz) , (vx,vy,vz) that describes the increment change in x,y,z coordinates.
I encoded it into my LED cube class in C++...
//---------------------------------------------------------------------------
class LED_cube
{
public:
int xs,ys,zs,***map;
LED_cube() { xs=0; ys=0; zs=0; map=NULL; }
LED_cube(LED_cube& a) { xs=0; ys=0; zs=0; map=NULL; *this=a; }
~LED_cube() { _free(); }
LED_cube* operator = (const LED_cube *a) { *this=*a; return this; }
LED_cube* operator = (const LED_cube &a);
void _free();
void resize(int _xs,int _ys,int _zs);
void cls(int col); // clear cube with col 0x00BBGGRR
void sphere(int x0,int y0,int z0,int r,int col); // draws sphere surface with col 0x00BBGGRR
void slice (char *uv,int slice,int col); // draws (XY,XZ,YZ) slice with col 0x00BBGGRR
void glDraw(); // render cube by OpenGL as 1x1x1 cube at 0,0,0
};
//---------------------------------------------------------------------------
void LED_cube::slice(char *uv,int slice,int col)
{
// detect basis vectors from uv string
int ux=0,uy=0,uz=0,us=0;
int vx=0,vy=0,vz=0,vs=0;
int x=slice,y=slice,z=slice,u,v,x0,y0,z0;
if (uv[0]=='X') { x=0; ux=1; us=xs; }
if (uv[0]=='Y') { y=0; uy=1; us=ys; }
if (uv[0]=='Z') { z=0; uz=1; us=zs; }
if (uv[1]=='X') { x=0; vx=1; vs=xs; }
if (uv[1]=='Y') { y=0; vy=1; vs=ys; }
if (uv[1]=='Z') { z=0; vz=1; vs=zs; }
// render slice
if ((x>=0)&&(x<xs)&&(y>=0)&&(y<ys)&&(z>=0)&&(z<zs))
for (u=0;u<us;u++,x+=ux,y+=uy,z+=uz)
{
x0=x; y0=y; z0=z;
for (v=0;v<vs;v++,x+=vx,y+=vy,z+=vz)
map[x][y][z]=col;
x=x0; y=y0; z=z0;
}
}
//---------------------------------------------------------------------------
As you can see it is quite nice and simple ... and still can be optimized much more. here usage and output example:
cube.resize(32,16,20);
cube.cls(0x00202020);
cube.slice("XY",5,0x000000FF);
cube.slice("XZ",5,0x0000FF00);
cube.slice("YZ",5,0x00FF0000);
cube.glDraw();
As you got your voxels stored in 1D array then just compute the address from x,y,z. so map[x][y][z] will became your voxels[(x*ys*zs)+(y*zs)+z] or what ever combination of axis order you got. This can be totally encoded into the basis vectors so you can have du=(ux*ys*zs)+(uy*zs)+uz and dv=... and increment the address directly not needing any multiplication latter ...
this is a really easy question but i cant figure out a way around it. Apparently the almost ordered has a bug that it might randomize a little bit more than you ask it. the code is rather simple:
public void Section1Task1AlmostOrdered(int arraySize, int percentage)
{
int[] testArray = new int[arraySize];
Console.WriteLine("Ordered List: ");
for (int i = 1; i <= testArray.Length; i++)
{
testArray[i-1] = i;
Console.Write(i + "\t");
}
Console.WriteLine("Almost Ordered List: ");
testArray = shuffler.AlmostOrdered(arraySize, percentage);
for (int i = 0; i < testArray.Length; i++)
{
Console.Write(testArray[i] + "\t");
}
}
The shuffler is this part of the code:
public int[] AlmostOrdered(int n, double p)
{
if (p > 100)
{
throw new InvalidOperationException("Cannot shuffle more than 100% of the numbers");
}
int shuffled = 0;
//Create and Populate an array
int[] array = new int[n];
for(int i = 1; i <= n; i++)
{
array[i-1] = i;
}
//Calculate numbers to shuffle
int numsOutOfPlace = (int) Math.Ceiling(n * (p / 100));
int firstRandomIndex = 0;
int secondRandomIndex = 0;
do
{
firstRandomIndex = this.random.Next(n-1);
// to make sure that the two numbers are not the same
do
{
secondRandomIndex = this.random.Next(n - 1);
} while (firstRandomIndex == secondRandomIndex);
int temp = array[firstRandomIndex];
array[firstRandomIndex] = array[secondRandomIndex];
array[secondRandomIndex] = temp;
shuffled++;
}
while (shuffled < numsOutOfPlace);
return array;
}
When i enter values 10 for array size and 40 for percentage to be shuffled, it is shuffling 5 numbers instead of 4. Is there a way to perfect this method to make it more accurate?
Likely the problem is with the calculation:
int numsOutOfPlace = (int)Math.Ceiling(n * (p / 100));
So if p=40 and n=10, then in theory you should get 4. But you're dealing with floating point numbers. So if (p/100) returns 0.400000000001, then the result will be 4.000000001, and Math.Ceiling will round that up to 5.
You might want to replace Math.Ceiling with Math.Round and see how that works out.