C# Changing only part of an array, in random places - c#

I've got a 2D array of int's in a console c# code (7X3 'cells'). The moment I create it, it is initialized with zeros.
I've got to change the value of only five 'cells' into 1, without changing the value of other 'cells'. I know it is a basic thing, but, as an absolute rookie in C#, I can't get it to work, no matter what cell I use. Can you help me?
Thanks in advance.
(Note: As I am a frequent user/contributer, I know it looks like a classic "Do my homework" question and I apologize, but as I'm really stuck on this weenie part (and the rest of my project is OK), I'd appreciate the help).

Here are two ways of solving this issue, the first one; called BruteForceRandomImplementation is easy to implement and understand but quite slow once you attempt to mark a very large number of locations with 1s due to it's brute-force like usage of Random till it fills enough locations.
/// <summary>
/// This method uses a random based algorithm to create a two-dimensional array of [height, width]
/// with exactly locationsToFind locations set to 1.
/// </summary>
/// <param name="height"></param>
/// <param name="width"></param>
/// <param name="locationsToFind"></param>
/// <returns></returns>
public int[,] BruteForceRandomImplementation(int height, int width, int locationsToFind)
{
var random = new Random();
locationsToFind = LimitLocationsToFindToMaxLocations(height, width, locationsToFind);
// Create our two-dimensional array.
var map = new int[height, width];
int locationsFound = 0;
// Randomly set positons to 1 untill we have set locationsToFind locations to 1.
while (locationsFound < locationsToFind)
{
// Get a random Y location - limit the max value to our height - 1.
var randomY = random.Next(height);
// Get a random X location - limit the max value to our width - 1.
var randomX = random.Next(width);
// Find another random location if this location is already set to 1.
if (map[randomY, randomX] == 1)
continue;
// Otherwise set our location to 1 and increment the number of locations we've found.
map[randomY, randomX] = 1;
locationsFound += 1;
}
return map;
}
With the following helper method to keep our locationsToFind range sane:
/// <summary>
/// Limits the locationsToFind variable to the maximum available locations. This avoids attempting to
/// mark more locations than are available for our width and height.
/// </summary>
/// <param name="height"></param>
/// <param name="width"></param>
/// <param name="locationsToFind"></param>
/// <returns></returns>
public int LimitLocationsToFindToMaxLocations(int height, int width, int locationsToFind)
{
return Math.Min(locationsToFind, height * width);
}
My second implementation called ShuffleImplementation is a lot faster when you are marking large numbers of unique locations. It creates a one-dimensional array, fills this with enough 1s to satisify your needs then shuffles this array using the Fisher-Yates shuffle to finally proceed to expand this one-dimensional array into a two-dimensional one:
/// <summary>
/// This method uses a shuffle based algorithm to create a two-dimensional array of [height, width]
/// with exactly locationsToFind locations set to 1.
/// </summary>
/// <param name="height"></param>
/// <param name="width"></param>
/// <param name="locationsToFind"></param>
/// <returns></returns>
public int[,] ShuffleImplementation(int height, int width, int locationsToFind)
{
locationsToFind = LimitLocationsToFindToMaxLocations(height, width, locationsToFind);
// Create a 1 dimensional array large enough to contain all our values.
var array = new int[height * width];
// Set locationToFind locations to 1.
for (int location = 0; location < locationsToFind; location++)
array[location] = 1;
// Shuffle our array.
Shuffle(array);
// Now let's create our two-dimensional array.
var map = new int[height, width];
int index = 0;
// Expand our one-dimensional array into a two-dimensional one.
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
map[y, x] = array[index];
index++;
}
}
return map;
}
/// <summary>
/// Shuffles a one-dimensional array using the Fisher-Yates shuffle.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="arr"></param>
public static void Shuffle<T>(T[] array)
{
var random = new Random();
for (int i = array.Length - 1; i > 0; i--)
{
int n = random.Next(i + 1);
Swap(ref array[i], ref array[n]);
}
}
/// <summary>
/// Swaps two values around.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="valueA"></param>
/// <param name="valueB"></param>
public static void Swap<T>(ref T valueA, ref T valueB)
{
T tempValue = valueA;
valueA = valueB;
valueB = tempValue;
}
Usage:
var map = BruteForceRandomImplementation(7, 3, 5);
Or:
var map = ShuffleImplementation(7, 3, 5);
Depending on which one you want to use. For a good example of the performance difference between the two, try:
int width = 1000;
int height = 1000;
int locationsToFind = (width * height) - 1;
var stopwatch = new Stopwatch();
stopwatch.Start();
BruteForceRandomImplementation(height, width, locationsToFind);
stopwatch.Stop();
Console.WriteLine(string.Format("BruteForceRandomImplementation took {0}ms", stopwatch.ElapsedMilliseconds));
stopwatch.Restart();
ShuffleImplementation(height, width, locationsToFind);
stopwatch.Stop();
Console.WriteLine(string.Format("ShuffleImplementation took {0}ms", stopwatch.ElapsedMilliseconds));
On my laptop BruteForceRandomImplementation took 1205ms and ShuffleImplementation took 67ms or nearly 18x faster.

