Add Items to List depending on Loop Value c# - c#

I have a method that returns a list. It takes 6 parameters (leg numbers). There is also a property that keeps track of the Number of Legs (it can have a value between 1-6). I can't seem to figure out if its possible to incorporate a loop and only add the list items depending on the Number of legs.
private List<T> ReturnListOfTypes<T>(T leg1, T leg2, T leg3,
T leg4, T leg5, T leg6)
{
List<T> legs = new List<T>();
for (int i = 0; i < NumberOfLegs; i++)
{
}
legs.Add(leg1);
legs.Add(leg2);
legs.Add(leg3);
legs.Add(leg4);
legs.Add(leg5);
legs.Add(leg6);
return legs;
}
If NumberOfLegs = 3, I only want the list to add:
legs.Add(leg1);
legs.Add(leg2);
legs.Add(leg3);
If NumberOfLegs = 1, it should only add leg 1 to the list:
legs.Add(leg1);
Right now, the list items are outside of the loop, because I can't figure out a way to only add the specified items depending on what the loop value is.

Maybe you could make it a params []:
private List<T> ReturnListOfTypes<T>(params T[] legs)
{
List<T> legList = new List<T>(NumberOfLegs);
for (int i = 0; i < Math.Min(NumberOfLegs, legs.Length); i++)
{
legList.Add(legs[i]);
}
return legList;
}
You can now still use it like in your code, but it's treated as an array. This presumes that the order of "legs" is always the order you want to return.
So you could use it for example so:
List<string> list = ReturnListOfTypes("1", "2", "3");

If I guess correctly, its all about adding number to particular leg.
So in this case:
private List<T> ReturnListOfTypes<T>(T leg1, T leg2, T leg3,
T leg4, T leg5, T leg6)
{
int NumberOfLegs = 6;
int j=1;
List<T> legs = new List<T>();
for (int i = 0; i <= (NumberOfLegs+1); i++)
{
j = j+i;
var newLeg = "leg"+j;
///Dont forget here, your for loop starts with 0, if you dont have declared leg0 then there would be error)
legs.Add(newLeg);
}
return legs;
}
///Start with i=1
int NumberOfLegs = 6;
List<T> legs = new List<T>();
for (int i = 1; i < (NumberOfLegs+1); i++)
{
var newLeg = "leg"+i;
legs.Add(newLeg);
}
return legs;
}
Also you could avoid outofRange with if statement before the loop: if(NumberOfLegs > 0)

I'm not sure I understood properly your problem, but the following code should do what you are looking for:
List<T> ReturnListOfTypes<T>(T leg1, T leg2, T leg3, T leg4, T leg5, T leg6)
{
List<T> legs = new List<T>();
if (NumberOfLegs >= 1) { legs.Add(leg1); }
if (NumberOfLegs >= 2) { legs.Add(leg2); }
if (NumberOfLegs >= 3) { legs.Add(leg3); }
if (NumberOfLegs >= 4) { legs.Add(leg4); }
if (NumberOfLegs >= 5) { legs.Add(leg5); }
if (NumberOfLegs >= 6) { legs.Add(leg6); }
return legs;
}
Hope it helps

Related

Sorting an array of enumeration

