Related
I am currently working on a recursively parallel Quicksort extension function for the List class. The code below represents the most basic thread distribution criteria I've considered because it should be the simplest to conceptually explain. It branches to the depth of the base-2 logarithm of the number of detected processors, and proceeds sequentially from there. Thus, each CPU should get one thread with a (roughly) equal, large share of data to process, avoiding excessive overhead time. The basic sequential algorithm is provided for comparison.
public static class Quicksort
{
/// <summary>
/// Helper class to hold information about when to parallelize
/// </summary>
/// <attribute name="maxThreads">Maximum number of supported threads</attribute>
/// <attribute name="threadDepth">The depth to which new threads should
/// automatically be made</attribute>
private class ThreadInfo
{
internal int maxThreads;
internal int threadDepth;
public ThreadInfo(int length)
{
maxThreads = Environment.ProcessorCount;
threadDepth = (int)Math.Log(maxThreads, 2);
}
}
/// <summary>
/// Helper function to perform the partitioning step of quicksort
/// </summary>
/// <param name="list">The list to partition</param>
/// <param name="start">The starting index</param>
/// <param name="end">The ending index/param>
/// <returns>The final index of the pivot</returns>
public static int Partition<T>(this List<T> list, int start, int end) where T: IComparable
{
int middle = (int)(start + end) / 2;
// Swap pivot and first item.
var temp = list[start];
list[start] = list[middle];
list[middle] = temp;
var pivot = list[start];
var swapPtr = start;
for (var cursor = start + 1; cursor <= end; cursor++)
{
if (list[cursor].CompareTo(pivot) < 0)
{
// Swap cursor element and designated swap element
temp = list[cursor];
list[cursor] = list[++swapPtr];
list[swapPtr] = temp;
}
}
// Swap pivot with final lower item
temp = list[start];
list[start] = list[swapPtr];
list[swapPtr] = temp;
return swapPtr;
}
/// <summary>
/// Method to begin parallel quicksort algorithm on a Comparable list
/// </summary>
/// <param name="list">The list to sort</param>
public static void QuicksortParallel<T>(this List<T> list) where T : IComparable
{
if (list.Count < 2048)
list.QuicksortSequential();
else
{
var info = new ThreadInfo(list.Count);
list.QuicksortRecurseP(0, list.Count - 1, 0, info);
}
}
/// <summary>
/// Method to implement parallel quicksort recursion on a Comparable list
/// </summary>
/// <param name="list">The list to sort</param>
/// <param name="start">The starting index of the partition</param>
/// <param name="end">The ending index of the partition (inclusive)</param>
/// <param name="depth">The current recursive depth</param>
/// <param name="info">Structure holding decision-making info for threads</param>
private static void QuicksortRecurseP<T>(this List<T> list, int start, int end, int depth,
ThreadInfo info)
where T : IComparable
{
if (start >= end)
return;
int middle = list.Partition(start, end);
if (depth < info.threadDepth)
{
var t = Task.Run(() =>
{
list.QuicksortRecurseP(start, middle - 1, depth + 1, info);
});
list.QuicksortRecurseP(middle + 1, end, depth + 1, info);
t.Wait();
}
else
{
list.QuicksortRecurseS(start, middle - 1);
list.QuicksortRecurseS(middle + 1, end);
}
}
/// <summary>
/// Method to begin sequential quicksort algorithm on a Comparable list
/// </summary>
/// <param name="list">The list to sort</param>
public static void QuicksortSequential<T>(this List<T> list) where T : IComparable
{
list.QuicksortRecurseS(0, list.Count - 1);
}
/// <summary>
/// Method to implement sequential quicksort recursion on a Comparable list
/// </summary>
/// <param name="list">The list to sort</param>
/// <param name="start">The starting index of the partition</param>
/// <param name="end">The ending index of the partition (inclusive)</param>
private static void QuicksortRecurseS<T>(this List<T> list, int start, int end) where T : IComparable
{
if (start >= end)
return;
int middle = list.Partition(start, end);
// Now recursively sort the (approximate) halves.
list.QuicksortRecurseS(start, middle - 1);
list.QuicksortRecurseS(middle + 1, end);
}
}
As far as I understand, this methodology should produce a one-time startup cost, then proceed in sorting the rest of the data significantly faster than sequential method. However, the parallel method takes significantly more time than the sequential method, which actually increases as the load increases. Benchmarked at a list of ten million items on a 4-core CPU, the sequential method averages around 18 seconds to run to completion, while the parallel method takes upwards of 26 seconds. Increasing the allowed thread depth rapidly exacerbates the problem.
Any help in finding the performance hog is appreciated. Thanks!
The problem is CPU cache conflict, also known as "false sharing."
Unless the memory address of the pivot point happens to fall on a cache line, one of the threads will get a lock on the L1 or L2 cache and the other one will have to wait. This can make performance even worse than a serial solution. The problem is described well in this article:
...where threads use different objects but those objects happen to be close enough in memory that they fall on the same cache line, and the cache system treats them as a single lump that is effectively protected by a hardware write lock that only one core can hold at a time. [1,2] This causes real but invisible performance contention; whichever thread currently has exclusive ownership so that it can physically perform an update to the cache line will silently throttle other threads that are trying to use different (but, alas, nearby) data that sits on the same line.
(snip)
In most cases, the parallel code ran actually ran slower than the sequential code, and in no case did we get any better than a 42% speedup no matter how many cores we threw at the problem.
Grasping at straws.... you may be able to do better if you separate the list into two objects, and pin them in memory (or even just pad them within a struct) so they are far enough apart that they won't have a cache conflict.
I have a very specific and long-winded question for you all. This question is both about programming and game-theory. I recently added spawnable ore to my Turn Based Strategy Game: http://imgur.com/gallery/0F5D5Ij (For those of you that look please forgive the development textures).
Now, onto the enigma that I have been contemplating. In my game, ore is generated each time a new map is created. 0-8 ore nodes are generated per level-creation. I already have this working; except it only generates "Emeraldite" at this point, which brings me to my question.
How would I, the programmer, make it so nodes have specific rarity? Consider this short mockup which is not actually game data:
(Pseudo Chances that a node will be one of the following)
Bloodstone 1 in 100
Default(Empty Node) 1 in 10
Copper 1 in 15
Emeraldite 1 in 35
Gold 1 in 50
Heronite 1 in 60
Platinum 1 in 60
Shadownite 1 in 75
Silver 1 in 35
Soranite 1 in 1000
Umbrarite 1 in 1000
Cobalt 1 in 75
Iron 1 in 15
I want to make it so that a generated node could be, theoretically, any of the above, however, with the odds also considered. I hope that question is clear enough. I have been trying to wrap my head around this, and even tried to write out a few if statements with randoms, however, I keep coming up empty handed.
Basically, I just want you guys to see my issue, and hopefully provide me with some insight on how I could approach this in a dynamic kind of way.
If any clarification is needed, please ask; sorry again if this was convoluted.
(I am adding C# as a tag only because that is the language I am using for this project)
I'd first represent the probability of each loot type as a simple number.
A probability in pure mathematics is conventionally expressed as a floating point number in the range 0 to 1, but for efficiency, you can use integers in any (large enough) range (each value is the 0-1 value multiplied by the maximum (which I'm calling MaxProbability here)).
e.g. Bloodstone (1 in 100) is 1/100 = 0.01, or MaxProbability * (1/100).
Copper (1 in 15) is 1/15 = 0.06667, or MaxProbability * (1/15).
I'm assuming that 'Default (Empty Node)' means the probability of none of the others.
In this case, the simplest way is not to define it - you get it if none of the others are chosen.
If 'Default' was included, the sum of all these probabilities would be 1 (i.e. 100%) (or MaxProbability, if using integers).
The 1/10 probability of 'Default' in your example is actually a contradiction because the total of all those probabilities is not 1 (it's 0.38247619 - the sum of the probability as calculated in my examples above).
Then you would choose a random number in the range 0 to 1 (or MaxProbability if using integers), and the chosen loot type is the first one in the list such that the sum of the probabilities of it and all previous ones ("cumulative probability") is greater than the random number.
e.g.
MaxProbability = 1000 (I'm using this to make it easy to read).
(For accurate probabilities, you could use 0x7FFFFFFF).
Type Probability Cumulative
---- ----------- ----------
Bloodstone 10 10 (0..9 yield Bloodstone)
Copper 67 77 (10+67) (10..76 yield Copper)
Emeraldite 29 105 (77+29)
Gold 20 125 etc.
Heronite 17 142
Platinum 17 159
Shadownite 13 172
Silver 29 200
Soranite 1 201
Umbrarite 1 202
Cobalt 13 216
Iron 67 282
Default (Empty Node) 7175 1000 (anything else)
e.g. If your random number in the range 0 to 999 (inclusive) was 184 (or anything in the range 172 to 199), you would choose "Silver" (the first one with cumulative probability greater than this).
You could hold the cumulative probabilities in an array and loop through it until you find one higher than the random number, or reach the end.
The order of the list does not matter.
You chose a random number only once per instance.
Including 'Default (Empty Node)' in the list means that the last cumulative probability will always be MaxProbability and the loop that searches it would never go past the end. (Alternatively, 'Default' can be omitted, and you choose it if the loop reaches the end of the list.)
Note that choosing a random number for each one in turn, e.g. a 1/10 chance of 'Bloodstone', then a 1/15 chance of Copper if not Bloodstone, skews the probabilities towards the earlier items:
The actual probability of Copper would be (1/15) * (1 - (1/10)) - 10% less than 1/15.
Here's code to do it (the actual choosing is 5 statements - in the method Choose ).
using System;
namespace ConsoleApplication1
{
class LootChooser
{
/// <summary>
/// Choose a random loot type.
/// </summary>
public LootType Choose()
{
LootType lootType = 0; // start at first one
int randomValue = _rnd.Next(MaxProbability);
while (_lootProbabilites[(int)lootType] <= randomValue)
{
lootType++; // next loot type
}
return lootType;
}
/// <summary>
/// The loot types.
/// </summary>
public enum LootType
{
Bloodstone, Copper, Emeraldite, Gold, Heronite, Platinum,
Shadownite, Silver, Soranite, Umbrarite, Cobalt, Iron, Default
};
/// <summary>
/// Cumulative probabilities - each entry corresponds to the member of LootType in the corresponding position.
/// </summary>
protected int[] _lootProbabilites = new int[]
{
10, 77, 105, 125, 142, 159, 172, 200, 201, 202, 216, 282, // (from the table in the answer - I used a spreadsheet to generate these)
MaxProbability
};
/// <summary>
/// The range of the probability values (dividing a value in _lootProbabilites by this would give a probability in the range 0..1).
/// </summary>
protected const int MaxProbability = 1000;
protected Random _rnd = new Random((int)(DateTime.Now.Ticks & 0x7FFFFFFF));
/// <summary>
/// Simple 'main' to demonstrate.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
var chooser = new LootChooser();
for(int n=0; n < 100; n++)
Console.Out.WriteLine(chooser.Choose());
}
}
}
You could rewrite all chances so that they use the same divisor (e.g. 1000), your chances then become
Bloodstone 10 in 1000
Default(Empty Node) 100 in 1000
Gold 20 in 1000
Next, create an array of a 1000 elements, and fill it with
10 Bloodstone elements,
100 Empty elements,
20 Gold elements,
etc.
Finally, generate a random number between 0 and 1000, and use that as the index into the element array, this will give
you your random element.
You might have to play with the chances a bit, since you'll probably want all 1000 array elements to be filled, but this is the general idea.
edit its not the most efficient implementation (at least in terms of memory usage, its running time should be good), but I chose this since it allows for a concise explanation that doesn't require a whole lot of math.
First of all, specifying the the default-empty node's probability is unnecessary. The other probabilities should be defined in such a way, that the empty node is created if no other type is created.
How to do this and ensure the generation probabiltiies are equal to those you specified? In short:
convert the probabilities to a floating point (it's a value with a common divisor of 1)
sum all probabilities and check if they are < 1
write a class which will store the all the probabilities
write a function which will get a random node based on those probabilities
For your example:
Bloodstone 1 in 100 = 0.01
Copper 1 in 15 ~= 0.07
Emeraldite 1 in 35 ~= 0.03
Gold 1 in 50 = 0.02
Default = 0.87
Now the class can be implemented in at least two ways. My option consumes much memory, does the computations once, but it also rounds the probability values which may introduce some error. Note, that the error depends on the arrSize variable - the larger it is, the smaller the error.
The other option is as in Bogusz's answer. It is more precise, but required more operations per each generated element.
Option suggested by Thomas requires a lot of repeatable code for each option hence is not versatile. Shellshock's answer will have invalid effective probabilities.
Astrotrain's idea to force yourself to use the same divisor is virtually the same as my own, though the implementation would be slightly different.
Here's a sample implementation of my idea (in java, but should be ported very easily):
public class NodeEntry {
String name;
double probability;
public NodeEntry(String name, double probability) {
super();
this.name = name;
this.probability = probability;
}
public NodeEntry(String name, int howMany, int inHowMany) {
this.name = name;
this.probability = 1.0 * howMany / inHowMany;
}
public final String getName() {
return name;
}
public final void setName(String name) {
this.name = name;
}
public final double getProbability() {
return probability;
}
public final void setProbability(double probability) {
this.probability = probability;
}
#Override
public String toString() {
return name+"("+probability+")";
}
static final NodeEntry defaultNode = new NodeEntry("default", 0);
public static final NodeEntry getDefaultNode() {
return defaultNode;
}
}
public class NodeGen {
List<NodeEntry> nodeDefinitions = new LinkedList<NodeEntry>();
public NodeGen() {
}
public boolean addNode(NodeEntry e) {
return nodeDefinitions.add(e);
}
public boolean addAllNodes(Collection<? extends NodeEntry> c) {
return nodeDefinitions.addAll(c);
}
static final int arrSize = 10000;
NodeEntry randSource[] = new NodeEntry[arrSize];
public void compile() {
checkProbSum();
int offset = 0;
for (NodeEntry ne: nodeDefinitions) {
int amount = (int) (ne.getProbability() * arrSize);
for (int a=0; a<amount;a++) {
randSource[a+offset] = ne;
}
offset+=amount;
}
while (offset<arrSize) {
randSource[offset] = NodeEntry.getDefaultNode();
offset++;
}
}
Random gen = new Random();
public NodeEntry getRandomNode() {
return randSource[gen.nextInt(arrSize)];
}
private void checkProbSum() {
double sum = 0;
for (NodeEntry ne: nodeDefinitions) {
sum+=ne.getProbability();
}
if (sum >1) {
throw new RuntimeException("nodes probability > 1");
}
}
public static void main(String[] args) {
NodeGen ng = new NodeGen();
ng.addNode(new NodeEntry("Test 1", 0.1));
ng.addNode(new NodeEntry("Test 2", 0.2));
ng.addNode(new NodeEntry("Test 3", 0.2));
ng.compile();
Map<NodeEntry, Integer> resCount = new HashMap<NodeEntry, Integer>();
int generations = 10000;
for (int a=0; a<generations; a++) {
NodeEntry node = ng.getRandomNode();
Integer val = resCount.get(node);
if (val == null) {
resCount.put(node, new Integer(1));
} else {
resCount.put(node, new Integer(val+1));
}
}
for (Map.Entry<NodeEntry, Integer> entry: resCount.entrySet()) {
System.out.println(entry.getKey()+": "+entry.getValue()+" ("+(100.0*entry.getValue()/generations)+"%)");
}
}
}
This makes sure the probabilities are actually uniform. If you checked for the first node spawn, then the other, then the other - you would get wrong results: nodes checked first would have increased probability.
Sample run:
Test 2(0.2): 1975 (19.75%)
Test 1(0.1): 1042 (10.42%)
Test 3(0.2): 1981 (19.81%)
default(0.0): 5002 (50.02%)
I think that it is easy to understand how it works.
(Cobalt, 20: means 1 of 20 -> 5%)
Dictionary<string, double> ore = new Dictionary<string, double>();
Random random = new Random();
private void AddOre(string Name, double Value)
{
ore.Add(Name, 1.0 / Value);
}
private string GetOreType()
{
double probSum = 0;
double rand = random.NextDouble();
foreach (var pair in ore)
{
probSum += pair.Value;
if (probSum >= rand)
return pair.Key;
}
return "Normal Ore"; //Reaches this point only if an error occurs.
}
private void Action()
{
AddOre("Cobalt", 20);
AddOre("Stone", 10);
AddOre("Iron", 100);
AddOre("GreenOre", 300);
//Add Common ore and sort Dictionary
AddOre("Common ore", 1 / (1 - ore.Values.Sum()));
ore = ore.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
Console.WriteLine(GetOreType());
}
Edit:
I add section "Add Common ore and sort Dictionary".
I recently had to do something similar, and I ended up with this generic "spawn generator".
public interface ISpawnable : ICloneable
{
int OneInThousandProbability { get; }
}
public class SpawnGenerator<T> where T : ISpawnable
{
private class SpawnableWrapper
{
readonly T spawnable;
readonly int minThreshold;
readonly int maxThreshold;
public SpawnableWrapper(T spawnable, int minThreshold)
{
this.spawnable = spawnable;
this.minThreshold = minThreshold;
this.maxThreshold = this.minThreshold + spawnable.OneInThousandProbability;
}
public T Spawnable { get { return this.spawnable; } }
public int MinThreshold { get { return this.minThreshold; } }
public int MaxThreshold { get { return this.maxThreshold; } }
}
private ICollection<SpawnableWrapper> spawnableEntities;
private Random r;
public SpawnGenerator(IEnumerable<T> objects, int seed)
{
Debug.Assert(objects != null);
r = new Random(seed);
var cumulativeProbability = 0;
spawnableEntities = new List<SpawnableWrapper>();
foreach (var o in objects)
{
var spawnable = new SpawnableWrapper(o, cumulativeProbability);
cumulativeProbability = spawnable.MaxThreshold;
spawnableEntities.Add(spawnable);
}
Debug.Assert(cumulativeProbability <= 1000);
}
//Note that it can spawn null (no spawn) if probabilities dont add up to 1000
public T Spawn()
{
var i = r.Next(0, 1000);
var retVal = (from s in this.spawnableEntities
where (s.MaxThreshold > i && s.MinThreshold <= i)
select s.Spawnable).FirstOrDefault();
return retVal != null ? (T)retVal.Clone() : retVal;
}
}
And you'd use it like:
public class Gem : ISpawnable
{
readonly string color;
readonly int oneInThousandProbability;
public Gem(string color, int oneInThousandProbability)
{
this.color = color;
this.oneInThousandProbability = oneInThousandProbability;
}
public string Color { get { return this.color; } }
public int OneInThousandProbability
{
get
{
return this.oneInThousandProbability;
}
}
public object Clone()
{
return new Gem(this.color, this.oneInThousandProbability);
}
}
var RedGem = new Gem("Red", 250);
var GreenGem = new Gem("Green", 400);
var BlueGem = new Gem("Blue", 100);
var PurpleGem = new Gem("Purple", 190);
var OrangeGem = new Gem("Orange", 50);
var YellowGem = new Gem("Yellow", 10);
var spawnGenerator = new SpawnGenerator<Gem>(new[] { RedGem, GreenGem, BlueGem, PurpleGem, OrangeGem, YellowGem }, DateTime.Now.Millisecond);
var randomGem = spawnGenerator.Spawn();
Evidently the spawn algorithm was not considered critical code so the overhead of this implementation was of no concern when compared to the ease of use. Spawns were run on world creation and it was easily more than fast enough.
Astrotrain already gave my answer but since I coded it up already I'll post it. Sorry for the syntax, I work mostly in Powershell and that is the context currently in my mind. Consider this psuedo code:
// Define the odds for each loot type
// Description,Freq,Range
LootOddsArray = "Bloodstone",1,100,
"Copper",1,15,
"Emeraldite,"1,35,
"Gold",1,50,
"Heronite",1,60,
"Platinum",1,60,
"Shadownite",1,75,
"Silver",1,35,
"Soranite",1,1000,
"Umbrarite",1,1000,
"Cobalt",1,75,
"Iron",1,15
// Define your lookup table. It should be as big as your largest odds range.
LootLookupArray(1000)
// Fill all the 'default' values with "Nothing"
for (i=0;i<LootLookupArray.length;i++) {
LootOddsArray(i) = "Nothing"
}
// Walk through your various treasures
for (i=0;i<LootOddsArray.length;i++)
// Calculate how often the item will appear in the table based on the odds
// and place that many of the item in random places in the table, not overwriting
// any other loot already in the table
NumOccsPer1000 = Round(LootOddsArray(i).Freq * 1000/LootOddsArray(i).Range)
for (l=0;l<NumOccsPer1000;l++) {
// Find an empty slot for the loot
do
LootIndex = Random(1000)
while (LootLookupArray(LootIndex) != "Nothing")
// Array(Index) is empty, put loot there
LootLookupArray(LootIndex) = LootOddsArray(i).Description
}
}
// Roll for Loot
Loot = LootLookupArray(Random(1000))
Use Random.Next http://msdn.microsoft.com/en-us/library/2dx6wyd4(v=vs.110).aspx:
Random rnd = new Random();
if (rnd.Next(1, 101) == 1)
// spawn Bloodstone
if (rnd.Next(1, 16) == 1)
// spawn Copper
if (rnd.Next(1, 36) == 1)
// spawn Emeraldite
The minimum value should always be 1, the maximum value is the odds of spawning the item + 1 (minValue is inclusive, maxValue is exclusive). Always test the return value for 1, e.g., for Bloodstone there is a 1 in 100 chance that the randomly generated number is 1. Of course, this uses a pseudo random number generator, which should be good enough for a game.
A slightly different approach to Astrotrains idea would be that instead of an array to use if statements . The upside is then that you need less memory, the downside that it will need more cpu time to calc the value of the node.
Thus:
Random rnd = new Random();
var number = rnd.next(1,1000);
if (number >= 1 && number <10)
{
// empty
}
else
{
if (number >= 10 && number <100)
{
// bloodstone
}
else
{
//......
}
}
Also a downside to this variant comapred to the array variant is that this one takes more place codewise at the location where you use it and is more prone to errors / corrections (try to add something inside it you need to update all variants).
Thus this here is metnioned for completeness sake but the array vairant (memory usage aside) is less prone to the problems that the if variant has.
I'm wondering if there is a sweet way I can do this in LINQ or something but I'm trying to most evenly distribute the letters of the alphabet across X parts where X is a whole number > 0 && <= 26. For example here might be some possible outputs.
X = 1 : 1 partition of 26
X = 2 : 2 partitions of 13
X = 3 : 2
partitions of 9 and one partition of 8
etc....
Ultimately I don't want to have any partitions that didn't end up getting at least one and I'm aiming to have them achieve the most even distribution that the range of difference between partition sizes is as small as posssible.
This is the code I tried orginally:
char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
int pieces = (int)Math.Round((double)alphabet.Count() / numberOfParts);
for (int i = 0; i < numberOfParts.Count; ++i) {
char[] subset = i == numberOfParts.Count - 1 ? alphabet.Skip(i * pieces).ToArray()
: alphabet.Skip(i * pieces).Take(pieces).ToArray();
... // more code following
This seemed to be working fine at first but I realized in testing that there is a problem when X is 10. Based on this logic I'm getting 8 groups of 3 and one group of 2, leaving the 10th group 0 which is no good as I'm going for the most even distribution.
The most ideal distribution for 10 in this case would be 6 groupings of 3 and 4 groupings of 2. Any thoughts on how this might be implemented?
In general, the easiest way to implement the logic is using the modulo operator, %. Get familiar with this operator; it's very useful for the situations where it helps. There are a number of ways to write the actual code to do the distribution of letters, using arrays or not as you wish etc., but this short expression should give you an idea:
"ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(letter) % partitionCount
This expression gives the zero-based index of the partition in which to deposit an upper-case letter. The string is just shown for convenience, but could be an array or some other way of representing the alphabet. You could loop over the alphabet, using logic similar to the above to choose where to deposit each letter. Up to you would be where to put the logic: inside a loop, in a method, etc.
There's nothing magical about modular arithmetic; it just "wraps around" after the end of the set of usable numbers is reached. A simple context in which we've all encountered this is in division; the % operator is essentially just giving a division remainder. Now that you understand what the % operator is doing, you could easily write your own code to do the same thing, in any language.
Putting this all together, you could write a utility, class or extension method like this one--note the % to calculate the remainder, and that simple integer division discards it:
/// <summary>
/// Returns partition sized which are as close as possible to equal while using the indicated total size available, with any extra distributed to the front
/// </summary>
/// <param name="totalSize">The total number of elements to partition</param>
/// <param name="partitionCount">The number of partitions to size</param>
/// <param name="remainderAtFront">If true, any remainder will be distributed linearly starting at the beginning; if false, backwards from the end</param>
/// <returns>An int[] containing the partition sizes</returns>
public static int[] GetEqualizedPartitionSizes(int totalSize, int partitionCount, bool remainderAtFront = true)
{
if (totalSize < 1)
throw new ArgumentException("Cannot partition a non-positive number (" + totalSize + ")");
else if (partitionCount < 1)
throw new ArgumentException("Invalid partition count (" + partitionCount + ")");
else if (totalSize < partitionCount)
throw new ArgumentException("Cannot partition " + totalSize + " elements into " + partitionCount + " partitions");
int[] partitionSizes = new int[partitionCount];
int basePartitionSize = totalSize / partitionCount;
int remainder = totalSize % partitionCount;
int remainderPartitionSize = basePartitionSize + 1;
int x;
if (remainderAtFront)
{
for (x = 0; x < remainder; x++)
partitionSizes[x] = remainderPartitionSize;
for (x = remainder; x < partitionCount; x++)
partitionSizes[x] = basePartitionSize;
}
else
{
for (x = 0; x < partitionCount - remainder; x++)
partitionSizes[x] = basePartitionSize;
for (x = partitionCount - remainder; x < partitionCount; x++)
partitionSizes[x] = remainderPartitionSize;
}
return partitionSizes;
}
I feel like the simplest way to achieve this is to perform a round robin distribution on each letter. Cycle through each letter of the alphabet and add to it, then repeat. Have a running count that determines what letter you will be putting your item in, then when it hits >26, reset it back to 0!
What I did in one app I had to distribute things in groups was something like this
var numberOfPartitions = GetNumberOfPartitions();
var numberOfElements = GetNumberOfElements();
while (alphabet.Any())
{
var elementsInCurrentPartition = Math.Ceil((double)numberOfPartitions / numberOfElements)
for (int i = 0; i < elementsInCurrentPartition; i++)
{
//fill your partition one element at a time and remove the element from the alphabet
numberOfElements--;
}
numberOfPartitions--;
}
This won't give you the exact result you expected (i.e. ideal result for 10 partitions) but it's pretty close.
p.s. this isn't tested :)
A pseudocode algorithm I have now tested:
Double count = alphabet.Count()
Double exact = count / numberOfParts
Double last = 0.0
Do Until last >= count
Double next = last + exact
ranges.Add alphabet, from:=Round(last), to:=Round(next)
last = next
Loop
ranges.Add can ignore empty ranges :-)
Here is a LinqPad VB.NET implementation of this algorithm.
So a Linq version of this would be something like
Double count = alphabet.Count();
Double exact = count / numberOfParts;
var partitions = Enumerable.Range(0, numberOfParts + 1).Select(n => Round((Double)n * exact));
Here is a LinqPad VB.NET implementation using this Linq query.
(sorry for formatting, mobile)
First, you need something like a batch method:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int groupSize)
{
var tempSource = source.Select(n => n);
while (tempSource.Any())
{
yield return tempSource.Take(groupSize);
tempSource = tempSource.Skip(groupSize);
}
}
Then, just call it like this:
var result = alphabet.Batch((int)Math.Ceiling(x / 26.0));
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have the following to solve and I'm not sure how to approach this:
There are parking lots that are adjacent to each other and their placement resembles a straight line. Each parking lot has a value (profit) assigned to it. You can purchase as many lots as you want BUT they have to be adjacent to each other (in a contiguous set).
INPUT (THIS IS GIVEN/WHAT YOU WOULD TYPE IN):
Number of lots: 9
Value for each parking lot: ie: -5, 0, 7, -6, 4, 3, -5, 0, 2
Representation (for easier viewing)
Each box contains profit of a each lot:
OUTPUT:
Should be:
3 6 8
meaning:
3 - start lot #,
6 - ending lot #,
8 - total profit (7 - 6 + 4 + 3)
If there is more than one answer, program should write the one that contains the smallest number of parking lots. If there is still more than one possible answer, your program can write any of them.
Please help. thanks in advance.
EDIT:
I got it working:
/// <summary>
/// The problem 2.
/// </summary>
public class MySuperAwesomeClass
{
#region Constants and Fields
/// <summary>
/// The seq end.
/// </summary>
private static int seqEnd = -1;
/// <summary>
/// The seq start.
/// </summary>
private static int seqStart;
#endregion
// Quadratic maximum contiguous subsequence sum algorithm.
#region Public Methods and Operators
/// <summary>
/// The max sub sum 2.
/// </summary>
/// <param name="a">
/// The a.
/// </param>
/// <returns>
/// The max sub sum 2.
/// </returns>
public static int maxSumSub(int[] a)
{
int maxSum = 0;
for (int i = 0; i < a.Length; i++)
{
int thisSum = 0;
for (int j = i; j < a.Length; j++)
{
thisSum += a[j];
if (thisSum > maxSum)
{
maxSum = thisSum;
seqStart = i;
seqEnd = j;
}
}
}
return maxSum;
}
#endregion
#region Methods
/// <summary>
/// The main.
/// </summary>
private static void Main()
{
Console.WriteLine("Enter N:");
string stringInput = Console.ReadLine();
int[] a = new int[Convert.ToInt16(stringInput)];
Console.WriteLine("Enter profit values:");
for (int i = 0; i < Convert.ToInt16(stringInput); i++)
{
string value = Console.ReadLine();
a[i] = Convert.ToInt16(value);
}
int maxSum = maxSumSub(a);
Console.WriteLine(string.Format("{0} {1} {2}", seqStart, seqEnd, maxSum));
Console.ReadKey();
}
#endregion
}
Except I can't figure out this part:
If there is more than one answer, program should write the one that contains the smallest number of parking lots.
This is the classic Maximum subset sum problem. No code as this is homework, but here is the general solution. I'm sure you can find code online by searching the title if you still get stuck.
Make first/last index variables for the max subset. These will hold the parking spaces of our answer. 3 and 6 respectively in your example.
Make a sum variable for the sum of the max subset. This will hold the sum of our answer. 8 in your example.
Make another set of first/last/sum variables which we will be our "current" variables.
Start at the beginning of the parking spaces. Place the current first and current last variable at the start and update the sum.
Now you're going to loop through each parking space by moving the current last variable and updating the sum.
If the current sum is greater then the max-so-far sum, save all the current variables into the max-so-far variables.
If at any point our current sum dips into the negative or becomes zero, our subset is not helping us get a max anymore, so restart it by moving the current first to where current last is and resetting current sum to zero.
Once we get to the end return the max-so-far variables
Here's a hint for a way you can make the algorithm more efficient: look at how the totals from each end add up. For example, from what you provided, from the left side the totals would be -5, -5, 2, -4, 0, 3, -2, -2, 0, and from the right side they would be 2, 2, -3, 0, 4, -2, 5, 5, 0.
I have a coding/maths problem that I need help translating into C#. It's a poker chip calculator that takes in the BuyIn, the number of players and the total amount of chips for each colour (there are x amount of colours) and their value.
It then shows you every possible combination of chips per person to equal the Buy In. The user can then pick the chipset distribution they would like to use. It's best illustrated with a simple example.
BuyIn: $10
Number of Players: 1
10 Red Chips, $1 value
10 Blue Chips, $2 value
10 Green Chips, $5 value
So, the possible combinations are:
R/B/G
10/0/0
8/1/0
6/2/0
5/0/1
4/3/0
2/4/0
1/2/1
etc.
I have spent a lot of time trying to come up with an algorithm in C#/.NET to work this out. I am stumbling on the variable factor - there's usually only 3 or 4 different chips colours in a set, but there could be any amount. If you have more than one player than you have to count up until TotalChips / NumberOfPlayers.
I started off with a loop through all the chips and then looping from 0 up to NumberOfChips for that colour. And this is pretty much where I have spent the last 4 hours... how do I write the code to loop through x amount of chips and check the value of the sum of the chips and add it to a collection if it equals the BuyIn? I need to change my approach radically methinks...
Can anyone put me on the right track on how to solve this please? Pseudo code would work - thank you for any advice!
The below is my attempt so far - it's hopeless (and wont compile, just an example to show you my thought process so far) - Might be better not to look at it as it might biased you on a solution...
private void SplitChips(List<ChipSuggestion> suggestions)
{
decimal valueRequired = (decimal)txtBuyIn.Value;
decimal checkTotal = 0;
ChipSuggestion suggestion;
//loop through each colour
foreach (Chip chip in (PagedCollectionView)gridChips.ItemsSource)
{
//for each value, loop through them all again
foreach (Chip currentChip in (PagedCollectionView)gridChips.ItemsSource)
{
//start at 0 and go all the way up
for (int i = 0; i < chip.TotalChipsInChipset; i++)
{
checkTotal = currentChip.ChipValue * i;
//if it is greater than than ignore and stop
if (checkTotal > valueRequired)
{
break;
}
else
{
//if it is equal to then this is a match
if (checkTotal == valueRequired)
{
suggestion = new ChipSuggestion();
suggestion.SuggestionName = "Suggestion";
chipRed.NumberPerPlayer = i;
suggestion.Chips.Add(chipRed);
chipBlue.NumberPerPlayer = y;
suggestion.Chips.Add(chipBlue);
chipGreen.NumberPerPlayer = 0;
suggestion.Chips.Add(chipGreen);
//add this to the Suggestion
suggestions.Add(suggestion);
break;
}
}
}
}
}
}
Here's an implementation that reads the number of chips, the chips (their worth and amount) and the buyin and displays the results in your example format. I have explained it through comments, let me know if you have any questions.
class Test
{
static int buyIn;
static int numChips;
static List<int> chips = new List<int>(); // chips[i] = value of chips of color i
static List<int> amountOfChips = new List<int>(); // amountOfChips[i] = number of chips of color i
static void generateSolutions(int sum, int[] solutions, int last)
{
if (sum > buyIn) // our sum is too big, return
return;
if (sum == buyIn) // our sum is just right, print the solution
{
for (int i = 0; i < chips.Count; ++i)
Console.Write("{0}/", solutions[i]);
Console.WriteLine();
return; // and return
}
for (int i = last; i < chips.Count; ++i) // try adding another chip with the same value as the one added at the last step.
// this ensures that no duplicate solutions will be generated, since we impose an order of generation
if (amountOfChips[i] != 0)
{
--amountOfChips[i]; // decrease the amount of chips
++solutions[i]; // increase the number of times chip i has been used
generateSolutions(sum + chips[i], solutions, i); // recursive call
++amountOfChips[i]; // (one of) chip i is no longer used
--solutions[i]; // so it's no longer part of the solution either
}
}
static void Main()
{
Console.WriteLine("Enter the buyin:");
buyIn = int.Parse(Console.ReadLine());
Console.WriteLine("Enter the number of chips types:");
numChips = int.Parse(Console.ReadLine());
Console.WriteLine("Enter {0} chips values:", numChips);
for (int i = 0; i < numChips; ++i)
chips.Add(int.Parse(Console.ReadLine()));
Console.WriteLine("Enter {0} chips amounts:", numChips);
for (int i = 0; i < numChips; ++i)
amountOfChips.Add(int.Parse(Console.ReadLine()));
int[] solutions = new int[numChips];
generateSolutions(0, solutions, 0);
}
}
Enter the buyin:
10
Enter the number of chips types:
3
Enter 3 chips values:
1
2
5
Enter 3 chips amounts:
10
10
10
10/0/0/
8/1/0/
6/2/0/
5/0/1/
4/3/0/
3/1/1/
2/4/0/
1/2/1/
0/5/0/
0/0/2/
Break the problem down recursively by the number of kinds of chips.
For the base case, how many ways are there to make an $X buy-in with zero chips? If X is zero, there is one way: no chips. If X is more than zero, there are no ways to do it.
Now we need to solve the problem for N kinds of chips, given the solution for N - 1. We can take one kind of chip, and consider every possible number of that chip up to the buy-in. For example, if the chip is $2, and the buy-in is $5, try using 0, 1, or 2 of them. For each of these tries, we have to use only the remaining N - 1 chips to make up the remaining value. We can solve that by doing a recursive call, and then adding our current chip to each solution it returns.
private static IEnumerable<IEnumerable<Tuple<Chip, int>>> GetAllChipSuggestions(List<Chip> chips, int players, int totalValue)
{
return GetAllChipSuggestions(chips, players, totalValue, 0);
}
private static IEnumerable<IEnumerable<Tuple<Chip, int>>> GetAllChipSuggestions(List<Chip> chips, int players, int totalValue, int firstChipIndex)
{
if (firstChipIndex == chips.Count)
{
// Base case: we have no chip types remaining
if (totalValue == 0)
{
// One way to make 0 with no chip types
return new[] { Enumerable.Empty<Tuple<Chip, int>>() };
}
else
{
// No ways to make more than 0 with no chip types
return Enumerable.Empty<IEnumerable<Tuple<Chip, int>>>();
}
}
else
{
// Recursive case: try each possible number of this chip type
var allSuggestions = new List<IEnumerable<Tuple<Chip, int>>>();
var currentChip = chips[firstChipIndex];
var maxChips = Math.Min(currentChip.TotalChipsInChipset / players, totalValue / currentChip.ChipValue);
for (var chipCount = 0; chipCount <= maxChips; chipCount++)
{
var currentChipSuggestion = new[] { Tuple.Create(currentChip, chipCount) };
var remainingValue = totalValue - currentChip.ChipValue * chipCount;
// Get all combinations of chips after this one that make up the rest of the value
foreach (var suggestion in GetAllChipSuggestions(chips, players, remainingValue, firstChipIndex + 1))
{
allSuggestions.Add(suggestion.Concat(currentChipSuggestion));
}
}
return allSuggestions;
}
}
For some large combinations this is propably not solvable in finite time.
(It is a NP problem)
http://en.wikipedia.org/wiki/Knapsack_problem
There are also links with Code? that could help you.
Hope this helps a bit.