I have a list of potential sites to place landing pads in a 2d array. They're kept in 2 ints, one for row and one for column. I need to Randomly add a few landing sites from this list but for some reason it more often than not uses the same spots. I'd like to exclude these spots somehow so I used this loop but it locks up into an infinite loop for some reason and I just can't figure out why!
for(int j = 0; j < amountLandingPads; j++)
{
int r = Random.Range(0,potSitesC.Length-1);
while(roomType[potSitesR[r],potSitesC[r]] == (int)room.Landing)
{
r = Random.Range(0,potSitesC.Length-1);
}
roomType[potSitesR[r],potSitesC[r]] = (int)room.Landing;
//do more stuff
}
To me it looks like if the current site is already designated as a landing site, randomly choose another until you find a site that isn't a landing pad, what am I doing wrong?
potSites.Length is always gonna be 20+ and ammountLandingPads is always potsites.Length/4 and minimum 1.
roomtype is the type of room at that position (in a 2d int array)
It looks like you're using the same int r and also potSitesR.Length to decide both the row-coord and the column-coord of the landing site. This will end up always selecting positions from both potSitesR and potSitesC with the same indices, i.e. (potSitesR[1], potSitesC[1]), or (potSitesR[2], potSitesC[2]), and so on... and always within the potSitesR.Length range.
Try using a different value for both for more randomization. Here's example code:
for(int j = 0; j < amountLandingPads; j++)
{
//In the following statement
//Removed -1 because int version is exclusive of second parameter
//Changed it to potSitesR.Length (from potSitesC.Length)
int r = Random.Range(0, potSitesR.Length);
//second randomized number for column-randomization.
int c = Random.Range(0, potSitesC.Length);
while (roomType[potSitesR[r],potSitesC[c]] == (int)room.Landing) //using both randomized numbers
{
r = Random.Range(0, potSitesR.Length); // r from potSitesR.Length
c = Random.Range(0, potSitesC.Length); // c from potSitesC.Length
}
roomType[potSitesR[r], potSitesC[c]] = (int)room.Landing;
//do more stuff
}
I hope that helps!
Related
I have a program that compares objects by their properties and then converts it all in a two dimensional array (or a matrix) with values being percentages (1 means 100% similar, 0 means completely different, anything in between is % of similarity).
After that I convert all of it to 1's and 0's to create a map of connections or what's called an adjacency matrix in graph theory which represents nodes that are connected to one another.
Here's what my situation looks like in terms of graph/matrix/array
It's clear from the image that there are 4 distinct groups of elements: a1 through a5 are all linked together and form first group, b1-b2-b3 form another group of elements, b4 and b6 are a third group of just two elements and b5 forms it's own fourth group because it isn't connected to anybody else.
I have the same array in my code. My question is how can I determine which elements belong to the same group or how to group them and store the information about them somewhere else? (be it the index of the element, the value or something else).
I also need to make sure that I don't have repeating groups, so for example elements b4 and b6 shouldn't form group b4-b6 and group b6-b4, only one of those groups should be present.
Here's part of my code where I tried to implement it by creating new array of strings (and I tried integer too, but it couldn't store the index [0,0] properly, it just stores 0), but this method still doesn't solve the issue of repeating groups or how to identify and pull them out of this array. This method can only correctly identify connections. Output of this method
public static string[,] CreateConnectionsMap(double[,] arr)
{
string[,] newArr = new string[arr.GetLength(0), arr.GetLength(1)];
for (int i = 0; i < arr.GetLength(0); i++)
{
for (int j = 0; j < arr.GetLength(1); j++)
{
if (arr[i, j] != 0 && arr[i, j] == arr[j, i])
{
newArr[i, j] = $"{i}|{j}";
}
else newArr[i, j] = "0";
}
}
return newArr;
}
Seems to me that you're trying to do clustering using a pairwise distance.
The most immediate solution that comes to my mind is to use the agglomerative clustering algorithm.
This library in c# should do the job.
Try something like this:
public static int[] CreateConnectionsMap(double[,] arr)
{
if (arr.GetLength(0) != arr.GetLength(1))
throw new ArgumentException("Matrix must be square.");
int[] groups = new int[arr.GetLength(0)];
for (int i = 0; i < arr.GetLength(0); i++)
{
groups[i] = i;
for (int j = 0; j < i; j++)
{
if (arr[i, j] != 0 && arr[i, j] == arr[j, i])
{
groups[i] = groups[j];
break;
}
}
}
return groups;
}
Be sure to test it!
You need to discover the maximal cliques in the graph.
copy graph to work
set flag true
while flag == true
clear current clique
while work contains nodes
if current clique empty
move arbitrary node from work to current clique
else
set flag false
LOOP w over nodes in work
LOOP c over nodes in current clique
if w connected to c
move w to current clique
set flag true
if flag == false
break
add current clique to list of cliques
Here is c++ code implementing this algorithm https://github.com/JamesBremner/PathFinder2/blob/dbd6ff06edabd6a6d35d5eb10ed7972dc2d779a6/src/cPathFinder.cpp#L483
I am trying to create an infinite map of 1 and 0's procedurally generated by PRNG but only storing a grid of 7x7 which is viewable. e.g. on the screen you will see a 7x7 grid which shows part of the map of 1's and 0's and when you push right it then shifts the 1's and 0's across and puts the next row in place.
If you then shift left as it is pseudo randomly generated it regenerates and shows the original. You will be able to shift either way and up and down infinitely.
The problem I have is that the initial map does not look that random and the pattern repeats itself as you scroll right or left and I think it is something to do with how I generate the numbers. e.g. you could start at viewing x=5000 and y= 10000 and therefore it need to generate a unique seed from these two values.
(Complexity is the variable for the size of the viewport)
void CreateMap(){
if(complexity%2==0) {
complexity--;
}
if (randomseed) {
levelseed=Time.time.ToString();
}
psuedoRandom = new System.Random(levelseed.GetHashCode());
offsetx=psuedoRandom.Next();
offsety=psuedoRandom.Next();
levellocation=psuedoRandom.Next();
level = new int[complexity,complexity];
middle=complexity/2;
for (int x=0;x<complexity;x++){
for (int y=0;y<complexity;y++){
int hashme=levellocation+offsetx+x*offsety+y;
psuedoRandom = new System.Random(hashme.GetHashCode());
level[x,y]=psuedoRandom.Next(0,2);
}
}
}
This is what I use for left and right movement,
void MoveRight(){
offsetx++;
for (int x=0;x<complexity-1;x++){
for (int y=0;y<complexity;y++){
level[x,y]=level[x+1,y];
}
}
for (int y=0;y<complexity;y++){
psuedoRandom = new System.Random(levellocation+offsetx*y);
level[complexity-1,y]=psuedoRandom.Next(0,2);
}
}
void MoveLeft(){
offsetx--;
for (int x=complexity-1;x>0;x--){
for (int y=0;y<complexity;y++){
level[x,y]=level[x-1,y];
}
}
for (int y=0;y<complexity;y++){
psuedoRandom = new System.Random(levellocation+offsetx*y);
level[0,y]=psuedoRandom.Next(0,2);
}
}
To distill it down I need to be able to set
Level[x,y]=Returnrandom(offsetx,offsety)
int RandomReturn(int x, int y){
psuedoRandom = new System.Random(Whatseedshouldiuse);
return (psuedoRandom.Next (0,2));
}
Your problem I think is that you are creating a 'new' random in loops... don't redeclare inside loops as you are... instead declare it at class level and then simply use psuedoRandom.Next e.g. See this SO post for an example of the issue you are experiencing
instead of re-instantiating a Random() class at every iteration like you are doing:
for (int x=0;x<complexity;x++){
for (int y=0;y<complexity;y++){
int hashme=levellocation+offsetx+x*offsety+y;
psuedoRandom = new System.Random(hashme.GetHashCode());
level[x,y]=psuedoRandom.Next(0,2);
}
}
do something more like
for (int x=0;x<complexity;x++){
for (int y=0;y<complexity;y++){
// Give me the next random integer
moreRandom = psuedoRandom.Next();
}
}
EDIT: As Enigmativity has pointed out in a comment below this post, reinstiating at every iteration is also a waste of time / resources too.
P.S. If you really need to do it then why not use the 'time-dependent default seed value' instead of specifying one?
I'd use SipHash to hash the coordinates:
It's relatively simple to implement
It takes both a key (seed for your map) and a message (the coordinates) as inputs
You can increase or decrease the number of rounds as a quality/performance trade-off.
Unlike your code, the results will be reproducible across processes and machines.
The SipHash website lists two C# implementations:
https://github.com/tanglebones/ch-siphash
https://github.com/BrandonHaynes/siphash-csharp
I've play a bit to create rnd(x, y, seed):
class Program
{
const int Min = -1000; // define the starting point
static void Main(string[] args)
{
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
Console.Write((RND(x, y, 123) % 100).ToString().PadLeft(4));
Console.WriteLine();
}
Console.ReadKey();
}
static int RND(int x, int y, int seed)
{
var rndX = new Random(seed);
var rndY = new Random(seed + 123); // a bit different
// because Random is LCG we can only move forward
for (int i = Min; i < x - 1; i++)
rndX.Next();
for (int i = Min; i < y - 1; i++)
rndY.Next();
// return some f(x, y, seed) = function(rnd(x, seed), rnd(y, seed))
return rndX.Next() + rndY.Next() << 1;
}
}
It works, but ofc an afwul implementation (because Random is LCG), it should give a general idea though. It's memory efficient (no arrays). Acceptable compromise is to implement value caching, then while located in certain part of map surrounding values will be taken from cache instead of generating.
For same x, y and seed you will always get same value (which in turn can be used as a cell seed).
TODO: find a way to make Rnd(x) and Rnd(y) without LCG and highly inefficient initialization (with cycle).
I solved it with this, after all of you suggested approaches.
void Drawmap(){
for (int x=0;x<complexity;x++){
for (int y=0;y<complexity;y++){
psuedoRandom = new System.Random((x+offsetx)*(y+offsety));
level[x,y]=psuedoRandom.Next (0,2);
}
}
EDIT: so it looks like this is normal behavior, so can anyone just recommend a faster way to do these numerous intersections?
so my problem is this. I have 8000 lists (strings in each list). For each list (ranging from size 50 to 400), I'm comparing it to every other list and performing a calculation based on the intersection number. So I'll do
list1(intersect)list1= number
list1(intersect)list2= number
list1(intersect)list888= number
And I do this for every list. Previously, I had HashList and my code was essentially this: (well, I was actually searching through properties of an object, so I
had to modify the code a bit, but it's basically this:
I have my two versions below, but if anyone knows anything faster, please let me know!
Loop through AllLists, getting each list, starting with list1, and then do this:
foreach (List list in AllLists)
{
if (list1_length < list_length) //just a check to so I'm looping through the
//smaller list
{
foreach (string word in list1)
{
if (block.generator_list.Contains(word))
{
//simple integer count
}
}
}
// a little more code, but the same, but looping through the other list if it's smaller/bigger
Then I make the lists into regular lists, and applied Sort(), which changed my code to
foreach (List list in AllLists)
{
if (list1_length < list_length) //just a check to so I'm looping through the
//smaller list
{
for (int i = 0; i < list1_length; i++)
{
var test = list.BinarySearch(list1[i]);
if (test > -1)
{
//simple integer count
}
}
}
The first version takes about 6 seconds, the other one takes more than 20 (I just stop there cuz otherwise it would take more than a minute!!!) (and this is for a smallish subset of the data)
I'm sure there's a drastic mistake somewhere, but I can't find it.
Well I have tried three distinct methods for achieving this (assuming I understood the problem correctly). Please note I have used HashSet<int> in order to more easily generate random input.
setting up:
List<HashSet<int>> allSets = new List<HashSet<int>>();
Random rand = new Random();
for(int i = 0; i < 8000; ++i) {
HashSet<int> ints = new HashSet<int>();
for(int j = 0; j < rand.Next(50, 400); ++j) {
ints.Add(rand.Next(0, 1000));
}
allSets.Add(ints);
}
the three methods I checked (code is what runs in the inner loop):
the loop:
note that you are getting duplicated results in your code (intersecting set A with set B and later intersecting set B with set A).
It won't affect your performance thanks to the list length check you are doing. But iterating this way is clearer.
for(int i = 0; i < allSets.Count; ++i) {
for(int j = i + 1; j < allSets.Count; ++j) {
}
}
first method:
used IEnumerable.Intersect() to get the intersection with the other list and checked IEnumerable.Count() to get the size of the intersection.
var intersect = allSets[i].Intersect(allSets[j]);
count = intersect.Count();
this was the slowest one averaging 177s
second method:
cloned the smaller set of the two sets I was intersecting, then used ISet.IntersectWith() and checked the resulting sets Count.
HashSet<int> intersect;
HashSet<int> intersectWith;
if(allSets[i].Count < allSets[j].Count) {
intersect = new HashSet<int>(allSets[i]);
intersectWith = allSets[j];
} else {
intersect = new HashSet<int>(allSets[j]);
intersectWith = allSets[i];
}
intersect.IntersectWith(intersectWith);
count = intersect.Count;
}
}
this one was slightly faster, averaging 154s
third method:
did something very similar to what you did iterated over the shorter set and checked ISet.Contains on the longer set.
for(int i = 0; i < allSets.Count; ++i) {
for(int j = i + 1; j < allSets.Count; ++j) {
count = 0;
if(allSets[i].Count < allSets[j].Count) {
loopingSet = allSets[i];
containsSet = allSets[j];
} else {
loopingSet = allSets[j];
containsSet = allSets[i];
}
foreach(int k in loopingSet) {
if(containsSet.Contains(k)) {
++count;
}
}
}
}
this method was by far the fastest (as expected), averaging 66s
conclusion
the method you're using is the fastest of these three. I certainly can't think of a faster single threaded way to do this. Perhaps there is a better concurrent solution.
I've found that one of the most important considerations in iterating/searching any kind of collection is to choose the collection type very carefully. To iterate through a normal collection for your purposes will not be the most optimal. Try using something like:
System.Collections.Generic.HashSet<T>
Using the Contains() method while iterating over the shorter list of two (as you mentioned you're already doing) should give close to O(1) performance, the same as key lookups in the generic Dictionary type.
I´m working on some codes in C# for school. But there is this exercise that is huge headache.
This is it: I have to develope a code that allows the user to set a value (put in an x) for a 2D array (5x5) from the keyboard. This means that when running the program the user should be able to set one value inside the array, something like "I wana set an "x" in 2,5 and 3,1". I just have no clue how to do that. it´s been already two weeks but i can´t figure it out.
This is what i have so far (updated, thnx to all, specially BradleyDotNET for support):
int[,] data = new int[5, 5];
public void load()
{
string[] input = Console.ReadLine().Split('=');
string[] coordinates = input[0].Split(',');
int[] intCoordinates = coordinates.Select(s => int.Parse(s)).ToArray();
data[intCoordinates[0]][intCoordinates[1]] = int.Parse(input[1]);
}
public void view()
{
Console.WriteLine("Matrix created is:");
for (int i = 0; i <= 4; i++)
{
Console.Write("\n");
for (int j = 0; j <= 4; j++)
{
Console.Write(data);
}
}
Console.ReadKey();
}
static void Main(string[] args)
{
Program objeto = new Program();
objeto.load();
objeto.view();
Console.ReadKey();
Console.Clear();
I also have to add a feature to let the user add as many "x" to the matriz as he wants, but im planning to do that with a "switch".
So, How do you set values inside the 2d array from keyboard?
Update: The mistake i get here is in line 10, inside "data". It says "Incorrect index number inside []. 2 was expected"
You didn't specify the format the input, so I"ll make one up. If the input was "2,4=10" (meaning set element[2][4] to 10), the code would be:
string[] input = Console.ReadLine().Split('=');
string[] coordinates = input[0].Split(',');
int[] intCoordinates = coordinates.Select(s => int.Parse(s)).ToArray();
matrix[intCoordinates [0]][intCoordinates [1]] = int.Parse(input[1]);
This code has a few problems with it, there is no range validation and if the user enters anything other than an int, it will throw. I'll leave those as an exercise to you, but feel free to ask if you run into trouble.
To explain, we use Console.ReadLine to get a whole line of input. Then we break it on the '=' character to get our coordinates and desired value. We then split the coordinates on ',' to get the different indices.
You can't use strings as array indices, so we call Select to invoke the int.Parse() function on each string, returning us a new array of ints.
Finally, we use the parsed indices to index into matrix and set it to the parsed value from the input.
Something like this should help you.
public void load()
{
for (int i = 0; i <= 4; i++)
{
for (int j = 0; j <= 4; j++)
{
Console.WriteLine("enter value for {0},{1}", i, j);
matrix[i,j]= int.Parse(Console.ReadLine());
}
}
}
BTW, in your view method start the loop from 0 to 4
I have created a program to spawn a number of points (the number is given by the user).
Therefore the program spawns N points
see the image for an example, it has 3 points in that case
What I need is to get all the possible distances between those villages (In the example it's distance: AB, AC, BC).
The points are stored in a single array (that scores x-coordinate and y-coordinate)
List<Villages>
I know that I new Pythagoras Theorem, I just cannot get the foreach loop right.
I would think you would want a regular nested for-loop rather than foreach.
Something like this should work:
for (int i = 0; i < villageList.Count; ++i)
{
for (int j = i + 1; j < villageList.Count; ++j)
{
distanceFunc(villageList[i], villagelist[j]);
}
}
Where distanceFunc is whatever implementation of a distance function you want to use and villageList is your List of villages.
The reason you would use for-loops is because you need the inner loop to start one element past the the outer loop (i + 1), and foreach loops don't let you easily access the index you're currently at (they let you access the element itself, but not easily see it's position in the array).
You need two for loops:
var villages = new List<Villages>() { ... };
for (int i = 0; i < villages.Count - 1; i++)
for (int j = i + 1; j < villages.Count; j++)
Console.WriteLine(getDistance(villages[i], villages[j]));
Where getDistance you should write yourself. It should return a distance between two specified Villages.
How about something like this pseudocode:
villages = [a, b, c, ...]
for i=0 to len(villages)-2:
for j=i+1 to len(villages)-1:
print(villages[i], villages[j], dist(villages[i], villages[j]))