Index was outside the bounds of array - c#

When I try run the aplication, it shows the Index was outside the bounds of the array at line float[] u_f = a[userid];
Any idea?
PS. the user ID can be every integer,but I take the index of the integer with is between(0, 1143600 for item) and (0, 89395 for user) and my calculation is based on that.Then, my calculation is based on the index of userid value which is stored in array a not based on the value of userid.
Thanks in advance
float[][] a = Enumerable.Range(0, 89395).Select(i => new float[100]).ToArray();
float[][] b = Enumerable.Range(0, 1143600).Select(j => new float[100]).ToArray();
int[] c = new int[1258038];
int[] d = new int [92160];
........
public float dotproduct(int userid, int itemid)
{
result = 0f;
float[] u_f = a[userid]; // <----Error Line (index was outside the bounds of array)
float[] i_f = b[itemid];
for (int i = 0; i < u_f.Length; i++)
{
result += u_f[i] * i_f[i];
}
return result;
}
private void btn_recomm_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(txtbx_id.Text) && String.IsNullOrEmpty(txtbx_itemid.Text) && !String.IsNullOrEmpty(txtbx_noofrecomm.Text))
{
int sc = Convert.ToInt32(txtbx_id.Text);
int n = Convert.ToInt32(txtbx_noofrecomm.Text);
int userseq=Array.IndexOf(d, sc);
var results = new List<float>(1143600);
for (int z = 0; z <= 1143600; z++)
{
results.Add(dotproduct(userseq, z));
}
var sb1 = new StringBuilder();
foreach (var resultwithindex in results.Select((r, index) => new { result = r, Index = index }).OrderByDescending(r => r.result).Take(n))
{
sb1.AppendFormat("{0}: {1}", d[resultwithindex.Index], resultwithindex.result);
sb1.AppendLine();
}
MessageBox.Show(sb1.ToString());
}
if (!String.IsNullOrEmpty(txtbx_id.Text) && !String.IsNullOrEmpty(txtbx_itemid.Text) && String.IsNullOrEmpty(txtbx_noofrecomm.Text))
{
int uid = Convert.ToInt32(txtbx_id.Text);
int iid = Convert.ToInt32(txtbx_itemid.Text);
int userseq0 = Array.IndexOf(d, uid);
int itemseq0 = Array.IndexOf(c, iid);
dotproduct(userseq0, itemseq0);
MessageBox.Show("The Score of item id " + itemseq0 + " is " + result);
}

It means the value of userID is higher than the maximum index number of the array a. The maximum index is count - 1.
The error message mentions that.
Also, it looks like a is two dimensional. Could this be your problem?

Obviously dotproduct gets called with userid value being equal to or greater than a.Length.
If that's not supposed to happen then add this line before declaring u_f array:
Debug.Assert(a.Length > userid);
Of course this won't solve the problem in itself but it will ensure you that whenever such a situation happens while you are testing, it won't go unnoticed or swallowed.
As a side note, clearer variable names would make it easier on you to read your code and figure out issues. Using actual types instead of jagged arrays would likely help too, if possible.

replace a[userid] with
a[userid-1]

Related

Loop nested loops

