C# Negative Number Result - c#

I am trying to create randomly 20 numbers between 1 and 100. I want to achieve this with declaring a delegate and event. I check if any of the 20 numbers is bigger than 50, then I multiply the nubmers, if not sum all numbers.
using System;
using System.Linq;
namespace ConsoleApp
{
class Program
{
// Delegate tanımla
public delegate int MyDelegate(int[] arr);
public event MyDelegate OnResultCalculated;
static void Main(string[] args)
{
Program p = new Program();
// 1-100 arasında 20 adet rastgele sayı üret
Random rnd = new Random();
int[] numbers = Enumerable.Range(1, 100).OrderBy(x => rnd.Next()).Take(20).ToArray();
// Eğer 50'den büyük sayı varsa çarp, yoksa topla
int result = numbers.Any(x => x > 50) ? p.Multiply(numbers) : p.Add(numbers);
// Event'i tetikle
p.OnResultCalculated?.Invoke(numbers);
Console.WriteLine(result);
Console.ReadLine();
}
public int Multiply(int[] arr)
{
return arr.Aggregate(1, (a, b) => a * b);
}
public int Add(int[] arr)
{
return arr.Sum();
}
}
}
The code is working but sometimes I get a negative result. What is the problem?

Like the comments tell you, it is possible that int is not big enough for your result. The highest possible value is 100^20. But this is just theoretical. So your int is to small for such numbers. The max. possible value of int is 2.147.483.647. If you calculate this value +1, it will result in the smallest value of int: -2.147.483.648. This is the reason why you may have a negative value: If the value is higher than the max. value of int, it will start from the smallest again.
One possible way is to reduce the random value, like you did. If you change int to long, the possibility of running into this issue is smaller, but still there.
If you combine both it will be a possible solution. Change the result type to long and just use 5 values. 100^5 is smaller than long.MaxValue.

Related

Generating random number between two numbers and ignoring numbers from the set

I am looking to generate a random number between two 2 digit numbers and if the random number is one of the numbers from the set, then ignore this number and re-randomize it. I am facing issue with the looping part. That is, if the number from the list is found twice, it would not loop again.
How do I achieve this?
public float[] nums_to_ignore = new float[] {64.0f, 80.0f, 90.0f, 92.0f, 85.0f, 73.0f, 86.0f};
public float random_num;
void Start()
{
random_num = Random.Range(60.0f, 95.0f);
for(int i = 0; i<nums_to_ignore.Length;i++)
{
if(random_num == nums_to_ignore[i])
{
random_num = Random.Range(60.0f, 95.0f); //Keep looping till you get a number not from the list
}
}
}
It would be more efficient to use a HashSet<float> for your numbers to ignore, as HashSet<T>.Contains has O(1) complexity.
You can continue to repeat your random generation using a do..while loop:
public HashSet<float> nums_to_ignore
= new HashSet<float> { 64.0f, 80.0f, 90.0f, 92.0f, 85.0f, 73.0f, 86.0f };
public float random_num;
void Start()
{
do random_num = Random.Range(60.0f, 95.0f);
while (nums_to_ignore.Contains(random_num));
}
The above would still work if you keep nums_to_ignore as a float[], but Enumerable.Contains has O(n) complexity.
Update
As pointed out by #derHugo, float values are approximations, and equality comparisons may yield incorrect results.
Fortunately, Unity has a method that is designed for comparing floats in this way: Mathf.Approximately:
void Start()
{
do random_num = Random.Range(60.0f, 95.0f);
while (nums_to_ignore.Any(n => Mathf.Approximately(n, random_num)));
}
Unfortunately, this means that GetHashCode cannot be used reliably with float, and the benefits of hashing cannot be achieved.
id go about it like this
Random random = new Random();
List<int> numbers = new List<int>();
int min = 60, max = 95;
for (int i = 0; i <= 100; i++)
{
int num;
do
num = random.Next(min,max);
while (numbers.Contains(num));
numbers.Add(num);
}
you can replace random.next by unitys Random.Range, i was sleeping on you using unity. the for i 0 to 100 loop was just there as an example how to fill the list you can easily replace it with a methode instead

what i need to make a random generated array with n numbers? [duplicate]

