Related
Let's say I have an array with integers, which represent the daily changes in the price of a stock, as an example the following array:
[3, -1, -4, 1, 5, -9, 2, 6].
How would I find the amount of subarrays which have a sum between two values (lower and upper, so l <= s <= u), such as -1 (=lower) and 0 (=upper)? In this case, the answer would be 5. You can have the subarrays
[3, -1, -4, 1]
[-1]
[-1, -4, 1, 5, -9, 2, 6]
[1, 5, -9, 2]
[-9, 2, 6]
Another example array would be:
[4, 2, 2, -6, 7]
with lower bound 3, upper bound 4. The answer to this would be 3. I have tried the following very naïve approach, which I'm certain there are many faster alternatives for. I'm wondering how I can solve this problem faster, with divide-and-conquer or possibly through dynamically programming.
Class
public class Stock
{
public int sequenceLength;
public int[] prices;
public int lowerBound;
public int upperBound;
public int count = 0;
public Stock()
{
sequenceLength = Int32.Parse(Console.ReadLine());
prices = new int[sequenceLength];
var split = Console.ReadLine();
var splitSpace = split.Split(' ');
for (int i = 0; i < sequenceLength; i++)
prices[i] = Int32.Parse(splitSpace[i]);
lowerBound = Int32.Parse(Console.ReadLine());
upperBound = Int32.Parse(Console.ReadLine());
}
}
Usage
static void Main(string[] args)
{
int testcases = Int32.Parse(Console.ReadLine());
Stock[] stock = new Stock[testcases];
for (int i = 0; i < testcases; i++)
stock[i] = new Stock();
int count = 0;
for (int i = 0; i < stock.Length; i++)
{
for (int j = 0; j < stock[i].sequenceLength - 1; j++)
{
int sum = stock[i].prices[j];
if (sum >= stock[i].lowerBound && sum <= stock[i].upperBound)
count++;
for (int k = j + 1; k < stock[i].sequenceLength; k++)
{
sum += stock[i].prices[k];
if (sum >= stock[i].lowerBound && sum <= stock[i].upperBound)
count++;
}
}
if (stock[i].prices[stock[i].sequenceLength - 1] >= stock[i].lowerBound && stock[i].prices[stock[i].sequenceLength - 1] <= stock[i].upperBound)
count++;
stock[i].count = count;
count = 0;
}
Console.Clear();
for (int i = 0; i < stock.Length; i++)
Console.WriteLine(stock[i].count);
}
There's already an answer with O(N^2) complexity, I'll propose a O(NlogN) solution.
Create an array sums, where sums[0] = array[0] and sums[i] = sums[i-1]+array[i]. Now, for each index i in sums, you need to find number of indexes j such that sums[i] - sums[j] is in range [lower, upper]. But how to find number of indexes j?
Create a balanced binary search tree (AVL tree). Insert sums[0] in it. Start processing nodes from left to right. After processing a node, add it to the tree. You can search for the number of indexes in range [lower, upper] in O(logN) complexity, and same applies for the insertion as well. That will give you a total time complexity of O(NlogN).
If I understand the problem (and the jury is out)
The premise is, the first loop works its way across the array. The second loop is in charge of keeping a sum and checking the range, then yielding the result
Obviously this is O(n2) time complexity
Given
public static IEnumerable<int[]> GetSubs(int[] source, int lower, int upper)
{
for (var i = 0; i < source.Length; i++)
for (int j = i, sum = 0; j < source.Length; j++)
{
sum += source[j];
if (sum >= lower && sum <= upper)
yield return source[i..(j+1)];
}
}
Usage
var array = new[] { -5, -4, -3, -2, -1, 0, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (var sequence in GetSubs(array,2,5))
Console.WriteLine($"{sequence.Sum()} : [{string.Join(", ", sequence)}]");
Output
5 : [-5, -4, -3, -2, -1, 0, 2, 3, 4, 5, 6]
4 : [-4, -3, -2, -1, 0, 2, 3, 4, 5]
3 : [-3, -2, -1, 0, 2, 3, 4]
2 : [-2, -1, 0, 2, 3]
4 : [-1, 0, 2, 3]
2 : [0, 2]
5 : [0, 2, 3]
2 : [2]
5 : [2, 3]
3 : [3]
4 : [4]
5 : [5]
Full Demo Here
Note : You could probably do this in linq with Enumerable.Range, however this is pretty easy to understand
If you just wanted the count, you could remove the iterator all together, and just increment a counter when the if condition is true
public static int GetSubs(int[] source, int lower, int upper)
{
var result = 0;
for (var i = 0; i < source.Length; i++)
for (int j = i, sum = 0; j < source.Length; j++)
{
sum+= source[j];
if (sum >= lower && sum <= upper)
result++;
}
return result;
}
I know that's easy, but I don't understand how I should do it.
1 23 29 18 43 20 5
to
5 1 23 29 18 43 20
I think we should use for-loop:
for (int i = 0; i < numbers.Count - 1; i++)
{
}
but I don't know what to do in it. Something like numbers[i] = numbers[i - 1] but it isn't working. I think there are some if checks which I miss.
The most straightforward way that comes to mind is a reverse loop.
int[] numbers = { 1, 23, 29, 18, 43, 20, 5};
int lastVal = numbers[numbers.Length - 1];
for (int i = numbers.Length -1; i > 0; i--)
numbers[i] = numbers[i-1];
numbers[0] = lastVal;
Just looping from the end (after saving the last value) and moving "up" the values, finally replacing the first value with the last
Here's a oneliner:
var numbers = new[] {1, 23, 29, 18, 43, 20, 5};
numbers = new[] {numbers.Last()}.Concat(numbers.Take(numbers.Length - 1)).ToArray();
This creates a new array containing the last element, then concatenates it with the original array excluding the last element.
What you want to do is make another array of the same size as the original one, then assign the last element and loop through the array up to the previous to last element.
Something like this:
int[] original = {1, 23, 29, 18, 43, 20, 5};
int[] altered = new int[original.length];
altered[0] = original[original.length - 1];
for (int i = 1; i < original.length - 1; i++)
{
altered[i] = original[i - 1];
}
You can perform a left rotation to the table by six positions on your case and create the requested new table
int[] myArray = new int[] { 1, 23, 29, 18, 43, 20, 5 };
var newArray = LeftRotationByD(myArray, 6);
and your function for the rotation would be:
private static int[] LeftRotationByD(int[] a, int k)
{
int[] b = new int[a.Length];
int index;
int length = a.Length;
int place;
for (int i = 0; i < length; i++)
{
index = i - k;
place = length + index;
if (index >= 0) b[index] = a[i];
else b[place] = a[i];
}
return b;
}
You can use this method to shift right:
void ShiftRight(int[] array, int count)
{
var clone = (int[])array.Clone();
for (var i = 0; i < array.Length; i++)
array[(i + count) % array.Length] = clone[i];
}
And use it this way:
var a = new int[] { 1, 2, 3, 4 };
ShiftRight(a, 1);
I have a 1D array of ints:
int[] array = { 10, 11, 12, 13, 14, 20, 21, 22, 23, 24, 30, 31, 32,33, 34,40,41,42,43, 44};
I would like to divide this 1D array into a 2D array of 4 rows and 5 columns, where the first 5 values goes into row 1, the next 5 in row 2 and so on. The final result should look like this:
array2D:
[[10, 11, 12, 13, 14]
[20, 21, 22, 23, 24]
[30, 31, 32, 33, 34]
[40, 41, 42, 43, 44]]
In reality the array will be much longer(maybe 100+ rows), but the number of columns is 5 and number of rows is dividable by 5. I have simplified for example. This is what I have tried so far:
int[] array = { 10, 11, 12, 13, 14, 20, 21, 22, 23, 24, 30, 31, 32,33, 34,40,41,42,43, 44};
int[,] array2D = new int[(array.Length/5),5];
int count_0 = 1;
int count_1 = 1;
int count_2 = 1;
int count_3 = 1;
int count_4 = 1;
for (int i = 0; i < array.Length; i++)
{
if (i < 5)
{
// works for the first 5 values:
array2D[0, i] = array[i];
}
// I tried this approach for the rest where I try to say that if Im at index 5,
//and every 5th element from here, append to it to index[i,0] of array2D and so on.
// This do not work, and is what I need help with.
else if (i >= 5 && i % 5 == 0)
{
array2D[count_0, 0] = array[i];
count_0++;
}
else if (i >= 6 && i % 5 == 0)
{
array2D[count_1, 1] = array[i];
count_1++;
}
else if (i >= 7 && i % 5 == 0)
{
array2D[count_2, 2] = array[i];
count_2++;
}
else if (i >= 8 && i % 5 == 0)
{
array2D[count_3, 3] = array[i];
count_3++;
}
else if (i >= 9 && i % 5 == 0)
{
array2D[count_4, 4] = array[i];
count_4++;
}
}
Of course for this example I could just say if > 5 && <10 {append to array2D} and if > 10 && <15 {append to array2D} and so on, but I want something that works for a large array of hundreds of values. If someone has a smarter way to do this please let me know.
Thank you for any help!
You can just calculate the indexes:
for(int i=0;i<array.Length;i++)
array2D[i/5, i%5] = array[i];
You can calculate the indexes easily using simple division. The row is calculated by the dividing i with the wanted column numbers. The column is simply the reminder of that division.
using System;
public class Program
{
public static T[,] SplitInto2DArray<T>(T[] array, int rows, int columns) {
T[,] result = new T[rows, columns];
for (int i = 0; i < array.Length; i++) {
result[i / columns, i % columns] = array[i];
}
return result;
}
public static void PrintArray<T>(T[,] array) {
for (int i = 0; i < array.GetLength(0); i++) {
for (int j = 0; j < array.GetLength(1); j++) {
Console.Write(array[i, j] + " ");
}
Console.WriteLine();
}
}
public static void Main()
{
int[] array = { 10, 11, 12, 13, 14, 20, 21, 22, 23, 24, 30, 31, 32, 33, 34, 40, 41, 42, 43, 44};
int[,] splittedArray = SplitInto2DArray(array, 4, 5);
PrintArray(splittedArray);
}
}
You can accomplish with LINQ using Enumerable.GroupBy.
array.GroupBy(x => x / 5)
.Select(x => x.ToArray())
.ToArray()
I have the following code to get a list of all possible slice start/end indexes, in order of descending slice length. I then iterate over the list and use it to slice up an array, breaking when I get a slice that I am looking for. I do this because I am looking for the largest slice that matches other parameters and I am looking to short cut past the other possible slices once I've found one.
I would prefer a couple of nested for loops to check the slice and move on, instead of having to get every possible range and sort them first, since the array can be up to a billion or so items large. I can't for the life of me figure out how to do it, or even how to phrase the question to search for it. Any help is appreciated.
byte[] data1 = { 54, 87, 23, 87, 45, 67, 7, 85, 65, 65, 3, 4, 55, 76, 65, 64, 5, 6, 4, 54, 45, 6, 4 };
List<Tuple<int, int>> ranges = new List<Tuple<int, int>>();
for (int i1 = 0; i1 < data1.Count(); i1++)
{
for (int i2 = i1 + 1; i2 < data1.Count(); i2++)
{
ranges.Add(new Tuple<int, int>(i1, i2));
}
}
ranges = ranges.OrderByDescending(x => x.Item2 - x.Item1).ToList();
If I understand the question correctly, you are looking for the largest subarray of an array (which you are calling a "slice"…maybe a term from some other programming language?) that meets some specific conditions. In your question, you are unspecific about the conditions themselves, so I assume that part is not important.
It seems what you are having difficulty with is arranging your code so that you necessarily inspect the longest subarrays first.
If all of that is correct, then you just need to arrange your loops differently. Currently, you are picking a starting index and then finding all subarrays that start at that index. Instead, since you want the longest subarrays to be inspect first, you should pick the length of the subarray, starting with the longest possible length, and select all subarrays that can be that long.
For example:
for (int i = data1.Length; i > 0; i--)
{
for (int j = 0; j < data1.Length - i + 1; j++)
{
// inspect subarray starting at index j, having length i
}
}
You may enumerate the slices directly by a couple of nested loops:
bool found = false;
for (int sliceLen = data1.Length; !found && sliceLen > 0; sliceLen--)
for (int sliceStart = 0; !found && sliceStart + sliceLen <= data1.Length; sliceStart++)
if (found = (
data1[sliceStart] == data1[sliceStart + sliceLen - 1] // check your condition here
))
Console.WriteLine($"Found: {sliceStart}:{sliceLen}");
Demo: https://ideone.com/lZRNJm
I figured it out. Here's what I needed, to iterate over each array slice in reverse length order.
byte[] data1 = { 54, 87, 23, 87, 45, 67, 7, 85, 65, 65, 3, 4, 55, 76, 65, 64, 5, 6, 4, 54, 45, 6, 4 };
for (int length = data1.Count() - 1; length > 0; length--)
{
int numberOfSlicesForLength = data1.Count() - length;
for (int start = 0; start < numberOfSlicesForLength; start++)
{
byte[] sliceValues = data1.Skip(start).Take(length);
}
}
I am working on a project and I have to implement Quicksort. I am having a stack overflow issue when I run Quicksort on an array of random integers (of size 100,000). I believe that I am running into an error that involves duplicate values. I attempted to research this problem and someone mentioned to consider when data[left] == pivot == data[right] but I don't know what to do with that in my case.
This is for my class so I am required to use this partitioning method, and I created the Quicksort method based off of instructions. Any help will be greatly appreciated. I'd like to figure out what I am doing wrong.
public static int Partition(int[] a, int left, int right, int pivotIndex) {
int temp;
int pivotValue = a[pivotIndex];
a[pivotIndex] = a[right];
a[right] = pivotValue;
int store = left;
for (int i = left; i < right; i++) {
if (a[i] < pivotValue) {
temp = a[store];
a[store] = a[i];
a[i] = temp;
store++;
}
}
temp = a[right];
a[right] = a[store];
a[store] = temp;
return store;
}
public static void Quicksort(int[] a, int left, int right) {
if ((right - left) <= 5)
InsertionSort(a);
else if (left < right) {
int mid = ((right - left) / 2) + left;
int pivot = Math.Max(Math.Min(a[left], a[right]), Math.Min(Math.Max(a[left], a[right]), a[mid]));
int pivotIndex = Array.IndexOf(a, pivot);
Partition(a, left, right, pivotIndex);
Quicksort(a, left, (pivotIndex - 1));
Quicksort(a, (pivotIndex + 1), right);
}
}
Your problem is in the way you choose the pivot index.
Although you are correctly choosing the median of three value, you are not finding the index of that median correctly.
Firstly, this is incorrect:
int pivotIndex = Array.IndexOf(a, pivot);
That will find the index of the first occurrence of the median value in the entire a[] array.
At the very least it should be like this:
int pivotIndex = Array.IndexOf(a, pivot, left, right-left+1);
However that still has several problems:
You will be doing a linear search for the pivot value over the entire partition. This is very inefficient.
It will find the first occurrence of the median value, regardless of what the actual index is.
If the left, right or mid values are the same, it should choose the mid index, but the linear search will choose the left index.
The solution is to change the way you compute the median of three so that you calculate the actual index rather just the value. This is rather more fiddly!
First I would recommend you to shuffle array before sorting, or at least check median value (or ninther) and use it during partitioning, it will probabilistically guarantee O(NLogN).
Also if you have duplicates it is a good idea to use 3 way partitioning. Below is an implementation of quick sort with three way partitioning and shuffling.
public static void Quicksort(int[] a) {
if (a == null) throw new ArgumentNullException("Array is null");
Shuffle(a);
Quicksort(a, 0, a.Length - 1);
}
private static void Quicksort(int[] a, int left, int right) {
if ((right - left) <= 5)
InsertionSort(a);
else if (left <= right) {
int lt = left, gt = right;
int v = a[left];
int i = left;
while (i <= gt) {
if (a[i] < v) Swap(a, lt++, i++);
else if (a[i] > v) Swap(a, i, gt--);
else i++;
}
// a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi].
Quicksort(a, left, lt - 1);
Quicksort(a, gt + 1, right);
}
}
public static void Shuffle(int[] a) {
if (a == null) throw new ArgumentNullException("Array is null");
int n = a.Length;
var theRandom = new Random();
for (int i = 0; i < n; i++) {
int r = i + theRandom.Next(n-i); // between i and n-1
int temp = a[i];
a[i] = a[r];
a[r] = temp;
}
}
private static void InsertionSort<T>(T[] array) where T:IComparable<T>
{
for (var i = 1; i < array.Length; i++)
{
var value = array[i];
var j = i - 1;
while ((j >= 0) && (array[j].CompareTo(value) > 0))
{
array[j + 1] = array[j--];
}
array[j + 1] = value;
}
}
private static void Swap(int[] a, int i, int j) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
class QuickSortExample
{
static int Partition(int[] arr, int left, int right)
{
int pivot = arr[left + ((right - left) / 2)]; // use a midpoint pivot to prevent worst case O(n log n), although pivot = arr[left] is fine
while (left < right)
{
while (arr[left] < pivot)
left++;
while (arr[right] > pivot)
right--;
// handle duplicate entries
if (arr[right] == pivot && arr[left] == pivot)
left++;
if (left < right)
{
int tmp = arr[right];
arr[right] = arr[left];
arr[left] = tmp;
}
}
return right;
}
static void QuickSort(int[] arr, int left, int right)
{
if(left < right)
{
int pivot = Partition(arr, left, right);
if (pivot > 1)
QuickSort(arr, left, pivot - 1);
if(pivot + 1 < right)
QuickSort(arr, pivot + 1, right);
}
}
static void TestQuickSort(int[] arr)
{
Console.WriteLine("Initial array[" + arr.Length + "] = { " + string.Join<int>(", ", arr) + " }");
QuickSort(arr, 0, arr.Length - 1);
Console.WriteLine("Sorted array[" + arr.Length + "] = { " + string.Join<int>(", ", arr) + " }\n");
}
static void QuicksortTestRandom(int maxSize)
{
Random r = new Random();
int[] randomValues = new int[r.Next(maxSize)];
for (int i = 0; i < randomValues.Length; i++)
{
randomValues[i] = r.Next(-999, 999);
}
TestQuickSort(randomValues);
}
static void Main(string[] args)
{
Console.WriteLine("QuickSort (Recursive), complexity = O(n log n)\n");
int[][] TestArrays = new int[][] {
new int[] { 6, 2, 5, 8, 1, 10, 0, 3, 7, 9, 4 },
new int[] { 3, 5, 4, 2, 5, 3, 5, 2, 4, 3, 5, 5, 4, 1, 4 },
new int[] { 67, 12, 95, 56, 1, 85, 85, 1, 100, 23, 60, 9, 0, 85, 0, 85, 85, 90, 85, 85 },
new int[] { 1 },
new int[] { 0, 0 },
new int[] { 1, 0 },
new int[] { 0, 1 },
new int[] { 1, 0, 2 },
new int[] { 1, 0, 1, 0 },
new int[] {2, 1, 2, 1, 2, 1, 2, 1 },
new int[] { 1, 0, -1 },
new int[] { },
new int[] { 1, 3, 6, 2, 7, 5, -1, 4, 8, 9, 0 },
};
foreach(int[] arr in TestArrays)
{
TestQuickSort(arr);
}
QuicksortTestRandom(16);
Console.WriteLine("done... press enter to end.\n");
Console.ReadLine();
}
}