I would like to create a nested loop where the nesting depth determined by the size on an array. For example I have an array of integers and I would like to check all tuples where . Is there a way to do this simply in c# (or any other language)?
The only easy idea I had was to just multiply the numbers and do a for loop up to that number but unfortunately the product reaches the int limit. Also in this case I have no way to add extra conditions on each level.
Here's a small example and the product approach:
int[] a = new int[]{2, 3, 2}; //we have an array like this. In the description above it is a_1, a_2, a_3
void f(int[] i) //I have a function that works on an array input
{...}
bool check(int[] i) //I have a checker function, with an array parameter too
{...}
//And I would like to perform this function on all these arrays
//f({0, 0, 0})
//f({0, 0, 1})
//010
//011
//020
//021
//100
//101
//110
//111
//120
//f({1, 2, 1})
//But if let's say check({1, 0}) is false then don't perform the f function on {1, 0, 0} and {1, 0, 1}
//A simple implementation if we know the size of the array a is the following
for(int i1=0;i1<a[0];i1++)
{
if(!check({i1}))
continue;
for(int i2=0;i2<a[1];i2++)
{
if(!check({i1, i2}))
continue;
for(int i3=0;i3<a[2];i3++)
{
if(!check({i1, i2, i3}))
continue;
f({i1, i2, i3});
}
}
}
//But this obviously fails as we have no idea apriori of the size of the array a
//An alternative I have is the following:
int prod = 1;
foreach(int x in a)
{
prod *= x;
}
for(int c=0;c<prod;c++)
{
int d=c;
int[] i = new int[a.Length];
for(int l=0;l<a.Length;l++)
{
i[l]=d%(a[l]);
d /= a[l];
}
f(i);
}
//But the problem with this implementation is that in my case prod is larger than the int limit. Also this loops through all the incorrect cases too, where the check function can highly reduce the number of cases to calculate.
I've managed to solve the problem. The idea is that increasing the i value by one is really easy and we can check the condition that we don't overstep the a values and don't violate the check function easily. Below is a code that works
int[] a = ...;
void f(int[] i){...}
bool check(int[] i){...}
int n = a.Length;
int[] i = new int[n];
while (true) //keep increasing the coordinates of i, while we can
{
for (int l = 0; l < n; l++)
{
int[] il = copyFirst(i, l);
while(!check(il)) //check for all first few coordinates if it is correct, skip if incorrect.
//There is a way to improve this even further, as we don't have to check the first few
// coords if it was correct before, so should only care about the recently changed section
{
i = increase(a, i, l);
if (i == null)
{
return;
}
else
{
il = copyFirst(i, l);
}
}
}
f(i);
i = increase(a, i, n-1);
if (i == null) return;
}
int[] copyFirst(int[] i, int l) //this is just a small helper function to copy the first few elements of i, to use in the check
{
int[] ret = new int[l];
for (int k = 0; k < l; k++)
{
ret[k] = i[k];
}
return ret;
}
int[] increase(int[] a, int[] i, int l) //this results in the increased vector and in null if we've reached the end
{
for (int k = l; k >= 0; k--)
{
i[k] = i[k] + 1;
if (i[k] >= a[k])
{
i[k] = 0;
}
else
{
return i;
}
}
return null;
}

Get highest number from each row of a triangle and sum it up

