What data structure or datatype would be good for holding data ranges, and return a value based on data that lies in that range?
For example suppose I have the following ranges
1-10 -> 1
11-35 -> 2.5
36-49-> 3.8
50-60 -> 1.2
61-80 -> 0.9
In this case given the number 41 I would want the number 3.8 returned (as 41 is between the ranges of 36 and 49).
Is there a clever way of representing such data in a datastructure in order to perform this lookup?
A comparatively convenient and very performant implementation would be to use a SortedList<int, Tuple<int, double>>. Use the lower bound for each segment as the key and a tuple of upper bound + mapped value for the value:
var list = new SortedList<int, Tuple<int, double>>
{
{ 1, Tuple.Create(10, 1.0) },
{ 11, Tuple.Create(35, 2.5) },
};
(Of course you could decide to use a better-looking data structure to declare your parameters in order to enhance code maintainability and internally convert to this before getting down to business).
Since list.Keys is guaranteed to be sorted, when looking up a value you can use binary search on it to find the index that is equal to or greater than your input value:
var index = list.Keys.BinarySearch(value);
if (index < 0 && ~index == 0) {
// no match, stop processing
}
else if (index < 0) {
// key not found as is, look at the previous interval
index = ~index - 1;
}
At this point index points at the only range that might include value, so all that remains is to test for that:
if(x >= list.Keys[index] && x <= list.Values[index].Item1) {
var result = list.Values[index].Item2;
}
else {
// no match
}
You wouldn't call this "clean", but it's very short and very fast.
You can use this code
Key :
public class Interval<T> where T : IComparable
{
public Nullable<T> Start { get; set; }
public Nullable<T> End { get; set; }
public Interval(T start, T end)
{
Start = start;
End = end;
}
public bool InRange(T value)
{
return ((!Start.HasValue || value.CompareTo(Start.Value) > 0) &&
(!End.HasValue || End.Value.CompareTo(value) > 0));
}
}
value : decimal
And you can use this Type : Dictionary<Interval, decimal>
Nota : you can define access methods
It took me a while but I have a QuickAndDirty method which assumes all given values are valid and ranges are adjacent, without using any data structures.
And a very specific data structure which will only return something if the given index is exactly in the specified range and which can be expanded at run-time.
public abstract class TreeNode
{
public static double QuickAndDirty(int index)
{
double result = 1.0;
if (index > 10)
result = 2.5;
if (index > 35)
result = 3.8;
if (index > 49)
result = 1.2;
if (index > 60)
result = 0.9;
return result;
}
public abstract double GetValue(int index);
public abstract TreeNode AddRange(int begin, int end, double value);
public static TreeNode MakeTreePart(Tuple<int, int, double>[] ranges)
{
return TreeNode.MakeTreePart(ranges, 0, ranges.Length >> 1, ranges.Length - 1);
}
private static TreeNode MakeTreePart(Tuple<int, int, double>[] ranges, int min, int index, int max)
{
if (index == min || index == max)
return new Leaf(ranges[index].Item1, ranges[index].Item2, ranges[index].Item3);
return new SegmentTree(
ranges[index].Item2 + .5,
TreeNode.MakeTreePart(ranges, min, index >> 1, index - 1),
TreeNode.MakeTreePart(ranges, index + 1, index << 1, max));
}
}
public class SegmentTree : TreeNode
{
private double pivot;
private TreeNode left, right;
public SegmentTree(double pivot, TreeNode left, TreeNode right)
{
this.pivot = pivot;
this.left = left;
this.right = right;
}
public override double GetValue(int index)
{
if (index < pivot)
return left.GetValue(index);
return right.GetValue(index);
}
public override TreeNode AddRange(int begin, int end, double value)
{
if (end < pivot)
this.left = this.left.AddRange(begin, end, value);
else
this.right = this.right.AddRange(begin, end, value);
// Do this to confirm to the interface.
return this;
}
}
public class Leaf : TreeNode
{
private int begin, end;
private double value;
public Leaf(int begin, int end, double value)
{
this.begin = begin;
this.end = end;
this.value = value;
}
public override double GetValue(int index)
{
if (index >= begin && index <= end)
return value;
throw new Exception("index out of range");
}
public override TreeNode AddRange(int begin, int end, double value)
{
if (this.end < begin)
return new SegmentTree(((double)this.end + begin) * .5, this, new Leaf(begin, end, value));
else if (end < this.begin)
return new SegmentTree(((double)end + this.begin) * .5, new Leaf(begin, end, value), this);
else throw new Exception("Indexes overlap.");
}
}
static void Main()
{
TreeNode start = new Leaf(36, 49, 3.8);
start = start.AddRange(11, 35, 2.5);
start = start.AddRange(1, 10, 1.0);
start = start.AddRange(50, 60, 1.2);
start = start.AddRange(61, 80, 0.9);
double[] values = new double[70];
for (int i = 1; i < values.Length; i++)
values[i] = start.GetValue(i);
TreeNode array = TreeNode.MakeTreePart(
new Tuple<int, int, double>[]
{
Tuple.Create(1, 10, 1.0),
Tuple.Create(11, 35, 2.5),
Tuple.Create(36, 49, 3.8),
Tuple.Create(50, 60, 1.2),
Tuple.Create(61, 80, 0.9)
});
for (int i = 1; i < values.Length; i++)
values[i] = start.GetValue(i);
}
Related
I created a Blackjack game for coding school and I'm modifying it to display other aspects of the game, like what card value total the dealer had and what value total I had at the end of the game. It all works well until the Dealer Busted and I'm getting this error:
System.InvalidOperationException: 'Sequence contains no elements'
I've googled and searched Stackoverflow, but I didn't understand any of the responses. Here's my github code for it.
https://github.com/CrystalReimche/TwentyOneGame
In the TwentyOneGame.cs, line 143 is where where I'm trying to get it to display.
foreach (KeyValuePair<Player, int> entry in Bets)
{
Players.Where(x => x.Name == entry.Key.Name).First().Balance += (entry.Value * 2);
Console.WriteLine($"{entry.Key.Name} won {entry.Value} and now has a balance of {entry.Key.Balance}!");
Console.WriteLine($"Dealer had {TwentyOneRules.DealerCardValue(Dealer.Hand)} and {entry.Key.Name} had {TwentyOneRules.PlayerCardValue(entry.Key.Hand)}");
}
In the TwentyOneRules.cs holds the methods for it.
public static Dictionary<Face, int> _cardValues = new Dictionary<Face, int>()
{
[Face.Two] = 2,
[Face.Three] = 3,
[Face.Four] = 4,
[Face.Five] = 5,
[Face.Six] = 6,
[Face.Seven] = 7,
[Face.Eight] = 8,
[Face.Nine] = 9,
[Face.Ten] = 10,
[Face.Jack] = 10,
[Face.Queen] = 10,
[Face.King] = 10,
[Face.Ace] = 1
};
public static int[] GetAllPossibleHandValues(List<Card> Hand)
{
int aceCount = Hand.Count(x => x.Face == Face.Ace); // Find out how many Ace's there are
int[] result = new int[aceCount + 1]; // Plus 1 means if there's 2 Aces, there's 3 possible results ((1,1)||(1,11)||(11,11))
int value = Hand.Sum(x => _cardValues[x.Face]); // Value is the lowest possible value with all Ace's == 1
result[0] = value;
if (result.Length == 1) return result;
for (int i = 1; i < result.Length; i++)
{
value += (i * 10);
result[i] = value;
}
return result;
}
public static bool CheckForBlackJack(List<Card> Hand)
{
int[] possibleValues = GetAllPossibleHandValues(Hand);
int value = possibleValues.Max();
if (value == 2) return true;
else return false;
}
public static bool IsBusted(List<Card> Hand)
{
int value = GetAllPossibleHandValues(Hand).Min();
if (value > 21) return true;
else return false;
}
public static bool ShouldDealerStay(List<Card> Hand)
{
int[] possibleHandvalues = GetAllPossibleHandValues(Hand);
foreach (int value in possibleHandvalues)
{
if (value > 16 && value < 22)
{
return true;
}
}
return false;
}
public static bool? CompareHands(List<Card> PlayerHand, List<Card> DealerHand)
{
int[] playerResults = GetAllPossibleHandValues(PlayerHand);
int[] dealerResults = GetAllPossibleHandValues(DealerHand);
int playerScore = playerResults.Where(x => x < 22).Max(); // Filter the values that's < 22 and bring me the max of the values
int dealerScore = dealerResults.Where(x => x < 22).Max();
if (playerScore > dealerScore) return true;
else if (playerScore < dealerScore) return false;
else return null; // this is a tie
}
public static int PlayerCardValue(List<Card> PlayerHand)
{
int[] playerResults = GetAllPossibleHandValues(PlayerHand);
int playerScore = playerResults.Where(x => x < 50).Max();
return playerScore;
}
public static int DealerCardValue(List<Card> DealerHand)
{
int[] dealerResults = GetAllPossibleHandValues(DealerHand);
int dealerScore = dealerResults.Where(x => x < 22).Max();
return dealerScore;
}
I'm thinking it has something to do with the Dictionary, since the methods work on other parts of the game that do not use the Dictionary. For example, if I busted, it would display my card value total and the dealer card value total.
if (busted)
{
Console.WriteLine($"{player.Name} Busted! You lose your bet of {Bets[player]}. Your balance is now {player.Balance}");
Console.WriteLine($"Dealer had {TwentyOneRules.DealerCardValue(Dealer.Hand)} and {player.Name} had {TwentyOneRules.PlayerCardValue(player.Hand)}");
}
I just don't have a good grasp on how to use the Dictionary properly yet.
The problem is that you're invoking Max() (in either DealerCardValue or PlayerCardValue) on a sequence which contains no elements, as the Where() return value is an empty sequence, therefore there's no max value. This is written in the Max() function's documentation in MSDN.
Add DefaultIfEmpty() before Max() in DealerCardValue and PlayerCardValue to prevent InvalidOperationException from being thrown, as shown here, like so:
public static int DealerCardValue(List<Card> DealerHand)
{
int[] dealerResults = GetAllPossibleHandValues(DealerHand);
int dealerScore = dealerResults.Where(x => x < 22).DefaultIfEmpty().Max();
return dealerScore;
}
I would like to ask what's actually wrong on this code. I tried to understand quicksort (2-way) by myself so I looked into this page: http://me.dt.in.th/page/Quicksort/#disqus_thread after that I tried to code it by myself and landed here:
public void Sort(Comparison<TList> del, long l, long r)
{
// inspired by: http://me.dt.in.th/page/Quicksort/
if (l >= r) return;
// partitioning
for(long i = l + 1; i <= r; i++)
{
if (del.Invoke(this[i], this[l]) < 0)
{
Swap(i, l);
}
}
// recursion
Sort(del, l, l - 1);
Sort(del, l + 1, r);
}
Then I looked into the comments on the mentioned website and found this:
void qsort(char *v[], int left, int right)
{
int i, last;
void swap(char *v[], int i, int j);
if (left >= right)
return;
swap(v, left, (left + right) / 2);
last = left;
for (i = left + 1; i <= right; i++)
if (strcmp(v[i], v[left]) < 0)
swap(v, ++last, i);
swap(v, left, last);
qsort(v, left, last - 1);
qsort(v, last + 1, right);
}
and now I'm really curious why my code is still working, tested it with this (it's included in a linked list by the way):
static void Main(string[] args)
{
MyList<int> obj;
do
{
obj = MyList.Random(100, 0, 100);
obj.Sort(stdc);
obj.Sort(stdc);
} while (obj.IsSorted(stdc));
Log("Not sorted", obj);
Console.ReadKey(true);
}
and this:
public bool IsSorted(Comparison<TList> del)
{
var el = start;
if (el != null)
{
while (el.Next != null)
{
if (del.Invoke(el.Value, el.Next.Value) > 0) // eq. to this[i] > this[i + 1]
return false;
el = el.Next;
}
}
return true;
}
and this:
public static MyList<int> Random(int num, int min = 0, int max = 1)
{
var res = new MyList<int>();
var rand = new Random();
while (num > 0)
{
res.Add(rand.Next(min, max));
num--;
}
return res;
}
Your quicksort code is not really quicksort, and it will be slow. The line
Sort(del, l, l - 1);
will not do anything, since the recursed into call will detect l >= (l-1). The line
Sort(del, l + 1, r);
only reduces the size of the partition [l,r] by one to [l+1,r], so the time complexity will always be O(n^2), but it looks like it will work, just slowly.
The qsort() function swaps the left and middle values, this will avoid worst case issue if data is already sorted, but doesn't do much else. The key is that it updates "last", so that when partition is completed, the value at v[last] will be the pivot value, and then it makes recursive calls using "last" instead of "left" or in your code's case, "l".
The qsort shown is a variation of Lomuto partition scheme. Take a look at the wiki article which also includes the alternate Hoare partition scheme:
https://en.wikipedia.org/wiki/Quicksort#Algorithm
I have an array of numbers, and I am trying to find the size of the largest subset, such that all numbers in the subset are less than 5 apart. They can assume to be sorted, if necessary.
For example:
[1, 2, 5, 6, 8, 9, 15]
4 is the largest subset. (5, 6, 8, 9)
I expect that there is a simple LINQ answer, but I am not thinking of it. I could sort it and then iterate through it with each starting number, keeping track of the most from that number, but that seems very ugly and inefficient. Any ideas?
Clarification on what I want to avoid:
(1,2,5) - 3
(2,5,6) - 3
(5,6,8,9) - 4
(6,8,9) - 3
(8,9) - 2
(9) - 1
(15) - 1
This problem can be solved by maintaining two pointers left and right.
Suppose you have a sorted array arrayof n numbers.
left = 0
right = 0
ans = INT_MIN
while right == n :
if array[right] - array[left] < 5:
right++
else:
left++
ans = max(right - left + 1, ans)
Start by making the biggest possible set at the beginning of the sorted list. Then keep removing values from the front until the next one fits, and add as many as possible after the next one too. This makes a new set. Keep track of the largest at any given moment.
Something like this in C#:
static IEnumerable<T[]> CloseSublists<T>(this IEnumerable<T> values, Func<T, T, bool> isClose) where T : IComparable<T> {
var window = new Queue<T>();
var enumerator = values.GetEnumerator();
if (!enumerator.MoveNext()) {
return;
}
bool more;
do {
window.Enqueue(enumerator.Current);
} while ((more = enumerator.MoveNext()) && isClose(window.Peek(), enumerator.Current));
yield return window.ToArray();
while (more) {
do {
window.Dequeue();
} while (window.Count != 0 && !isClose(window.Peek(), enumerator.Current));
do {
window.Enqueue(enumerator.Current);
} while ((more = enumerator.MoveNext()) && isClose(window.Peek(), enumerator.Current));
yield return window.ToArray();
}
}
and
public static T MaxBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> key) where TKey : IComparable<TKey> {
var enumerator = items.GetEnumerator();
enumerator.MoveNext();
var max = enumerator.Current;
TKey maxKey = key(max);
while (enumerator.MoveNext()) {
T current = enumerator.Current;
TKey currentKey = key(current);
int relation = currentKey.CompareTo(maxKey);
if (relation > 0) {
max = current;
maxKey = currentKey;
}
}
return max;
}
used as:
int[] x = {1, 2, 5, 6, 8, 9, 15};
x.CloseSublists((a, b) => b < a + 5).MaxBy(l => l.Length)
Based on answer of Prince, I rewrote it to C# and improved a bit:
protected int MaxSubLen(int[] arr, int diffLessThan)
{
int l = 0, r = 0;
while (r < arr.Length)
{
if (arr[r] - arr[l] >= diffLessThan)
{
++l;
}
++r;
}
return r - l;
}
and, just for fun, the sequence returning generic version:
protected IEnumerable<T> MaxSubarray<T>(IList<T> arr, Func<T, T, bool> isClose_L_R)
{
int l = 0, r = 0, start = 0;
while (r < arr.Count)
{
if (isClose_L_R(arr[l], arr[r]))
{
start = l;
}
else
{
++l;
}
++r;
}
for (int i = start; i < start + r - l; ++i)
{
yield return arr[i];
};
}
I have this function:
int round(double val) {
if (val >= 0) {
return (int)Math.Floor(val + 0.5);
}
return (int)Math.Ceiling(val - 0.5);
}
I call it in my program many times, I mean MANY times, so every milisecond of it's running time matters. Is there any way of having it faster than it is now? Thx
EDIT:
The function is part of algorithm for computing the tangent direction of a line in an image. It is taken from academic article. As it handles the angles in radian values, it uses small, precise numbers.
I/O examples:
0 -> 0
1 -> 1
1.1 -> 1
1.51 -> 2
-0.1 -> 0
-1 -> -1
-1.1 -> -1
-1.51 -> -2
EDIT2:
According to comments, I change the examined function to this one:
int round(double val) {
return (int)Math.Round(val, MidpointRounding.AwayFromZero);
}
The updated question is: Is the Math.Round function the fastest way of rounding?
You can speed it up. This is many times faster:
if (val >= 0)
{
return (int)(val + 0.5d);
}
return = (int)(val - 0.5d);
You avoid all those Math library stuff. Problem is, does it really matter? For 1500000 conversions the time of You first function is 18ms. Your EDIT2 function is 36ms. This function is 4ms.
According to this measurement the processor can compare two doubles, add two doubles and convert one in about 2,5ns. But if it does not have it in the cache, reading from main memory can take 100ns. Measuring can be misleading sometimes.
Here is complete code
#region stopky
public class Stopky
{
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long frequency);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool QueryPerformanceCounter(out long ticks);
protected static double frequency = -1;
public void setStart()
{
QueryPerformanceCounter(out tickStart);
}
public double getTimeFromStart
{
get
{
QueryPerformanceCounter(out tickNow);
double time = (tickNow - tickStart) / frequency;
return time;
}
}
private long tickStart;
private long tickNow;
public Stopky()
{
if (frequency < 0)
{
long tmp;
QueryPerformanceFrequency(out tmp);
if (tmp == 0)
{
throw new NotSupportedException("Error while querying "
+ "the high-resolution performance counter.");
}
frequency = tmp;
}
setStart();
}
public void Show()
{
MessageBox.Show(this.getTimeFromStart.ToString());
}
}
#endregion
private void button2_Click(object sender, EventArgs e)
{
double[] examples = new double[] { 0, 1, 1.1, 1.51, -0.1, -1, -1.1, -1.51 };
int totalCount = 1500000;
double[] examplesExpanded = new double[totalCount];
for (int i = 0, j = 0; i < examplesExpanded.Length; ++i)
{
examplesExpanded[i] = examples[j];
if (++j >= examples.Length) { j = 0; }
}
int[] result1 = new int[totalCount];
int[] result2 = new int[totalCount];
int[] result3 = new int[totalCount];
Stopky st = new Stopky();
for (int i = 0; i < examplesExpanded.Length; ++i)
{
result1[i] = (int)Math.Round(examplesExpanded[i], MidpointRounding.AwayFromZero);
}
st.Show();
st = new Stopky();
for (int i = 0; i < examplesExpanded.Length; ++i)
{
double val = examplesExpanded[i];
if (val >= 0)
{
result2[i] = (int)Math.Floor(val + 0.5);
}
result2[i] = (int)Math.Ceiling(val - 0.5);
}
st.Show();
st = new Stopky();
for (int i = 0; i < examplesExpanded.Length; ++i)
{
double val = examplesExpanded[i];
if (val >= 0)
{
result3[i] = (int)(val + 0.5d);
}
else
{
result3[i] = (int)(val - 0.5d);
}
}
st.Show();
for (int i = 0; i < totalCount; ++i)
{
if(result1[i] != result2[i] || result1[i] != result3[i])
{
MessageBox.Show("ERROR");
}
}
MessageBox.Show("OK");
}
Some notes
i < examplesExpanded.Length is slightly faster than i < totalCount, although it is counter intuitive. The reason is, range check can be avoided.
Release can be significantly faster than Debug (here is the difference not big)
Why not use the inbuilt Math.Round method?
int round(double val) {
if (val >= 0) {
return Math.Round(val, MidpointRounding.AwayFromZero);
}
return Math.Round(val, MidpointRounding.ToEven);
}
https://msdn.microsoft.com/en-us/library/system.math.round(v=vs.110).aspx
I have the following code which fetches a country ID based on the IP address:
countryID = GetAllCountryIPRanges().Single(c => c.BeginIPNum <= intIp && intIp <= c.EndIPNum).CountryID;
It's unfortunately quite slow as there's ~200,000 records. The ranges do not overlap, and GetAllCountryIPRanges() is in ascending order by BeginIPNum.
How do I implement .BinarySearch() on this list to find the correct record?
List has a binary search method but since binary search is so easy to implement and since the IComparator you would need to define is so complex due to the range thing I suggest you implement the binary search method
Something like this (NOT TESTED!)
public static IPRange BinarySearch(List<IPRange> source, int intIp)
{
int startIndex = 0;
int endIndex = source.Count;
while (endIndex >= startIndex)
{
int middleIndex = startIndex + (endIndex - startIndex) / 2;
if (source[middleIndex].BeginIPNum <= intIp && intIp <= source[middleIndex].EndIPNum)
{
return source[middleIndex];
}
else if (source[middleIndex].BeginIPNum < intIp)
{
startIndex = middleIndex + 1;
}
else
{
endIndex = middleIndex - 1;
}
}
return null;
}
Assuming the List is sorted and there are no overlapping ranges.
Gotta love the elegance of recursion... (not tested)
public static IPRange BinarySearch(IList<IPRange> ipList, int ip)
{
return BinarySearch(source, ip, 0, ipList.Count - 1);
}
public static IPRange BinarySearch(IList<IPRange> ipList, int ip, int min, int max)
{
if (min > max)
{
throw new Assertion("Error: ipList is empty, out-of-order, or does not contain an element that includes the IP");
}
int mid = (min + max) / 2;
var midIp = ipList[mid];
if (ip < midIp.BeginIpNum)
{
return BinarySearch(ipList, ip, min, mid-1);
}
if (ip > midIp.EndIpNum)
{
return BinarySearch(ipList, ip, mid+1, max);
}
return midIp;
}