This question already has answers here:
Select a random item from a weighted list
(4 answers)
Closed 4 years ago.
Is there a shorter way to check my random number from 1 - 100 (catNum) against this table of animals? This one doesn't look so bad but I have several more larger tables to work through, I would like to use less lines than I would have to using the statement below:
if (catNum < 36) { category = "Urban"; }
else if (catNum < 51) { category = "Rural"; }
else if (catNum < 76) { category = "Wild"; }
else if (catNum < 86) { category = "Wild Birds"; }
else { category = "Zoo"; }
Example of further tables:
I prefer to use something like this instead of many if/else
A category class
class Category
{
public int Min { get; set; }
public int Max { get; set; }
public string Name { get; set; }
}
Initialise categories once and fill it with your values
var categories = new List<Category>();
and finally a method to resolve the category
public static string Get(int currentValue)
{
var last = categories.Last(m => m.Min < currentValue);
//if the list is ordered
//or
// var last = categories.FirstOrDefault(m => m.Min <= currentValue && m.Max >= currentValue);
return last?.Name;
}
One alternative is to build up a full list of the items, then you can just select one, at random, by index:
var categories =
Enumerable.Repeat("Urban", 35)
.Concat(Enumerable.Repeat("Rural", 15))
.Concat(Enumerable.Repeat("Wild", 25))
.Concat(Enumerable.Repeat("Wild Birds", 10))
.Concat(Enumerable.Repeat("Zoo", 15))
.ToArray();
var category = categories[45]; //Rural;
Yes, this is a well-studied problem and there are solutions that are more efficient than the if-else chain that you've already discovered. See https://en.wikipedia.org/wiki/Alias_method for the details.
My advice is: construct a generic interface type which represents the probability monad -- say, IDistribution<T>. Then write a discrete distribution implementation that uses the alias method. Encapsulate the mechanism work into the distribution class, and then at the use site, you just have a constructor that lets you make the distribution, and a T Sample() method that gives you an element of the distribution.
I notice that in your example you might have a Bayesian probability, ie, P(Dog | Urban). A probability monad is the ideal mechanism to represent these things because we reformulate P(A|B) as Func<B, IDistribution<A>> So what have we got? We've got a IDistribution<Location>, we've got a function from Location to IDistribution<Animal>, and we then recognize that we put them together via the bind operation on the probability monad. Which means that in C# we can use LINQ. SelectMany is the bind operation on sequences, but it can also be used as the bind operation on any monad!
Now, given that, an exercise: What is the conditioned probability operation in LINQ?
Remember the goal is to make the code at the call site look like the operation being performed. If you are logically sampling from a discrete distribution, then have an object that represents discrete distributions and sample from it.
string[] animals = new string[] { "Urban", "Rural", "Wild", "Wild Birds", "Zoo" };
int[] table = new int[] { 35, 50, 75, 85 };
for (int catNum = 10; catNum <= 100; catNum += 10)
{
int index = Array.BinarySearch(table, catNum);
if (index < 0) index = ~index;
Console.WriteLine(catNum + ": " + animals[index]);
}
Run online: https://dotnetfiddle.net/yMeSPB
Related
I am creating a Dungeons and Dragons Character Creator. There is a randomize feature that is going to create a complete character sheet. There is a part that I have gotten to and I am not quite sure the best way to proceed.
The way I have the racial modifiers set up is with if statements. Here is an example.
if (raceInt == 0 || raceInt == 2 || raceInt == 10)
{
raceStrMod = 2;
}
if (raceInt == 3 || raceInt == 4 || raceInt == 5 || raceInt == 11 || raceInt == 12)
{
raceDexMod = 2;
}
However there are races that have modifiers that let you select two stats to add a modifier to, such as Strength or Dexterity. What would be the best way to select two random ints for just those races?
For example, the half-elf race which would get +2 to Dex and then +1 to two other random stats. So I need to find a way to randomly select two of the remaining ints to make the value = 1.
My race mod ints are initialized as
int raceStrMod = 0;
int raceDexMod = 0;
int raceConMod = 0;
int raceIntMod = 0;
int raceWisMod = 0;
int raceChaMod = 0;
Then the if statements assign a value dependent on which race was randomly selected.
Thank you all for the input! This is how I ended up coding it
if (raceInt == 9)
{
int randomX = rnd.Next(1, 5);
int randomY = rnd.Next(1, 5);
int attempts = 0;
while (randomX == randomY && attempts < 10)
{
randomY = rnd.Next(1, 5);
attempts++;
}
//if they are still not unique after 10 attempts
if (randomX == randomY)
{
if (randomX == 5)
randomY = 1;
else
randomY = randomX + 1;
}
int[] randomNumbers = { randomX, randomY };
foreach (int i in randomNumbers)
{
switch (i)
{
case 1:
raceStrMod = 1;
break;
case 2:
raceDexMod = 1;
break;
case 3:
raceConMod = 1;
break;
case 4:
raceIntMod = 1;
break;
case 5:
raceWisMod = 1;
break;
}
}
}
Has your class introduced you to enum types yet? If not, is there any restriction on your final project with respect to using language features that weren't taught in the class?
Your question is arguably too broad, as there are many different ways to address this sort of thing even in real-world code, and the classroom context introduces potential roadblocks that while might constrain the question, being unknown they make it impossible to know what answer is actually going to work for you.
That said…
Ignoring the classroom aspect and focusing only on the problem itself, I would use enum types and dictionaries for this sort of thing. For example:
enum Attribute
{
Strength,
Dexterity,
Constitution,
Charisma,
Intelligence,
Wisdom,
Count, // must always be last
}
Dictionary<Attribute, int> modifiers = new Dictionary<Attribute, int>();
Then you can pick a random attribute like (assuming you have a random variable referencing a Random object…don't make the classic newbie mistake of creating a new Random object every time you want to pick a new random number):
Attribute attributeToModify = (Attribute)random.Next((int)Attribute.Count);
And you can store that selection like:
modifiers[attributeToModify] = 1;
This can be used to store however many modifiers you like. You can encapsulate that in an object representing the character itself, or you could put it into a separate AttributeModifiers class. One advantage of doing the latter would be that if you have modifiers that come from different sources, you can track that in the character object as a list of AttributeModifier instances, each in turn keeping track of what the actual source of those modifiers are.
This just barely scratches the surface. As I noted, the question itself is fairly broad. But I strongly recommend using the available language features to ensure that your variables represent things in a type-specific way, rather than just using int values for things that aren't really integers, and to use collection classes that more correctly represent the semantics of what your code is intended to do.
Note that this also means you probably should have an enum type for the races. E.g.:
enum Race
{
Dwarf,
Elf,
HalfElf,
Halfling,
HalfOrc,
Human,
// etc.
}
And your chain of if statements is probably better represented as a switch:
Attribute racialMod;
switch (race)
{
case Human:
case Halfling:
// etc.
racialMod = Attribute.Strength;
break;
case Elf:
case HalfElf:
// etc.
racialMod = Attribute.Dexterity;
break;
}
modifiers[racialMod] = 2;
Something like that. The point is to make sure the code reads more like what the original specification would say (if you actually had written one). This will make the code easier to understand, and it will be less likely for you to put bugs in the code (e.g. you accidentally type the wrong magic, unnamed integer).
I am creating a Dungeons and Dragons Character Creator.
That's a fun beginner project; I did the same when I was learning to program.
I need to find a way to randomly select two of the remaining...
You need to find two distinct values, call then x and y. The solution you've arrived at is:
Generate x
Try to generate y ten times
If no attempt succeeded to find a distinct y, hard-code a choice.
That works, and you almost never have to use the hard-coded choice. But I thought you might be interested to know that there is an easier way to generate two distinct numbers. Let's suppose we want two distinct numbers from 0, 1, 2, 3 or 4. (Obviously if you want a different range, say, 1 through 5, you can solve that problem by generating two distinct numbers 0->4 and then adding one to each.)
The improved algorithm is:
Choose x between 0 and 4 as usual.
Choose n between 1 and 4.
y = (x + n) % 5;
Think about it this way. Suppose we make a list like this:
0, 1, 2, 3, 4, 0, 1, 2, 3
We randomly choose x from the first five entries on the list, and then we choose y by stepping forwards between 1 and 4 steps. Since the list does not repeat in one to four steps, we know that we'll get two unique elements. The math does the equivalent of that.
You could similarly have used % in your program:
if (randomX == 5)
randomY = 1;
else
randomY = randomX + 1;
could be written
randomY = randomX % 5 + 1
If you're unfamiliar with %, it is the remainder operator. It is the complement of the / operator. The rule is:
int x = whatever;
int y = whatever;
int r = x % y;
is the same as:
int r = x - (x / y) * y;
That is, it is the remainder when x is divided by y. Keep in mind that the remainder can be negative!
(Disclaimer: I don't love this option, but couldn't think of another way other than reflection which is even nastier)
You could define a class that masks the fact that all of the mods are stored as an array and therefore can be indexed using a random number.
Something like the following:
public class StatMods
{
public int RaceStrMod { get { return this.mods[0]; } set { this.mods[0] = value; } }
public int RaceDexMod { get { return this.mods[1]; } set { this.mods[1] = value; } }
public int RaceConMod { get { return this.mods[2]; } set { this.mods[2] = value; } }
public int RaceIntMod { get { return this.mods[3]; } set { this.mods[3] = value; } }
public int RaceWisMod { get { return this.mods[4]; } set { this.mods[4] = value; } }
public int RaceChaMod { get { return this.mods[5]; } set { this.mods[5] = value; } }
private readonly int[] mods;
private static readonly Random rand = new Random();
public StatMods()
{
this.mods = new int[6];
}
public void ApplyRandomMod(int modification)
{
this.mods[rand.Next(0, 6)] += modification;
}
}
How to use method in linq selectmany statement?
public class BIT128Object
{
public long High {get; set;}
public long Low {get; set;}
public string Message {get; set;}
}
This class is used by express the integer indices from bit array.
I want to extract all indices from BIT128Object.
(Exactly i want all cases of index and message pairs like a group by)
I define below method to parse bit high and bit low to int indices array.
private int[] ParseIndices(long high, long low)
{
List<int> indexList = new List<int>();
BitArray highBitArr = new BitArray(BitConverter.GetBytes(high));
if(highBitArr != null)
{
for (int i = 0; i < 64; ++i)
{
if (highBitArr.Get(i) == true)
indexList.Add(i + 64);
}
}
BitArray lowBitArr = new BitArray(BitConverter.GetBytes(low));
if(lowBitArr != null)
{
for (int i = 0; i < 64; ++i)
{
if (lowBitArr.Get(i) == true)
indexList.Add(i);
}
}
return indexList.ToArray();
}
And then, there is a BIT128Object list which has over 1000 count of elements.
I query by linq statement to get all each index's total count like this.
[Index] [Count] [Message]
0 120 hello
1 90 world
1 3 hi
2 3 hi
2 2 hhhhiiii
2 1 hiiiiii
12231151 1 new
12231151 1 new2222
105452984641 1 world
And below is my linq statement.
var datum = bitObjectList.SelectMany(x => ParseIndices(x.High, x.Low)
.Select(z => new {
Index = z,
Message = x.Message
}));
But there is an exception occur while query linq.
The exception message is below.
Cannot parse expression 'value(ClassName)' as it has an unsupported
type. Only query sources (that is, expressions that implement
IEnumerable) and query operators can be parsed.
I debugged as much as, i found that i can not pass x.High and x.Low parameters to ParseIndices(). Maybe it seems i must pass fixed value like ParseIndices(10,10000).
It looks like that i can't use method in selectmany statement in this case.
May i have some helps?
You cannot use ParseIndices(x.High, x.Low)in LINQ query because the LINQ doesn't recognize & support it. You need another approach on that.
I'm making a poker hand evaluator for some programming practice which i have been submitting to codereview stack exchange. I need to be able to compare hands properly and for this I need to see the value of pairs. My Current check hand for pairs class
private static PokerHandsRank CheckHandForPairs(Hand hand)
{
var faceCount = (from card in hand.Cards
group card by card.Face
into g
let count = g.Count()
orderby count descending
select count).Take(2).ToList(); // take two to check if multiple pairs of pairs, if second in list is 1 there will be two pairs
switch (faceCount[0])
{
case 1: return PokerHandsRank.HighCard;
case 2: return faceCount[1] == 1 ? PokerHandsRank.Pair : PokerHandsRank.TwoPair;
case 3: return faceCount[1] == 1 ? PokerHandsRank.ThreeOfKind : PokerHandsRank.FullHouse;
case 4: return PokerHandsRank.FourOfKind;
default: throw new Exception("something went wrong here");
}
}
As you can see I've used linq to get a list of the most appeared pairing however I'm not sure how to finish it off to get the face value of the card once I've separated them.
This is my current compareto method
public int CompareTo(Hand other)
{
if (HandRank == other.HandRank) //if the hand rank is equal, sort the cards by face value and compare the two biggest
{
sortHandbyFace(this); // sorts cards into order by face
sortHandbyFace(other);
for (int i = 4; 0 <= i; i--)
{
if (Cards[i].Face == other.Cards[i].Face)
{
if (i == 0) return 0;
continue;
}
return Cards[i].Face > other.Cards[i].Face ? 1 : -1;
}
}
return HandRank > other.HandRank ? 1 : -1;
the compare to works perfect for comparing high card issues however i need to add a check if the two hand ranks are equal and then check if their value is a pair, two pair, fullhouse or three of kind (then to find the highest face value of the pair)
If you need more information on my program feel free to check my codereview post https://codereview.stackexchange.com/questions/152857/beginnings-of-a-poker-hand-classifier-part-2?noredirect=1&lq=1
This probably isn't exactly what you're looking for, since you're comparing objects and not just ints, but this could help you get started. Based on this question here: How to get frequency of elements stored in a list in C#.
using System.Linq;
List<int> ids = //
int maxFrequency = 0;
int IDOfMax = 0;
foreach(var grp in ids.GroupBy(i => i))
{
if (grp.Count() > maxFrequency)
{
maxFrequency = grp.Count();
IDOfMax = grp.Key;
}
}
// The object (int in this case) that appears most frequently
// can be identified with grp.key
Update: after rereading the question, it sounds like you need to try to return a new object with the count and the face value from your query.
You can do this like so:
public class FaceCountResult
{
public int Count { get; set; }
public Face FaceValue { get; set; }
public FaceCountResult(int count, Face faceValue)
{
Count = count;
FaceValue = faceValue;
}
}
Then, faceCount should look something like this:
var faceCount = (from card in hand.Cards
group card by card.Face
into g
let count = g.Count()
orderby count descending
select new FaceCountResult(count, card.Face);
I'm not sure how the Take(2) part would factor into this, as I don't quite understand that part of the code.
You can then do a switch on faceCount[0].Count and use faceCount[0].FaceValue to get the face value.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have the following class:
internal class Course
{
public int CourseCode { get; set; }
public string DeptCode { get; set; }
public string Name { get; set; }
}
and the following code is the 2 dimensional array I have:
Course[][] courses = new Course[3][];
courses[0] = new Course[] {
new Course() { CourseCode = 100, DeptCode = "EGR", Name = "EGR A" },
new Course() { CourseCode = 100, DeptCode = "EGR", Name = "EGR B" }
};
courses[1] = new Course[] {
new Course() { CourseCode = 200, DeptCode = "EN", Name = "EN A" }
};
courses[2] = new Course[] {
new Course() { CourseCode = 300, DeptCode = "PHY", Name = "PHY A" }
};
What I want to do is get the different combinations that each item in a group could do with the other groups; for example with the previous code, the results would be:
1. EGR A - EN A - PHY A
2. EGR B - EN A - PHY A
Answers:
To get the number of combinations possible, we can use Rule of Product, in the above case, the possible combinations will be (2 * 1 * 1) = 2 which is indeed the 2 combinations I wrote above.
LordTakkera has given the perfect answer, thank you so much!
You could use a nested for loop:
for (int i = 0; i < courses[0].Length; i++)
{
for (int j = 0; j < courses[1].Length; i++)
{
for (int k = 0; k < courses[2].Length; i++)
{
//Do whatever you need with the combo, accessed like:
//courses[0][i], courses[1][j], courses[2][k]
}
}
}
Of course, this solution gets really messy the more nesting you need. If you need to go any deeper, I would use some kind of recursive function to traverse the collection and generate the combinations.
It would be something like:
class CombinationHelper
{
public List<List<Course>> GetAllCombinations(Course[][] courses)
{
return GetCourseCombination(courses, 0);
}
public List<List<Course>> GetCourseCombination(Course[][] courses, int myIndex)
{
List<List<Course>> combos = new List<List<Course>>();
for (int i = 0; i < courses[myIndex].Length; i++)
{
if (myIndex + 1 < courses.GetLength(0))
{
foreach (List<Course> combo in GetCourseCombination(courses, myIndex + 1))
{
combo.Add(courses[myIndex][i]);
combos.Add(combo);
}
}
else
{
List<Course> newCombination = new List<Course>() { courses[myIndex][i] };
combos.Add(newCombination);
}
}
return combos;
}
}
I tested this (substituting "int" for "Course" to make verification easier) and it produced all 8 combinations (though not in order, recursion tends to do that. If I come up with the ordering code, I'll post, but it shouldn't be too difficult).
Recursive functions are hard enough for me to come up with, so my explanation won't be very good. Basically, we start by kicking the whole thing off with the "0" index (so that we start from the beginning). Then we iterate over the current array. If we aren't the last array in the "master" array, we recurse into the next sub-array. Otherwise, we create a new combination, add ourselves to it, and return.
As the recursive stack "unwinds" we add the generated combinations to our return list, add ourselves to it, and return again. Eventually the whole thing "unwinds" and you are left with one list of all the combinations.
Again, I'm sure that was a very confusing explanation, but recursive algorithms are (at least for me) inherently confusing. I would be happy to attempt to elaborate on any point you would like.
Take a look at your second index - the 0 or 1. If you look only at that, you see a binary number from 0 to 7.
Count from 0 to 7, translate it to bits and get the combination pattern you need.
I have this data structure:
class Product
{
public string Name { get; set; }
public int Count { get; set; }
}
var list = new List<Product>(){ { Name = "Book", Count = 40}, { Name = "Car", Count = 70}, { Name = "Pen", Count = 60}........... } // 500 product object
var productsUpTo100SumCountPropert = list.Where(.....) ????
// productsUpTo100SumCountPropert output:
// { { Name = "Book", Count = 40}, { Name = "Pen", Count = 60} }
I want to sum the Count properties of the collection and return only products objects where that property Count sum is less than or equal to 100.
If is not possible with linq, what is a better approach that I can use?
Judging from the comments you've left on other peoples' answers and your gist (link), it looks like what you're trying to solve is in fact the Knapsack Problem - in particular, the 0/1 Knapsack Problem (link).
The Wikipedia page on this topic (that I linked to) has a short dynamic programming solution for you. It has pseudo-polynomial running time ("pseudo" because the complexity depends on the capacity you choose for your knapsack (W).
A good preprocessing step to take before running the algorithm is to find the greatest common denominator (GCD) of all of your item weights (w_i) and then divide it out of each value.
d <- GCD({w_1, w_2, ..., w_N})
w_i' <- w_i / d //for each i = 1, 2, ..., N
W' <- W / d //integer division here
Then solve the problem using the modified weights and capacity instead (w_i' and W').
The greedy algorithm you use in your gist won't work very well. This better algorithm is simple enough that it's worth implementing.
You need the Count extension method
list.Count(p => p.Count <= 100);
EDIT:
If you want the sum of the items, Where and Sum extension methods could be utilized:
list.Where(p => p.Count <= 100).Sum(p => p.Count);
list.Where(p=> p.Count <= 100).ToList();