I have below triangle of numbers which will be sent as parameter to a function
5
9 6
4 6 8
0 7 1 5
Now this will be received as string in below function with the format 5#9#6#4#6#8#0#7#1#5. So far I've tried to ripple only the digits from #
public class Sample
{
public static string validtrianglesum(string input)
{
string sum="0";
foreach(char num in input)
{
if(!num.Equals('#'))
{
Console.PrintLine(num); //here am getting only the nums excluding #
//How to sum up based on each row
}
}
return sum; //return
}
}
how could highest number from each row and sum them and how could I identify the rows to sum it up? Hope to find some help.
Let's break this down as follows:
Firstly, turn the input into an array of numbers:
string input = "5#9#6#4#6#8#0#7#1#5";
var numbers = input.Split('#').Select(int.Parse).ToArray();
Now let's assume we have a MakeTriangular(int[]) method that turns an array of numbers into a sequence of rows with the first row being of length 1, the second of length 2 and so on, so that it returns IEnumerable<IEnumerable<int>>.
Then we can use that along with Linq to calculate the sum of the maximum value in each row as follows:
int sum = MakeTriangular(numbers).Sum(row => row.Max());
Which gives the answer.
The implementation of MakeTriangular() could look like this:
public static IEnumerable<IEnumerable<int>> MakeTriangular(int[] numbers)
{
for (int i = 0, len = 1; i < numbers.Length; i += len, ++len)
yield return new ArraySegment<int>(numbers, i, len);
}
Putting it all together into a compilable Console app:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
string input = "5#9#6#4#6#8#0#7#1#5";
var numbers = input.Split('#').Select(int.Parse).ToArray();
int sum = MakeTriangular(numbers).Sum(row => row.Max());
Console.WriteLine(sum);
}
public static IEnumerable<IEnumerable<int>> MakeTriangular(int[] numbers)
{
for (int i = 0, len = 1; i < numbers.Length; i += len, ++len)
yield return new ArraySegment<int>(numbers, i, len);
}
}
}
Summing up all values in each row:
private static IEnumerable<int> Sum(string input)
{
int i = 0, s = 0, z = 1;
foreach (var v in input.Split('#').Select(int.Parse))
{
s += v;
if (++i != z) continue;
z++;
yield return s;
s = i = 0;
}
}
The same in one line:
private static IEnumerable<int> Sum(string input) => new Func<int, int, IEnumerable<int>>((i, z) => input.Split('#').Select(int.Parse).GroupBy(e => i++ == z && (i = 1) != null ? ++z : z, e => e).Select(e => e.Sum()))(0, 1);
Summing up all the highest values in each row:
private static int Sum(string input)
{
int i = 0, s = 0, z = 1, m = 0;
foreach (var v in input.Split('#').Select(int.Parse))
{
if (v > m) m = v;
if (++i != z) continue;
z++;
s += m;
i = m = 0;
}
return s;
}
Same in one line:
private static int Sum(string input) => new Func<int, int, int>((i, z) => input.Split('#').Select(int.Parse).GroupBy(e => i++ == z && (i = 1) != null ? ++z : z, e => e).Select(e => e.Max()).Sum())(0, 1);
I am returning the sums as IEnumerable<int> and with the yield return. If you just want to print out the answers change the return type to void and remove the yield return s; line.
One way to solve this is to determine the size of the triangle. By size I mean the height/width. E.g, the provided triangle has a size of 4.
If the size is n then the number of elements in the triangle will be n(n + 1)/2. When the number of elements in the input is known this can be solved to determine n (the size) by solving a second degree polynomial and picking the positive solution (the expression below involving a square root):
var triangle = "5#9#6#4#6#8#0#7#1#5";
var values = triangle.Split('#').Select(Int32.Parse).ToList();
var sizeAsDouble = (-1 + Math.Sqrt(1 + 8*values.Count))/2;
var size = (Int32) sizeAsDouble;
if (sizeAsDouble != size)
throw new ArgumentException("Input data is not a triangle.");
So with the provided input size will be 4. You can then use the size to select each row in the triangle and perform the desired arithmetic:
var maxValues = Enumerable
.Range(0, size)
.Select(i => new { Start = i*(i + 1)/2, Count = i + 1 })
.Select(x => values.Skip(x.Start).Take(x.Count))
.Select(v => v.Max());
The first Select will compute the necessary indices to correctly slice the array of values which is done in the second Select. Again the formula n(n + 1)/2 is used. If you want to you can merge some of these Select operations but I think spliting them up makes it clearer what is going on.
The output of this will be the numbers 5, 9, 8, 7. If you want to sum these you can do it like this:
return maxValues.Sum();
You can use LINQ:
string input = "5#9#6#4#6#8#0#7#1#5";
var nums = input.Split('#').Select(s => Int32.Parse(s));
var res = Enumerable.Range(0, nums.Count())
.Select(n => nums.Skip(Enumerable.Range(0, n).Sum()).Take(n));
.Where(x => x.Any()); // here you have IEnumerable<int> for every row
.Select(arr => arr.Max());
Please give credit to Widi :) but this is your request
var rows = Sum("5#9#6#4#6#8#0#7#1#5");
var total = rows.Sum();
private static IEnumerable<int> Sum(string inp)
{
int i = 0, s = 0, z = 1;
foreach (var v in inp.Split('#').Select(int.Parse))
{
s = Math.Max(s, v);
if (++i == z)
{
z++;
yield return s;
s = i = 0;
}
}
}
I would use 2 functions:
1st one to convert the string into a tree representation:
List<List<int>> GetTree(string data)
{
List<List<int>> CompleteTree = new List<List<int>>();
List<int> ValuesInLine = new List<int>();
int partsinrow = 1;
int counter = 0;
foreach (string part in data.Split('#'))
{
int value = int.Parse(part);
ValuesInLine.Add(value);
if (++counter == partsinrow)
{
CompleteTree.Add(ValuesInLine);
ValuesInLine = new List<int>();
counter = 0;
partsinrow++;
}
}
return CompleteTree;
}
2nd one to sum up the maximum of the lines:
int GetSumOfTree(List<List<int>> tree)
{
int sum = 0;
foreach (List<int> line in tree)
{
line.Sort();
int max = line[line.Count - 1];
sum += max;
}
return sum;
}

Returning -1 as value of variable;

