Pick random element from list with probability - c#

I Have a list that contains four item (A, B, C, D). Every item has a probability to be chosen. Let's say for example A has 74% of chance to be picked, B 15%, C 7% ,and D 4%.
I want to create a function that choose randomly an item according to its probability.
Any help please?

Define a class for your items like this:
class Items<T>
{
public double Probability { get; set; }
public T Item { get; set; }
}
then initialize it
var initial = new List<Items<string>>
{
new Items<string> {Probability = 74 / 100.0, Item = "A"},
new Items<string> {Probability = 15 / 100.0, Item = "B"},
new Items<string> {Probability = 7 / 100.0, Item = "C"},
new Items<string> {Probability = 4 / 100.0, Item = "D"},
};
then you need to convert it to aggregate a sum of probabilities from 0 to 1
var converted = new List<Items<string>>(initial.Count);
var sum = 0.0;
foreach (var item in initial.Take(initial.Count - 1))
{
sum += item.Probability;
converted.Add(new Items<string> {Probability = sum, Item = item.Item});
}
converted.Add(new Items<string> {Probability = 1.0, Item = initial.Last().Item});
now you can pick an item from converted collection with respect to probability:
var rnd = new Random();
while (true)
{
var probability = rnd.NextDouble();
var selected = converted.SkipWhile(i => i.Probability < probability).First();
Console.WriteLine($"Selected item = {selected.Item}");
}
NOTE: my implementation have O(n) complexity. You can optimize it with binary search (because values in converted collection are sorted)

My apologies for answering this one like this - I'm kinda viewing it as a sort of "Euler.Net" puzzle, and a way of playing around with Generics.
Anyway, here's my go at it:
public class WeightedItem<T>
{
private T value;
private int weight;
private int cumulativeSum;
private static Random rndInst = new Random();
public WeightedItem(T value, int weight)
{
this.value = value;
this.weight = weight;
}
public static T Choose(List<WeightedItem<T>> items)
{
int cumulSum = 0;
int cnt = items.Count();
for (int slot = 0; slot < cnt; slot++)
{
cumulSum += items[slot].weight;
items[slot].cumulativeSum = cumulSum;
}
double divSpot = rndInst.NextDouble() * cumulSum;
WeightedItem<T> chosen = items.FirstOrDefault(i => i.cumulativeSum >= divSpot);
if (chosen == null) throw new Exception("No item chosen - there seems to be a problem with the probability distribution.");
return chosen.value;
}
}
Usage:
WeightedItem<string> alice = new WeightedItem<string>("alice", 1);
WeightedItem<string> bob = new WeightedItem<string>("bob", 1);
WeightedItem<string> charlie = new WeightedItem<string>("charlie", 1);
WeightedItem<string> diana = new WeightedItem<string>("diana", 4);
WeightedItem<string> elaine = new WeightedItem<string>("elaine", 1);
List<WeightedItem<string>> myList = new List<WeightedItem<string>> { alice, bob, charlie, diana, elaine };
string chosen = WeightedItem<string>.Choose(myList);

using System;
public class Test{
private static String[] values = {"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","B","B","B","B","B","B","B","B","B","B","B","B","B","B","C","C","C","C","C","C","C","D","D","D","D",};
private static Random PRNG = new Random();
public static void Main(){
Console.WriteLine( values[PRNG.Next(values.Length)] );
}
}

Related

Generate 4 differents numbers randomly

