C语言 用0和1填充矩阵并在必要时将周围单元格置为0的一种高效而优美的解法

bvpmtnay  于 2023-03-11  发布在  其他
关注(0)|答案(6)|浏览(261)

我需要用随机的0或1填充一个矩阵,但是如果一个单元格包含1,那么它周围的所有单元格(总共8个单元格)都应该设置为0。我已经尝试过实现这段代码,但是我不确定是否有更简单的方法来实现它。你能建议一个更有效或更优雅的解决方案吗?谢谢!
作为一个初学者,我尝试以这种方式实现我的代码:

for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        a[i][j] = rand() % 2;
        if (a[i][j] == 1) {
            a[i-1][j-1] = 0; 
            a[i-1][j]   = 0; 
            a[i-1][j+1] = 0;
            a[i][j-1]   = 0; 
            a[i][j+1]   = 0;
            a[i+1][j+1] = 0; 
            a[i+1][j]   = 0; 
            a[i+1][j+1] = 0;
        }
    }
}
p4tfgftt

p4tfgftt1#

你也许应该分两步来写,否则你可能会在之前擦除的单元格上再次写1,而且你必须避免在矩阵之外写,下面是一个例子:

int main()
{
    // First fills the matrix :
    int a[5][5] = {0};
    printf("Random Matrix :\n");
    for (int i = 0; i < 5; i++){
        for (int j = 0; j < 5; j++){
            a[i][j] = rand() % 2;
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
    // Next filters it :
    printf("---------\nFiltered Matrix :\n");
    for (int i = 0; i < 5; i++){
        for (int j = 0; j < 5; j++){
            if (a[i][j] == 1) {
                if (i > 0) {
                    a[i-1][j]              = 0; 
                    if (j > 0) a[i-1][j-1] = 0;
                    if (j < 4) a[i-1][j+1] = 0;
                }
                if (i < 4) {
                    a[i+1][j]              = 0; 
                    if (j < 4) a[i+1][j+1] = 0;
                }
                if (j > 0) a[i][j-1] = 0; 
                if (j < 4) a[i][j+1] = 0; 
               
            }
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

产出

Random Matrix :
1 0 1 1 1 
1 0 0 1 1 
0 1 0 1 1 
0 0 0 0 0 
1 0 1 1 0 
---------
Filtered Matrix :
1 0 1 0 1 
0 0 0 0 0 
0 1 0 1 0 
0 0 0 0 0 
1 0 1 0 0
holgip5t

holgip5t2#

除了访问超出边界的数组之外,你的程序是正常的。下面是一个更通用的方法,适用于范围大于1个单元格的情况

#include <stdio.h>
#include <stdlib.h>

#define ROW 5
#define COL 5
#define RAN 1 // range

int main() {

    int a[ROW][COL];
    
    for (int i = 0; i < ROW; i++) {
    
        for (int j = 0; j < COL; j++) {
        
            if( (a[i][j] = rand() % 2) ) {
                
                for(int k = i-RAN; k <= i+RAN && k < ROW; k++) {
                    
                    for(int l = j-RAN; l <= j+RAN && l < COL; l++) {
                        
                        if( k >= 0 && l >= 0 && !(k == i && l == j)) {
                            
                            a[k][l] = 0;
                        }
                    }
                }
            }
        }
    }
    
    for (int i = 0; i < ROW; i++) {
    
        for (int j = 0; j < COL; j++) {
            
            printf("%d ", a[i][j]);
        }
        puts("");
    }
    
    return 0;
}
4xrmg8kj

4xrmg8kj3#

有一种更简单的方法可以做到这一点:你需要使用曼哈顿公式。代码可以是:

int distance = sqrt(pow((x1-x2),2) + pow((y1-y2),2))

其中x1,y1是我的单元格的坐标,x2,y2是包含1的其他单元格的坐标。如果distance〈1,则无法将单元格设置为1。因此,可以创建一个包含所有包含1的单元格的坐标的矩阵,并在将单元格设置为1之前对整个矩阵进行检查。

nzrxty8p

nzrxty8p4#

我不认为有一种更简单或更干净的方法来完成这项工作。您的代码很清楚,并且尽可能地简短。有一些替代方法具有更好的属性,例如不使1的外观偏向边缘,特别是矩阵的右下角,但这是一个不同的标准。
正如我在注解中所写的,您确实需要避免超出数组的边界(这将使代码变得更复杂),当然,没有必要向随机数添加0。
通过用其他方式对矩阵进行编码,比如在unsigned char数组中按位编码,您可能能够更“聪明”地完成这一操作,但这不会更简单,也可能不会更清晰。
解决溢出问题的一个非常干净的方法可能是声明数组在每个维度上大两个元素,并对真实的元素使用从1开始的索引。这将在实际数据的所有方面提供一个余量,以便所有写入都在实际数组的范围内,即使有些超出了有意义的数据。即:

int a[7][7] = {0};  // a[1][1] ... a[5][5] will contain the real data

for (int i = 1; i < 6; i++) {
    for (int j = 1; j < 6; j++) {
        a[i][j] = rand() % 2;
        if (a[i][j] == 1) {
            a[i-1][j-1] = 0; 
            // ...
        }
    }
}

假设生成的实际矩阵只包含1 <= i && i <= 51 <= j && j <= 5元素,那么使用i - 1j + 1等索引也没关系,因为它们对所有ij实数都有效。

50pmv0ei

50pmv0ei5#

您的代码有问题:你写入坐标在0..4范围之外的矩阵单元格。
以下是修改后的版本:

for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        a[i][j] = 0;
        if (rand() % 2) {
            for (int ii = i - 1; i <= i + 1; i++) {
                if (ii >= 0 && ii < 5) {
                    for (jj = j - 1; j <= j + 1; j++) {
                        if (jj >= 0 && jj < 5)
                            a[ii][jj] = 0;
                    }
                }
            }
            a[i][j] = 1;
        }
    }
}
w8ntj3qf

w8ntj3qf6#

一些想法
你的代码是一个循环中的循环。对于二维数组,你可以考虑在一个循环中完成所有操作。对于小数组,即:1-5这真的不重要。对于较大的数组,如果将i,j作为数组的函数计算,则算法会更快。请参见:Map a 2D array onto a 1D array
你也可以根据代码缩进的目的,给变量命名为'a','i',j,比如row,col或者其他更有意义的名字。
另一部分让我有点困惑,假设你的数组是5x 5,也假设你的最终目标是确保每个“1”总是有8个邻居为零,一旦第一个随机数生成产生“1”,并迫使8个邻居为零,那么就不再需要更新4个邻居。(i,j+1)/(i+1,j)/(i+1,j+1)/(i-1,j).正如@Weather Vane所指出的,你正在覆盖你的工作,这可能是你得到“多余”感觉的地方。

#define MAX_ARRAY 5

int in_bounds(int row,int col)
{
if (row >= MAX_ARRAY) return 0;
if (col >= MAX_ARRAY) return 0;
return 1;
}

void print_matrix (int matrix[MAX_ARRAY][MAX_ARRAY],int size)
{

for (int i=0;i<size;i++)
 {
 for (int j=0;j<size;j++)
  {
  fprintf(stdout,"%d",matrix[i][j]);
//  fprintf(stdout,"%d,%d ",i,j);
  }
 fprintf(stdout,"\n");

 }
}

int main()
{
//for stack overflow: https://stackoverflow.com/questions/65398722/is-there-a-cleaner-way-to-write-this-c-code/65399364#65399364
   srand(time(0));
    int matrix[MAX_ARRAY][MAX_ARRAY];
     memset(matrix, -1, sizeof(int) * MAX_ARRAY * MAX_ARRAY);
  
    for (int counter=0;counter<MAX_ARRAY * MAX_ARRAY;counter++)
      {
      int col=counter / MAX_ARRAY;
      int row=counter % MAX_ARRAY;

      if (matrix[row][col] == -1)
        {
        matrix[row][col] = rand() %2;
        if (matrix[row][col] == 1)
         {
            if (in_bounds(row,col+1)) matrix[row][col+1] = 0;
            if (in_bounds(row+1,col)) matrix[row+1][col] = 0;
            if (in_bounds(row+1,col+1)) matrix[row+1][col+1] = 0;
            if (in_bounds(row-1,col)) matrix[row-1][col] = 0;
            if (in_bounds(col,row-1)) matrix[col][row-1] = 0;
            if (in_bounds(row-1,col-1)) matrix[row-1][col-1] = 0;
            if (in_bounds(row-1,col+1)) matrix[row-1][col+1] = 0;
            if (in_bounds(row+1,col-1)) matrix[row+1][col-1] = 0;
        }
       }

  }
print_matrix(matrix,MAX_ARRAY);
}

相关问题