0

I am a very basic user and do not know much about commands used in C, so please bear with me...I cant use very complicated codes. I have some knowledge in the stdio.h and ctype.h library, but thats about it. I have a matrix in a txt file and I want to load the matrix based on my input of number of rows and columns

For example, I have a 5 by 5 matrix in the file. I want to extract a specific 2 by 2 submatrix, how can I do that ?

I created a nested loop using :

FILE *sample
sample=fopen("randomfile.txt","r"); 
for(i=0;i<rows;i++){
  for(j=0;j<cols;j++){
     fscanf(sample,"%f",&matrix[i][j]);
   }
 fscanf(sample,"\n",&matrix[i][j]);
}
fclose(sample);

Sadly the code does not work .. If I have this matrix :

5.00 4.00 5.00 6.00 
5.00 4.00 3.00 25.00 
5.00 3.00 4.00 23.00 
5.00 2.00 352.00 6.00

And inputting 3 for row and 3 for column, I get :

5.00 4.00 5.00
6.00 5.00 4.00
3.00 25.00 5.00

Not only this isnt a 2 by 2 submatrix, but even if I wanted the first 3 rows and first 3 columns, its not printing it correctly....

I need to start at row 3 and col 3, then take the 2 by 2 submatrix !

I should have ended up with :

4.00 23.00 
352.00 6.00

I heard that I can use fgets and sscanf to accomplish this. Here is my trial code :

fgets(garbage,1,fin);
sscanf(garbage,"\n");