When I try run the aplication, it shows the Index was outside the bounds of the array at line float[] u_f = a[userid]; when I check the value of userid, it is -1;
Any idea?
PS. the user ID can be every integer,but I take the index of the integer with is between(0, 1143600 for item) and (0, 89395 for user) and my calculation is based on that.Then, my calculation is based on the index of userid value which is stored in array a not based on the value of userid.
Thanks in advance.
float[][] a = Enumerable.Range(0, 89395).Select(i => new float[100]).ToArray();
float[][] b = Enumerable.Range(0, 1143600).Select(j => new float[100]).ToArray();
int[] c = new int[1258038];
int[] d = new int [92160];
........
public float dotproduct(int userid, int itemid)
{
result = 0f;
float[] u_f = a[userid]; // <----Error Line (index was outside the bounds of array-The value of user id is -1)
float[] i_f = b[itemid];
for (int i = 0; i < u_f.Length; i++)
{
result += u_f[i] * i_f[i];
}
return result;
}
private void btn_recomm_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(txtbx_id.Text) && String.IsNullOrEmpty(txtbx_itemid.Text) && !String.IsNullOrEmpty(txtbx_noofrecomm.Text))
{
int sc = Convert.ToInt32(txtbx_id.Text);
int n = Convert.ToInt32(txtbx_noofrecomm.Text);
int userseq=Array.IndexOf(d, sc);
var results = new List<float>(1143600);
for (int z = 0; z <= 1143600; z++)
{
results.Add(dotproduct(userseq, z));
}
var sb1 = new StringBuilder();
foreach (var resultwithindex in results.Select((r, index) => new { result = r, Index = index }).OrderByDescending(r => r.result).Take(n))
{
sb1.AppendFormat("{0}: {1}", d[resultwithindex.Index], resultwithindex.result);
sb1.AppendLine();
}
MessageBox.Show(sb1.ToString());
}
if (!String.IsNullOrEmpty(txtbx_id.Text) && !String.IsNullOrEmpty(txtbx_itemid.Text) && String.IsNullOrEmpty(txtbx_noofrecomm.Text))
{
int uid = Convert.ToInt32(txtbx_id.Text);
int iid = Convert.ToInt32(txtbx_itemid.Text);
int userseq0 = Array.IndexOf(d, uid);
int itemseq0 = Array.IndexOf(c, iid);
dotproduct(userseq0, itemseq0);
MessageBox.Show("The Score of item id " + itemseq0 + " is " + result);
}
Your problem is this line:
int userseq=Array.IndexOf(d, sc);
is presumably returning -1 which you are then passing into your dotproduct function which is then causing it to fail. You need to decide on what logic to apply in the case that you can't find sc in d and implement that.
Though as others have said validating your input to dotproduct would help you find problems a bit more easily.
I am not sure what you are asking. You have an error because you are try to access to the index -1 of a. An array begin at 0. The value -1 does not exist. userid should be between 0 and a.Length.
Is your method supposed to accept a userid value that's inferior to zero? If so then you cannot use that to define an array's length. Period.
If -1 is not a valid value, I would add this right on the first line of your dotproduct method, since it's public:
if (userid < 0) throw new ArgumentOutOfRangeException("userid");
As for why it's -1 in the current context, as others pointed out if the text value you try to find in the d array isn't there, the IndexOf method will return -1. You will have to validate the user's input before calling in your method.
I will also say what I already told you in your other post: name your variables in a clear way and - if that makes sense in your scenario - use custom classes rather than jagged arrays. Your code will be far easier to read and maintain afterwards.
int userseq=Array.IndexOf(d, sc);
it returns -1 if item sc is not present in array d. Then you pass userseq variable as first argument of a function dotproduct and take array index by it's first argument (userid). So here is your problem - in the IndexOf.
I don't know how to fix it - it's your buiseness logic and you must decide how to handle the case when input data isn't present in array. Maybe show message to user immediately.

How to populate two separate arrays from one comma-delimited list?

