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;
Related
I want to test a binary search in console app. I've populalated my array. I have no errors. Code runs... I'm not sure what lines of code I need to test this.
Here is my code..
internal class Program
{
static void Main(string[] args)
{
//Initialise array
int[] arr = { 800, 11, 50, 771, 649, 770, 240, 9 };
int key = 0;
Program.BinarySearchDisplay(arr, key);
}
//Binary Search Method
public static object BinarySearchDisplay(int[] arr, int key)
{
int minNum = 0;
int maxNum = arr.Length - 1;
while (minNum <= maxNum)
{
int mid = (minNum + maxNum) / 2;
if (key == arr[mid])
{
return ++mid;
}
else if (key < arr[mid])
{
maxNum = mid - 1;
}
else
{
minNum = mid + 1;
}
}
return "None";
}
}
}
A couple of things of note here. Although technically correct, it is not a good idea to return values of different types from a function. C# being statically typed provides the opportunity to expect certain return types that are used to check the code doesn't contain errors at compile type.
A function needs to return a value, and so you need to assign this value to a variable. This is an example of how to do this for a function called Fun()
int x = Fun(a,b,c);
Here is one way to achieve what you are trying to do. Have a function that returns a value if successful or flag when it does not succeed. Return a bool type indicating success, and include an out parameter that gets assigned a value before the function exits.
Notice that you store the results of the function in a variable found of type bool and then act accordingly using an if(found) statement
class Program
{
static void Main(string[] args)
{
//Initialise array
int[] arr = { 800, 11, 50, 771, 649, 770, 240, 9 };
int key = 50;
Console.WriteLine($"Array: {string.Join(",", arr)}");
Console.WriteLine($"Key: {key}");
// call the function an store the results in a variable
bool found = BinarySearchDisplay(arr, key, out int index);
if (found)
{
Console.WriteLine($"Found arr[{index}] = {arr[index]}");
}
else
{
Console.WriteLine("Not Found.");
}
}
/// <summary>
/// Binary Search Method.
/// </summary>
/// <param name="arr">The array of values.</param>
/// <param name="key">The key to search for.</param>
/// <param name="index">The array index if key was found, -1 otherwise.</param>
/// <returns>true if key found, false otherwise.</returns>
public static bool BinarySearchDisplay(int[] arr, int key, out int index)
{
int minIndex = 0;
int maxIndex = arr.Length - 1;
index = -1;
while (minIndex <= maxIndex)
{
index = (minIndex + maxIndex) / 2;
if (key == arr[index])
{
return true;
}
else if (key < arr[index])
{
maxIndex = index - 1;
}
else
{
minIndex = index + 1;
}
}
return false;
}
}
Note that Program.BinarySearchDisplay() is replaced by just BinarySearchDisplay(). This is because the function is within the same class Program and there is no need to fully qualify its name.
The result of the above code is
Array: 800,11,50,771,649,770,240,9
Key: 50
Found arr[2] = 50
I think there was an error in the op code in return ++mid; as the next index value is returned. Maybe this was done because indexing is 0-based, and the op wanted a 1-based result. But mixing the two styles is dangerous and will lead to bugs.
This is a similar pattern to bool int.TryParse(string, out int) to convert a string to a number if possible, or return false otherwise.
if(int.TryParse("1150", out int x))
{
// x holds the value 1150
}
I wish to read in from an input file and store the incoming data into an array of structs using class methods from ListClass. Which I have done. I then wish to read in from a separate file, that has a different format as the first, into a separate array but using the same ListClass. Is this possible?
I have tried using an abstract class but don't seem to get it quite right. for a small example:
class ListClass
{
public struct struct1
{
public string strPart;
public string strDescrip;
public int intQty;
}
public struct struct2
{
public string strPart;
public char chrSold;
public int intQtySold;
}
public int MAX_ELEMENTS = 4;
//PRIVATE DATA MEMBERS
private arr[] listArr;
private int length;
private int currPos;
public ListClass()
{
length = 0;
currPos = 0;
listArr = new arr[MAX_ELEMENTS];
}
public ListClass(int size)
{
listArr = new Array[size];
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Create a deep copy of the list
//Pre: List has been instantiated and orig contains list to be copied
//Post: An identical, deep copy of the list has been created.
public ListClass(/*in*/ ListClass orig) //list to be copied
{
length = orig.length;
//currPos = orig.currPos;
// Allocate the new list
listArr = new Array[MAX_ELEMENTS];
// Copy over all the values
for (int i = 0; i < MAX_ELEMENTS; i++)
listArr[i] = orig.listArr[i];
}//end copy constructor
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Indicates whether or not the list is empty
//Pre: List has been instantiated
//Post: Returns true if list is empty and false, otherwise
public bool IsEmpty()
{
return (length == 0);
}//end IsEmpty
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Indicates whether or not the list is full
//Pre: List has been instantiated
//Post: Returns true if list is full and false, otherwise
public bool IsFull()
{
return false;
} //end IsFull
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Inserts item into the list
//Pre: List is not full
//Post: Item has been inserted at the end of the current list, length has
// been modified
//Error Handling:
// if the key already exists within list, do not insert newItem and return false
// if the list is full, do not insert the item and return false
public void Insert(/*in*/ Array newItem) //item to be added
{
// Make sure there is space
if (NeedToExpand())
//Expand if needed and insert new item
Expand();
listArr[length] = newItem;
length++;
} //end Insert
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Deletes an item from the list
//Pre: Method Find has been called to find the item to delete, and the
// that item is in the list.CurrPos now points at the item to be deleted
//Post: The item denoted by currPos has been deleted from the list, lemgth has
// been updated.
//Error Handling: If the list is empty, no changes are made
public void Delete(string key)
{
if (IsEmpty())
Console.WriteLine("List is Empty");
else
{
if(!Find(key))
Console.WriteLine("Item not Found");
else
{
if (length > 0)
{
for (int i = currPos; i < length; i++)
listArr[i] = listArr[i + 1];
length--;
}
if (NeedToShrink())
Contract();
}
}
}//end Delete
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Moves to the beginning of the list
//Pre: List has been instantiated
//Post: currPos has been set to the first position in the list
public void FirstPosition()
{
currPos = 0;
}//end FirstPosition
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Moves to the next element in the list
//Pre: List has been instantiated
//Post: currPos has been moved to the next position in the list
//Error Handling: if currPos is already at the end of the list, currPos is not modified
public void NextPosition()
{
if (!EndOfList())
currPos++;
else
Console.WriteLine("End of List");
} //end NextPosition
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Determines if currPos is at the end of the list
//Pre: List has been instantiated
//Post: Returns true if currPos is at the end of the list, and false, otherwise
// end-of-list denotes the first empty index in the list.
public bool EndOfList()
{
return (currPos == length - 1);
}//end EndOfList
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Determines whether or not item is in the list
//Pre: item is assigned a value
//Post: If item is in the list then true is returned and currPos contains
// the index of the item in the list, otherwise,
// false is returned and currPos is at zero.
public bool Find(/*in*/ string key) // item to be found
{
bool found = false;
currPos = 0;
while (!found && currPos < length)
{
if (listArr[currPos] == key)
found = true;
else
currPos++;
}// End while
if (!found)
currPos = 0;
return found;
}//end Find
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Returns the current item in the list(denoted by currPos)
//Pre: List is not Empty
//Post: Returns the item at currPos
//Error Handling: if Retrieve is called on an empty list an InvRec with a key set
// to int.MinValue is returned.
public object Retrieve()
{
object inv = new object();
if (!IsEmpty())
return listArr[currPos];
else
{
Console.WriteLine("The list is empty");
return inv;
}
}//end Retrieve
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Purpose: Clears the list
//Pre: List has been instantiated
//Post: List has been restored to its initial condition
public void Clear()
{
currPos = 0;
length = 0;
}//end Clear
//Purpose: indicates if the list needs to be expanded
//Pre: insert must be called with a new item to insert
//Post: returns true if length equals MAX_ELEMENTS, false otherwise
private bool NeedToExpand()
{
if (length == MAX_ELEMENTS)
return true;
else
return false;
}
//Purpose: indicates if the list needs to be shrunk
//Pre: delete must be called with a item to delete
//Post: returns true if length is 25% of MAX_ELEMENTS, false otherwise
private bool NeedToShrink()
{
if ((float)MAX_ELEMENTS * 0.25f == length)
return true;
else
return false;
}
//Purpose: to expand the space of the list
//Pre: NeedToExpand must return true
//Post: the size of the list is doubled
private void Expand()
{
MAX_ELEMENTS *= 2;
ListClass tempList = new ListClass(MAX_ELEMENTS);
for (int i = 0; i < length; i++)
tempList.listArr[i] = listArr[i];
listArr = tempList.listArr;
}
//Purpose: to contract the size of the list
//Pre: NeedToContract must return true
//Post: the size of the list is shrunk
private void Contract()
{
if(MAX_ELEMENTS != 4)
{
MAX_ELEMENTS /= 2;
ListClass tempList = new ListClass(MAX_ELEMENTS);
for (int i = 0; i < length; i++)
tempList.listArr[i] = listArr[i];
listArr = tempList.listArr;
}
}
If the code snippet makes any sense. I have two differently formatted structs. That I would like to store into arrays with using all the methods in ListClass on each.
OK, well … given that this post is marked with the C# tag, I would suggest using an approach that is conventional for C#.
First comment, C# has a lot of collections built in, so conventionally one does not try to write them all over again. Another point, the types of Struct1 and Struct2 are different and it would seem that they have different meanings -- we could rename them perhaps InventoryAmount and SoldAmount.
So you just need to declare two lists in your main class, like this
using System.Collections.Generic;
...
List<InventoryAmount> InventoryAmounts { get; } = new List<InventoryAmount>();
List<SoldAmount> SoldAmounts { get; } = new List<SoldAmount>();
Then you read in your arrays and just add them to the lists like so:
InventoryAmount[] inventoryAmounts = ...
this.InventoryAmounts.AddRange( inventoryAmounts);
SoldAmount[] soldAmounts = …
this.SoldAmounts.AddRange( soldAmounts);
Actually this approach does a bit of extra memory allocation since the arrays are not used after they are added to the list. You could avoid this by putting each value directly into the list as it is read. Or you could just leave them in the arrays.
Another comment: methods like FirstPosition, NextPosition are more conventionally done using Enumerator and Enumerable. These are built in to the List class and arrays and are used by "foreach" like so:
foreach (InventoryAmount ia in this.InventoryAmounts) {
...
}
foreach (InventoryAmount ia in inventoryAmounts) {
...
}
Responding to your comment, again I really wouldn't suggest carrying forward the ListClass code. You would be far better off leveraging the lists that are built into c#.
On the other hand if you really are determined to do it the hard way (i.e. probably the wrong way), then who am I to stop you?
Two of the prominent hard ways to do it would be either (a) declare a struct that could contain either an InventoryAmount or a SoldAmount, or (b) declare the array as object[] and use c# boxing. Both approaches are likely meaningless from a business point of view and are also inefficient.
For the first approach (a), just declare a joint struct for example like this:
public struct InventoryAmountOrSoldAmount
{
public InventoryAmountOrSoldAmount( InventoryAmount ia => this.InventoryAmount = ia;
public InventoryAmountOrSoldAmount( SoldAmount sa => this.SoldAmount = sa;
public InventoryAmount { get;}
public SoldAmount { get;}
}
Then just use this as the type of your array:
private InventoryAmountOrSoldAmount[] listArr = new InventoryAmountOrSoldAmount[MAXELEMENTS];
Or the second approach (b) would be to declare the array like this:
private object[] listArr = new object[MAXELEMENTS];
Then you can assign each element of the array either an InventoryAmount or a SoldAmount. C# will box the struct by putting a copy on the heap and putting a pointer to it in the array element.
Hi guys im trying to learn permutation and recurtion. And im looking for a way how can i use bought at the same time.
MAIN:
namespace Recursive_Permutation
{
class Program
{
static void Main(string[] args)
{
int[] array = new int[5] { 0, 0, 0, 0, 0 };
CalPermutations CP = new CalPermutations();
CP.Run(array, array.Length-1);
}
}
}
Here is my Simple code:
namespace Recursive_Permutation
{
public class CalPermutations
{
public int Run(int[] array,int indexer)
{
if (indexer > array.Length)
{
return 1;
}
else
{
for (int i = 0; i <= array.Length; i++)
{
array[indexer] = i;
Display(array);
}
Run(array, indexer-1);
}
return indexer;
}
public void Display(int[] array)
{
foreach (int num in array)
{
Console.Write(num);
}
Console.WriteLine();
}
}
}
And here is the Output of the program:
Question:
it might be simple to other but im kind of confuse now in how can i manipulate it that it still count the first digit (position [0]) 1 to 5 and go next position and (position 1) and add 1 and go back to [0] and start count again till it reaches 5.
i hope that my explanation is understandable.. thx.
I put together this simpler example of using recursion and permutation. It uses strings internally, but produces the same result.
Its for proof of concept only, since nobody will us recursion for this simple stuff in a professional environment. Recursion can have a big memory impact, but makes describing some problems in a simple way possible.
If I had to choose between iterative and recursive solutions, I would take the iterative one most of the time.
// Main entrance
public void DoStuff()
{
// define variations
List<string> possibilities = new List<string>() { "0", "1", "2", "3", "4", "5" };
// resultlist, will be filled later
List<string> permutations = new List<string>();
// how many values will be taken from the possibilities
int digits = 5;
//do the work
Permute(permutations, possibilities, digits, "");
// display the work
foreach (var item in permutations)
{
Console.WriteLine(item);
}
}
/// <summary>
/// generates a List of permuted strings
/// </summary>
/// <param name="permutations">resultlist</param>
/// <param name="possibilities">possible values of the digit</param>
/// <param name="digitsLeft">how many digits shall be appended</param>
/// <param name="current">the current value of the unfinished result</param>
private void Permute(List<string> permutations, List<string> possibilities, int digitsLeft, string current)
{
// uncomment to see how it works in detail
//Console.WriteLine("step:"+current);
// most important: define Stop conditions for the recursion
// safety stop
if (digitsLeft < 0)
{// end of digits :), normally we never end up here
return;
}
// normal stop
if (digitsLeft == 0)
{// normal endpoint, add the found permutation to the resultlist
permutations.Add(current);
return;
}
// now prepare the recursion, try each possibility
foreach (var item in possibilities)
{
// digitsLeft need to be decreased, since we add a concrete digit to the current value (current+item)
// important: (current + item) generates a new string in memory, the old values won't be touched, permutations possibilities are references, so no memory impact here
Permute(permutations, possibilities, digitsLeft - 1, current + item);
}
}
Update: added comments on each method as per comment.
public class Program
{
public static void Main(string[] args)
{
int[] array = new int[] { 0, 0, 0, 0, 0};
CalPermutations CP = new CalPermutations();
CP.Run(array, 0);
}
}
public class CalPermutations
{
// Prints all the permutations of array, starting at the specified index.
public void Run(int[] array, int indexer)
{
if (indexer < 0 || indexer >= array.Length)
return;
// Keep [0, indexer] combination constant, change combination on right i.e. (indexer, Array.length).
Run(array, indexer+1);
// All the elements on right have finished, increment current element.
array[indexer]++;
// Check if current combination is STILL valid.
if(array[indexer] <= array.Length)
{
// since current combination is still valid, display it, and execute the process again on the new combination.
Display(array);
Run(array, indexer);
}
else
{
// Since current element is out of range, reset it.
array[indexer] = 1;
}
}
// Prints all the elements in array.
public void Display(int[] array)
{
foreach (int num in array)
Console.Write(num);
Console.WriteLine();
}
}
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.
I need an elegant method that takes an enumerable and gets the enumerable of enumerables each of the same number of elements in it but the last one:
public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(this IEnumerable<TValue> values, Int32 chunkSize)
{
// TODO: code that chunks
}
This is what I have tried:
public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(this IEnumerable<TValue> values, Int32 chunkSize)
{
var count = values.Count();
var numberOfFullChunks = count / chunkSize;
var lastChunkSize = count % chunkSize;
for (var chunkIndex = 0; chunkSize < numberOfFullChunks; chunkSize++)
{
yield return values.Skip(chunkSize * chunkIndex).Take(chunkSize);
}
if (lastChunkSize > 0)
{
yield return values.Skip(chunkSize * count).Take(lastChunkSize);
}
}
UPDATE
Just discovered there was a similar topic about splitting a list Split List into Sublists with LINQ
If memory consumption isn't a concern, then like this?
static class Ex
{
public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(
this IEnumerable<TValue> values,
int chunkSize)
{
return values
.Select((v, i) => new {v, groupIndex = i / chunkSize})
.GroupBy(x => x.groupIndex)
.Select(g => g.Select(x => x.v));
}
}
Otherwise you could get creative with the yield keyword, like so:
static class Ex
{
public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(
this IEnumerable<TValue> values,
int chunkSize)
{
using(var enumerator = values.GetEnumerator())
{
while(enumerator.MoveNext())
{
yield return GetChunk(enumerator, chunkSize).ToList();
}
}
}
private static IEnumerable<T> GetChunk<T>(
IEnumerator<T> enumerator,
int chunkSize)
{
do
{
yield return enumerator.Current;
} while(--chunkSize > 0 && enumerator.MoveNext());
}
}
>= .Net 6
Built-in Enumerable.Chunk method:
// Giving an enumerable
var e = Enumerable.Range(1, 999);
// Here it is. Enjoy :)
var chunks = e.Chunk(29);
// Sample, iterating over chunks
foreach(var chunk in chunks) // for each chunk
{
foreach(var item in chunk) // for each item in a chunk
{
Console.WriteLine(item);
}
}
< .Net 6 ?
Copy and paste MS Chunk source code into your project. Just few lines of code.
Here is a extension method using Take and Skip:
public static IList<IList<T>> Chunk<T>(this IList<T> source, int chunksize)
{
while (source.Any())
{
yield return source.Take(chunksize);
source = source.Skip(chunksize);
}
}
(updated to use IList rather than IEnumerable)
If you don't have .net 6, you might opt to patch the Chunk method from it into your project. The only adaptations you'll likely need to make are in relation to the exception helpers the .net source uses, as your own project probably won't have ThrowHelper in.
Their code:
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
would probably be more like:
throw new ArgumentNullException(nameof(source));
The following code block has had these adjustments applied; you can make a new file called Chunk.cs and drop the following code into it:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
namespace System.Linq
{
public static partial class Enumerable
{
/// <summary>
/// Split the elements of a sequence into chunks of size at most <paramref name="size"/>.
/// </summary>
/// <remarks>
/// Every chunk except the last will be of size <paramref name="size"/>.
/// The last chunk will contain the remaining elements and may be of a smaller size.
/// </remarks>
/// <param name="source">
/// An <see cref="IEnumerable{T}"/> whose elements to chunk.
/// </param>
/// <param name="size">
/// Maximum size of each chunk.
/// </param>
/// <typeparam name="TSource">
/// The type of the elements of source.
/// </typeparam>
/// <returns>
/// An <see cref="IEnumerable{T}"/> that contains the elements the input sequence split into chunks of size <paramref name="size"/>.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source"/> is null.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="size"/> is below 1.
/// </exception>
public static IEnumerable<TSource[]> Chunk<TSource>(this IEnumerable<TSource> source, int size)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (size < 1)
{
throw new ArgumentOutOfRangeException(nameof(size));
}
return ChunkIterator(source, size);
}
private static IEnumerable<TSource[]> ChunkIterator<TSource>(IEnumerable<TSource> source, int size)
{
using IEnumerator<TSource> e = source.GetEnumerator();
while (e.MoveNext())
{
TSource[] chunk = new TSource[size];
chunk[0] = e.Current;
int i = 1;
for (; i < chunk.Length && e.MoveNext(); i++)
{
chunk[i] = e.Current;
}
if (i == chunk.Length)
{
yield return chunk;
}
else
{
Array.Resize(ref chunk, i);
yield return chunk;
yield break;
}
}
}
}
}
You should verify that incorporating their MIT licensed code into your project does not unduly impact your own license intentions
Only had some quick testing but this seems to work:
public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(this IEnumerable<TValue> values, Int32 chunkSize)
{
var valuesList = values.ToList();
var count = valuesList.Count();
for (var i = 0; i < (count / chunkSize) + (count % chunkSize == 0 ? 0 : 1); i++)
{
yield return valuesList.Skip(i * chunkSize).Take(chunkSize);
}
}
As other answers have already pointed out, from .NET 6 upwards, there is an Enumerable.Chunk extension method.
Unfortunately (in my opinion), this method returns an IEnumerable<T[]>, which undermines the memory-conservation benefits of processing an IEnumerable<T> one element at a time:
public IEnumerable<HugeObject> CreateHugeObjects(int count) {
for (var i = 0; i < count; ++i) {
yield return new HugeObject(i);
}
}
public static int AggregateSomehow(IEnumerable<HugeObject> chunk) {
return 0;
}
public void Consume() {
var source = CreateHugeObjects(1000);
var chunks = source.Chunk(100);
var result = chunks.Select(AggregateSomehow);
}
In this example, the underlying type of chunk in AggregateSomehow will be HugeObject[100], meaning that 100 instances of HugeObject have to be loaded into memory simultaneously to perform the method call.
Before the availability of Enumerable.Chunk, I used to write my own extension named Partition like so:
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int size) {
if (source == null) {
throw new ArgumentNullException(nameof(source));
}
if (size < 1) {
throw new ArgumentOutOfRangeException(nameof(size));
}
using var e = source.GetEnumerator();
while (e.MoveNext()) {
yield return new Partitioner<T>(e, size);
}
}
private class Partitioner<T> : IEnumerable<T>
{
private class PartitionerEnumerator : IEnumerator<T>
{
private readonly IEnumerator<T> m_Source;
private readonly int m_Size;
private int m_Index = -1;
private bool m_Disposed = false;
public PartitionerEnumerator(IEnumerator<T> source, int size) {
m_Source = source;
m_Size = size;
}
public T Current => m_Source.Current;
object IEnumerator.Current => Current;
public void Dispose() {
if (!m_Disposed) {
m_Disposed = true;
while (++m_Index < m_Size && m_Source.MoveNext()) { }
}
}
public bool MoveNext() {
if (m_Index == -1) {
++m_Index;
return true;
} else {
return ++m_Index < m_Size && m_Source.MoveNext();
}
}
public void Reset() => throw new NotImplementedException();
}
private readonly PartitionerEnumerator m_Enumerator;
public Partitioner(IEnumerator<T> source, int size) {
m_Enumerator = new PartitionerEnumerator(source, size);
}
public IEnumerator<T> GetEnumerator() => m_Enumerator;
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
This approach takes into account three considerations:
The original source is only enumerated once (which is often missed by Skip/Take implementations)
Within normal nested/chained LINQ expressions, only one element at a time has to be in memory (which is ignored by the current implementation)
When any partition is processed only partly and then disposed prematurely, the PartitionerEnumerator.Dispose method makes sure that the underlying enumerator still gets forwarded the rest of the count (which is often missed by nested-loop approaches:)
public static IEnumerable<IEnumerable<T>> PartitionWrong<T>(this IEnumerable<T> source, int size) {
if (source == null) {
throw new ArgumentNullException(nameof(source));
}
if (size < 1) {
throw new ArgumentOutOfRangeException(nameof(size));
}
static IEnumerable<T> EnumeratePartition(IEnumerator<T> e, int size) {
var i = 0;
do {
yield return e.Current;
} while (++i < size && e.MoveNext())
}
using (var e = source.GetEnumerator()) {
while (e.MoveNext()) {
yield return EnumeratePartition(e, size);
}
}
}
This approach will work if all sub-sequences are fully enumerated, e. g. by calling Count or Sum on them, but it fails for partial enumeration like calling First on them:
var source = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
source.PartitionWrong(3).Select(c => c.Count()); // 3, 3, 3
source.PartitionWrong(3).Select(c => c.Sum()); // 6, 15, 24
source.PartitionWrong(3).Select(c => c.First()); // 1, 2, 3, 4, 5, 6, 7, 8, 9 but should be 1, 4, 7
My implementation will work for all of the above, but still has several shortcomings, which weren't relevant for my applications, but the first two are probably the reason why the .NET team chose "the easy way out" and use an array that gets filled immediately:
If you save all Partition objects at first, then were to process them round-robin, one element at a time, the order of the original IEnumerable is not preserved in the partitions, i. e. the first partition is not guaranteed to contain the first three elements. As a side effect, if the number of elements does not divide evenly into the partition size, it is "random" which partition is shorter than size. It is not even necessarily guaranteed that only one partition is shorter.
Using this in a parallelized context suffers from the same issues as (1), but TBH I never even looked into the thread-safetiness of my code.
The benefits of pre-mature enumeration abortion (like calling Any or All on the sub-sequences) will not prevent the rest of the currently enumerated Partion's elements to be created (although this is obviously true for Chunk as well, where all elements got created upon entering the chunk)
So in a nutshell - if you are not planning on using parallelization or aren't dependant on ordered processing, and run into a memory problem when using .NET 6's Chunk, my old code might be helpful to you.