I am a novice in Xamarin ,
I want to generate randomly 4 numbers which are in a list and this 4 numbers must be different .
In the example below I have a list of Ids and I am trying to pick 4 id randomly in the list and those 4 Ids must be each differents.
Here is my methode, I cannot see how I can continue and make it simple :
public MyWordsList()
{
InitializeComponent();
Dictionary<int, int> WordId = new Dictionary<int, int>();
int u= 0;
// TestAnswer.IsVisible = false;
foreach (var w in mywords)
{
WordId[u] = w.ID;
u++;
}
Random rnd = new Random();
// this is not ok because I can have the same number
word11.Text = WordsList[rnd.Next(1, 20)];
word12.Text = WordsList[rnd.Next(1, 20)];
word13.Text = WordsList[rnd.Next(1, 20)];
word14.Text = WordsList[rnd.Next(1, 20)];
}
If you have a better solution, I will take.
Thanks
You can write a short generic function which picks X amount of random and unique items from a specified collection and returns them:
private static IEnumerable<T> GetUniqueRandomItems<T>(int count, IList<T> allItems)
{
if (new HashSet<T>(allItems).Count < count)
{
throw new ArgumentException(nameof(allItems));
}
Random random = new Random();
HashSet<T> items = new HashSet<T>();
while (items.Count < count)
{
T value = allItems[random.Next(0, allItems.Count)];
items.Add(value);
}
return items;
}
You can later use it like this:
string[] randomIds = GetUniqueRandomItems(4, WordsList).ToArray();
word11.Text = randomIds[0];
word12.Text = randomIds[1];
word13.Text = randomIds[2];
word14.Text = randomIds[3];
call a method like the following:
private int CreateUniqueRandom(int min, int max, ICollection<int> existingNums)
{
var rnd = new Random();
var newNum = rnd.Next(min, max);
while (existingNums.Contains(newNum))
newNum = rnd.Next(min, max);
return newNum;
}
Passing through a list of the numbers that you have created so far
You probably won't need this, but just to show a method of unique random number generation with no duplicate check:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var randoms = GenerateRandoms(10, 1, 10).OrderBy(v => v);
foreach (var random in randoms)
{
Console.WriteLine(random);
}
Console.ReadLine();
}
private static int[] GenerateRandoms(int randomCount, int min, int max)
{
var length = max - min + 1;
if (randomCount > length) { throw new ArgumentException($"Cannot generate {randomCount} random numbers between {min} and {max} (inclusive)."); }
var values = new List<int>(length);
for (var i = 0; i < length; i++)
{
values.Insert(i, min + i);
}
var randomGenerator = new Random();
var randoms = new List<int>();
for (var i = 0; i < randomCount; i++)
{
var val = randomGenerator.Next(0, values.Count);
randoms.Add(values[val]);
values.RemoveAt(val);
}
return randoms.ToArray();
}
}
}

Eliminate duplicates from array c#