I have a comma delimited text file that contains 20 digits separated by commas. These numbers represent earned points and possible points for ten different assignments. We're to use these to calculate a final score for the course.
Normally, I'd iterate through the numbers, creating two sums, divide and be done with it. However, our assignment dictates that we load the list of numbers into two arrays.
so this:
10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85
becomes this:
int[10] earned = {10,20,30,40,45,50,20,45,85};
int[10] possible = {10,20,35,50,50,50,20,90,85};
Right now, I'm using
for (x=0;x<10;x++)
{
earned[x] = scores[x*2]
poss [x] = scores[(x*2)+1]
}
which gives me the results I want, but seems excessively clunky.
Is there a better way?
The following should split each alternating item the list into the other two lists.
int[20] scores = {10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85};
int[10] earned;
int[10] possible;
int a = 0;
for(int x=0; x<10; x++)
{
earned[x] = scores[a++];
possible[x] = scores[a++];
}
You can use LINQ here:
var arrays = csv.Split(',')
.Select((v, index) => new {Value = int.Parse(v), Index = index})
.GroupBy(g => g.Index % 2,
g => g.Value,
(key, values) => values.ToArray())
.ToList();
and then
var earned = arrays[0];
var possible = arrays[1];
Get rid of the "magic" multiplications and illegible array index computations.
var earned = new List<int>();
var possible = new List<int>();
for (x=0; x<scores.Length; x += 2)
{
earned.Add(scores[x + 0]);
possible.Add(scores[x + 1]);
}
This has very little that would need a text comment. This is the gold standard for self-documenting code.
I initially thought the question was a C question because of all the incomprehensible indexing. It looked like pointer magic. It was too clever.
In my codebases I usually have an AsChunked extension available that splits a list into chunks of the given size.
var earned = new List<int>();
var possible = new List<int>();
foreach (var pair in scores.AsChunked(2)) {
earned.Add(pair[0]);
possible.Add(pair[1]);
}
Now the meaning of the code is apparent. The magic is gone.
Even shorter:
var pairs = scores.AsChunked(2);
var earned = pairs.Select(x => x[0]).ToArray();
var possible = pairs.Select(x => x[1]).ToArray();
I suppose you could do it like this:
int[] earned = new int[10];
int[] possible = new int[10];
int resultIndex = 0;
for (int i = 0; i < scores.Count; i = i + 2)
{
earned[resultIndex] = scores[i];
possible[resultIndex] = scores[i + 1];
resultIndex++;
}
You would have to be sure that an equal number of values are stored in scores.
I would leave your code as is. You are technically expressing very directly what your intent is, every 2nd element goes into each array.
The only way to improve that solution is to comment why you are multiplying. But I would expect someone to quickly recognize the trick, or easily reproduce what it is doing. Here is an excessive example of how to comment it. I wouldn't recommend using this directly.
for (x=0;x<10;x++)
{
//scores contains the elements inline one after the other
earned[x] = scores[x*2] //Get the even elements into earned
poss [x] = scores[(x*2)+1] //And the odd into poss
}
However if you really don't like the multiplication, you can track the scores index separately.
int i = 0;
for (int x = 0; x < 10; x++)
{
earned[x] = scores[i++];
poss [x] = scores[i++];
}
But I would probably prefer your version since it does not depend on the order of the operations.
var res = grades.Select((x, i) => new {x,i}).ToLookup(y=>y.i%2, y=>y.x)
int[] earned = res[0].ToArray();
int[] possible = res[1].ToArray();
This will group all grades into two buckets based on index, then you can just do ToArray if you need result in array form.
here is an example of my comment so you do not need to change the code regardless of the list size:
ArrayList Test = new ArrayList { "10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85" };
int[] earned = new int[Test.Count / 2];
int[] Score = new int[Test.Count / 2];
int Counter = 1; // start at one so earned is the first array entered in to
foreach (string TestRow in Test)
{
if (Counter % 2 != 0) // is the counter even
{
int nextNumber = 0;
for (int i = 0; i < Score.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(Score[i])))
{
nextNumber = i;
break;
}
}
Score[nextNumber] = Convert.ToInt32(TestRow);
}
else
{
int nextNumber = 0;
for (int i = 0; i < earned.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(earned[i])))
{
nextNumber = i;
break;
}
}
earned[nextNumber] = Convert.ToInt32(TestRow);
}
Counter++
}

Find the smallest window of input array that contains all the elements of query array