Hello I have an array of enumerations, and I'm trying sort that array by their enumeration value and get the array index's of the top
private enum Values
{
LOW,
MEDIUM,
HIGH
}
private Values[] Settings = new Values[10];
Settings[0] = LOW;
Settings[1] = HIGH;
Settings[2] = MEDIUM;
Settings[3] = LOW;
Settings[4] = LOW;
Settings[5] = LOW;
Settings[6] = LOW;
Settings[7] = LOW;
Settings[8] = MEDIUM;
Settings[9] = MEDIUM;
Basically now, with what I have above, I need to sort the settings array by enumeration value and get the array indexes of the top (let's say 3) items;
So I'd get back values 1, 2, 8
The platform I'm using does not support LINQ so those handy functions are not available.
I've been trying to wrap my head around this but it would help to have another pair of eyes.
thanks.
Implement a wrapper reference type,
class ValueWrapper : IComparable<ValueWrapper>
{
public Values Value { get; set; }
public int Index { get; set; }
public int CompareTo(ValueWrapper other)
{
return this.Value.CompareTo(other.Value) * -1; // Negating since you want reversed order
}
}
Usage -
ValueWrapper[] WrappedSettings = new ValueWrapper[10];
for(int i = 0; i < WrappedSettings.Length; i++)
{
WrappedSettings[i] = new ValueWrapper { Value = Settings[i], Index = i };
}
Array.Sort(WrappedSettings);
WrappedSettings will be sorted as you specified, preserving the indexes they were in the original array.
how about this:
Values first = Values.Low,second = Values.Low,third = Values.Low;
int firstI = -1,secondI = -1, thirdI = -1;
for(int i = 0;i < Settings.Length;i++)
{
if(Settings[i] > first || firstI == -1)
{
third = second;
thirdI = secondI;
second= first;
secondI= firstI;
first = Settings[i];
firstI = i;
}
else if(Settings[i] > second || secondI == -1)
{
third = second;
thirdI = secondI;
second = Settings[i];
secondI = i;
}
else if(Settings[i] > third || thirdI == -1)
{
third = Settings[i];
thirdI = i;
}
}
So, since you say you work in an environment where Linq is not available, I assume that other things like generics, nullables and so on will not be available either. A very low-tech solution.
Basic idea:
For every possible enum value, from highest to lowest, go through the list. If we find that value, output it and remember how many we have output. If we reach 3, stop the algorithm.
So, we first look for HIGH in the list, then for MEDIUM and so on.
class Program
{
private enum Values
{
LOW,
MEDIUM,
HIGH
}
static void Main(string[] args)
{
// Sample data
Values[] settings = new Values[10];
settings[0] = Values.LOW;
settings[1] = Values.HIGH;
settings[2] = Values.MEDIUM;
settings[3] = Values.LOW;
settings[4] = Values.LOW;
settings[5] = Values.LOW;
settings[6] = Values.LOW;
settings[7] = Values.LOW;
settings[8] = Values.MEDIUM;
settings[9] = Values.MEDIUM;
// Get Values of the enum type
// This list is sorted ascending by value but may contain duplicates
Array enumValues = Enum.GetValues(typeof(Values));
// Number of results found so far
int numberFound = 0;
// The enum value we used during the last outer loop, so
// we skip duplicate enum values
int lastValue = -1;
// For each enum value starting with the highest to the lowest
for (int i= enumValues.Length -1; i >= 0; i--)
{
// Get this enum value
int enumValue = (int)enumValues.GetValue(i);
// Check whether we had the same value in the previous loop
// If yes, skip it.
if(enumValue == lastValue)
{
continue;
}
lastValue = enumValue;
// For each entry in the list where we are searching
for(int j=0; j< settings.Length; j++)
{
// Check to see whether it is the currently searched value
if (enumValue == (int)settings[j])
{
// if yes, then output it.
Console.WriteLine(j);
numberFound++;
// Stop after 3 found entries
if (numberFound == 3)
{
goto finished;
}
}
}
}
finished:
Console.ReadLine();
}
}
Output is as requested 1,2,8
I'm not sure if this is exactly what you want because it doesn't sort the original array, but one way to get the indexes of the top three values is to simply store the indexes of the top values in another array. Then we can loop through the original array, and for each item, see if it's larger than any of the items at the indexes we've stored so far. If it is, then swap it with that item.
For example:
// Start the topIndexes array with all invalid indexes
var topIndexes = new[] {-1, -1, -1};
for (var settingIndex = 0; settingIndex < Settings.Length; settingIndex++)
{
var setting = Settings[settingIndex];
var topIndexLessThanSetting = -1;
// Find the smallest topIndex setting that's less than this setting
for (int topIndex = 0; topIndex < topIndexes.Length; topIndex++)
{
if (topIndexes[topIndex] == -1)
{
topIndexLessThanSetting = topIndex;
break;
}
if (setting <= Settings[topIndexes[topIndex]]) continue;
if (topIndexLessThanSetting == -1 ||
Settings[topIndexes[topIndex]] < Settings[topIndexes[topIndexLessThanSetting]])
{
topIndexLessThanSetting = topIndex;
}
}
topIndexes[topIndexLessThanSetting] = settingIndex;
}
// topIndexes = { 1, 2, 8 }

Distribute matches 'tournament friendly'

Say I have four teams, ABCD, I want to create matches so that the teams evenly have to do them:
not desired
A vs B
A vs C
A vs D
B vs C
B vs D
C vs D
desired
A vs B
C vs D
B vs C
A vs D
A vs C
B vs D
Another way of putting it: I want the teams to have a match as few in a row as possible.
Target language is C#, but I can translate easily.
Edit: Quick sidenote, it can be more than 4 teams!
One way to solve this is by the following steps:
Create a collection containing the total number of match combinations.
Create a new collection with the same length as the collection in step 1.
Go through the items in step 1, add the same item in step 2, with the condition that the next item to be added should have the maximum difference between it and the last added item.
Some sample code:
// Just a container to conveniently hold a match between two teams
public class Match
{
public Match(string teamOne, string teamTwo)
{
TeamOne = teamOne;
TeamTwo = teamTwo;
}
public string TeamOne { get; private set; }
public string TeamTwo { get; private set; }
public override string ToString()
{
return String.Format("{0} vs {1}", TeamOne, TeamTwo);
}
}
public class MatchMaking
{
public MatchMaking()
{
Teams = new List<string> { "A", "B", "C", "D", "E" };
}
public IList<string> Teams { get; private set; }
public IList<Match> GetMatches()
{
var unorderedMatches = GetUnorderedMatches();
// The list that we will eventually return
var orderedMatches = new List<Match>();
// Track the most recently added match
Match lastAdded = null;
// Loop through the unordered matches
// Add items to the ordered matches
// Add the one that is most different from the last added match
for (var i = 0; i < unorderedMatches.Count; i++)
{
if (lastAdded == null)
{
lastAdded = unorderedMatches[i];
orderedMatches.Add(unorderedMatches[i]);
unorderedMatches[i] = null;
continue;
}
var remainingMatches = unorderedMatches.Where(m => m != null);
// Get the match which is the most different from the last added match
// We do this by examining all of the unadded matches and getting the maximum difference
Match mostDifferentFromLastAdded = null;
int greatestDifference = 0;
foreach (var match in remainingMatches)
{
var difference = GetDifference(lastAdded, match);
if (difference > greatestDifference)
{
greatestDifference = difference;
mostDifferentFromLastAdded = match;
}
if (difference == 2)
{
break;
}
}
// Add the most different match
var index = unorderedMatches.ToList().IndexOf(mostDifferentFromLastAdded);
lastAdded = unorderedMatches[index];
orderedMatches.Add(unorderedMatches[index]);
unorderedMatches[index] = null;
}
return orderedMatches;
}
// Create a list containing the total match combinations with an arbitrary order
private List<Match> GetUnorderedMatches()
{
var totalNumberOfCombinations = AdditionFactorial(Teams.Count - 1);
var unorderedMatches = new List<Match>(totalNumberOfCombinations);
for (int i = 0; i < Teams.Count; i++)
{
for (int j = 0; j < Teams.Count; j++)
{
if (j <= i) continue;
unorderedMatches.Add(new Match(Teams[i], Teams[j]));
}
}
return unorderedMatches;
}
// Get the difference between two matches
// 0 - no difference, 1 - only one team different, 2 - both teams different
private int GetDifference(Match matchOne, Match matchTwo)
{
var matchOneTeams = new HashSet<string> { matchOne.TeamOne, matchOne.TeamTwo };
var matchTwoTeams = new HashSet<string> { matchTwo.TeamOne, matchTwo.TeamTwo };
var intersection = matchOneTeams.Intersect(matchTwoTeams);
return (intersection.Count() - 2) * -1;
}
// Just a helper to get the total number of match combinations
private int AdditionFactorial(int seed)
{
int result = 0;
for (int i = seed; i > 0; i--)
{
result += i;
}
return result;
}
}
public class Program
{
private static void Main(string[] args)
{
var matchMaking = new MatchMaking();
foreach (var match in matchMaking.GetMatches())
{
Console.WriteLine(match);
}
}
}
I think you may achieve what you need doing as follows. If you've got n number of teams, all the possible matches between teams can be represented with a Kn Complete graph.
The way I would come up with your desired sorting, is taking (removing) edges from that graph, one at a time, always trying to find an edge that matches teams that didn't match immediately before. Further more, I think this approach (with a little variation) can be used to generate the best way to maximize concurrent matches, if each time you take an edge you pick the one with the teams that haven't played in most time.
For simplicity, lets assume that teams are ints in the range of 0 to n-1. The graph can simply be represented with a boolean matrix. To pick the match with the teams that haven't played in most time, you can keep track of the last time each team played. For n teams, you'll have a total of n*(n-1)/2 number of matches.
IEnumerable<Tuple<int,int>> GenerateMatches(int n) {
bool[,] pendingMatches = new bool[n,n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++)
pendingMatches[i,j] = true;
}
int[] lastPlayed = new int[n];
int totalMatches = n*(n-1)/2;
for (int m = 1; m <= totalMatches; m++) {
Tuple<int, int> t = null;
int longestPlayed = -1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (pendingMatches[i,j]) {
int moreRecentlyPlayed = Math.Max(lastPlayed[i], lastPlayed[j]);
int timeSinceLastPlay = m - moreRecentlyPlayed;
if (timeSinceLastPlay > longestPlayed) {
longestPlayed = timeSinceLastPlay;
t = Tuple.Create(i,j);
}
}
}
}
lastPlayed[t.Item1] = lastPlayed[t.Item2] = m;
pendingMatches[t.Item1, t.Item2] = false;
yield return t;
}
}
The part that chooses the next match are the two most nested fors. The complexity of that part can be improved if you use some kind of priority queue adapted to adjust the priority of edges that involve the teams of the last picked edge after updating the lastPlayed array.
Hope that helps.

