Example:
{ 54, 87, 23, 87, 45, 67, 7, 85, 65, 65, 3, 4, 55, 76, 65, 64, 5, 6, 4, 54, 45, 6, 4 };
{ 76, 57, 65, 3, 4, 55, 76, 65, 64, 5, 6, 4, 54, 45, 8, 65, 66, 57, 6, 7, 7, 56, 6, 7, 44, 57, 8, 76, 54, 67 };
Basically, I have two byte[], and need to find the largest identical sequence of bytes in both.
I have tried the obvious thing and wrote some code that bruteforces the result:
var bestIndex = 0;
var bestCount = 0;
for (var i1 = 0; i1 + bestCount < data1.Length; i1++)
{
var currentCount = 0;
for (var i2 = 0; i2 < data2.Length; i2++)
{
if (data1[i1 + currentCount] == data2[i2])
{
currentCount++;
if (i1 + currentCount == data1.Length)
{
bestCount = currentCount;
bestIndex = i1;
break;
}
}
else
{
if (currentCount > bestCount)
{
bestCount = currentCount;
bestIndex = i1;
}
currentCount = 0;
}
}
if (currentCount > bestCount)
{
bestCount = currentCount;
bestIndex = i1;
}
}
However, in my application the byte arrays will be much larger, up to a GB even. So basically I need a hint / code on how to be more efficient than that.
I had a couple thoughts on this. I'm not sure if this helps or hurts, but have you considered working backwards through the largest possibilities first, so you can terminate as soon as you've found a match.
byte[] b1 = { 54, 87, 23, 87, 45, 67, 7, 85, 65, 65, 3, 4, 55, 76, 65, 64, 5, 6, 4, 54, 45, 6, 4 };
byte[] b2 = { 76, 57, 65, 3, 4, 55, 76, 65, 64, 5, 6, 4, 54, 45, 8, 65, 66, 57, 6, 7, 7, 56, 6, 7, 44, 57, 8, 76, 54, 67 };
//figure out which one is smaller, since that one will limit the range options
byte[] smaller;
byte[] bigger;
if (b1.Count() > b2.Count())
{
bigger = b1;
smaller = b2;
}
else
{
bigger = b2;
smaller = b1;
}
// doesn't matter what order we put these in, since they will be ordered later by length
List<Tuple<int, int>> ranges = new List<Tuple<int, int>>();
Parallel.For(0, smaller.Count(), (i1) => {
Parallel.For(i1 + 1, smaller.Count(), (i2) =>
{
ranges.Add(new Tuple<int, int>(i1, i2));
});
});
// order by length of slice produced by range in descending order
// this way, once we get an answer, we know nothing else can be longer
ranges = ranges.OrderByDescending(x => x.Item2 - x.Item1).ToList();
Tuple<int, int> largestMatchingRange = new Tuple<int, int>(0, 0);
foreach (Tuple<int, int> range in ranges)
{
bool match = true; // set in outer loop to allow for break
for (int i1 = 0; i1 < bigger.Count(); i1++)
{
if (bigger.Count() <= i1 + (range.Item2 - range.Item1))
{
//short cut if the available slice from the bigger array is shorter than the range length
match = false;
continue;
}
match = true; // reset to true to allow for new attempt for each larger array slice
for (int i2 = range.Item1, i1Temp = i1; i2 < range.Item2; i2++, i1Temp++)
{
if (bigger[i1Temp] != smaller[i2])
{
match = false;
break;
}
}
if (match)
{
largestMatchingRange = range;
break;
}
}
if (match)
{
break;
}
}
byte[] largestMatchingBytes = smaller.Skip(largestMatchingRange.Item1).Take(largestMatchingRange.Item2 - largestMatchingRange.Item1).ToArray();
Instead of checking the bytes one by one, you can save the index locations for each byte value in a dictionary of lists. In your case arrays of 256 lists might be better.
List<int>[] index(byte[] a) { // List<long> if the array can be more than 2GB
var lists = new List<int>[256];
for(int i = 0; i < a.Length; i++) {
var b = a[i];
if (lists[b] == null) lists[b] = new List<int>();
lists[b].Add(i);
}
return lists;
}
then you can loop over the 256 possible byte values
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 };
byte[] data2 = { 76, 57, 65, 3, 4, 55, 76, 65, 64, 5, 6, 4, 54, 45, 8, 65, 66, 57, 6, 7, 7, 56, 6, 7, 44, 57, 8, 76, 54, 67 };
var indexes1 = index(data1);
var indexes2 = index(data2);
var bestIndex = 0;
var bestCount = 0;
for (var b = 0; b < 256; b++)
{
var list1 = indexes1[b]; if (list1 == null) continue;
var list2 = indexes1[b]; if (list2 == null) continue;
foreach(var index1 in list1)
{
foreach (var index2 in list2)
{
// your code here
for (var i1 = index1; i1 < data1.Length - bestCount; i1++)
{
var currentCount = 0;
for (var i2 = index2; i2 < data2.Length; i2++)
{
if (data1[i1 + currentCount] == data2[i2])
{
currentCount++;
if (i1 + currentCount == data1.Length)
{
bestCount = currentCount;
bestIndex = i1;
break;
}
}
else
{
if (currentCount > bestCount)
{
bestCount = currentCount;
bestIndex = i1;
}
currentCount = 0;
}
}
if (currentCount > bestCount)
{
bestCount = currentCount;
bestIndex = i1;
}
}
}
}
}
var best = data1.Skip(bestIndex).Take(bestCount);
Debug.Print(bestIndex + ", " + bestCount + ": " + string.Join(", ", best));
In theory this feels like it will take less comparisons for bigger arrays, but in practice it will have more memory cache misses, so I am not sure if it will be faster than a more linear parallel version like in the other answer. I did not think into this too much but hopefully it can give you some ideas in case I got it wrong.
Update
I just realized how bad this idea is for a regular machine with less than 32 GB of memory as the list of indexes will take more than 4 times the memory of the byte array.
I figured out the loops, this one should be faster.
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 };
byte[] data2 = { 76, 57, 65, 3, 4, 55, 76, 65, 64, 5, 6, 4, 54, 45, 8, 65, 66, 57, 6, 7, 7, 56, 6, 7, 44, 57, 8, 76, 54, 67 };
//figure out which one is smaller, since that one will limit the range options
byte[] smaller;
byte[] bigger;
if (data1.Count() > data2.Count())
{
bigger = data1;
smaller = data2;
}
else
{
bigger = data2;
smaller = data1;
}
Tuple<int, int> largestMatchingRange = new Tuple<int, int>(0, 0);
//iterate over slices in reverse length order
for (int length = smaller.Count() - 1; length > 0; length--)
{
int numberOfSlicesForLength = smaller.Count() - length;
bool match = true; // set in outer loop to allow for break
for (int start = 0; start < numberOfSlicesForLength; start++)
{
//within a collection of similarly sized slices, we start with the slice found first within the array
Tuple<int, int> range = new Tuple<int, int>(start, start + length);
for (int i1 = 0; i1 < bigger.Count(); i1++)
{
if (bigger.Count() <= i1 + (range.Item2 - range.Item1))
{
//short cut if the available slice from the bigger array is shorter than the range length
match = false;
continue;
}
match = true; // reset to true to allow for new attempt for each larger array slice
for (int i2 = range.Item1, i1Temp = i1; i2 < range.Item2; i2++, i1Temp++)
{
if (bigger[i1Temp] != smaller[i2])
{
match = false;
break;
}
}
if (match)
{
largestMatchingRange = range;
break;
}
}
if (match)
{
break;
}
}
if (match)
{
break;
}
}
byte[] largestMatchingBytes = smaller.Skip(largestMatchingRange.Item1).Take(largestMatchingRange.Item2 - largestMatchingRange.Item1).ToArray();
Related
I have this array. If the minimum Value is defined as 22, I want the get the maximum number of integers that are next to each other that are higher than 22. For example, here 22 and 23 and higher or equal to 22. The loop breaks at 21. The counter is 2 integers. Then the loop goes to 22,23,23,24,22 and the counter is now 5 integers.At the end I need to get the range that delivers the maximum number of integers that is 5 in this case.
How will I write this code please? I am working for 2 days on this now.
Array:
int[] tempList = { 20, 22, 23, 21, 19, 18, 20, 22, 23, 23, 24, 22, 21 };
Min Value: 22
int[] tempList = { 20, 22, 23, 21, 19, 18, 20, 22, 23, 23, 24, 22, 21 };
var g = tempList.ToArray();
int lastStartIndex = -1;
int lastEndIndex = -1;
int min = 20;
int lastSum = 0;
int i = 0;
while ( i < g.Length)
{
if (g[i] > min)
{
lastStartIndex = i;
int lastSumTemp = 0;
int j = lastStartIndex ;
while (j<g.Length)
{
if (g[j] > min)
{
lastSumTemp += g[j];
j++;
}
else
{
if (lastSumTemp> lastSum)
{
lastSum = lastSumTemp;
lastEndIndex = j;
}
break;
}
}
if (j==g.Length && g[j-1] > min)
{
if (lastSumTemp > lastSum)
{
lastSum = lastSumTemp;
lastEndIndex = j-1;
}
break;
}
i = j + 1;
}
else
{
i++;
}
}
Console.WriteLine(lastStartIndex);
Console.WriteLine(lastEndIndex);
Console.WriteLine(lastSum);
Console.ReadLine();
This question already has answers here:
Array of an unknown length in C#
(11 answers)
Closed 2 years ago.
I am designing a program that asks for the user to input 2 numbers. It then finds the prime factors of each number. The number of prime factors depends on what number the user inputs. I need an array whose number of elements isn't known ahead of time so that I can input the prime factors of one of the numbers into the array of variable length.
int a = -1, b = -1;
string sa, sb;
int GCF = 0;
int LCM = 0;
int temp = 1, tempb = 1;
int[] primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97};
while (aValid == true)
{
for (int i = 0; i < 25; i++)
{
if (a % primes[i] == 0)
{
Console.Write(" ");
Console.Write(primes[i]);
//my attempt below to input current prime factor into variable array
int[] arrb = primes[i];
a = a / primes[i];
}
}
if (a == 1)
{
break;
}
}
You can use List<int> instead of int array.
When declaring a new List you don't need to specify the capacity.
In your code, add the following line above while loop:
List<int> arrb = new List<int>();
Then replace int[] arrb = primes[i]; with
arrb.Add(primes[i]);
I need to loop through a 2D array of ints that is 4x4, but I also have to create 4 2x2 arrays out of it. Then I have to loop through each of those 4 2x2 arrays to pick out the average of the numbers in each 2x2 array.
public int[,] Reduced(Sampler sampler)
{
int[,] a = new int[SampleSize,SampleSize];
for (int r = 0; r < Math.Sqrt(image.Length); r+=SampleSize)
{
for (int c = 0; c < Math.Sqrt(image.Length); c+=SampleSize)
{
InsideLoop(a, r, c);
}
}
return a;
}
private void InsideLoop(int[,] a, int r, int c)
{
for (r = 0; r < SampleSize; r++)
{
for (c = 0; c < SampleSize; c++)
{
a[r, c] = image[r, c];
Console.WriteLine("Value: {0}", a[r, c]);
}
}
}
This is essentially what I've got so far, but it's working how it's written instead of how I'd like it to work. For this example, SampleSize is a variable that is set to 2. What this does currently is print out the numbers that create the first 2x2 array four times. My laptop battery is about to die, so I can't elborate more, but if anyone has any tips while I'm driving home. I had to finish posting this on my phone.
Does this work?
int sampleSize = 2;
int[,] data = {
{1, 2, 3, 4 },
{5, 6, 7, 8 },
{9, 10, 11, 12 },
{13, 14, 15, 16 }
};
//assume input data is a perfect square as per your example
int max = (int)Math.Sqrt(data.Length);
List<int[,]> samples = new List<int[,]>();
int startX = 0;
while (startX + sampleSize <= max)
{
int startY = 0;
while (startY + sampleSize <= max)
{
int[,] sample = new int[sampleSize, sampleSize];
for (int x = 0; x < sampleSize;x++)
{
for (int y = 0; y < sampleSize; y++)
{
sample[x, y] = data[x + startX, y + startY];
}
}
samples.Add(sample);
startY += sampleSize;
}
startX += sampleSize;
}
//for output testing
foreach (int[,] sample in samples)
{
Console.WriteLine(sample[0, 0].ToString().PadLeft(2) + " | " + sample[0, 1]);
Console.WriteLine(" ----- ");
Console.WriteLine(sample[1, 0].ToString().PadLeft(2) + " | " + sample[1, 1]);
Console.WriteLine();
Console.WriteLine();
}
Console.ReadLine();
and here's the output
1 | 2
-----
5 | 6
3 | 4
-----
7 | 8
9 | 10
-----
13 | 14
11 | 12
-----
15 | 16
Generalized version:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
int[,] original = new int[,] { { 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 },
{ 13, 14, 15, 16 } };
int[,] harder = new int[,] { { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
{ 10, 11, 12, 13, 14, 15, 16, 17, 18 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27 },
{ 28, 29, 30, 31, 32, 33, 34, 35, 36 },
{ 37, 38, 39, 40, 41, 42, 43, 44, 45 },
{ 46, 47, 48, 49, 50, 51, 52, 53, 54 },
{ 55, 56, 57, 58, 59, 60, 61, 62, 63 },
{ 64, 65, 66, 67, 68, 69, 70, 71, 72 },
{ 73, 74, 75, 76, 77, 78, 79, 80, 81 } };
IterateArray(original);
Console.ReadLine();
}
static void IterateArray(int[,] array)
{
double tDim = Math.Sqrt(Math.Sqrt(array.Length));
int dim = (int)tDim;
if (dim != tDim) throw new ArgumentException("Not a valid array!");
for (int i = 0; i < dim; i++)
{
IterateRows(array, dim, i);
}
}
static void IterateRows(int[,] array, int dim, int pass)
{
int maxRow = dim * dim;
IList<int> list = new List<int>(maxRow);
for (int curRow = 0; curRow < maxRow; curRow++)
{
IterateColumns(array, dim, curRow, pass, list);
if (list.Count == maxRow)
{
PrintNewArray(list, dim);
list.Clear();
}
}
}
static void IterateColumns(int[,] array, int dim, int row, int pass, IList<int> list)
{
int maxCol = dim + (dim * pass);
for (int curCol = pass * dim; curCol < maxCol; curCol++)
{
list.Add(array[row, curCol]);
}
}
static void PrintNewArray(IList<int> list, int dim)
{
for(int i = 0; i < list.Count; i++)
{
if (i % dim == 0)
{
Console.WriteLine();
}
Console.Write($"{list[i]} ");
}
Console.WriteLine($"\nAverage {list.Average()}");
}
}
I have a sequence. For example:
new [] { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 }
Now I have to remove duplicated values without changing the overall order. For the sequence above:
new [] { 10, 1, 5, 25, 45, 40, 100, 1, 2, 3 }
How to do this with LINQ?
var list = new List<int> { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
List<int> result = list.Where((x, index) =>
{
return index == 0 || x != list.ElementAt(index - 1) ? true : false;
}).ToList();
This returns what you want. Hope it helped.
var list = new List<int> { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
var result = list.Where((item, index) => index == 0 || list[index - 1] != item);
It may be technically possible (though I don't think you can with a one-liner) to solve this with LINQ, but I think it's more elegant to write it yourself.
public static class ExtensionMethods
{
public static IEnumerable<T> PackGroups<T>(this IEnumerable<T> e)
{
T lastItem = default(T);
bool first = true;
foreach(T item in e)
{
if (!first && EqualityComparer<T>.Default.Equals(item, lastItem))
continue;
first = false;
yield return item;
lastItem = item;
}
}
}
You can use it like this:
int[] packed = myArray.PackGroups().ToArray();
It's unclear from the question what should be returned in the case of 1,1,2,3,3,1. Most answers given return 1,2,3, whereas mine returns 1,2,3,1.
You can use Contains and preserve order
List<int> newList = new List<int>();
foreach (int n in numbers)
if (newList.Count == 0 || newList.Last() != n)
newList.Add(n);
var newArray = newList.ToArray();
OUTPUT:
10, 1, 5, 25, 45, 40, 100, 1, 2, 3
Did you try Distinct?
var list = new [] { 10, 20, 20, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
list = list.Distinct();
Edit: Since you apparently only want to group items with the same values when consecutive, you could use the following:
var list = new[] { 10, 1, 1, 5, 25, 45, 45, 45, 40, 100, 1, 1, 2, 2, 3 };
List<int> result = new List<int>();
foreach (int item in list)
if (result.Any() == false || result.Last() != item)
result.Add(item);
I have this program that is reading the test string from a textbox and convert it to a byte array that makeup data to be diplayed on a screen. I am getting very close. The code can currently pull the text, convert it to a char array, and then replace the zeros in the byte array with useful data from a 2 dimensional array that contains 5 bits for all the letters of the alphabet. I am have a problem though. The code only seems to run once. If I click the button a second time I end up with an "indexOutOfRange exception unhandled." Also it only seems to work for one letter at a time
EX: if I type "A" it will display, but if I type "AA" I get the same error.
Here is the WordArray[]
byte[,] Letters = new byte[18, 5] { { 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 },
{ 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 },
{ 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 },
{ 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 },
{ 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 },
{ 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 },
{ 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 },
{ 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 },
{ 63, 72, 72, 63, 0 },
{ 127, 73, 73, 54, 0 } };
Here is the click_button method:
int Aindex = 0;
int LettersIndex = 0;
private void SendButton_Click(object sender, EventArgs e)
{
WordIndex = 0;
if (Aindex > 0)
{
Aindex = 0;
}
string CurrentTextString = textBox1.Text;
char[] charArray = CurrentTextString.ToCharArray();
if (textBox1.Text == "")
{
}
else
{
foreach (char c in charArray)
{
int index = 0;
CharAsciiArray[index] = Convert.ToChar((Convert.ToInt32(c)));
textBox2.Text += CharAsciiArray[index] + " ";
charCount++;
index++;
}
for (int NumberofBytes = 0; NumberofBytes < charCount; NumberofBytes++)
{
LettersIndex = 0;
// int currentChar = CharAsciiArray[index] - 65;
//textBox2.Text += currentChar;
int currentCharAscii = (CharAsciiArray[Aindex]);
int currentChar = currentCharAscii - 'A';
for (int NumberofBits = 0; NumberofBits < 5; NumberofBits++)
{
// textBox2.Text += currentChar;
WordArray[WordIndex + 3] = Letters[currentChar, LettersIndex];
textBox2.Text += WordArray[WordIndex] + " ";
LettersIndex++;
WordIndex++;
}
Aindex++;
}
SendingData = true;
//SendNextByte();
serialPort1.Write(WordArray, 0, WordArray.Length);
}
}
In the following loop
foreach (char c in charArray)
{
int index = 0;
CharAsciiArray[index] = Convert.ToChar((Convert.ToInt32(c)));
textBox2.Text += CharAsciiArray[index] + " ";
charCount++;
index++;
}
You apparently want to increase the index on each iteration but you're resetting index to 0 every time.
Besides that, make up your mind on what pattern you want to follow when naming your variables. For instance:
Why is AIndex not AnIndex and how would you name the next index? AnotherIndex? Does it need to be global? Why does charArray start with a lowercase c and NumberOfBytes with an uppercase N? Write code as if you would have to explain it to your wife / husband (who knows?) and it'll be easier to maintain / debug.