Problem: Given an input array of integers of size n, and a query array of integers of size k, find the smallest window of input array that contains all the elements of query array and also in the same order.
I have tried below approach.
int[] inputArray = new int[] { 2, 5, 2, 8, 0, 1, 4, 7 };
int[] queryArray = new int[] { 2, 1, 7 };
Will find the position of all query array element in inputArray.
public static void SmallestWindow(int[] inputArray, int[] queryArray)
{
Dictionary<int, HashSet<int>> dict = new Dictionary<int, HashSet<int>>();
int index = 0;
foreach (int i in queryArray)
{
HashSet<int> hash = new HashSet<int>();
foreach (int j in inputArray)
{
index++;
if (i == j)
hash.Add(index);
}
dict.Add(i, hash);
index = 0;
}
// Need to perform action in above dictionary.??
}
I got following dictionary
int 2--> position {1, 3}
int 1 --> position {6}
int 7 --> position {8}
Now I want to perform following step to findout minimum window
Compare int 2 position to int 1 position. As (6-3) < (6-1)..So I will store 3, 6 in a hashmap.
Will compare the position of int 1 and int 7 same like above.
I cannot understand how I will compare two consecutive value of a dictionary. Please help.
The algorithm:
For each element in the query array, store in a map M (V → (I,P)), V is the element, I is an index into the input array, P is the position in the query array. (The index into the input array for some P is the largest such that query[0..P] is a subsequence of input[I..curr])
Iterate through the array.
If the value is the first term in the query array: Store the current index as I.
Else: Store the value of the index of the previous element in the query array, e.g. M[currVal].I = M[query[M[currVal].P-1]].I.
If the value is the last term: Check if [I..curr] is a new best.
Complexity
The complexity of this is O(N), where N is the size of the input array.
N.B.
This code expects that no elements are repeated in the query array. To cater for this, we can use a map M (V → listOf((I,P))). This is O(NhC(Q)), where hC(Q) is the count of the mode for the query array..
Even better would be to use M (V → listOf((linkedList(I), P))). Where repeated elements occur consecutively in the query array, we use a linked list. Updating those values then becomes O(1). The complexity is then O(NhC(D(Q))), where D(Q) is Q with consecutive terms merged.
Implementation
Sample java implementation is available here. This does not work for repeated elements in the query array, nor do error checking, etc.
I don't see how using HashSet and Dictionary will help you in this. Were I faced with this problem, I'd go about it quite differently.
One way to do it (not the most efficient way) is shown below. This code makes the assumption that queryArray contains at least two items.
int FindInArray(int[] a, int start, int value)
{
for (int i = start; i < a.Length; ++i)
{
if (a[i] == value)
return i;
}
return -1;
}
struct Pair
{
int first;
int last;
}
List<Pair> foundPairs = new List<Pair>();
int startPos = 0;
bool found = true;
while (found)
{
found = false;
// find next occurrence of queryArray[0] in inputArray
startPos = FindInArray(inputArray, startPos, queryArray[0]);
if (startPos == -1)
{
// no more occurrences of the first item
break;
}
Pair p = new Pair();
p.first = startPos;
++startPos;
int nextPos = startPos;
// now find occurrences of remaining items
for (int i = 1; i < queryArray.Length; ++i)
{
nextPos = FindInArray(inputArray, nextPos, queryArray[i]);
if (nextPos == -1)
{
break; // didn't find it
}
else
{
p.last = nextPos++;
found = (i == queryArray.Length-1);
}
}
if (found)
{
foundPairs.Add(p);
}
}
// At this point, the foundPairs list contains the (start, end) of all
// sublists that contain the items in order.
// You can then iterate through that list, subtract (last-first), and take
// the item that has the smallest value. That will be the shortest sublist
// that matches the criteria.
With some work, this could be made more efficient. For example, if 'queryArray' contains [1, 2, 3] and inputArray contains [1, 7, 4, 9, 1, 3, 6, 4, 1, 8, 2, 3], the above code will find three matches (starting at positions 0, 4, and 8). Slightly smarter code could determine that when the 1 at position 4 is found, since no 2 was found prior to it, that any sequence starting at the first position would be longer than the sequence starting at position 4, and therefore short-circuit the first sequence and start over at the new position. That complicates the code a bit, though.
You want not a HashSet but a (sorted) tree or array as the value in the dictionary; the dictionary contains mappings from values you find in the input array to the (sorted) list of indices where that value appears.
Then you do the following
Look up the first entry in the query. Pick the lowest index where it appears.
Look up the second entry; pick the lowest entry greater than the index of the first.
Look up the third; pick the lowest greater than the second. (Etc.)
When you reach the last entry in the query, (1 + last index - first index) is the size of the smallest match.
Now pick the second index of the first query, repeat, etc.
Pick the smallest match found from any of the starting indices.
(Note that the "lowest entry greater" is an operation supplied with sorted trees, or can be found via binary search on a sorted array.)
The complexity of this is approximately O(M*n*log n) where M is the length of the query and n is the average number of indices at which a given value appears in the input array. You can modify the strategy by picking that query array value that appears least often for the starting point and going up and down from there; if there are k of those entries (k <= n) then the complexity is O(M*k*log n).
After you got all the positions(indexes) in the inputArray:
2 --> position {0,2} // note: I change them to 0-based array
1 --> position {5,6} // I suppose it's {5,6} to make it more complex, in your code it's only {5}
7 --> position {7}
I use a recursion to get all possible paths. [0->5->7] [0->6->7] [2->5->7] [2->6->7]. The total is 2*2*1=4 possible paths. Obviously the one who has Min(Last-First) is the shortest path(smallest window), those numbers in the middle of the path don't matter. Here comes the code.
struct Pair
{
public int Number; // the number in queryArray
public int[] Indexes; // the positions of the number
}
static List<int[]> results = new List<int[]>(); //store all possible paths
static Stack<int> currResult = new Stack<int>(); // the container of current path
static int[] inputArray, queryArray;
static Pair[] pairs;
After the data structures, here is the Main.
inputArray = new int[] { 2, 7, 1, 5, 2, 8, 0, 1, 4, 7 }; //my test case
queryArray = new int[] { 2, 1, 7 };
pairs = (from n in queryArray
select new Pair { Number = n, Indexes = inputArray.FindAllIndexes(i => i == n) }).ToArray();
Go(0);
FindAllIndexes is an extension method to help find all the indexes.
public static int[] FindAllIndexes<T>(this IEnumerable<T> source, Func<T,bool> predicate)
{
//do necessary check here, then
Queue<int> indexes = new Queue<int>();
for (int i = 0;i<source.Count();i++)
if (predicate(source.ElementAt(i))) indexes.Enqueue(i);
return indexes.ToArray();
}
The recursion method:
static void Go(int depth)
{
if (depth == pairs.Length)
{
results.Add(currResult.Reverse().ToArray());
}
else
{
var indexes = pairs[depth].Indexes;
for (int i = 0; i < indexes.Length; i++)
{
if (depth == 0 || indexes[i] > currResult.Last())
{
currResult.Push(indexes[i]);
Go(depth + 1);
currResult.Pop();
}
}
}
}
At last, a loop of results can find the Min(Last-First) result(shortest window).
Algorithm:
get all indexes into the inputArray
of all queryArray values
order them ascending by index
using each index (x) as a starting
point find the first higher index
(y) such that the segment
inputArray[x-y] contains all
queryArray values
keep only those segments that have the queryArray items in order
order the segments by their lengths,
ascending
c# implementation:
First get all indexes into the inputArray of all queryArray values and order them ascending by index.
public static int[] SmallestWindow(int[] inputArray, int[] queryArray)
{
var indexed = queryArray
.SelectMany(x => inputArray
.Select((y, i) => new
{
Value = y,
Index = i
})
.Where(y => y.Value == x))
.OrderBy(x => x.Index)
.ToList();
Next, using each index (x) as a starting point find the first higher index (y) such that the segment inputArray[x-y] contains all queryArray values.
var segments = indexed
.Select(x =>
{
var unique = new HashSet<int>();
return new
{
Item = x,
Followers = indexed
.Where(y => y.Index >= x.Index)
.TakeWhile(y => unique.Count != queryArray.Length)
.Select(y =>
{
unique.Add(y.Value);
return y;
})
.ToList(),
IsComplete = unique.Count == queryArray.Length
};
})
.Where(x => x.IsComplete);
Now keep only those segments that have the queryArray items in order.
var queryIndexed = segments
.Select(x => x.Followers.Select(y => new
{
QIndex = Array.IndexOf(queryArray, y.Value),
y.Index,
y.Value
}).ToArray());
var queryOrdered = queryIndexed
.Where(item =>
{
var qindex = item.Select(x => x.QIndex).ToList();
bool changed;
do
{
changed = false;
for (int i = 1; i < qindex.Count; i++)
{
if (qindex[i] <= qindex[i - 1])
{
qindex.RemoveAt(i);
changed = true;
}
}
} while (changed);
return qindex.Count == queryArray.Length;
});
Finally, order the segments by their lengths, ascending. The first segment in the result is the smallest window into inputArray that contains all queryArray values in the order of queryArray.
var result = queryOrdered
.Select(x => new[]
{
x.First().Index,
x.Last().Index
})
.OrderBy(x => x[1] - x[0]);
var best = result.FirstOrDefault();
return best;
}
test it with
public void Test()
{
var inputArray = new[] { 2, 1, 5, 6, 8, 1, 8, 6, 2, 9, 2, 9, 1, 2 };
var queryArray = new[] { 6, 1, 2 };
var result = SmallestWindow(inputArray, queryArray);
if (result == null)
{
Console.WriteLine("no matching window");
}
else
{
Console.WriteLine("Smallest window is indexes " + result[0] + " to " + result[1]);
}
}
output:
Smallest window is indexes 3 to 8
Thank you everyone for your inputs. I have changed my code a bit and find it working. Though it might not be very efficient but I'm happy to solve using my head :). Please give your feedback
Here is my Pair class with having number and position as variable
public class Pair
{
public int Number;
public List<int> Position;
}
Here is a method which will return the list of all Pairs.
public static Pair[] GetIndex(int[] inputArray, int[] query)
{
Pair[] pairList = new Pair[query.Length];
int pairIndex = 0;
foreach (int i in query)
{
Pair pair = new Pair();
int index = 0;
pair.Position = new List<int>();
foreach (int j in inputArray)
{
if (i == j)
{
pair.Position.Add(index);
}
index++;
}
pair.Number = i;
pairList[pairIndex] = pair;
pairIndex++;
}
return pairList;
}
Here is the line of code in Main method
Pair[] pairs = NewCollection.GetIndex(array, intQuery);
List<int> minWindow = new List<int>();
for (int i = 0; i <pairs.Length - 1; i++)
{
List<int> first = pairs[i].Position;
List<int> second = pairs[i + 1].Position;
int? temp = null;
int? temp1 = null;
foreach(int m in first)
{
foreach (int n in second)
{
if (n > m)
{
temp = m;
temp1 = n;
}
}
}
if (temp.HasValue && temp1.HasValue)
{
if (!minWindow.Contains((int)temp))
minWindow.Add((int)temp);
if (!minWindow.Contains((int)temp1))
minWindow.Add((int)temp1);
}
else
{
Console.WriteLine(" Bad Query array");
minWindow.Clear();
break;
}
}
if(minWindow.Count > 0)
{
Console.WriteLine("Minimum Window is :");
foreach(int i in minWindow)
{
Console.WriteLine(i + " ");
}
}
It is worth noting that this problem is related to the longest common subsequence problem, so coming up with algorithms that run in better than O(n^2) time in the general case with duplicates would be challenging.
Just in case someone is interested in C++ implementation with O(nlog(k))
void findMinWindow(const vector<int>& input, const vector<int>& query) {
map<int, int> qtree;
for(vector<int>::const_iterator itr=query.begin(); itr!=query.end(); itr++) {
qtree[*itr] = 0;
}
int first_ptr=0;
int begin_ptr=0;
int index1 = 0;
int queptr = 0;
int flip = 0;
while(true) {
//check if value is in query
if(qtree.find(input[index1]) != qtree.end()) {
int x = qtree[input[index1]];
if(0 == x) {
flip++;
}
qtree[input[index1]] = ++x;
}
//remove all nodes that are not required and
//yet satisfy the all query condition.
while(query.size() == flip) {
//done nothing more
if(queptr == input.size()) {
break;
}
//check if queptr is pointing to node in the query
if(qtree.find(input[queptr]) != qtree.end()) {
int y = qtree[input[queptr]];
//more nodes and the queue is pointing to deleteable node
//condense the nodes
if(y > 1) {
qtree[input[queptr]] = --y;
queptr++;
} else {
//cant condense more just keep that memory
if((!first_ptr && !begin_ptr) ||
((first_ptr-begin_ptr)>(index1-queptr))) {
first_ptr=index1;
begin_ptr=queptr;
}
break;
}
} else {
queptr++;
}
}
index1++;
if(index1==input.size()) {
break;
}
}
cout<<"["<<begin_ptr<<" - "<<first_ptr<<"]"<<endl;
}
here the main for calling it.
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main() {
vector<int> input;
input.push_back(2);
input.push_back(5);
input.push_back(2);
input.push_back(8);
input.push_back(0);
input.push_back(1);
input.push_back(4);
input.push_back(7);
vector<int> query1;
query1.push_back(2);
query1.push_back(8);
query1.push_back(0);
vector<int> query2;
query2.push_back(2);
query2.push_back(1);
query2.push_back(7);
vector<int> query3;
query3.push_back(1);
query3.push_back(4);
findMinWindow(input, query1);
findMinWindow(input, query2);
findMinWindow(input, query3);
}

Categories