LINQ get x amount of elements from a list

I have a query which I get as:
var query = Data.Items
.Where(x => criteria.IsMatch(x))
.ToList<Item>();
This works fine.
However now I want to break up this list into x number of lists, for example 3. Each list will therefore contain 1/3 the amount of elements from query.
Can it be done using LINQ?
You can use PLINQ partitioners to break the results into separate enumerables.
var partitioner = Partitioner.Create<Item>(query);
var partitions = partitioner.GetPartitions(3);
You'll need to reference the System.Collections.Concurrent namespace. partitions will be a list of IEnumerable<Item> where each enumerable returns a portion of the query.
I think something like this could work, splitting the list into IGroupings.
const int numberOfGroups = 3;
var groups = query
.Select((item, i) => new { item, i })
.GroupBy(e => e.i % numberOfGroups);
You can use Skip and Take in a simple for to accomplish what you want
var groupSize = (int)Math.Ceiling(query.Count() / 3d);
var result = new List<List<Item>>();
for (var j = 0; j < 3; j++)
result.Add(query.Skip(j * groupSize).Take(groupSize).ToList());
If the order of the elements doesn't matter using an IGrouping as suggested by Daniel Imms is probably the most elegant way (add .Select(gr => gr.Select(e => e.item)) to get an IEnumerable<IEnumerable<T>>).
If however you want to preserve the order you need to know the total number of elements. Otherwise you wouldn't know when to start the next group. You can do this with LINQ but it requires two enumerations: one for counting and another for returning the data (as suggested by Esteban Elverdin).
If enumerating the query is expensive you can avoid the second enumeration by turning the query into a list and then use the GetRange method:
public static IEnumerable<List<T>> SplitList<T>(List<T> list, int numberOfRanges)
{
int sizeOfRanges = list.Count / numberOfRanges;
int remainder = list.Count % numberOfRanges;
int startIndex = 0;
for (int i = 0; i < numberOfRanges; i++)
{
int size = sizeOfRanges + (remainder > 0 ? 1 : 0);
yield return list.GetRange(startIndex, size);
if (remainder > 0)
{
remainder--;
}
startIndex += size;
}
}
static void Main()
{
List<int> list = Enumerable.Range(0, 10).ToList();
IEnumerable<List<int>> result = SplitList(list, 3);
foreach (List<int> values in result)
{
string s = string.Join(", ", values);
Console.WriteLine("{{ {0} }}", s);
}
}
The output is:
{ 0, 1, 2, 3 }
{ 4, 5, 6 }
{ 7, 8, 9 }
You can create an extension method:
public static IList<List<T>> GetChunks<T>(this IList<T> items, int numOfChunks)
{
if (items.Count < numOfChunks)
throw new ArgumentException("The number of elements is lower than the number of chunks");
int div = items.Count / numOfChunks;
int rem = items.Count % numOfChunks;
var listOfLists = new List<T>[numOfChunks];
for (int i = 0; i < numOfChunks; i++)
listOfLists[i] = new List<T>();
int currentGrp = 0;
int currRemainder = rem;
foreach (var el in items)
{
int currentElementsInGrp = listOfLists[currentGrp].Count;
if (currentElementsInGrp == div && currRemainder > 0)
{
currRemainder--;
}
else if (currentElementsInGrp >= div)
{
currentGrp++;
}
listOfLists[currentGrp].Add(el);
}
return listOfLists;
}
then use it like this :
var chunks = query.GetChunks(3);
N.B.
in case of number of elements not divisible by the number of groups, the first groups will be bigger. e.g. [0,1,2,3,4] --> [0,1] - [2,3] - [4]