So you've defined a multi-dimensional array (basically a matrix):
int[,] cells = new int[7,3];
And then you initialize all of the values to 0.
To change a value, you should be able to do it like this, for example:
cells[3,2] = 1;
Is that what you've tried? Are you receiving any exceptions or warnings?
If you're using jagged arrays (arrays of arrays), then it might be like:
int[][] cells = new int[7][3];
And then you can set values:
cells[3][2] = 1;

var array = new int[7,3];
array[5,2] = 1;
array[3,2] = 1;
Keep in mind that the array indices are zero-based, so the valid range of int[7,3] is [0..6,0..2]

int[,] cells = new int[7,3];
cells[3,2] = 1;

Related

My Quicksort Algorithm only works with First Index as the Pivot

I've started working on Sort Algorithms and got Quicksort working with the Activation Method as the first index for the pivot.
Now, if i try to use either a random index or the the last one it either just doesn't do anything or it just sorts the last half. A Video Showing what i mean.
My Code:
class Quicksort : Sortalgorithm
{
int ActivationMethod = -1;
Random rand = new Random();
/// <summary>
/// The Constructor for the Quicksort Algorithm
/// </summary>
/// <param name="arr">The Array to sort</param>
/// <param name="Actv">Activation Method: 0=First; 1=Last; 2=Random</param>
/// <param name="del">The Delay for the Algorithm between each sort</param>
/// <param name="sound">Wether Sound should be played</param>
/// <param name="gui">The Instance of the GUI</param>
public Quicksort(int[] arr, int Actv, int del, bool sound, gui gui) : base(del, sound, gui)
{
if (arr.Length < 5)
{
throw new Exception("Array has too few Entries");
}
ActivationMethod = Actv;
toSort = arr; // Is A Global Array from the inherited Class
}
protected override int sort()
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
return qsort(getPivot(), toSort.Length-1);
}
/// <summary>
/// The Quicksort Recursive Logic. Both Params are needed for the "sub-array"
/// </summary>
/// <param name="start">StartIndex for the Current Array</param>
/// <param name="end">EndIndex for the Current Array</param>
/// <returns></returns>
private int qsort(int start, int end)
{
if (start < end)
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
int pivot = partition(start, end); //get the pivot
qsort(start, pivot); // do the same for the frist part (smaller or equal to pivot)
qsort(pivot + 1, end); //and now for the second part (the largen than pivot)
}
return 1;
}
/// <summary>
/// The Partitioning Logic for The Array. Both Params are needed for the "sub-array"
/// </summary>
/// <param name="start">StartIndex for the Current Array</param>
/// <param name="end">EndIndex for the Current Array</param>
/// <returns></returns>
private int partition(int start, int end)
{
int pivot = toSort[start]; //Depending on Activiation Method
int swapIndex = start;
for (int i = start + 1; i <= end; i++)
{
if (toSort[i] < pivot) //Sort the Index Accrodingly
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
swapIndex++;
swap(i, swapIndex); // Is A Method inherited from a Class
}
}
swap(start, swapIndex);
return swapIndex;
}
/// <summary>
/// Get The Pivot returning on the Declared Activation Method in the Construcor
/// </summary>
/// <returns>PivotIndex</returns>
private int getPivot()
{
switch (ActivationMethod)
{
case 0:
return 0;
case 1:
return toSort.Length - 1;
case 2:
return rand.Next(0, toSort.Length - 1);
default:
throw new Exception("No Activationmethod Defined");
};
}
}
I've been Stuck for quite a while with this problem, any help would be appreciated
There's a flaw in your logic.
You do qsort(getPivot(), toSort.Length-1);
at the top level to select the pivot, and then in partition() you do
int pivot = toSort[start];
That's not going to work: the first argument to qsort is where to start sorting, so anything but zero here at the top level will prevent parts of the input from participating in the sort.
Instead the pivot needs to be selected for each partition, so that must take place inside partition():
private int partition(int start, int end)
{
int pivot = toSort[getPivot(start, end)]; //Depending on Activiation Method
And then getPivot() must be modified to take the start and end arguments into account when calculating the pivot index to be within the proper range, based on the desired pivot selection method.

Sometimes list doesn't get initialized

SOLVED: I figured it out myself. I can't select my answer for 2 days. Thank you everyone!
I have this weird bug. Sometimes my list is 0, when I can't see why. Every time I stepped through the debugger, it worked. It's making me crazy. Please help! This is travelling salesperson branch and bound, by the way.
public static BSSFPair generateBSSF(ref City[] Cities, int numberOfTrials)
{
int n = Cities.Length;
//Declare variable for updating.
double minCostOfBSSF = double.PositiveInfinity;
List<int> minRandomCityOrder = new List<int>();
//Try many random paths, and keep track of the minimum-cost path. Then select the minimum-cost path as the BSSF.
for (int iteration = 0; iteration < numberOfTrials; iteration++)
{
//Choose a random path.
List<int> randomCityOrder = new List<int>(generateRandomOrderInts(n)); //a list of ints of cities. Each city int only occurs once.
//Determine cost of route using the TSPSolution class.
System.Collections.ArrayList cities = new System.Collections.ArrayList(); //a list of City objects
foreach (int i in randomCityOrder)
{
cities.Add(Cities[i]);
}
ProblemAndSolver.TSPSolution bssf = new ProblemAndSolver.TSPSolution(cities);
double costOfBSSF = bssf.costOfRoute();
//Update the minimums.
if (costOfBSSF < minCostOfBSSF)
{
minCostOfBSSF = costOfBSSF;
minRandomCityOrder = new List<int>(randomCityOrder);
}
}
//return the path and the cost of the BSSF.
//<---- This is where the method with the bug was called.
return new BSSFPair(minCostOfBSSF, convertCityListToEdges(minRandomCityOrder)); //<---- THIS IS WHERE THE METHOD WITH THE BUG WAS CALLED.
}
This method is where the error happens:
/// <summary>
/// Converts a list of cities (WITHOUT THE LAST CITY BEING A DUPLICATE OF THE FIRST CITY) to a list of edges
/// (WITH THE LAST EDGE GOING BACK TO THE START OF THE FIRST EDGE because it wraps around so you can easily draw it).
/// </summary>
/// <param name="minRandomCityOrder"></param>
/// <returns></returns>
public static List<Edge> convertCityListToEdges(List<int> minRandomCityOrder)
{
if(minRandomCityOrder.Count < 2)
{
//Right here->
throw new NotImplementedException(); //<------- RIGHT HERE. minRandomCityOrder count is 0. How did that happen?
}
int n = minRandomCityOrder.Count;
//Convert the BSSF path to a list of edges.
List<Edge> newBssfPath = new List<Edge>();
int prev = minRandomCityOrder[0];
for (int i = 1; i < n; i++)
{
newBssfPath.Add(new Edge(prev, minRandomCityOrder[i]));
prev = minRandomCityOrder[i];
}
//Add edge from end to start.
newBssfPath.Add(new Edge(minRandomCityOrder[n - 1], minRandomCityOrder[0]));
return newBssfPath;
}
Utility function called by the code below. I've tested this myself, and it never returns an empty list.
/// <summary>
/// Generate a list of ints in the range [0, (maximum-1)] (Inclusive) in a random order. Each int occurs only once in the list.
/// </summary>
/// <param name="maximum"> "maximum" is the upper bound, and is not included in the list.</param>
/// <returns>the random-ordered list.</returns>
private static List<int> generateRandomOrderInts(int maximum)
{
if (maximum < 1)
{
throw new NotImplementedException();
}
Random random = new Random();
List<int> intsToAdd = new List<int>();
List<int> randomOrderList = new List<int>();
for (int i = 0; i < maximum; i++)
{
intsToAdd.Add(i);
}
while (intsToAdd.Count > 0)
{
//Returns a random int between 0 and n-1, inclusive.
int randomInt = random.Next(intsToAdd.Count);
randomOrderList.Add(intsToAdd[randomInt]);
intsToAdd.RemoveAt(randomInt);
}
return randomOrderList;
}
I found out what was wrong. In this method:
public static BSSFPair generateBSSF(ref City[] Cities, int numberOfTrials)
{
int n = Cities.Length;
//Declare variable for updating.
double minCostOfBSSF = double.PositiveInfinity;
List<int> minRandomCityOrder = new List<int>();//<--------------here
...
It starts as empty. The problem is that this assumes that the minimum (which is initialized to infinity) will be updated, and then the minRandomCityOrder will get a new list initialized to it. But, if I randomly pick paths that have a cost of infinity, then it will never be updated.
So, this is what fixed my code:
public static BSSFPair generateBSSF(ref City[] Cities, int numberOfTrials)
{
int n = Cities.Length;
//Declare variable for updating.
double minCostOfBSSF = double.PositiveInfinity;
List<int> minRandomCityOrder = new List<int>(generateRandomOrderInts(n)); //<---fixed
...
Now, if it doesn't update the best solution so far, it won't break the code and will just have a garbage path to calculate.
I guess this condition is true and your list gets resetted:
//Update the minimums.
if (costOfBSSF < minCostOfBSSF)
{
minCostOfBSSF = costOfBSSF;
minRandomCityOrder = new List<int>(randomCityOrder);
}
it does not mean that you are initializing the list with randomCityOrder-number of items but that the list has this initial capacity. If you want to have that number of items you might try:
minRandomCityOrder = Enumerable.Range(0, randomCityOrder).ToList();
...but I don't know if it makes sense in your algorithm. I'm just showing what's possible.

Writing a removeLast method

Situation:
I'm new to c# and currently learning the ropes whilst also polishing up on my data structures, I decided to make a class that'd perform multiple functions to a linear array, since It was suggested to me that I should start with linear arrays then work on a circular one.
The methods my class currently provides are:
adding an item to the front-most position of the array,
adding an item to the back-most position of the array,
removing the first item in the array,
removing the last item in the array, //todo
clearing the current array list of it's values,
displaying the array list contents to the user.
Problem:
I'm having difficulty constructing a method that removes the last item of the array, I've looked online and the pre-writen methods seem complex and I can't get my head around them due to my in-experience. I realize it must be easy for others to write a method like this, but I'm truly stumped.
I'd like to learn how to write a method that removes the last value in the array, in the most simpliest to understand way. Here is my current code for my LinearArrayList class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace arraydatastructuresactual
{
public class LinearArrayList
{
private int count; //how many numbers currently stored
private int[] values; //array to hold values entered into list
//constructors
/// <summary>
/// //creates a linear array that can hold max values
/// </summary>
/// <param name="max"></param>
public LinearArrayList(int max)
{
count = 0;
values = new int[max]; //makes the linear array as big as the max value
}
/// <summary>
/// //default constructor sets capacity to 10
/// </summary>
public LinearArrayList()
{
count = 0;
values = new int[10];
}
/// <summary>
/// returns true if list is empty
/// otherwise turns false
/// </summary>
/// <returns>true if empty otherwise false</returns>
public bool isEmpty()
{
return (count == 0);
}
/// <summary>
/// returns true if list is full
/// otherwise turns false
/// </summary>
/// <returns>true if full otherwise false</returns>
public bool isFull()
{
return (values.Length <= count);
}
/// <summary>
/// if not full adds value to the end of list
/// throws exception if list is fulll
/// </summary>
/// <param name="value">value to add to end of list</param>
public void addLast(int value) //adds an item to the last position in the array.
{
if (isFull())
{
throw new Exception("List Full");
}
else
{
values[count++] = value;
}
}
public void addFirst(int value) //Adds an item to the first position in the array.
{
if (isFull())
{
throw new Exception("List Full");
}
else
{
for (int i = count; i > 0; i--)
{
values[i] = values[i--];
}
values[0] = value;
count++;
}
}
public int removeFirst() //removes the first item from the array
{
if (isEmpty())
throw new Exception("List is Empty");
int value = values[0];
count--;
for (int i = 0; i < count; i++)
{
values[i] = values[i + 1];
}
return value;
}
public int removeLast() //todo //removes the last item from the array
{
if (isEmpty())
throw new Exception("List is Empty");
int value = values[0];
count--;
for (int i = count; i < count; i++)
{
values[i] = values[i + 1];
}
return value;
}
public void displayUI()//displays contents of list
{
}
public void destroy() //Empties The List
{
}
}
}
If someone could share their experience on how I'd go about achieving this, then many thanks, I tried to repurpose my removeFirst method but I messed up, tried to do this a few times and I'm completely stumped now.
You just need to write
public int removeLast()
{
if (isEmpty())
throw new Exception("List is Empty");
count--;
return values[count];
}
this will return the last item in the values array WITHOUT changing its size but decrementing the variable that keeps track of the item actually inserted in the array.
Notice that I don't try to change the value in the location pointed by the count variable. It is still there until you overwrite it adding another value
So you could still write this
// Creates an array with space for 10 ints
LinearArrayList la = new LinearArrayList();
la.addLast(34);
la.addLast(23);
la.addLast(56);
la.addLast(467);
la.addLast(43);
la.addLast(666);
la.addLast(7989);
la.addLast(82);
la.addLast(569);
la.addLast(100);
int value = la.removeLast();
// This will work because you still have a slot free in the 10 ints array
la.addLast(1110);
// While this will fail because the array has now all slots filled
la.addLast(9435);
An alternative to Sybren's answer:
int lastValue = values[values.Length - 1];
int[] newValues = new int[values.Length - 1];
Array.Copy(values, newValues, newValues.Length);
values = newValues;
return lastValue;
or
int lastValue = values[values.Length - 1];
Array.Resize(values, values.Length - 1);
return lastValue;
If you don't want to use any existing methods of Array class, you can also:
int lastValue = values[values.Length - 1];
int[] newValues = new int[values.Length - 1];
for (int i = 0; i < newValues.Length; i++)
{
newValues[i] = values[i];
}
values = newValues;
return lastValue;
Edit
Forget this, do what #Steve said.
Here's a way to do it. You're converting your array to a list, getting the last element from the list, removing the last element from the list, and convert it back to an array.
var numbersList = values.ToList();
var last = numbersList.Last();
numbersList.Remove(last);
values = numbersList.ToArray();
return last;

Split String Array[index] > constant returning True when it should be false

First things first, I'm brand new here and I'm hoping that this adheres to your guidelines for questions. I don't believe other questions/threads here would be applicable (at least they didn't appear to be).
Anyways, I'm a programming novice taking a C# class in college. The assignment I'm working on has to do with the Split() method. I'm supposed to ask the user for names and (bowling) scores in a textbox, split it into a temporary array, then take the appropriate index values from the temporary Array and place them in Name and Score arrays. It should handle up to 10 players, but it should work for fewer than that (partially filled array).
It will calculate and output the high score + who has it, the low score + who has it, and the average score, as well as outputting all the names and scores. I want to reject any entered scores that aren't between 0 and 300 before they enter the array, but when I enter a value outside of that range and debug, it says that the greater than/less than/ equal to is true. Obviously, if I enter 9001 or -3 etc. as a score I'd want it to be rejected. Here is that part of the code.
public void GetSplit(string _stg)
{
string[] tempArray;
//Split the temp array
tempArray = _stg.Split();
//Professor said a for loop would be unnecessary since it's a GUI
//LOW = 300
if(score[index] <= LOW && score[index] >= 0)
{//this part here^^ is returning true when it should be false
//1st score is in the 2nd slot in tempArray
//if input is not between 0-300 and is NOT an integer, yell at them
if (!int.TryParse(tempArray[1], out score[index]))
{
MessageBox.Show(YELL_INT);
return;
}
bool status = (int.TryParse(tempArray[1], out score[index]) ? true :false);
//1st name is in 1st slot in tempArray
name[index++] = tempArray[0];
}
}
If anyone has a solution as to how or why this isn't working, as well as an example on how to get it to work (even a redirect to another answered question that's similar to the one I'm asking that I missed), that'd awesome. Thanks!
I don't know why it would be necessary for me to add this, but here's all of the code from the program:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Proj_08
{
public partial class FrmMain : Form
{
private BowlingScores bs;//reference to bowlingscores class
/// <summary>
/// Purpose: Use the BowlingScores class to display names of players and scores
/// as well as high score + name, low score + name, and average score
/// </summary>
public FrmMain()
{
InitializeComponent();
}
/// <summary>
/// Purpose: Initialize BowlingScores and _tempArray
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
bs = new BowlingScores();
}
/// <summary>
/// Purpose: Close out of program
/// </summary>
/// <param name="sender">Button Clear Click Event</param>
/// <param name="e">EventArgs Object</param>
public void MSpExit_Click(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Purpose: Send the contents of the textbox to the GetScore method in BowlingScores class
/// </summary>
/// <param name="sender">Entry Enter Key Press Event</param>
/// <param name="e">KeyPressEventArgs Object</param>
private void TxtEntry_KeyPress(object sender, KeyPressEventArgs e)
{
if(e.KeyChar == (char)Keys.Enter)
{
bs.GetSplit(TxtEntry.Text);
TxtEntry.Text = string.Empty;
}
}
/// <summary>
/// Purpose: show everything in RichTextBox
/// </summary>
/// <param name="sender">Button Calc Click Event</param>
/// <param name="e">EventArgs Object</param>
public void BtnCalc_Click(object sender, EventArgs e)
{
//Output returned string from GetAll method
RTbOutput.Text = bs.GetAll();
}
/// <summary>
/// Purpose: Clear the textboxes and reset all arrays and references and the index
/// </summary>
/// <param name="sender">Button Clear Click Event</param>
/// <param name="e">EventArgs Object</param>
private void BtnClear_Click(object sender, EventArgs e)
{
bs = new BowlingScores();
TxtEntry.Text = string.Empty;
RTbOutput.Text = string.Empty;
}
}
//class BowlScores
public class BowlingScores
{
private const int LOW = 300;
private const int ASIZE = 10;
private const string YELL_INT = "Invalid Score";
private const string HEADER = "ERROR";//still have to add this
private int[] score;
private string[] name;
private int index;
/// <summary>
/// Purpose: Constructor for BowlingScores Class
/// </summary>
public BowlingScores()
{
index = 0;
score = new int[ASIZE];
name = new string[ASIZE];
}
/// <summary>
/// Purpose: Getter/Setter for name array
/// </summary>
public string[] Name
{
get { return name; }
set { name = value; }
}
/// <summary>
/// Purpose: Getter/Setter for score array
/// </summary>
public int[] Score
{
get { return score; }
set { score = value; }
}
/// <summary>
/// Purpose: Capture text from textbox and split into name and score arrays
/// </summary>
/// <param name="_stg"></param>
public void GetSplit(string _stg)
{
//int index = 0;
string[] tempArray;
//Split the temp array
tempArray = _stg.Split();
if(score[index] <= LOW && score[index] >= 0)
{
//1st score is in the 2nd slot in tempArray
//if input is not between 0-300 and is NOT an integer, yell at them
if (!int.TryParse(tempArray[1], out score[index]))
{
MessageBox.Show(YELL_INT, HEADER, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
bool status = (int.TryParse(tempArray[1], out score[index]) ? true : false);
//1st name is in 1st slot in tempArray
name[index++] = tempArray[0];
//increment index to continue filling respective arrays
}
}
/// <summary>
/// Purpose: Calculate High, Low, Average
/// </summary>
/// <returns></returns>
public string CalcData()
{
int high = 0;//to figure high score
string holdHigh = "";
int low = LOW;//to figure low score
string holdLow = "";
double sum = 0.0;
double avg = 0.0;
double count = 0.0;//to calculate average,
//
for (int index = 0; index < score.Length && name[index] != null; index++)
{
//calculate high score
//if an entered score is greater than 0, replace high with that entered score
if (score[index] > high && score[index] <= LOW && score[index] >= 0)
{
high = score[index];
holdHigh = name[index];
}
//calculate the low score
//if an entered score is less than 300, replace low with that entered score
if (score[index] < low && score[index] <= LOW && score[index] >= 0)
{
low = score[index];
holdLow = name[index];
}
//calculate sum and average
if (score[index] <= LOW && score[index] >= 0)
sum += score[index];
count = index + 1;
avg = (sum / count);
}
return string.Format("Congratulations {0}, you got the high score of {1}!\nBetter luck next time, {2}, you got the lowest score of {3}\nThe average score is {4:F2}", holdHigh, high, holdLow, low, avg);
}
/// <summary>
/// Purpose: Get all the names and scores and output them as a string
/// </summary>
/// <returns></returns>
public string GetAll()
{
string outputStg = "";
//as long as entry isn't null and index is less than Length
for(int index = 0; index < score.Length && name[index] != null ; index++)
{
//if the score is above 300 or below 0, don't return those values
if (score[index] <= LOW && score[index] >= 0 )
outputStg += name[index] + "\t\t" + score[index] + "\n";
}
return outputStg += "\n" + CalcData();
}
}
}
So from looking at your code, one flaw I saw so far is that you are checking score when nothing has been added to it yet! if your tempArray contains the data the user has input, you should be checking that first.
So like
// I'm assuming your incoming string is formatted like "Name Score Name Score"
// E.G. "Ryan 140 Tim 400"
int result;
string[] tempArray = _stg.Split();
if (int.TryParse(tempArray[1], out result))
{
if (result <= LOW && result >= 0)
// within bounds, add it to score array, etc.
}

Faster way to perform rectangle merging based on their intersection

this is being used in a motion detection problem.
Basically, I perform a motion detection algorithm on an image, and get a list of blobs, where each blob hopefully corresponds to an object that has moved. However, we have to merge these blobs as there might be many small ones touching each other that should be one large blob.
I merge the blobs together using this algorithm.
//Expand all blobs by 1x1 to ensure that we can use intersection
//blobs is a List<blob>
foreach (Blob blob in blobs)
{
blob.BoundingBox.Inflate(1, 1);
}
bool needToRestartMerging = true;
while (needToRestartMerging == true)
{
int count = blobs.Count;
needToRestartMerging = false;
for (int i = 0; i < count - 1; i++)
{
for (int j = i + 1; j < count; j++)
{
//BoundingBox is a simple System.Drawing.Rectangle
if (blobs[i].BoundingBox.IntersectsWith(blobs[j].BoundingBox))
{
Blob newBlob = blobs[i].Merge(blobs[j]);
blobs.RemoveAt(i);
blobs.RemoveAt(j-1);
blobs.Add(newBlob);
needToRestartMerging = true;
count = blobs.Count;
}
}
}
}
This is how I merge the blobs:
/// <summary>
/// Given a Pixel Location, we resize the Blob so that it is included in the BoundingBox
/// </summary>
/// <param name="x">Pixel XCoordinate</param>
/// <param name="y">Pixel YCoordinate</param>
public void ResizeToPixelLocation(int x, int y)
{
numPixels++;
if (x >= _boundingBox.Right)
{
_boundingBox.Width = x - _boundingBox.X;
}
if (y >= _boundingBox.Bottom)
{
_boundingBox.Height = y - _boundingBox.Y;
}
if (x <= _boundingBox.Left)
{
int oldLeft = _boundingBox.Left;
int xOffset = x - _boundingBox.Left;
_boundingBox.Offset(xOffset, 0);
_boundingBox.Width += (oldLeft - x);
}
if (y <= _boundingBox.Top)
{
int oldTop = _boundingBox.Top;
int yOffset = y - _boundingBox.Top;
_boundingBox.Offset(0, yOffset);
_boundingBox.Height += (oldTop - y);
}
}
/// <summary>
/// Merge this blob with another blob
/// </summary>
/// <param name="blob2">The second blob</param>
/// <returns></returns>
public Blob Merge(Blob blob2)
{
Blob newBlob = new Blob(BoundingBox.X, BoundingBox.Y);
newBlob.ThreadID = this.ThreadID;
newBlob.numPixels = NumPixels + blob2.NumPixels;
newBlob.BoundingBox = BoundingBox;
newBlob.ResizeToPixelLocation(blob2.BoundingBox.X, blob2.BoundingBox.Y);
newBlob.ResizeToPixelLocation(blob2.BoundingBox.Right, blob2.BoundingBox.Bottom);
return newBlob;
}
I may have about 0-150 blobs in all. I'd like to know if there's a faster way to do a blob merging.
Thanks
I would suggest something along the lines of:
mergeConnected(input):
output = new RectangleSet
while input.length > 0 do
nextRect = input.pop()
intersected = output.findIntersected(nextRect)
if intersected then
output.remove(intersected)
input.push(nextRect.merge(intersected))
else
output.insert(nextRect)
done
return output
Every loop iteration either removes from output or adds to output and removes from input, so the total number of loop iterations is no larger than twice the number of output rectangles.
To improve the performance of output.findIntersected, you can represent your set of rectangles as a data structure optimized for intersection searching (as opposed to a plain list). For instance, you can keep a data structure sorted by the maximum X of your rectangles to cull several of them, and then insert rectangles sorted by minimum X. Plain classic solutions, such as kd-trees or adaptive binary trees, could also work, but the insertion/removal time might adversely affect performance.

Categories

Resources