How do I generate a random integer in C#?
The Random class is used to create random numbers. (Pseudo-random that is of course.).
Example:
Random rnd = new Random();
int month = rnd.Next(1, 13); // creates a number between 1 and 12
int dice = rnd.Next(1, 7); // creates a number between 1 and 6
int card = rnd.Next(52); // creates a number between 0 and 51
If you are going to create more than one random number, you should keep the Random instance and reuse it. If you create new instances too close in time, they will produce the same series of random numbers as the random generator is seeded from the system clock.
The question looks very simple but the answer is bit complicated. If you see almost everyone has suggested to use the Random class and some have suggested to use the RNG crypto class. But then when to choose what.
For that we need to first understand the term RANDOMNESS and the philosophy behind it.
I would encourage you to watch this video that I made which goes in depth in the philosophy of RANDOMNESS using C# https://www.youtube.com/watch?v=tCYxc-2-3fY
First thing let us understand the philosophy of RANDOMNESS. When we tell a person to choose between RED, GREEN and YELLOW what happens internally. What makes a person choose RED or YELLOW or GREEN?
Some initial thought goes into the persons mind which decides his choice, it can be favorite color , lucky color and so on. In other words some initial trigger which we term in RANDOM as SEED.This SEED is the beginning point, the trigger which instigates him to select the RANDOM value.
Now if a SEED is easy to guess then those kind of random numbers are termed as PSEUDO and when a seed is difficult to guess those random numbers are termed SECURED random numbers.
For example a person chooses is color depending on weather and sound combination then it would be difficult to guess the initial seed.
Now let me make an important statement:-
*“Random” class generates only PSEUDO random number and to generate SECURE random number we need to use “RNGCryptoServiceProvider” class.
Random class takes seed values from your CPU clock which is very much predictable. So in other words RANDOM class of C# generates pseudo random numbers , below is the code for the same.
Random random = new Random();
int randomNumber = random.Next();
While the RNGCryptoServiceProvider class uses OS entropy to generate seeds. OS entropy is a random value which is generated using sound, mouse click, and keyboard timings, thermal temp etc. Below goes the code for the same.
using (RNGCryptoServiceProvider rg = new RNGCryptoServiceProvider())
{
byte[] rno = new byte[5];
rg.GetBytes(rno);
int randomvalue = BitConverter.ToInt32(rno, 0);
}
To understand OS entropy see this video of mine starting at 14:30 https://www.youtube.com/watch?v=tCYxc-2-3fY where the logic of OS entropy is explained. So putting in simple words RNG Crypto generates SECURE random numbers.
Every time you do new Random() it is initialized. This means that in a tight loop you get the same value lots of times. You should keep a single Random instance and keep using Next on the same instance.
//Function to get random number
private static readonly Random getrandom = new Random();
public static int GetRandomNumber(int min, int max)
{
lock(getrandom) // synchronize
{
return getrandom.Next(min, max);
}
}
Beware that new Random() is seeded on current timestamp.
If you want to generate just one number you can use:
new Random().Next( int.MinValue, int.MaxValue )
For more information, look at the Random class, though please note:
However, because the clock has finite resolution, using the parameterless constructor to create different Random objects in close succession creates random number generators that produce identical sequences of random numbers
So do not use this code to generate a series of random number.
Random r = new Random();
int n = r.Next();
I wanted to add a cryptographically secure version:
RNGCryptoServiceProvider Class (MSDN or dotnetperls)
It implements IDisposable.
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
byte[] randomNumber = new byte[4];//4 for int32
rng.GetBytes(randomNumber);
int value = BitConverter.ToInt32(randomNumber, 0);
}
create a Random object
Random rand = new Random();
and use it
int randomNumber = rand.Next(min, max);
you don't have to initialize new Random() every time you need a random number, initiate one Random then use it as many times as you need inside a loop or whatever
You could use Jon Skeet's StaticRandom method inside the MiscUtil class library that he built for a pseudo-random number.
using MiscUtil;
...
for (int i = 0; i < 100;
Console.WriteLine(StaticRandom.Next());
I've tried all of these solutions excluding the COBOL answer... lol
None of these solutions were good enough. I needed randoms in a fast for int loop and I was getting tons of duplicate values even in very wide ranges. After settling for kind of random results far too long I decided to finally tackle this problem once and for all.
It's all about the seed.
I create a random integer by parsing out the non-digits from Guid, then I use that to instantiate my Random class.
public int GenerateRandom(int min, int max)
{
var seed = Convert.ToInt32(Regex.Match(Guid.NewGuid().ToString(), #"\d+").Value);
return new Random(seed).Next(min, max);
}
Update: Seeding isn't necessary if you instantiate the Random class once. So it'd be best to create a static class and call a method off that.
public static class IntUtil
{
private static Random random;
private static void Init()
{
if (random == null) random = new Random();
}
public static int Random(int min, int max)
{
Init();
return random.Next(min, max);
}
}
Then you can use the static class like so..
for(var i = 0; i < 1000; i++)
{
int randomNumber = IntUtil.Random(1,100);
Console.WriteLine(randomNumber);
}
I admit I like this approach better.
The numbers generated by the inbuilt Random class (System.Random) generates pseudo random numbers.
If you want true random numbers, the closest we can get is "secure Pseudo Random Generator" which can be generated by using the Cryptographic classes in C# such as RNGCryptoServiceProvider.
Even so, if you still need true random numbers you will need to use an external source such as devices accounting for radioactive decay as a seed for an random number generator. Since, by definition, any number generated by purely algorithmic means cannot be truly random.
Modified answer from here.
If you have access to an Intel Secure Key compatible CPU, you can generate real random numbers and strings using these libraries: https://github.com/JebteK/RdRand and https://www.rdrand.com/
Just download the latest version from here, include Jebtek.RdRand and add a using statement for it. Then, all you need to do is this:
// Check to see if this is a compatible CPU
bool isAvailable = RdRandom.GeneratorAvailable();
// Generate 10 random characters
string key = RdRandom.GenerateKey(10);
// Generate 64 random characters, useful for API keys
string apiKey = RdRandom.GenerateAPIKey();
// Generate an array of 10 random bytes
byte[] b = RdRandom.GenerateBytes(10);
// Generate a random unsigned int
uint i = RdRandom.GenerateUnsignedInt();
If you don't have a compatible CPU to execute the code on, just use the RESTful services at rdrand.com. With the RdRandom wrapper library included in your project, you would just need to do this (you get 1000 free calls when you signup):
string ret = Randomizer.GenerateKey(<length>, "<key>");
uint ret = Randomizer.GenerateUInt("<key>");
byte[] ret = Randomizer.GenerateBytes(<length>, "<key>");
As described in other answers, a good secured approach would be to use a secure cryptographic generator. All examples here show the usage of RNGCryptoServiceProvider which is writing a long code compared to the solution I suggest.
Use RandomNumberGenerator which is written on top of cryptography API’s. It is as secure as RNGCryptoServiceProvider and same randomness.
// Gives a random number for the integer range.
// You can simply update the parameters as your needs.
RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
Just as a note for future reference.
If you're using .NET Core, multiple Random instances aren't as dangerous as before. I'm aware that this question is from 2010, but since this question is old but has some attraction, I think it's a good thing to document the change.
You may refer to this question I made a while back:
Did Microsoft change Random default seed?
Basically, they have changed the default seed from Environment.TickCount to Guid.NewGuid().GetHashCode(), so if you create 2 instances of Random it shouldn't display the same numbers (1:4 billion).
You can see the file diffs from .NET Framework/.NET Core (2.0.0+) here: https://github.com/dotnet/coreclr/pull/2192/commits/9f6a0b675e5ac0065a268554de49162c539ff66d
It isn't as safe as RNGCryptoServiceProvider, but at least it won't give you weird results.
By #Enigmativity:
This is now out-of-date. There was a considerable backlash against using Guids. The code is now Interop.GetRandomBytes((byte*)&result, sizeof(int));
This is the class I use. Works like RandomNumber.GenerateRandom(1, 666)
internal static class RandomNumber
{
private static Random r = new Random();
private static object l = new object();
private static Random globalRandom = new Random();
[ThreadStatic]
private static Random localRandom;
public static int GenerateNewRandom(int min, int max)
{
return new Random().Next(min, max);
}
public static int GenerateLockedRandom(int min, int max)
{
int result;
lock (RandomNumber.l)
{
result = RandomNumber.r.Next(min, max);
}
return result;
}
public static int GenerateRandom(int min, int max)
{
Random random = RandomNumber.localRandom;
if (random == null)
{
int seed;
lock (RandomNumber.globalRandom)
{
seed = RandomNumber.globalRandom.Next();
}
random = (RandomNumber.localRandom = new Random(seed));
}
return random.Next(min, max);
}
}
While this is okay:
Random random = new Random();
int randomNumber = random.Next()
You'd want to control the limit (min and max mumbers) most of the time. So you need to specify where the random number starts and ends.
The Next() method accepts two parameters, min and max.
So if i want my random number to be between say 5 and 15, I'd just do
int randomNumber = random.Next(5, 16)
I wanted to demonstrate what happens when a new random generator is used every time. Suppose you have two methods or two classes each requiring a random number. And naively you code them like:
public class A
{
public A()
{
var rnd=new Random();
ID=rnd.Next();
}
public int ID { get; private set; }
}
public class B
{
public B()
{
var rnd=new Random();
ID=rnd.Next();
}
public int ID { get; private set; }
}
Do you think you will get two different IDs? NOPE
class Program
{
static void Main(string[] args)
{
A a=new A();
B b=new B();
int ida=a.ID, idb=b.ID;
// ida = 1452879101
// idb = 1452879101
}
}
The solution is to always use a single static random generator. Like this:
public static class Utils
{
public static readonly Random random=new Random();
}
public class A
{
public A()
{
ID=Utils.random.Next();
}
public int ID { get; private set; }
}
public class B
{
public B()
{
ID=Utils.random.Next();
}
public int ID { get; private set; }
}
Numbers calculated by a computer through a deterministic process, cannot, by definition, be random.
If you want a genuine random numbers, the randomness comes from atmospheric noise or radioactive decay.
You can try for example RANDOM.ORG (it reduces performance)
For strong random seed I always use CryptoRNG and not Time.
using System;
using System.Security.Cryptography;
public class Program
{
public static void Main()
{
var random = new Random(GetSeed());
Console.WriteLine(random.Next());
}
public static int GetSeed()
{
using (var rng = new RNGCryptoServiceProvider())
{
var intBytes = new byte[4];
rng.GetBytes(intBytes);
return BitConverter.ToInt32(intBytes, 0);
}
}
}
Random rand = new Random();
int name = rand.Next()
Put whatever values you want in the second parentheses
make sure you have set a name by writing prop and double tab to generate the code
Random random = new Random ();
int randomNumber = random.Next (lowerBound,upperBound);
If you want a CSRNG to generate random numbers between a min and max, this is for you. It will initialize Random classes with secure random seeds.
class SecureRandom : Random
{
public static byte[] GetBytes(ulong length)
{
RNGCryptoServiceProvider RNG = new RNGCryptoServiceProvider();
byte[] bytes = new byte[length];
RNG.GetBytes(bytes);
RNG.Dispose();
return bytes;
}
public SecureRandom() : base(BitConverter.ToInt32(GetBytes(4), 0))
{
}
public int GetRandomInt(int min, int max)
{
int treashold = max - min;
if(treashold != Math.Abs(treashold))
{
throw new ArithmeticException("The minimum value can't exceed the maximum value!");
}
if (treashold == 0)
{
throw new ArithmeticException("The minimum value can't be the same as the maximum value!");
}
return min + (Next() % treashold);
}
public static int GetRandomIntStatic(int min, int max)
{
int treashold = max - min;
if (treashold != Math.Abs(treashold))
{
throw new ArithmeticException("The minimum value can't exceed the maximum value!");
}
if(treashold == 0)
{
throw new ArithmeticException("The minimum value can't be the same as the maximum value!");
}
return min + (BitConverter.ToInt32(GetBytes(4), 0) % treashold);
}
}
I will assume that you want a uniformly distributed random number generator like below. Random number in most of programming language including C# and C++ is not properly shuffled before using them. This means that you will get the same number over and over, which isn't really random. To avoid to draw the same number over and over, you need a seed. Typically, ticks in time is ok for this task. Remember that you will get the same number over and over if you are using the same seed every time. So try to use varying seed always. Time is a good source for seed because they chage always.
int GetRandomNumber(int min, int max)
{
Random rand = new Random((int)DateTime.Now.Ticks);
return rand.Next(min, max);
}
if you are looking for random number generator for normal distribution, you might use a Box-Muller transformation. Check the answer by yoyoyoyosef in Random Gaussian Variable Question. Since you want integer, you have to cast double value to integer at the end.
Random rand = new Random(); //reuse this if you are generating many
double u1 = 1.0-rand.NextDouble(); //uniform(0,1] random doubles
double u2 = 1.0-rand.NextDouble();
double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) *
Math.Sin(2.0 * Math.PI * u2); //random normal(0,1)
double randNormal =
mean + stdDev * randStdNormal; //random normal(mean,stdDev^2)
Random Gaussian Variables
Sorry, OP indeed requires a random int value, but for the simple purpose to share knowledge if you want a random BigInteger value you can use following statement:
BigInteger randomVal = BigInteger.Abs(BigInteger.Parse(Guid.NewGuid().ToString().Replace("-",""), NumberStyles.AllowHexSpecifier));
There are a number utility functions or services that are better cached in the same way that System.Random should be, so it lends itself to a generic implementation:
static public class CachedService<T> where T : new() {
static public T Get { get; } = new T();
}
To use for random (or similar):
CachedService<System.Random>.Get.Next(999);
Why not use int randomNumber = Random.Range(start_range, end_range) ?
Use one instance of Random repeatedly
// Somewhat better code...
Random rng = new Random();
for (int i = 0; i < 100; i++)
{
Console.WriteLine(GenerateDigit(rng));
}
...
static int GenerateDigit(Random rng)
{
// Assume there'd be more logic here really
return rng.Next(10);
}
This article takes a look at why randomness causes so many problems, and how to address them.
http://csharpindepth.com/Articles/Chapter12/Random.aspx
Random r=new Random();
int Numbers=r.next(min value, max value);
Try these simple steps to create random numbers:
Create function:
private int randomnumber(int min, int max)
{
Random rnum = new Random();
return rnum.Next(min, max);
}
Use the above function in a location where you want to use random numbers. Suppose you want to use it in a text box.
textBox1.Text = randomnumber(0, 999).ToString();
0 is min and 999 is max. You can change the values to whatever you want.
I always have methods that generate random numbers which help for various purposes. I hope this may help you too:
public class RandomGenerator
{
public int RandomNumber(int min, int max)
{
var random = new Random();
return random.Next(min, max);
}
public string RandomString(int size, bool lowerCase)
{
var builder = new StringBuilder();
var random = new Random();
char ch;
for (int i = 0; i < size; i++)
{
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
builder.Append(ch);
}
if (lowerCase)
return builder.ToString().ToLower();
return builder.ToString();
}
}
Quick and easy for inline, use bellow code:
new Random().Next(min, max);
// for example unique name
strName += "_" + new Random().Next(100, 999);

C# multiplying a method containing an instance of Random.next

I'm running this class and method
public class DiceRoll
{
static Random _r =new Random();
public int Random6()
{
int n = _r.Next(1, 7);
return n;
}
}
I'm trying to use it inside an if statement in another class like so:
if (input == "d6")
{
int dice = diceRoll.Random6();
Console.WriteLine(dice);
}
my question is this. If I call diceRoll.Random6(); with a multiplier like this diceRoll.Random6() * 2; Is it creating two random numbers and adding them or is it just taking one and multiplying it by two?
I think it's probably the latter but I just wanted to check before I got into convoluted programming.
If it is the latter is there a way to make my program call multiple instances of Random6 without going diceRoll.Random6() + diceRoll.Random6(); That works ok for doing it twice but if I want to do it four or six times it'll become inelegant.
Thanks in advance.
Call the method from within a loop
int result = 0;
for(int i = 0; i < 10; i++)
{
result += Random6();
}
It just generates 1 random number then multiplies it by 2
you can just do it in a loop, or if you want to be fancy
var sum = Enumerable.Range(1, 6).Sum(x => diceRoll.Random6())
which you might want to break down into...
var rolls = Enumerable.Range(1, 6).Select(x => diceRoll.Random6()).ToList();
var sumOfRolls = rolls.Sum();
that way you have the sum and a list of each individual roll.

Reading out dice

We need to make some small program for school that rolls 5 dices and see if you get a three of a kind with it, if so, increase points etc.
The problem isnt to reading out the dice, I have the knowledge to get it done, but I want it to be a little efficient, not a ugly piece of code that takes up half a page. I have found ways to filter out the the duplicates in an array, but not the other way around. It rolls with 5 dices, so its an array with 5 numbers, is there like a built in function or a nice, efficient way of returning the number that has been rolled three times or return null if none of the number are rolled three times?
Hope anyone can push me in the right direction. :)
You can do it easily and succinctly with LINQ:
var diceRolls = new[] {1, 3, 3, 3, 4};
var winningRolls = diceRolls.GroupBy(die => die).Select(groupedRoll => new {DiceNumber = groupedRoll.Key, Count = groupedRoll.Count()}).Where(x => x.Count >= 3).ToList();
What this is doing is grouping the rolls by the roll number ("Key") and the count of occurrences of that roll. Then, it's selecting any rolls that have a count greater than or equal to 3. The result will be a List containing your winning rolls.
One approach is to store a 6-element array containing the count of how many dice have that face. Loop through the 5 dice and increment the appropriate face's total count.
var rolls = new List<Roll>();
// run as many rolls as you want. e.g.:
rolls.Add(new Roll(5));
var threeOfAKindRolls = rolls.Where(r => r.HasThreeOfAKind());
public class Roll
{
public Roll( int diceCount )
{
// Do your random generation here for the number of dice
DiceResults = new int[0]; // your results.
ResultCounts = new int[6]; // assuming 6 sided die
foreach (var diceResult in DiceResults)
{
ResultCounts[diceResult]++;
}
}
public int[] DiceResults { get; private set; }
public int[] ResultCounts { get; private set; }
public bool HasThreeOfAKind()
{
return ResultCounts.Any(count => count >= 3);
}
}
This code can be shortened somewhat if you don't need the result counts to perform other tests on the results:
public Roll( int diceCount )
{
// Do your random generation here for the number of dice
DiceResults = new int[0]; // your results.
}
public bool HasThreeOfAKind()
{
ResultCounts = new int[6]; // assuming 6 sided die
foreach (var diceResult in DiceResults)
{
// Increment and shortcut if the previous value was 2
if( (ResultCounts[diceResult]++) == 2) return true;
}
return false;
}
Given what you are describing your answer as looking like it sounds like you're trying to do a massive comparison. That's the wrong approach.
Pretend it's 20 dice rather than 5, a good answer will work just as well in a larger case.
I would use something like the following:
public int? WinningRoll(IEnumerable<int> rolls)
{
int threshold = rolls.Count() / 2;
var topRollGroup = rolls.GroupBy(r => r)
.SingleOrDefault(rg => rg.Count() > threshold);
if (topRollGroup != null)
return topRollGroup.Key;
return null;
}
This will work with any number of rolls, not just 5, so if you had 10 rolls, if 6 of them were the same value, that value would be returned. If there is no winning roll, null is returned.

C# Look up dictionary

I'm currently trying to create a program that estimates location based on signal strength. The signal strength value is an int and then i need a lookup dictionary with ranges.
So I would have something like:
Signal Strenth Position
0-9 1
10-19 2
20-29 3
and then I would want to look up what position a signal strength relates to, for example 15 would relate to position 2.
I know I can just have a load of if statements but is there a good way to do this using some sort of lookup dictionary?
If you have arbitrary but consecutive ranges you can use an array of the upper bounds and perform a binary search to get the position:
// Definition of ranges
int[] ranges = new int[] { 9, 19, 29 };
// Lookup
int position = Array.BinarySearch(ranges, 15);
if (position < 0)
position = ~position;
// Definition of range names
string[] names = new string[] { "home", "street", "city", "far away" };
Console.WriteLine("Position is: {0}", names[position]);
Array.BinarySearch returns the index of the item in the array if it exists (array must be sorted obviously) or the bitwise inverted index where the item should be inserted to keep the array sorted.
What about :
int position = signalStrength / 10 + 1;
Kindness,
Dan
When you want to use Dictionary, you need at least some special key type to deal with the ranges. KeyType can be abstract and two derived types KeyTypeRange(int int) and KEyTypeSearch( int). Some special comparison logic must be implemented to compare an KeyTypeSearch with an KeyTypeRange.
SortedDictionary<KeyType,int> lookup = new Dictionary<int,int>();
lookup.Add( new KeyTypeRange(1,10),1);
lookup.Add( new KeyTypeRange(11,20),2);
lookup.Add( new KeyTypeRange(21,30),3);
lookup.TryGetValue( new KeyTypeSearch(15) );
It shows a possible solution to use different esearch keys and key values in dictionaries. But this seems to be Overkill for this problem. This problem is solved best by the BinarySearch solution.
Good is a function of purpose. All of the above solutions work well presuming that any given range is a small number of integers. Otherwise you may want to use whatever the real world math function is to determine your group. For instance, for the example given, your answer function would be x % 10 + 1; That'll run much faster than a dictionary.
You could do a Dictionary, where the first int is the signal strength and the second int is the position. You would need to add an entry for every value in the range (so, one for signal strength 0, position 1, signal strength 1, position 1, etc.), but it would be a very quick, single line lookup.
Something like:
Dictionary<int, int> values;
values = new Dictionary<int, int>();
values[0] = 1;
values[1] = 1;
...
values[29] = 3;
and then, to access it:
Console.WriteLine(values[27].ToString());
For future expansion i would do 2 dictionaries.
Just in case those rates change
so a
dictionary<string,dictionary<int,int>>
or just use custom classes
the string would be static strings like low med, high, then you can change the ranges in your foreach initilixing the initial values
One solution would be to use a simple list, where each position in the list represents a different position that you're scanning for. In code, it might look something like this (assuming that all position numbers are sequential):
** Note: I have not actually run this code to make sure it works as-is... you might also need to implement an IEqualityComparer on Range in order for the IndexOf operation to return the proper position:
public class Controller
{
List m_positions = new List();
public void LoadPositions()
{
m_positions.Add(new Range(0, 9));
m_positions.Add(new Range(10, 19));
m_positions.Add(new Range(20, 29));
}
public int GetPosition (int signal)
{
Range range = m_positions.Single(a => IsBetween(signal, a.Min, a.Max));
return m_positions.IndexOf(range);
}
private static bool IsBetween (int target, int min, int max)
{
return min = target;
}
}
It's probably pretty self-explanatory, but to avoid any confusion, here's what the Range class might look like:
public class Range
{
public Range(int min, int max)
{
this.Min = min;
this.Max = max;
}
public int Min
{
get;
private set;
}
public int Max
{
get;
private set;
}
}
if there is a direct correlation between signal range and the position then use what #agileguy suggested.
If you have positions distributed non linearly across the signal strength then one way would be:
class SignalStrengthPositionMapper
{
private static readonly int[] signalStrength = { Int32.MinValue, 0, 5, 11, 15, 20, 27, 35 };
public static int GetPosition(int strength)
{
return StrengthSearch(0, signalStrength.Length, strength);
}
// modified binary search
private static int StrengthSearch(int start, int end, int strength)
{
int mid = 0;
while (start <= end)
{
mid = (start + end) / 2;
if (strength >= signalStrength[mid]) // lower bound check
{
start = mid + 1;
if (strength < signalStrength[start]) // upper bound check
return mid;
}
else if (strength < signalStrength[mid]) // upper bound check
{
end = mid - 1;
if (strength >= signalStrength[end]) // lower bound check
return mid;
}
}
return 0;
}
}
Try using generics:
Dictionary<int,int> lookup = new Dictionary<int,int>();
lookup.Add(0,1);
lookup.Add(1,1);
lookup.Add(2,1);
lookup.Add(3,1);
...
lookup.Add(9,1);
lookup.Add(10,2);
lookup.Add(11,2);
etc
Then, lookup[22] would return value of 3. I suggest using a set of loops to create your 'ranges'. With this method, you're guaranteed O(1) access time.

Categories

Resources