C#: Dice Permutation without Repetition

How can I change my C# code below to list all possible permutations without repetitions? For example: The result of 2 dice rolls would produce 1,1,2 so that means 2,1,1 should not appear.
Below is my code:
string[] Permutate(int input)
{
string[] dice;
int numberOfDice = input;
const int diceFace = 6;
dice = new string[(int)Math.Pow(diceFace, numberOfDice)];
int indexNumber = (int)Math.Pow(diceFace, numberOfDice);
int range = (int)Math.Pow(diceFace, numberOfDice) / 6;
int diceNumber = 1;
int counter = 0;
for (int i = 1; i <= indexNumber; i++)
{
if (range != 0)
{
dice[i - 1] += diceNumber + " ";
counter++;
if (counter == range)
{
counter = 0;
diceNumber++;
}
if (i == indexNumber)
{
range /= 6;
i = 0;
}
if (diceNumber == 7)
{
diceNumber = 1;
}
}
Thread.Sleep(1);
}
return dice;
}
The simplest possible way I could think of:
List<string> dices = new List<string>();
for (int i = 1; i <= 6; i++)
{
for (int j = i; j <= 6; j++)
{
for (int k = j; k <= 6; k++)
{
dices.Add(string.Format("{0} {1} {2}", i, j, k));
}
}
}
I have written a class to handle common functions for working with the binomial coefficient, which is the type of problem that your problem falls under. It performs the following tasks:
Outputs all the K-indexes in a nice format for any N choose K to a file. The K-indexes can be substituted with more descriptive strings or letters. This method makes solving this type of problem quite trivial.
Converts the K-indexes to the proper index of an entry in the sorted binomial coefficient table. This technique is much faster than older published techniques that rely on iteration. It does this by using a mathematical property inherent in Pascal's Triangle. My paper talks about this. I believe I am the first to discover and publish this technique, but I could be wrong.
Converts the index in a sorted binomial coefficient table to the corresponding K-indexes.
Uses Mark Dominus method to calculate the binomial coefficient, which is much less likely to overflow and works with larger numbers.
The class is written in .NET C# and provides a way to manage the objects related to the problem (if any) by using a generic list. The constructor of this class takes a bool value called InitTable that when true will create a generic list to hold the objects to be managed. If this value is false, then it will not create the table. The table does not need to be created in order to perform the 4 above methods. Accessor methods are provided to access the table.
There is an associated test class which shows how to use the class and its methods. It has been extensively tested with 2 cases and there are no known bugs.
To read about this class and download the code, see Tablizing The Binomial Coeffieicent.
I'm bad at math as well, this may or may not be helpful...
Program.cs
namespace Permutation
{
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Generating list.");
var dice = new List<ThreeDice>();
for (int x = 1; x <= 6; x++)
{
for (int y = 1; y <= 6; y++)
{
for (int z = 1; z <= 6; z++)
{
var die = new ThreeDice(x, y, z);
if (dice.Contains(die))
{
Console.WriteLine(die + " already exists.");
}
else
{
dice.Add(die);
}
}
}
}
Console.WriteLine(dice.Count + " permutations generated.");
foreach (var die in dice)
{
Console.WriteLine(die);
}
Console.ReadKey();
}
}
}
ThreeDice.cs
namespace Permutation
{
using System;
using System.Collections.Generic;
public class ThreeDice : IEquatable<ThreeDice>
{
public ThreeDice(int dice1, int dice2, int dice3)
{
this.Dice = new int[3];
this.Dice[0] = dice1;
this.Dice[1] = dice2;
this.Dice[2] = dice3;
}
public int[] Dice { get; private set; }
// IEquatable implements this method. List.Contains() will use this method to see if there's a match.
public bool Equals(ThreeDice other)
{
// Get the current dice values into a list.
var currentDice = new List<int>(this.Dice);
// Check to see if the same values exist by removing them one by one.
foreach (int die in other.Dice)
{
currentDice.Remove(die);
}
// If the list is empty, we have a match.
return currentDice.Count == 0;
}
public override string ToString()
{
return "<" + this.Dice[0] + "," + this.Dice[1] + "," + this.Dice[2] + ">";
}
}
}
Good luck.
The important part of the question is that you want distinct sets (regardless of order). So for example, a dice roll of [1, 2, 1] is equal to a dice roll of [1, 1, 2].
I'm sure there are a number of ways to skin this cat, but the first thought that comes to mind is to create a EqualityComparer which will compare the list of dice in the way you want, and then use LINQ with the Distinct() method.
Here is the EqualityComparer, which takes 2 List<int> and says they are equal if the elements are all equal (regardless of order):
private class ListComparer : EqualityComparer<List<int>>
{
public override bool Equals(List<int> x, List<int> y)
{
if (x.Count != y.Count)
return false;
x.Sort();
y.Sort();
for (int i = 0; i < x.Count; i++)
{
if (x[i] != y[i])
return false;
}
return true;
}
public override int GetHashCode(List<int> list)
{
int hc = 0;
foreach (var i in list)
hc ^= i;
return hc;
}
}
And here is the code that uses it. I'm using LINQ to build up the list of all combinations... you could also do this with nested for loops but I like this better for some reason:
public static void Main()
{
var values = new[] { 1,2,3,4,5,6 };
var allCombos = from x in values
from y in values
from z in values
select new List<int>{ x, y, z };
var distinctCombos = allCombos.Distinct(new ListComparer());
Console.WriteLine("#All combos: {0}", allCombos.Count());
Console.WriteLine("#Distinct combos: {0}", distinctCombos.Count());
foreach (var combo in distinctCombos)
Console.WriteLine("{0},{1},{2}", combo[0], combo[1], combo[2]);
}
Hope that helps!
Here is generic c# version using recursion (basically the recursive method takes number of dices or number of times the dice has been tossed) and returns all the combinations strings ( for ex, for '3' as per the question - there will be 56 such combinations).
public string[] GetDiceCombinations(int noOfDicesOrnoOfTossesOfDice)
{
noOfDicesOrnoOfTossesOfDice.Throw("noOfDicesOrnoOfTossesOfDice",
n => n <= 0);
List<string> values = new List<string>();
this.GetDiceCombinations_Recursive(noOfDicesOrnoOfTossesOfDice, 1, "",
values);
return values.ToArray();
}
private void GetDiceCombinations_Recursive(int size, int index, string currentValue,
List<string> values)
{
if (currentValue.Length == size)
{
values.Add(currentValue);
return;
}
for (int i = index; i <= 6; i++)
{
this.GetDiceCombinations_Recursive(size, i, currentValue + i, values);
}
}
Below are corresponding tests...
[TestMethod]
public void Dice_Tests()
{
int[] cOut = new int[] { 6, 21, 56, 126 };
for(int i = 1; i<=4; i++)
{
var c = this.GetDiceCombinations(i);
Assert.AreEqual(cOut[i - 1], c.Length);
}
}

