As per following the inital thread make efficient the copy of symmetric matrix in c-sharp from cMinor.
I would be quite interesting with some inputs in how to build a symmetric square matrix multiplication with one line vector and one column vector by using an array implementation of the matrix, instead of the classical
long s = 0;
List<double> columnVector = new List<double>(N);
List<double> lineVector = new List<double>(N);
//- init. vectors and symmetric square matrix m
for (int i=0; i < N; i++)
{
for(int j=0; j < N; j++){
s += lineVector[i] * columnVector[j] * m[i,j];
}
}
Thanks for your input !
The line vector times symmetric matrix equals to the transpose of the matrix times the column vector. So only the column vector case needs to be considered.
Originally the i-th element of y=A*x is defined as
y[i] = SUM( A[i,j]*x[j], j=0..N-1 )
but since A is symmetric, the sum be split into sums, one below the diagonal and the other above
y[i] = SUM( A[i,j]*x[j], j=0..i-1) + SUM( A[i,j]*x[j], j=i..N-1 )
From the other posting the matrix index is
A[i,j] = A[i*N-i*(i+1)/2+j] // j>=i
A[i,j] = A[j*N-j*(j+1)/2+i] // j< i
For a N×N symmetric matrix A = new double[N*(N+1)/2];
In C# code the above is:
int k;
for(int i=0; i<N; i++)
{
// start sum with zero
y[i]=0;
// below diagonal
k=i;
for(int j=0; j<=i-1; j++)
{
y[i]+=A[k]*x[j];
k+=N-j-1;
}
// above diagonal
k=i*N-i*(i+1)/2+i;
for(int j=i; j<=N-1; j++)
{
y[i]+=A[k]*x[j];
k++;
}
}
Example for you to try:
| -7 -6 -5 -4 -3 | | -2 | | -5 |
| -6 -2 -1 0 1 | | -1 | | 21 |
| -5 -1 2 3 4 | | 0 | = | 42 |
| -4 0 3 5 6 | | 1 | | 55 |
| -3 1 4 6 7 | | 7 | | 60 |
To get the quadratic form do a dot product with the multiplication result vector x·A·y = Dot(x,A*y)
You could make matrix multiplication pretty fast with unsafe code. I have blogged about it.
Making matrix multiplication as fast as possible is easy: Use a well-known library. Insane amounts of performance work has gone into such libraries. You cannot compete with that.
Related
This question already has answers here:
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 2 years ago.
Background:
Hi, I am currently learning C# and doing some practices at HackerRank.
So I've come across a staircase problem which I should code to receive an integer input, then output the staircase 'diagram' using spaces and hashes.
Challenge faced:
The codes below give me a Runtime Error that says " System.ArgumentOutOfRangeException: Length cannot be less than zero. "
// Complete the staircase function below.
static void staircase(int n) {
// Find number of spaces needed
string space = "";
for (int i = 1; i < n; i++) {
space += " ";
}
string hash = "#";
for (int j = 0; j < n; j++) {
space = space.Substring(0, space.Length - j);
Console.WriteLine(space + hash);
hash += "#";
}
}
However, when I change the code in the second for loop from
space = space.Substring(0, space.Length - j);
Console.WriteLine(space + hash);
to Console.WriteLine(space.Substring(0, space.Length - j) + hash);
It then runs successfully, I see no difference and am confused why does it work?
Those two are not the same.
This line:
Console.WriteLine(space.Substring(0, space.Length - j) + hash);
is similar to this:
string temporarySpace = space.Substring(0, space.Length - j);
Console.WriteLine(temporarySpace + hash);
In there, you are not modifying the variable space, instead, you are creating a temporary one
I changed your code so that you can see this behaviour:
https://dotnetfiddle.net/ZhXIWa
for (int j = 0; j < n; j++) {
var tempSpace = space.Substring(0, space.Length - j);
Console.Write($"space: {space.Length} | ");
Console.Write($"tempSpace: {tempSpace.Length} | ");
Console.Write(space + hash);
Console.Write(Environment.NewLine);
hash += "#";
}
// Output
space: 9 | tempSpace: 9 | #
space: 9 | tempSpace: 8 | ##
space: 9 | tempSpace: 7 | ###
space: 9 | tempSpace: 6 | ####
space: 9 | tempSpace: 5 | #####
space: 9 | tempSpace: 4 | ######
space: 9 | tempSpace: 3 | #######
space: 9 | tempSpace: 2 | ########
space: 9 | tempSpace: 1 | #########
space: 9 | tempSpace: 0 | ##########
In there you will see how many characters both variables (space and temporarySpace) have. You can modify the code to the one that is not working and you will see the difference :)
I have a scenario where the user will input a number that will be the multiplier for an image (the image isn't from a file it's just text on the console using special characters). It could be thought of as ASCII art but I didn't go the route of using ASCII values for this scenario. I instead am using multiple strings, and I want to only multiply specific characters within each string, where it will match the original scale of the original 'image', but bigger depending on what the user picks.
I've tried going down the ASCII route, but my code was pretty messy. I tried to make each line of the image a separate method, and call each of them through a parameter, but never got to the parameter part because I got confused on how I could change the specific characters like stated above.
static void Main (string[] args)
{
string edgeBorder = "#================#";
int multiplier;
multiplier = Int32.Parse(Console.ReadLine());
Console.WriteLine(multiplier);
Console.WriteLine("Sure! Coming right up...");
//Top layer of quilt
for (int i = 0; i < multiplier; i++)
{
Console.Write(edgeBorder + " ");
}
Console.Write("\n");
//top half of quilt
for (int line = 1; line <= 4; line++)
{
for (int s = 0, s < (8 - 2 * line) * multiplier; s++)
{ //s for space
Console.Write(" ");
}
Console.Write("|" + " ");
if (line == 1)
{
for (int d = 0; d < 2 * multiplier; d++)
{ //d for diamond
Console.Write("<>");
}
}
else
{
Console.Write("<>");
for (int p = 0; p < 4 * multiplier * (line - 1); p++)
{ //p for period
Console.Write(".");
}
Console.Write("<>");
}
Console.Write(" " + "|");
for (int s = 0, s < (8 - 2 * line) * multiplier; s++)
{ //s for space
Console.Write(" ");
}
Console.Write("\n");
}
//bottom half of quilt
for (int line = 4; line >= 1; line--)
{
for (int s = 0, s < (8 - 2 * line) * multiplier; s++)
{ //s for space
Console.Write(" ");
}
Console.Write("|" + " ");
if (line == 1)
{
for (int d = 0; d < 2 * multiplier; d++)
{ //d for diamond
Console.Write("<>");
}
}
else
{
Console.Write("<>");
for (int p = 0; p < 4 * multiplier * (line - 1); p++)
{ //p for period
Console.Write(".");
}
Console.Write("<>");
}
Console.Write(" " + "|");
for (int s = 0, s < (8 - 2 * line) * multiplier; s++)
{ //s for space
Console.Write(" ");
}
Console.Write("\n");
}
//bottom layer of quilt
for (int i = 0; i < multiplier; i++)
{
Console.Write(edgeBorder + " ");
}
}
//I went a different route, and decided not to use an array, only issue is it
//keeps telling me 's' is already defined in the scope, but when I change it to
//something different it says the same exact thing
/*$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$
and be this if multiplied by 2
$================$ $================$
| <><><><> |
| <>........<> |
| <>................<> |
|<>........................<>|
|<>........................<>|
| <>................<> |
| <>........<> |
| <><><><> |
$================$ #================$
I tried putting this into the "what's expected but it wouldn't format properly*/
I'm expecting an 'image' to be shown in the console, that's at whatever scale the user wants it to be. I'm lost on how to implement my idea to the code.
I put the expected part in the code section, it wouldn't format properly.
A. Inflate
Algorithm
image_width = firstLine.Lenght;
for each line:
if( first or last)
repeat with a space in between
else
pad right until width == image_width
repeat each air molecule (the dot) twice
( pad left, pad right ) until width == image_width
Code
private static IEnumerable<string> Inflate(string[] lines, int scale, string air)
{
// image_width = firstLine.Lenght;
// for each line:
// if( first or last)
// repeat with a space in between
// else
// pad right until width == image_width
// repeat each air molecule (the dot) twice
// ( pad left, pad right ) until width == image_width
var imageWidth = lines[0].TrimEnd().Length;
return lines.Select((line, i) =>{
if (i == 0 || i == lines.Length - 1)
return string.Join("", Enumerable.Repeat(line.TrimEnd(), scale));
line = line.PadRight(imageWidth, ' ');
line = line.Replace(air, string.Join("", Enumerable.Repeat(air, scale)));
while (line.Length < imageWidth * scale) line = " " + line + " ";
return line;
});
}
private static string Inflate(string input, int scale, string air)
=> string.Join(Environment.NewLine, Inflate(
input.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries), scale, air));
Test code
// I Maually removed '/*' from the input
// Please note that the first line ends with a space,
// but this space trimmed and is not considered part of the image
string input = #"$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$";
Console.WriteLine(input);
Console.WriteLine(Inflate(input, air: ".", scale: 2));
Console.WriteLine(Inflate(input, air: ".", scale: 3));
Console.WriteLine(Inflate(input, air: ".", scale: 4));
Output
Notes:
The images don't contain the spaces between the repeated header and footer, and your example output has spaces, but these spaces make the images asymmetrical, so I didn't includ them.
Your test image changes | <><> | to | <><><><> |, and this alogorithm doesn't. It could be modified to do that. It could also be extended to not have gaps so that air (.) doesn't escape, but that's for another day.
$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$
$================$$================$
| <><> |
| <>........<> |
| <>................<> |
|<>........................<>|
|<>........................<>|
| <>................<> |
| <>........<> |
| <><> |
$================$$================$
$================$$================$$================$
| <><> |
| <>............<> |
| <>........................<> |
|<>....................................<>|
|<>....................................<>|
| <>........................<> |
| <>............<> |
| <><> |
$================$$================$$================$
$================$$================$$================$$================$
| <><> |
| <>................<> |
| <>................................<> |
|<>................................................<>|
|<>................................................<>|
| <>................................<> |
| <>................<> |
| <><> |
$================$$================$$================$$================$
B. Multiply the atoms
This is not exactly what you want, but it may be good enough.
var output = string.Join(Environment.NewLine,
lines.Select( l => new string(l.SelectMany(ch => Enumerable.Repeat(ch, scale)).ToArray())));
Test
Code
string input = #"/*$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$";
Console.WriteLine(input);
var lines = input.Split(new [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
int scale = 2;
Console.WriteLine("");
Console.WriteLine($"Scale : {scale}");
Console.WriteLine("");
var newLines =
lines.Select( l => new string(l.SelectMany(ch => Enumerable.Repeat(ch, scale)).ToArray()));
var output = string.Join(Environment.NewLine,newLines);
Console.WriteLine(output);
Output
/*$================$
| <><> |
| <>....<> |
| <>........<> |
|<>............<>|
|<>............<>|
| <>........<> |
| <>....<> |
| <><> |
$================$
Scale : 2
//**$$================================$$
|| <<>><<>> ||
|| <<>>........<<>> ||
|| <<>>................<<>> ||
||<<>>........................<<>>||
||<<>>........................<<>>||
|| <<>>................<<>> ||
|| <<>>........<<>> ||
|| <<>><<>> ||
$$================================$$
I am looking for more efficient algorithm for printing numbers that are palindromic (for example 1001) and their power to 2 (1001 * 1001 = 1002001) are palindromic too. In my algorithm I think I make unnecessary checks to determine if number is palindromic. How can I improve it?
In [1000,9999] range I found this kind of 3 numbers: 1001, 1111 and 2002.
This is my algorithm:
for (int i = n; i <= m; i++)
{
if (checkIfPalindromic(i.ToString()))
{
if (checkIfPalindromic((i * i).ToString()))
Console.WriteLine(i);
}
}
this is my method to determine if number is palindromic:
static bool checkIfPalindromic(string A)
{
int n = A.Length - 1;
int i = 0;
bool IsPalindromic = true;
while (i < (n - i))
{
if (A[i] != A[n - i])
{
IsPalindromic = false;
break;
}
i++;
}
return IsPalindromic;
}
Instead of checking very number for "palindromness", it may be better to iterate through palindromes only. For that just iterate over the first halves of the number and then compose palindrome from it.
for(int half=10;half<=99;++half)
{
const int candidate=half*100+Reverse(half);//may need modification for odd number of digits
if(IsPalindrome(candidate*candidate))
Output(candidate);
}
This will make your program O(sqrt(m)) instead of O(m), which will probably beat all improvements of constant factors.
What you have already seems fairly efficient
Scale is checking 1,000,000 integers
Note : i use longs
Disclaimer : I must admit these results are a little sketchy, ive added more scaling so you can see
Results
Mode : Release
Test Framework : .Net 4.7.1
Benchmarks runs : 10 times (averaged)
Scale : 1,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
-----------------------------------------------------------------
Mine2 | 0.107 ms | 0.102 ms | 0.01 | 358,770 | Yes | 5.83 %
Original | 0.114 ms | 0.098 ms | 0.05 | 361,810 | Base | 0.00 %
Mine | 0.120 ms | 0.100 ms | 0.03 | 399,935 | Yes | -5.36 %
Scale : 10,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
-------------------------------------------------------------------
Mine2 | 1.042 ms | 0.944 ms | 0.17 | 3,526,050 | Yes | 11.69 %
Mine | 1.073 ms | 0.936 ms | 0.19 | 3,633,369 | Yes | 9.06 %
Original | 1.180 ms | 0.920 ms | 0.29 | 3,964,418 | Base | 0.00 %
Scale : 100,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
--------------------------------------------------------------------
Mine2 | 10.406 ms | 9.502 ms | 0.91 | 35,341,208 | Yes | 6.59 %
Mine | 10.479 ms | 9.332 ms | 1.09 | 35,592,718 | Yes | 5.93 %
Original | 11.140 ms | 9.272 ms | 1.72 | 37,624,494 | Base | 0.00 %
Scale : 1,000,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
-------------------------------------------------------------------------
Original | 106.271 ms | 101.662 ms | 3.61 | 360,996,200 | Base | 0.00 %
Mine | 107.559 ms | 102.695 ms | 5.35 | 365,525,239 | Yes | -1.21 %
Mine2 | 108.757 ms | 104.530 ms | 4.81 | 368,939,992 | Yes | -2.34 %
Mode : Release
Test Framework : .Net Core 2.0
Benchmarks runs : 10 times (averaged)
Scale : 1,000,000
Name | Average | Fastest | StDv | Cycles | Pass | Gain
-------------------------------------------------------------------------
Mine2 | 95.054 ms | 87.144 ms | 8.45 | 322,650,489 | Yes | 10.54 %
Mine | 95.849 ms | 89.971 ms | 5.38 | 325,315,589 | Yes | 9.79 %
Original | 106.251 ms | 84.833 ms | 17.97 | 350,106,144 | Base | 0.00 %
Given
protected override List<int> InternalRun()
{
var results = new List<int>();
for (var i = 0; i <= Input; i++)
if (checkIfPalindromic(i) && checkIfPalindromic(i * (long)i))
results.Add(i);
return results;
}
Mine1
private static unsafe bool checkIfPalindromic(long value)
{
var str = value.ToString();
fixed (char* pStr = str)
{
for (char* p = pStr, p2 = pStr + str.Length - 1; p < p2;)
if (*p++ != *p2--)
return false;
}
return true;
}
Mine2
private static bool checkIfPalindromic(long value)
{
var str = value.ToString();
var n = str.Length - 1;
for (var i = 0; i < n - i; i++)
if (str[i] != str[n - i])
return false;
return true;
}
More optimistic way is to use int instead of string. this algorithm is about two time faster:
static int[] pow10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
static bool checkIfPalindromic(int A)
{
int n = 1;
int i = A;
if (i >= 100000000) { n += 8; i /= 100000000; }
if (i >= 10000) { n += 4; i /= 10000; }
if (i >= 100) { n += 2; i /= 100; }
if (i >= 10) { n++; }
int num = A / pow10[(n+1) / 2];
for (; num % 10 == 0;)
num /= 10;
int reversedNum = 0;
for (int input = A % pow10[ n / 2]; input != 0; input /= 10)
reversedNum = reversedNum * 10 + input % 10;
return num == reversedNum;
}
Usage:
for (int i = n; i <= m; i++)
if (checkIfPalindromic(i) && checkIfPalindromic(i * i))
Console.WriteLine(i);
Benchmark:
Bemchmark in range of [1000, 99999999] on Core2Duo CPU:
This algorithm: 12261ms
Your algorithm: 24181ms
Palindromic Numbers:
1001
1111
2002
10001
10101
10201
11011
11111
11211
20002
20102
you can use Linq to simplify your code
sample:-
static void Main(string[] args)
{
int n = 1000, m = 9999;
for (int i = n; i <= m; i++)
{
if (CheckIfNoAndPowerPalindromic(i))
{
Console.WriteLine(i);
}
}
}
private static bool CheckIfNoAndPowerPalindromic(int number)
{
string numberString = number.ToString();
string numberSquareString = (number * number).ToString();
return (Enumerable.SequenceEqual(numberString.ToCharArray(), numberString.ToCharArray().Reverse()) &&
Enumerable.SequenceEqual(numberSquareString.ToCharArray(), numberSquareString.ToCharArray().Reverse()));
}
output:-
1001
1111
2002.
Loop up to len/2 as follow:
static bool checkIfPalindromic(string A)
{
for (int i = 0; i < A.Length / 2; i++)
if (A[i] != A[A.Length - i - 1])
return false;
return true;
}
We can get an interesting optimisation by changing the palindromic checking method and using a direct integer reversing method instead of converting first to a string then looping in the string.
I used the method in the accepted answer from this question:
static int reverse(int n)
{
int left = n;
int rev = 0;
int r = 0;
while (left > 0)
{
r = left % 10;
rev = rev * 10 + r;
left = left / 10;
}
return rev;
}
I also used the StopWatch from System.Diagnostics to measure the elapsed time.
My function to check if a number is a palindromic number is:
static bool IsPalindromicNumber(int number)
{
return reverse(number) == number;
}
For n value of 1000 and for different values of m I get the following results for the elapsed time in milliseconds:
---------------------------------------------------------
| m | original | mine | optimisation|
---------------------------------------------------------
|9999 |6.3855 |4.2171 | -33.95% |
---------------------------------------------------------
|99999 |71.3961 |42.3399 | -40.69% |
---------------------------------------------------------
|999999 |524.4921 |342.8899 | -34.62% |
---------------------------------------------------------
|9999999 |7016.4050 |4565.4563 | -34.93% |
---------------------------------------------------------
|99999999 |71319.658 |49837.5632 | -30.12% |
---------------------------------------------------------
The measured values are an indicative and not absolute because from one run of the program to another they are different but the pattern stays the same and the second approach appears always faster.
To measure using the StopWatch:
With your method:
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
for (int i = n; i <= m; i++)
{
if (checkIfPalindromic(i.ToString()))
{
if (checkIfPalindromic((i * i).ToString()))
Console.WriteLine(i);
}
}
stopWatch.Stop();
Console.WriteLine("First approach: Elapsed time..." + stopWatch.Elapsed + " which is " + stopWatch.Elapsed.TotalMilliseconds + " miliseconds");
I used of course exact same approach with my changes:
With my method:
Stopwatch stopWatch2 = new Stopwatch();
stopWatch2.Start();
for (int i = n; i <= m; i++)
{
if (IsPalindromicNumber(i) && IsPalindromicNumber(i*i))
{
Console.WriteLine(i);
}
}
stopWatch2.Stop();
Console.WriteLine("Second approach: Elapsed time..." + stopWatch2.Elapsed + " which is " + stopWatch2.Elapsed.TotalMilliseconds + " miliseconds");
Sorry for the previous question, but I was so confused and stressed :S.
I have this function in R:
shift <- function(d, k) rbind( tail(d,k), head(d,-k), deparse.level = 0 )
and this data frame:
A B value
1 1 0.123
2 1 0.213
3 1 0.543
1 2 0.313
2 2 0.123
3 2 0.412
this function will transform this data frame to (in case k=1) :
A B value
3 2 0.412
1 1 0.123
2 1 0.213
3 1 0.543
1 2 0.313
2 2 0.123
Code:
string[] data = File.ReadAllLines("test.txt");
decimal[,] numbers = new decimal[data.Length, 3];
for(int x = 0; x < data.Length; x++)
{
string[] temp = data[x].Split(' ');
for(int y = 1; y < temp.Length; y++)
{
numbers[x,y] = Convert.ToDecimal(temp[y]);
}
}
that's the code i'm using to get the values from the text file , but i want to create the function that will rotate this table.
I want to make the same function for a text file in Java or C#.
How this can be done?
I'm storing the data in C# in a 2D array: decimal[,]
UPDATE:
your function will rotate them like the previous example, what i want to do is this:
i have this table:
A B value
1 1 0.123
2 1 0.213
3 1 0.543
1 2 0.313
2 2 0.123
3 2 0.412
i want it to become(in case of shift by 2) :
A B value
3 1 0.543
1 2 0.313
2 2 0.123
3 2 0.412
1 1 0.123
2 1 0.213
I think this will do what you want but I must admit I'm not that familiar with C# so I'd expect there to be a more idiomatic form with less looping:
static decimal[,] Rotate(decimal[,] input, int k)
{
int m = input.GetLength(0);
int n = input.GetLength(1);
decimal[,] result = new decimal[m, n];
for (int i=0; i<m; i++)
{
int p = (i + k) % m;
if (p < 0)
p += m;
for (int j=0; j<n; j++)
result[p, j] = input[i, j];
return result;
}
Regarding your update, that is handled by passing a negative value for k. For your example pass k=-2.
This is basically a restatement of this question: Java: Multi-dimensional array vs. One-dimensional but for C#.
I have a set amount of elements that make sense to store as a grid.
Should I use a array[x*y] or a array[x][y]?
EDIT: Oh, so there are one dimensional array[x*y], multidimensional array[x,y] and jagged array[x][y], and I probably want jagged?
There are many advantages in C# to using jagged arrays (array[][]). They actually will often outperform multidimensional arrays.
That being said, I would personally use a multidimensional or jagged array instead of a single dimensional array, as this matches the problem space more closely. Using a one dimensional array is adding complexity to your implementation that does not provide real benefits, especially when compared to a 2D array, as internally, it's still a single block of memory.
I ran a test on unreasonably large arrays and was surprised to see that Jagged arrays([y][x]) appear to be faster than the single dimension array with manual multiplication [y * ySize + x]. And multi dimensional arrays [,] are slower but not by that much.
Of course you would have to test out on your particular arrays, but it would seem like the different isn't much so you should just use whichever approach fits what you are doing the best.
0.280 (100.0% | 0.0%) 'Jagged array 5,059x5,059 - 25,593,481'
| 0.006 (2.1% | 2.1%) 'Allocate'
| 0.274 (97.9% | 97.9%) 'Access'
0.336 (100.0% | 0.0%) 'TwoDim array 5,059x5,059 - 25,593,481'
| 0.000 (0.0% | 0.0%) 'Allocate'
| 0.336 (99.9% | 99.9%) 'Access'
0.286 (100.0% | 0.0%) 'SingleDim array 5,059x5,059 - 25,593,481'
| 0.000 (0.1% | 0.1%) 'Allocate'
| 0.286 (99.9% | 99.9%) 'Access'
0.552 (100.0% | 0.0%) 'Jagged array 7,155x7,155 - 51,194,025'
| 0.009 (1.6% | 1.6%) 'Allocate'
| 0.543 (98.4% | 98.4%) 'Access'
0.676 (100.0% | 0.0%) 'TwoDim array 7,155x7,155 - 51,194,025'
| 0.000 (0.0% | 0.0%) 'Allocate'
| 0.676 (100.0% | 100.0%) 'Access'
0.571 (100.0% | 0.0%) 'SingleDim array 7,155x7,155 - 51,194,025'
| 0.000 (0.1% | 0.1%) 'Allocate'
| 0.571 (99.9% | 99.9%) 'Access'
for (int i = 6400000; i < 100000000; i *= 2)
{
int size = (int)Math.Sqrt(i);
int totalSize = size * size;
GC.Collect();
ProfileTimer.Push(string.Format("Jagged array {0:N0}x{0:N0} - {1:N0}", size, totalSize));
ProfileTimer.Push("Allocate");
double[][] Jagged = new double[size][];
for (int x = 0; x < size; x++)
{
Jagged[x] = new double[size];
}
ProfileTimer.PopPush("Allocate", "Access");
double total = 0;
for (int trials = 0; trials < 10; trials++)
{
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
total += Jagged[y][x];
}
}
}
ProfileTimer.Pop("Access");
ProfileTimer.Pop("Jagged array");
GC.Collect();
ProfileTimer.Push(string.Format("TwoDim array {0:N0}x{0:N0} - {1:N0}", size, totalSize));
ProfileTimer.Push("Allocate");
double[,] TwoDim = new double[size,size];
ProfileTimer.PopPush("Allocate", "Access");
total = 0;
for (int trials = 0; trials < 10; trials++)
{
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
total += TwoDim[y, x];
}
}
}
ProfileTimer.Pop("Access");
ProfileTimer.Pop("TwoDim array");
GC.Collect();
ProfileTimer.Push(string.Format("SingleDim array {0:N0}x{0:N0} - {1:N0}", size, totalSize));
ProfileTimer.Push("Allocate");
double[] Single = new double[size * size];
ProfileTimer.PopPush("Allocate", "Access");
total = 0;
for (int trials = 0; trials < 10; trials++)
{
for (int y = 0; y < size; y++)
{
int yOffset = y * size;
for (int x = 0; x < size; x++)
{
total += Single[yOffset + x];
}
}
}
ProfileTimer.Pop("Access");
ProfileTimer.Pop("SingleDim array");
}
Pros of array[x,y]:
- Runtime will perform more checks for you. Each index access will be checked to be within allowed range. With another approach you could easily do smth like a[y*numOfColumns + x] where x can be more than "number of columns" and this code will extract some wrong value without throwing an exception.
- More clear index access. a[x,y] is cleaner than a[y*numOfColumns + x]
Pros of array[x*y]:
- Easier iteration over the entire array. You need only one loop instead of two.
And winner is... I would prefer array[x,y]