I'm making a basic Deal or No Deal game, in doing so I have to pick 10 finalists from an array, at random, without repeats.
I have my structure and arrays set out like this
public struct People
{
public string firstname;
public string lastname;
public int age;
}
class Program
{
public static People[] People1 = new People[40];
public static People[] Finalists1 = new People[10];
public static People[] Finalist1 = new People[1];
And my finalists method set out like this
Random rand = new Random();
for (int i = 0; i < Finalists1.Length; i++)
{
num = rand.Next(0, People1.Length);
Finalists1[i].lastname = People1[num].lastname;
Finalists1[i].firstname = People1[num].firstname;
Finalists1[i].age = People1[num].age;
}
How can I eliminate duplicate entries, while maintaining 10 people in the array?
Since initial array doesn't contain duplicates, you can sort it in random order and pick up 10 top items:
Finalists1 = People1
.OrderByDescending(item => 1) // if people have some points, bonuses etc.
.ThenBy(item => Guid.NewGuid()) // shuffle among peers
.Take(10) // Take top 10
.ToArray(); // materialize as an array
If people are selected to the final are not completely random (e.g. contestant can earn points, bonuses etc.) change .OrderByDescending(item => 1), e.g.
.OrderByDescending(item => item.Bonuses)
If you don't want to use Linq, you can just draw Peoples from urn without returning:
private static Random random = new Random();
...
List<People> urn = new List<People>(People1);
for (int i = 0; i < Finalists1.Length; ++i) {
int index = random.Next(0, urn.Count);
Finalists1[i] = urn[index];
urn.RemoveAt(index);
}
You can hold a list or hash set of numbers you have already drawn. Then just roll the dice again to get another random number.
Random rand = new Random();
HashSet<int> drawnNumbers = new HashSet<int>();
for (int i = 0; i < Finalists1.Length; i++)
{
do
{
num = rand.Next(0, People1.Length);
}
while (drawnNumbers.Contains(num));
Finalists1[i] = People1[num];
}
You can change the type of Finalists1 to a HashSet, that does not allow duplicates.
Then change your loop to
while(Finalists1.Length < 10)
{
// random pick from array People1 (you don't need to create a new one)
num = rand.Next(0, People1.Length);
var toAdd = People1[num];
// add to hash-set. Object won't be added, if already existing in the set
Finalists1.Add(toAdd);
}
You probably need to override the Equals method of class People, if you really need to create a new object to add to the hash-set.
You can group people array and select distinct that way.
If you use List you can remove person from the list
`var peopleArray = new People[40];
var peopleCollection = peopleArray.GroupBy(p => new { p.age, p.firstname, p.lastname }).Select(grp => grp.FirstOrDefault()).ToList();
var finalists = new People[10];
var rand = new Random();
for (var i = 0; i < finalists.Length; i++)
{
var index = rand.Next(0, peopleCollection.Count);
var person = peopleCollection[index];
finalists[i].lastname = person.lastname;
finalists[i].firstname = person.firstname;
finalists[i].age = person.age;
peopleCollection.Remove(person);
}
shuffle and take the first 10, for example
People1.Shuffle();
Finalists1= People1.Take(10).ToArray();
you can find shuffle code from StackOverflow or search for "Fisher-Yates shuffle C#" Below methods are taken from This SO Post. Read the answers for more information on why GUID is not used etc..
public static class ThreadSafeRandom
{
[ThreadStatic] private static Random Local;
public static Random ThisThreadsRandom
{
get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
}
}
static class MyExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
Swap each selected element in People1 to with the end of the array, and decrement an end-of-array index so that you're only selecting from what's left on the next iteration.
People tempPerson = new People;
int lastElem = People1.length - 1;
for (int i = 0; i < Finalists1.Length; i++)
{
num = rand.Next(0, lastElem + 1);
Finalists1[i] = People1[num];
//swap last entry in People1 with People1[num]
tempPerson = People1[num];
People1[num] = People1[lastElem];
People1[lastElem] = tempPerson;
lastElem--;
}
Sorry if there's a syntax error, I'm mostly using Java and C# these days.
BTW You don't have to set the fields individually since each array stores objects of type Person.

Combining var and Dynamic statements

Heres a fun problem I have.
I have a function that returns a var of items;
var Items = new { sumList = SumList, ratesList = List, sum = List.Sum() };
return Items;
From a function that is dynamic:
public override dynamic GetRates()
and I return it to a function I else where and try to apply it to my code:
dynamic res = cl.mainC.GetRates();
List<double> MashkantaSumList = res.sumList;
Now when I try to apply it, it says the object doesnt exist. But if I look in the debugger the items are happily there as a generic list or what not.
How do I resolve this?
EDIT:
as per request I'll post the full code:
//virtual
public virtual dynamic TotalMashkanta(int i, double sum, double ribit, string[] discount)
{
return 0;
}
//override
public override dynamic TotalMashkanta(int i, double sum, double ribit, string[] discount)
{
double SumTemp = sum;
double monthlyRibit = ribit / 12;
Double permPayPerMont = Financial.Pmt(monthlyRibit, i, sum, 0, DueDate.EndOfPeriod);
List<double> MashkantaList = new List<double>();
List<double> MashkantaSumList = new List<double>();
for (int j = 1; j <= i; j++)
{
MashkantaList.Add(Mashkanta(j, sum, ribit, permPayPerMont) * (1 - CalcDiscount((j / 12) + 1, discount)));
SumTemp = getSum(j, sum, ribit, permPayPerMont * -1); ;
MashkantaSumList.Add(SumTemp);
}
var K_Mashkanta = new { sumList = MashkantaSumList, ratesList = MashkantaList, sum = MashkantaList.Sum() };
return K_Mashkanta;
}
//Function that calls the results
public void GetSilukinTable(string Path, string ClientID, DAL.Client client, string partner_checked, string insurance_Amount, string Premiya_Structure_Mashkanta, string Premiya_Life_Mashkanta, string Discount_Life_Mashkanta, string Loan_Period,string Loan_EndDate, string Bank, string Loan_Interest, string Loan_Amount, string Discount_Loan, string AgentNotes, string ManID)
{
BL.CalculateLogic.Companies t = BL.CalculateLogic.Companies.כלל;
if(ManID == "211") t = BL.CalculateLogic.Companies.הפניקס;
if(ManID == "207") t = BL.CalculateLogic.Companies.הראל;
if(ManID == "206") t = BL.CalculateLogic.Companies.מנורה;
if(ManID == "208") t = BL.CalculateLogic.Companies.הכשרה;
BL.CalculateLogic cl = new BL.CalculateLogic(client, t);
DateTime LoanEnd = DateTime.Now;
int months = 0;
if (DateTime.TryParse(Loan_EndDate, out LoanEnd))
months = BL.Calculating_Companies.Company.GetMonthsBetween(DateTime.Now, LoanEnd);
else
months = Int32.Parse(Loan_Period) * 12;
string[] Discount = Discount_Loan.Split('-');
dynamic res = cl.mainC.TotalMashkanta(months, Double.Parse(Loan_Amount), Double.Parse(Loan_Interest.Trim('%')), Discount);
var MashkantaSumList = res.sumList;
List<double> MashkantaList = res.ratesList;
List<double> MashkantaSumListPartner = new List<double>();
List<double> MashkantaListPartner = new List<double>();
List<double> MashkantaListSum = res.ratesList;
}
The compiler is happy about it because dynamic is compiled and checked at run time. Whatever the problem is, the types don't match. It evaluates this at run time, so you won't see issues at compile time. (Advice: use dynamic only when you really must! Else you will have this kind of problems all the time!)
I tried your code using this and it works fine:
static dynamic GetRates()
{
List<double> SumList = new List<double>();
List<double> List = new List<double>();
var Items = new { sumList = SumList, ratesList = List, sum = List.Sum() };
return Items;
}
static void Main(string[] args)
{
dynamic res = GetRates();
List<double> MashkantaSumList = res.sumList;
}

How to append N elements to a List?

I build a list:
int TOTAL = 10;
List<MyObject> myList = myOtherList.Select
(s => new MyObject
{
Prop1 = s.prop1
})
.ToList<MyObject>();
Here myList have M elements (M = myList.Count())
I need to append N elements to myList so that the myList have TOTAL elements, in other words N+M = TOTAL.
How can I accomplish it?
Just keep adding new items until you hit your desired size:
while (myList.Count < TOTAL)
{
myList.Add(NewItem);
}
LINQ approach:
myList.AddRange(Enumerable.Range(0, TOTAL - myList.Count).Select(i => myList2[i]));
just remember to check whether the myList.Count is smaller than TOTAL, and myList2 has enough elements.
This will get "n" elements from myOtherList and add them to myList.
myList.AddRange(myOtherList.Select(s => new MyObject { Prop1 = s.prop1 }).Take(n));
The nice thing about Take is that if there are not enough elements in myOtherList, it won't cause an error - it'll just get all the elements.
If count exceeds the number of elements in source, all elements of source are returned.
If count is less than or equal to zero, source is not enumerated and an empty IEnumerable is returned.
Here a full example illustratting how you can achieve this with a single line of code
list.AddRange(list2.Take(total - list.Count));
full example
internal class Program
{
private static void Main(string[] args)
{
List<MyObject> list = new List<MyObject>();
for (int i = 0; i < 5; i++)
{
list.Add(new MyObject() {Id = i}
);
}
List<MyObject> list2 = new List<MyObject>();
for (int i = 0; i < 5; i++)
{
list2.Add(new MyObject() {Id = i});
}
int total = 10;
list.AddRange(list2.Take(total - list.Count));
}
class MyObject
{
public int Id { get; set; }
}
}
You can use myList.AddRange() with a limited list in parameter or use myList.Add() in the ForEach Linq.
int TOTAL = 10;
List<MyObject> myList = something;
if (myList < TOTAL)
{
myOtherList.Select
(s => new MyObject
{
Prop1 = s.prop1
})
.ToList<MyObject>()
.Take(TOTAL - myList.Count)
.ForEach(ob => myList.Add(ob));
}
or
int TOTAL = 10;
List<MyObject> myList = something;
if (myList < TOTAL)
{
myList.AddRange(myOtherList.Select
(s => new MyObject
{
Prop1 = s.prop1
})
.ToList<MyObject>()
.Take(TOTAL - myList.Count));
}
What about:
public static IEnumerable<T> ConcatN<T>(this IEnumerable<T> source, IEnumerable<T> source2, int maxSize)
{
if (maxSize == 0)
throw new ArgumentException(
"maxSize cannot be zero", "maxSize");
var sourceCount = source.Count();
if (sourceCount > maxSize)
throw new ArgumentException(
"source has more elements than the given maxSize");
if (sourceCount + source2.Count() < maxSize)
throw new ArgumentOutOfRangeException(
"source and source2 has combined, more elements than the given maxSize");
return source.Concat(source2.Take(maxSize - sourceCount));
}
If the number of items in source exceeds the given maxSize, an exception is throwen.
If the number of items in source + source2 is less than the given maxSize, an exception is throwen.
Usage:
var number1 = Enumerable.Range(0, 10);
var number2 = Enumerable.Range(100, 10);
var number12 = number1.ConcatN(number2, 15);
Console.WriteLine("Length = {0} | Sum = {1}",
number12.Count(), number12.Sum());
// Output: "Length = 15 | Sum = 555"
Try
myList.AddRange()
this is used to add a list to your existing list. (It'll work with any IEnumerable of the same type as the existing list).
Marked down... ooooh I didn't realise we had to write entire solutions for people and there was me thinking that I was wise spending 10 years learning software development when I could have just used stack overflow instead of my brain

How to check list A contains any value from list B?

List A:
1, 2, 3, 4
List B:
2, 5
How to check if list A contains any value from list B?
e.g. something like A.contains(a=>a.id = B.id)?
If you didn't care about performance, you could try:
a.Any(item => b.Contains(item))
// or, as in the column using a method group
a.Any(b.Contains)
But I would try this first:
a.Intersect(b).Any()
I've profiled Justins two solutions. a.Any(a => b.Contains(a)) is fastest.
using System;
using System.Collections.Generic;
using System.Linq;
namespace AnswersOnSO
{
public class Class1
{
public static void Main(string []args)
{
// How to check if list A contains any value from list B?
// e.g. something like A.contains(a=>a.id = B.id)?
var a = new List<int> {1,2,3,4};
var b = new List<int> {2,5};
var times = 10000000;
DateTime dtAny = DateTime.Now;
for (var i = 0; i < times; i++)
{
var aContainsBElements = a.Any(b.Contains);
}
var timeAny = (DateTime.Now - dtAny).TotalSeconds;
DateTime dtIntersect = DateTime.Now;
for (var i = 0; i < times; i++)
{
var aContainsBElements = a.Intersect(b).Any();
}
var timeIntersect = (DateTime.Now - dtIntersect).TotalSeconds;
// timeAny: 1.1470656 secs
// timeIn.: 3.1431798 secs
}
}
}
You can Intersect the two lists:
if (A.Intersect(B).Any())
You can check if a list is inside of another list with this
var list1 = new List<int> { 1, 2, 3, 4, 6 };
var list2 = new List<int> { 2, 3 };
bool a = list1.Any(c => list2.Contains(c));
For faster and short solution you can use HashSet instead of List.
a.Overlaps(b);
Overlaps documentation
This method is an O(n) instead of O(n^2) with two lists.
I write a faster method for it can make the small one to set. But I test it in some data that some time it's faster that Intersect but some time Intersect fast that my code.
public static bool Contain<T>(List<T> a, List<T> b)
{
if (a.Count <= 10 && b.Count <= 10)
{
return a.Any(b.Contains);
}
if (a.Count > b.Count)
{
return Contain((IEnumerable<T>) b, (IEnumerable<T>) a);
}
return Contain((IEnumerable<T>) a, (IEnumerable<T>) b);
}
public static bool Contain<T>(IEnumerable<T> a, IEnumerable<T> b)
{
HashSet<T> j = new HashSet<T>(a);
return b.Any(j.Contains);
}
The Intersect calls Set that have not check the second size and this is the Intersect's code.
Set<TSource> set = new Set<TSource>(comparer);
foreach (TSource element in second) set.Add(element);
foreach (TSource element in first)
if (set.Remove(element)) yield return element;
The difference in two methods is my method use HashSet and check the count and Intersect use set that is faster than HashSet. We dont warry its performance.
The test :
static void Main(string[] args)
{
var a = Enumerable.Range(0, 100000);
var b = Enumerable.Range(10000000, 1000);
var t = new Stopwatch();
t.Start();
Repeat(()=> { Contain(a, b); });
t.Stop();
Console.WriteLine(t.ElapsedMilliseconds);//490ms
var a1 = Enumerable.Range(0, 100000).ToList();
var a2 = b.ToList();
t.Restart();
Repeat(()=> { Contain(a1, a2); });
t.Stop();
Console.WriteLine(t.ElapsedMilliseconds);//203ms
t.Restart();
Repeat(()=>{ a.Intersect(b).Any(); });
t.Stop();
Console.WriteLine(t.ElapsedMilliseconds);//190ms
t.Restart();
Repeat(()=>{ b.Intersect(a).Any(); });
t.Stop();
Console.WriteLine(t.ElapsedMilliseconds);//497ms
t.Restart();
a.Any(b.Contains);
t.Stop();
Console.WriteLine(t.ElapsedMilliseconds);//600ms
}
private static void Repeat(Action a)
{
for (int i = 0; i < 100; i++)
{
a();
}
}
Sorry, if this is irelevant, but will return list with matches using FindAll() in case you need this:
private bool IsContain(string cont)
{
List<string> ListToMatch= new List<string>() {"string1","string2"};
if (ListToMatch.ToArray().Any(cont.Contains))
{
return false;
}
else
return true;
}
And usage:
List<string> ListToCheck = new List<string>() {"string1","string2","string3","string4"};
List<string> FinalList = ListToCheck.FindAll(IsContain);
The final list contains only the matched elements string1 and string2 from list to check.
Can easy be switched to int List.
I use this to count:
int cnt = 0;
foreach (var lA in listA)
{
if (listB.Contains(lA))
{
cnt++;
}
}

Categories

Resources