How to sort a part of an array with int64 indicies in C#?

The .Net framework has an Array.Sort overload that allows one to specify the starting and ending indicies for the sort to act upon. However these parameters are only 32 bit. So I don't see a way to sort a part of a large array when the indicies that describe the sort range can only be specified using a 64-bit number. I suppose I could copy and modify the the framework's sort implementation, but that is not ideal.
Update:
I've created two classes to help me around these and other large-array issues. One other such issue was that long before I got to my memory limit, I start getting OutOfMemoryException's. I'm assuming this is because the requested memory may be available but not contiguous. So for that, I created class BigArray, which is a generic, dynamically sizable list of arrays. It has a smaller memory footprint than the framework's generic list class, and does not require that the entire array be contiguous. I haven't tested the performance hit, but I'm sure its there.
public class BigArray<T> : IEnumerable<T>
{
private long capacity;
private int itemsPerBlock;
private int shift;
private List<T[]> blocks = new List<T[]>();
public BigArray(int itemsPerBlock)
{
shift = (int)Math.Ceiling(Math.Log(itemsPerBlock) / Math.Log(2));
this.itemsPerBlock = 1 << shift;
}
public long Capacity
{
get
{
return capacity;
}
set
{
var requiredBlockCount = (value - 1) / itemsPerBlock + 1;
while (blocks.Count > requiredBlockCount)
{
blocks.RemoveAt(blocks.Count - 1);
}
while (blocks.Count < requiredBlockCount)
{
blocks.Add(new T[itemsPerBlock]);
}
capacity = (long)itemsPerBlock * blocks.Count;
}
}
public T this[long index]
{
get
{
Debug.Assert(index < capacity);
var blockNumber = (int)(index >> shift);
var itemNumber = index & (itemsPerBlock - 1);
return blocks[blockNumber][itemNumber];
}
set
{
Debug.Assert(index < capacity);
var blockNumber = (int)(index >> shift);
var itemNumber = index & (itemsPerBlock - 1);
blocks[blockNumber][itemNumber] = value;
}
}
public IEnumerator<T> GetEnumerator()
{
for (long i = 0; i < capacity; i++)
{
yield return this[i];
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
And getting back to the original issue of sorting... What I really needed was a way to act on each element of an array, in order. But with such large arrays, it is prohibitive to copy the data, sort it, act on it and then discard the sorted copy (the original order must be maintained). So I created static class OrderedOperation, which allows you to perform an arbitrary operation on each element of an unsorted array, in a sorted order. And do so with a low memory footprint (trading memory for execution time here).
public static class OrderedOperation
{
public delegate void WorkerDelegate(int index, float progress);
public static void Process(WorkerDelegate worker, IEnumerable<int> items, int count, int maxItem, int maxChunkSize)
{
// create a histogram such that a single bin is never bigger than a chunk
int binCount = 1000;
int[] bins;
double binScale;
bool ok;
do
{
ok = true;
bins = new int[binCount];
binScale = (double)(binCount - 1) / maxItem;
int i = 0;
foreach (int item in items)
{
bins[(int)(binScale * item)]++;
if (++i == count)
{
break;
}
}
for (int b = 0; b < binCount; b++)
{
if (bins[b] > maxChunkSize)
{
ok = false;
binCount *= 2;
break;
}
}
} while (!ok);
var chunkData = new int[maxChunkSize];
var chunkIndex = new int[maxChunkSize];
var done = new System.Collections.BitArray(count);
var processed = 0;
var binsCompleted = 0;
while (binsCompleted < binCount)
{
var chunkMax = 0;
var sum = 0;
do
{
sum += bins[binsCompleted];
binsCompleted++;
} while (binsCompleted < binCount - 1 && sum + bins[binsCompleted] <= maxChunkSize);
Debug.Assert(sum <= maxChunkSize);
chunkMax = (int)Math.Ceiling((double)binsCompleted / binScale);
var chunkCount = 0;
int i = 0;
foreach (int item in items)
{
if (item < chunkMax && !done[i])
{
chunkData[chunkCount] = item;
chunkIndex[chunkCount] = i;
chunkCount++;
done[i] = true;
}
if (++i == count)
{
break;
}
}
Debug.Assert(sum == chunkCount);
Array.Sort(chunkData, chunkIndex, 0, chunkCount);
for (i = 0; i < chunkCount; i++)
{
worker(chunkIndex[i], (float)processed / count);
processed++;
}
}
Debug.Assert(processed == count);
}
}
The two classes can work together (that's how I use them), but they don't have to. I hope someone else finds them useful. But I'll admit, they are fringe case classes. Questions welcome. And if my code sucks, I'd like to hear tips, too.
One final thought: As you can see in OrderedOperation, I'm using ints and not longs. Currently that is sufficient for me despite the original question I had (the application is in flux, in case you can't tell). But the class should be able to handle longs as well, should the need arise.
You'll find that even on the 64-bit framework, the maximum number of elements in an array is int.MaxValue.
The existing methods that take or return Int64 just cast the long values to Int32 internally and, in the case of parameters, will throw an ArgumentOutOfRangeException if a long parameter isn't between int.MinValue and int.MaxValue.
For example the LongLength property, which returns an Int64, just casts and returns the value of the Length property:
public long LongLength
{
get { return (long)this.Length; } // Length is an Int32
}
So my suggestion would be to cast your Int64 indicies to Int32 and then call one of the existing Sort overloads.
Since Array.Copy takes Int64 params, you could pull out the section you need to sort, sort it, then put it back. Assuming you're sorting less than 2^32 elements, of course.
Seems like if you are sorting more than 2^32 elements then it would be best to write your own, more efficient, sort algorithm anyway.

Categories