Random index of a value in an array - c#

In my program, I have a function that finds the nearest index to an integer.
var indexWinnder = Array.IndexOf(scoreArray, nearestScore)
But the way that Array.IndexOf works is it finds the first match and uses that. I want a random index. Not the first. not the last. Is there any way I can do this?

There is no built-in method for that, but you could use your own method instead. My example uses a generic version of possible implementation.
class Program
{
static void Main(string[] args)
{
var arr = new int[] { 1, 2, 3, 1, 1, 5, 2, 6, 1 };
var randomIndex = RandomIndexOf(arr, 1);
Console.WriteLine(randomIndex);
Console.ReadKey();
}
static int RandomIndexOf<T>(ICollection<T> arr, T element)
{
var indexes = arr.Select((x, i) => new { Element = x, Index = i })
.Where(x => element.Equals(x.Element))
.Select(x => x.Index)
.ToList();
if (indexes.Count == 0) // there is no matching elements
{
return -1;
}
var rand = new Random();
var randomIndex = rand.Next(0, indexes.Count);
return indexes[randomIndex];
}
}

Maybe something like this is what you want:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
int[] sampleArray = new int[] { 1, 2, 3, 2, 1, 3, 1, 2, 3 };
var indices = getAllIndices(sampleArray, i => i == 2);
var rnd = new Random();
var i = rnd.Next(0, indices.Count());
var randomIndex = indices.ElementAt(i);
Console.WriteLine(randomIndex);
Console.ReadLine();
}
static IEnumerable<int> getAllIndices(int[] array, Predicate<int> predicate)
{
for (var i = 0; i < array.Length; i++)
{
if (predicate(array[i]))
yield return i;
}
}
}
HTH
Update
Don't forget to check for empty arrays, null arguments etc.

I dont know if i understood your problem right but if u just want a random index you could write a method and use:
Random rnd = new Random();
int index = rnd.Next(MinValue, MaxValue); // e.g: MinValue: 0, MaxValue: Length of the Array
and then just use that index as the array index.
Random isnt the best option if u really want a random one because it follows a specific pattern that will occur again and again and again. If u want something even more Random you could look into
RNGCryptoServiceProvider: https://www.dotnetperls.com/rngcryptoserviceprovider.
Hope this helps!

Related

Appending some value to a list or an array