But this doesnt work either :(

What am I doing wrong ?

Please help. Thanks !

NLed
  • 1,845
  • 14
  • 39
  • 68
  • 2
    Why did you delete this question the last time you posted it? http://stackoverflow.com/questions/2796071/how-to-extract-a-submatrix-from-a-matrix If you delete your questions it makes it less likely will people want to put time into giving good answers. – Mark Byers May 09 '10 at 13:36
  • 2
    I had a mistake in the question, I wanted a 2by2 submatrix (mistake from my part) – NLed May 09 '10 at 13:37
  • 1
    In the future, if there is a mistake in your question you are better off just correcting it instead of making an entirely new post. – dbyrne May 09 '10 at 13:49

3 Answers3

5

OK, so you want to read a submatrix of size n x m, starting at positions x, y in the big matrix of size p x q. You need two things:

  1. (verify that x + n <= p and y + m <= q)
  2. skip to the first element of the matrix you want to read. This requires first skipping the first y - 1 rows
  3. skip x - 1 elements from the next row, then read n elements into your submatrix. Repeat m times.

Your current implementation starts reading from the very first element of the matrix, then reads elements contiguously into the submatrix. An updated version:

FILE *sample = fopen("randomfile.txt", "r");
// skip the first y-1 rows
for (i = 0; i < y - 1; i++) {
  fscanf(sample, "%*[^\n]\n", &matrix[i][j]);
}
for (i = 0; i < m; i++) {
  // skip the first x-1 numbers
  for (j = 0; j < x - 1; j++) {
     fscanf(sample, "%*f");
  }
  // read n numbers
  for (j = 0; j < n; j++) {
     fscanf(sample, "%f", &matrix[i][j]);
  }
  if (x + n < p) {
    // consume the rest of the line
    fscanf(sample, "%*[^\n]\n");
  }
}
fclose(sample);

Update: to read the submatrix from an array instead is even simpler, just requires a bit more calculation. The gist is, a matrix of size p x q can be stored in a contiguous array of size p x q such that matrix[i,j] can be read from array[i*(j-1)+j] (approximately - there may be off-by-one errors and I am never sure which is the column and which is the row, but hopefully you get the idea :-)

So the code would be something like

for (i = 0; i < m; i++) {
  for (j = 0; j < n; j++) {
     submatrix[i][j] = array[(y + i) * p + x + j];
  }
}
Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • Thanks for replying. What if the matrix is 50x25. Am I going to use the row or column for comparison ? n=2 , x+2<50 or x+2<25 ? – NLed May 09 '10 at 13:54
  • Thank you very much, my code turned out to be somewhat similar but I had an error of forgetting to read it into a new matrix. I have a question though, what if the large matrix is not part of a file ? What if a user enters a matrix in an array, and then needs to extract a submatrix from it. How will the scanf syntax change ? – NLed May 09 '10 at 14:18
  • Yes a standard input. I tried doing that, and replacing sample by the original array. I got this : scanf(matrix[i][j],"%*[^\n]\n",&sub[i][j]); --Where sub is the submatrix array... but that didnt work ..It gives an error Argument no 1 of 'scanf' must be of type 'char', not 'float' – NLed May 09 '10 at 14:27
  • This is my refined scan function : scanf("%*[^\n]\n",matrix[i][j],&sub[i][j]); -- Is this correct ? – NLed May 09 '10 at 14:32
  • @ZaZu, if you already have the numbers in memory (whether in an array or any other data structure), you don't need scanf anymore - you can just assign values directly. See my update, even if it is not exactly what you want (as I am not sure I understand that right), hopefully it points you to the right direction. – Péter Török May 09 '10 at 14:38
  • Thank you for the info, so for the option of choosing a submatrix from numbers in the memory, I should use this loop only ? Ok sounds good, but am I supposed to declare array[] as a new array ? Or can I use matrix[][] ? – NLed May 09 '10 at 14:44
  • @ZaZu, I used an array because you mentioned in your comment above that "a user enters a matrix in an array". For a matrix, the innermost statement in the loop should be `submatrix[i][j] = bigmatrix[y + i][x + j]` – Péter Török May 09 '10 at 14:50
  • @Peter, I got this now : user enters 3 by 3 matrix. Then to extract, chooses row 2, and col 2. if the big matrix was `[ 1 1 1 ] [1 2 3 ] [ 1 4 5]`, they should extract `[ 2 3 ] [ 4 5 ]` .. What I get is the first number (2), and then the program fails. All I did was add `bigmatrix[i+col][j+row]` .. isnt this correct ? – NLed May 09 '10 at 15:04
  • @ZaZu, are you sure you get 2 and not 5? Note that in C/C++, arrays are indexed from 0, so the element in the middle of a 3x3 matrix would be `bigmatrix[1][1]`. You must always be clear about whether you are using 0 or 1 based indexes in your calculations. – Péter Török May 09 '10 at 15:23
  • @Peter, Hmm yeah, you're right. Well I dont know what happened, im not getting any number .. It simply crashes before it gives 2 or a 5 ... it should be `[i+col][j+row]` right ? – NLed May 09 '10 at 15:25
  • @ZaZu, so as it seems that your `row` and `col` are 1 based, you should decrement them by 1 in the index calculations, i.e. `bigmatrix[i+col-1][j+row-1]`. – Péter Török May 09 '10 at 15:29
  • @Peter .. AHHHH that did the trick, I was going ahead of the col and row by 1 right ? is this why it crashed ? What do you mean 1 based ? I start the loop by i=0 and and increment until i<2 .. Isnt this 0 based ? it starts at 0 :S .. Please explain ! Thanks :D – NLed May 09 '10 at 15:31
  • @Peter, Is there a way I can check if the user's input for col and row are valid for the bigger matrix ? for example, if they input col=row=4 and the bigger matrix is only 3x3 .. how can I do that ? A do-while statement seems appropriate but how can I check for a value in one part of the array ? `bigmatrix[i][j]` .. can I put `while(col<=bigmatrix[i+col-1][j] && row<=bigmatrix[i][j+row-1])` ? – NLed May 09 '10 at 15:51
  • @ZaZu, `if (row <= bigmatrixRows && col <= bigmatrixCols) { // do the copying } else { // display an error message }` – Péter Török May 09 '10 at 16:10
  • Thanks works perfectly, I forgot to reference it back to a structure that I have. I really appreciate that you kept up with me all along, thank you very much. – NLed May 09 '10 at 16:17
2

Let's take this in stages. First a couple of minor fixes to your code:

for(i=0;i<rows;i++){
  for(j=0;j<cols;j++){
    float dummy;  /* this will make thing easier later */
    fscanf(sample,"%f",&dummy);
    matrix[i][j] = dummy;
  }
/* fscanf(sample,"\n",&matrix[i][j]); this isn't even legal */
}

Now we define what we want:

int startrow = 2; /* The starting index. Remember we index 0,1,2,3 */
int startcol = 2;
int resultrows = 2; /* How many rows we want in our answer */
int resultcols = 2;
float result[resultrows][resultcols];

Now we ignore what we don't want:

for(i=0;i<rows;i++){
  for(j=0;j<cols;j++){
    float dummy;
    fscanf(sample,"%f",&dummy);
    if(i >= startrow && i < startrow + resultrows &&
       j >= startcol && j < startcol + resultcols){
      matrix[i][j] = dummy;
    }
  }
}

Notice that now only the values we want are copied into matrix, the rest of matrix is uninitialized gibberish. Now write it into result instead:

for(i=0;i<rows;i++){
  for(j=0;j<cols;j++){
    float dummy;
    fscanf(sample,"%f",&dummy);
    if(i >= startrow && i < startrow + resultrows &&
       j >= startcol && j < startcol + resultcols){
      result[i-startrow][j-startcol] = dummy;
    }
  }
}

EDIT:
If you want to copy a submatrix from a larger matrix already in memory, the inner loop should be

for(j=0;j<cols;j++){
  if(i >= startrow && i < startrow + resultrows &&
     j >= startcol && j < startcol + resultcols){
      result[i-startrow][j-startcol] = matrix[i][j];
  }
}
Beta
  • 96,650
  • 16
  • 149
  • 150
  • Thank you very much for taking your time and writing this, im testing this and hopefully Ill get it to work. Thanks – NLed May 09 '10 at 14:45
  • I tried using your method but I preferred Peter's way because its similar to the way my course is given .. Thank you though, really appreciate it. – NLed May 09 '10 at 15:43
2

The trick is to make the compiler treat your specific array element as the starting point of your matrix; the following code snippet does that:

(int(*)[SIZE_OF_2ND_DIM])(&a[4][3])

The following program captures the intended purpose:

#include <stdio.h>

int num;

void print( int a[][num], int row, int col )
{
  int i, j;
  for(i = 0; i < row; i++)
  {
    for(j = 0; j < col; j++)
      printf("%3d ", a[i][j]);
    printf("\n");
  }
}


int main()
{
  int a[10][10];
  int i, j;

  for(i = 0; i < 10; i++)
    for(j = 0; j < 10; j++)
      a[i][j] = i*10+j;

  num = 10;
  print(a, 10, 10);

  printf("\n\n");

  print((int(*)[num])(&a[4][3]), 5, 4);

  return 0;
}

Here is the corresponding output:

  0   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  82  83  84  85  86  87  88  89
 90  91  92  93  94  95  96  97  98  99


 43  44  45  46
 53  54  55  56
 63  64  65  66
 73  74  75  76
 83  84  85  86
LKB
  • 157
  • 6