so I am trying to take all the numbers from a list that are less than 5 and I try to put them in another list and print that list. I have no idea how to do that in c#. I will be very grateful if you'd help me. Thank you and here is my code:
using System;
namespace exercices
{
class Hello
{
static void Main(string[] args)
{
int[] a = { 1, 2, 3, 4, 5, 6, };
int[] b = { };
for (int i = 0; i < a.Length; i++)
{
if (a[i] < 5)
{
b.Append(a[i]);
}
}
}
}
Method 1. Just check every element
You should use List<T> collection in this method. It's possible to convert it to array later, if you need.
List<int> b = new();
foreach (int element in a)
{
if (element < 5)
{
b.Add(element);
}
}
Method 2. Use Array.FindAll<T> method
int[] b = Array.FindAll(a, element => element < 5);
Method 3. Use LINQ
int[] b = a.Where(element => element < 5).ToArray();
You could do something like this:
using System;
namespace exercices
{
class Hello
{
static void Main(string[] args)
{
int[] a = { 1, 2, 3, 4, 5, 6, };
int[] b = { };
for (int i = 0; i < a.Length;i++)
{
Int number=a[i];
if (number < 5)
b.Insert(i, number);
}
}
}
}
You can avoid explicit for loops using LINQ: the Where() method returns an IEnumerable object; you can then create an array or a list from the IEnumerable with the corresponding ToArray()/ToList() method, as shown below:
int[] a = { 1, 2, 3, 4, 5, 6};
IEnumerable<int> lessThanFiveElements = a.Where(element => element < 5);
int[] lessThanFiveArray = lessThanFiveElements.ToArray();
List<int> lessThanFiveList = lessThanFiveElements.ToList();
In case you have a console application, you can then print the resulting array/list with a Console.WriteLine() command, as shown below
Console.WriteLine(string.Join(",", lessThanFiveElements));
where I used the string.Join() in order to have comma separated elements, which accepts the separator as a first parameter and any IEnumerable as the second one.

c# Select N elements from list uniformly without shuffle

I want to select K elements uniformly from a list of N elements without shuffling a list. So the algorithm should always produce the same result.
E.g.
int N = 100;
List<int> myList = Enumerable.Range(0, N-1).ToList();
int K = 20;
Here i expect the result: 0, 5, 10, 15, 20, ...
E.g. if K == 50 I expect: 0, 2, 4, 6, 8, ...
But I don't know how to solve it if e.g. K = 53? I still want to take uniformly elements from the list, but in this case we can't simply take each second element from the list. Simple approach would be to shuffle elements and take first K elements from the list, order again the list but in that case the result of algorithm would each time produce different result and i need each time the same result.
Any help would be appreciated.
Find what the indices would be if they were all equidistant, and round to the nearest integer.
int N = 100;
List<int> myList = Enumerable.Range(0, N).ToList();
double K = 53;
List<int> solutions=new List<int>();
for (int i=1;i<=K;i++)
{
solutions.Add(myList[(int)Math.Round(i*(N/K)-1)]);
}
Console.WriteLine(solutions.Count);
Console.WriteLine(string.Join(", ", solutions));
See a sample at: DEMO
EDIT: Math.Floor works better than Math.Round as Math.Round gives the wrong solution to N=5, K=3:
Math.Round->1,2,4
Math.Floor->0,2,4
See a sample at: DEMO2
This problem is exactly same as line drawing algorithm: https://en.wikipedia.org/wiki/Line_drawing_algorithm
In your case, you want to draw a line from (0, 0) to (20, 52)
You could still shuffle the list. Please see the follwing example
var n = 100;
var k = 53;
var originalList = GetListWithNElements(n);
var shuffledList = ShuffleList(originalList);
var firstKElements = GetFirstKElements(k, shuffledList);
[...]
List<int> GetListWithNElements(int n)
{
return Enumerable.Range(0, n-1).ToList();
}
List<int> ShuffleList(List<int> originalList)
{
List<int> copy = originalList.ToList();
List<int> result = new List<int>();
Random randomGenerator = new Random(314159);
while(copy.Any())
{
int currentIndex = randomGenerator.Next(copy.Count);
result.Add(copy[currentIndex]);
copy.RemoveAt(currentIndex);
}
return result;
}
List<int> GetFirstKElements(int k, List<int> shuffledList)
{
return shuffledList.Take(k).ToList();
}
Random is initialized with a constant seed and hence will produce the very same sequence of "random" values each time, hence you will take the same elements each time.
Try following :
static void Main(string[] args)
{
const int N = 100;
{
List<int> myList = Enumerable.Range(0, N - 1).ToList();
int seed = 5;
int numberOfItems = 50;
List<int> results = TakeNItems(myList, seed, numberOfItems);
}
}
static List<int> TakeNItems(List<int> data, int seed, int numberOfItems)
{
Random rand = new Random(seed);
return data.Select((x,i) => new { x = x, index = i, rand = rand.Next()})
.OrderBy(x => x.rand)
.Take(numberOfItems)
.OrderBy(x => x.index)
.Select(x => x.x)
.ToList();
}

getting the index of an array, multiple times, then storing them in an array c#

I want to get the index of an array which I have done with Array.IndexOf(array, value). This works good with one value but I want to get every occurrence of the value and store the index's into another array. For example, the name 'tom' appears 5 times in an array, I want to find the index positions of each one.
Maybe something like this? This uses a list rather than an array, but it follows the same idea.
List<int> Indexes = new List<int>();
for (int i = 0; i < array.Count(); i++)
{
if (array[i] == "tom")
{
Indexes.Add(i);
}
}
This solution is like the previous one, but will run faster:
string value = "tom";
int[] indices = stringArray.Where(s => s != null)
.Select((s, i) => s.Equals(value) ? i: -1)
.Where(i => i >= 0)
.ToArray();
If I'm not mistaken, you can add another parameter to IndexOf(), which will let you specify where in the array to start. This should give you more or less what you need:
var indices = new List<int>();
int i = Array.IndexOf(array, val);
while(i > -1){
indices.Add(i);
i = Array.IndexOf(array, val, i+1);
}
// ... and if it is important to have the result as an array:
int[] result = indices.ToArray();
Practical example:
var array = new int[]{ 1, 2, 3, 3, 4, 5, 3, 6, 7, 8, 3};
int val = 3;
var indices = new List<int>();
int i = Array.IndexOf(array, val);
while(i > -1){
indices.Add(i);
i = Array.IndexOf(array, val, i+1);
}
// ... and if it is important to have the result as an array:
int[] result = indices.ToArray();
Edit: Just realized a while-loop may well be a lot cleaner than a for-loop for this.
Edit 2: Due to popular demand (see comment below), here`s the original beautiful non-basic for-loop, re-introduced just for your reading pleasure:
for(int i = Array.IndexOf(array, val); i > -1; i = Array.IndexOf(array, val, i+1)){
indices.Add(i);
}
Could create an extension method to do it
namespace Extensions
{
public static class ArrayExtension
{
public static IEnumerable<int> GetIndicesOf<T>(this T[] target, T val, int start = 0)
{
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = start; i < target.Length; i++)
{
if (comparer.Equals(target[i], val))
{
yield return i;
}
}
}
}
}
Add the using statement for your namespace with the extension method using Extensions; in the file you want to call it in.
Then to call it just do the following to get the indices.
IEnumerable<int> indices = array.GetIndicesOf(value);
or to get an array just do
int[] indicies = array.GetIndicesOf(value).ToArray();
You can use LINQ's Select overload which uses elements index as well, like:
var indices = stringArray.Select((s, i) => new {Value = s, Index = i})
.Where(r => r.Value == "tom")
.Select(r => r.Index);

How to generate random INT that cannot be a value in a List<int>?

I'm trying to do the following.
Let's say I have a List, and I want to generate a new int in a specific range, but the value cannot be already defined in the List.
List<int> PredefinedIntsList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
int NewRandomValue = rnd.Next(0, 10);
rnd.Next (obviously) comes up with 1, 3, 4, 8 or 9. But I ONLY want it to return 2, 5, 6, 7 or 10.
Any ideas?
As always, LINQ is your friend:
[TestMethod]
public void RandomTest()
{
var except = new[] {1, 2, 3, 5};
GetRandomExcept(1, 5, except).Should().Be(4);
}
private static int GetRandomExcept(int minValue, int maxValue, IEnumerable<int> except)
{
return GetRandoms(minValue, maxValue).Except(except).First();
}
private static IEnumerable<int> GetRandoms(int minValue, int maxValue)
{
var random = new Random();
while (true) yield return random.Next(minValue, maxValue);
}
Just keep in mind that you never should call GetRandoms().ToArray() or .Max() or .OrderBy() and so on, because you will get an endless loop and generate randoms forever.
What you can do though, is call GetRandoms().Take(10).ToArray() to get the next 10 random integers in an array.
Try examining if you can use the contains() method of List class... Simple solution would be to just generate values and checking contains and rejecting values that are already in the List until you get a value that is not. Also it might be more appropriate to use the Set Class because a set can not contain two elements that are equal.
What you need to do sample from other set. Lets say P is your predefinedIntsList and A is {1,2...9}.
You need to create a list, N = A-P. You will randomly sample from this set of numbers. It can be written more elegantly I suppose but see below example.
class Program
{
static void Main(string[] args)
{
List<int> predefinedIntsList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
List<int> newIntsList = new List<int>();
int upperBound = 10;
for (int i = 0; i < upperBound; i++)
{
if (!predefinedIntsList.Contains(i))
{
newIntsList.Add(i);
}
}
for (int i = 0; i < 20; i++)
{
int newRandomValueIndex = rnd.Next(0, newIntsList.Count);
int newRandomValue = newIntsList[newRandomValueIndex];
Console.WriteLine(newRandomValue);
}
}
}
Output
2
0
6
7
5
5
5
7
0
7
6
6
5
5
5
0
6
7
0
7
rnd.Next (obviously) comes up with 1, 3, 4, 8 or 9. But I ONLY want it
to return 2, 5, 6, 7 or 10.
Obviously not, it will return value that is either 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9 :P. If you're looking to include 10, and not 0, you want .Next(1, 11)
There are two choices that can work: either try generating values until you succeed, or if the range is small enough, generate the range, mark elemens that can't be picked, and sort them to last ones, and then pick random between [0..possibleToPickElementsCount]
The first approach would look something like this:
public static class RandomExtensions
{
public static int Next(this Random random,
int minInclusive,
int maxExclusive,
IList<int> values)
{
// this will crash if values contains
// duplicate values.
var dic = values.ToDictionary(val => val);
// this can go into forever loop,
// think about this a bit.
for(;;){
var randomNumber= random.Next(minInclusive, maxExclusive);
if(!dic.ContainsKey(randomNumber))
return randomNumber;
}
}
}
The second approach is this, however it's only to give you an idea:
public static class RandomExtensions
{
class NormalizedPair
{
public int Value {get;set;}
public PairStatus Status {get;set;}
public NormalizedPair(int value){
Value = value;
}
public enum PairStatus {
Free,
NotFree
}
}
private static Random _internalRandom = new Random();
public static int Next(this Random random,
int minInclusive,
int maxExclusive,
IList<int> values)
{
var elements = maxExclusive - minInclusive;
var normalizedArr = new NormalizedPair[elements];
var normalizedMinInclusive = 0;
var normalizedMaxExclusive = maxExclusive - minInclusive;
var normalizedValues = values
.Select(x => x - minInclusive)
.ToList();
for(var j = 0; j < elements; j++)
{
normalizedArr[j] = new NormalizedPair(j){
Status = NormalizedPair.PairStatus.Free
};
}
foreach(var val in normalizedValues)
normalizedArr[val].Status = NormalizedPair.PairStatus.NotFree;
return normalizedArr
.Where(y => y.Status == NormalizedPair.PairStatus.Free) // take only free elements
.OrderBy(y => _internalRandom.Next()) // shuffle the free elements
.Select(y => y.Value + minInclusive) // project correct values
.First(); // pick first.
}
}
Or, if you're fan of sets:
public static int Next2(this Random random,
int minInclusive,
int maxExclusive,
IList<int> values)
{
var rangeSet = new HashSet<int>(
Enumerable.Range(
minInclusive,
maxExclusive - minInclusive));
// remove gibberish
rangeSet.ExceptWith(new HashSet<int>(values));
// this can be swapped out with
// yates shuffle algorithm
return rangeSet.OrderBy(x => _internalRandom.Next())
.First();
}
Rather than write logic to determine whether the random number has already been selected I prefer to generate a second list with the items in a random order.
That is easy to do with LINQ
var originalList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
var secondList = originalList.OrderBy(x=>rnd.NextDouble());
The call to rnd.NextDouble returns a random seed that is used by the OrderBy method to sort each int value.
When I need to run thought the randomized items more than once, with a new sort order each time I walk the list, I will use a Queue to store the items. Then dequeue the items as needed. When the queue is empty it's time to refill.
private Queue<int> _randomizedItems;
private void RandomTest()
{
var originalList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
var temp = originalList.OrderBy(r=>rnd.NextDouble());
_randomizedItems = new Queue<int>(temp);
while (_randomizedItems.Count >0)
{
MessageBox.Show(_randomizedItems.Dequeue().ToString());
}
}

Deleting A Specified Element In An Array Using Random Class

class Check
{
public static void Main()
{
int[] arr1 = new int[] { 1, 2, 3 };
Console.WriteLine("The Number That Left Us Is");
Random rnd = new Random();
int r = rnd.Next(arr1.Length);
int Left = (arr1[r]);
Console.WriteLine(Left);
}
}
If 2 is generated i want the 2 to be removed and the remaining i have to be left with should be 1 and 3.
Can anyone help. Can it be done in array.
Arrays can not be re-sized, one you set them they are that size forever.
The "best" option is use a List<int> instead of an int[]
class Check
{
public static void Main()
{
List<int> arr1 = List<int>int[] { 1, 2, 3 };
Console.WriteLine("The Number That Left Us Is");
Random rnd = new Random();
int r = rnd.Next(arr1.Length);
int Left = (arr1[r]);
arr1.RemoveAt(r);
Console.WriteLine(Left);
}
}
To actually create a new array of one size smaller will take more code.
class Check
{
public static void Main()
{
int[] arr1 = int[] { 1, 2, 3 };
Console.WriteLine("The Number That Left Us Is");
Random rnd = new Random();
int r = rnd.Next(arr1.Length);
int Left = (arr1[r]);
int oldLength = arr1.Length;
arrTmp = arr1;
arr1 = new int[oldLength - 1];
Array.Copy(arrTmp, arr1, r);
Array.Copy(arrTmp, r+1, arr1, r, oldLength - r - 1);
Console.WriteLine(Left);
}
}
You mention "You gotta stick with the arrays", it is VERY easy to turn the list in to an array
class Check
{
public static void Main()
{
List<int> arr1 = List<int>int[] { 1, 2, 3 };
Console.WriteLine("The Number That Left Us Is");
Random rnd = new Random();
int r = rnd.Next(arr1.Length);
int Left = (arr1[r]);
arr1.RemoveAt(r);
Console.WriteLine(Left);
SomeFunctionThatTakesAnArrayAsAnArgument(arr1.ToArray());
}
}
Arrays cannot be resized, if you want to remove items use a List<T>.
However, you can create a new one. If you want to keep all items but one at the random index:
arr1 = arr1.Where((i, index) => index != r).ToArray();
With a list you can use RemoveAt which is more efficient than creating arrays:
var list = new List<int> { 1, 2, 3 };
list.RemoveAt(r);
Arrays can't be dynamically resized in .NET, so you can't really 'remove' an item from an array (you could set it to 0, but I don't think that's what you want).
Try using a List<int> instead:
List<int> list = new List<int>() { 1, 2, 3 };
Console.WriteLine("The Number That Left Us Is");
Random rnd = new Random();
int r = rnd.Next(list.Count);
int Left = (list[r]);
list.Remove(Left);
Console.WriteLine(Left);
Try to following it removes the item from the list and creates a new array without the specific item.
static void Main(string[] args)
{
int[] arr1 = new int[] { 1, 2, 3 };
Console.WriteLine("The Number That Left Us Is");
Random rnd = new Random();
int r = rnd.Next(arr1.Length);
// Create a new array except the item in the specific location
arr1 = arr1.Except(new int[]{arr1[r]}).ToArray();
int Left = (arr1[r]);
Console.WriteLine(Left);
}

Categories