1

Дано задание: разработать функцию int is_tridiagonal (int *mat,int n); mat – это квадратная матрица размером nxn. Функция должна вернуть 1, если mat – трехдиагональная матрица, и 0 в противном случае.

Трехдиагональная матрица(везде кроме "трех главных" диагоналей стоят 0)

Выполнил задачу элементарным методом, с помощью цикла перебора элементов i-ых - строк, j - столбцов

Но нужно как-то обойтись без этих i,j и решить задачу с помощью указателей

В общем, стоит преобразовать мои циклы примерно на вот такие, не понимаю как это сделать

int *p; ... for (p = &a[0][0]; p <= &a[NUM_ROWS-1][NUM_COLS-1]; p++)

    #include <stdio.h>

#define cols 6

void user_matrix(int matrix[cols][cols]); void matrix_outp(int matrix[cols][cols]); int is_tridiagonal(int matrix[cols][cols]);

int main() { int matrix[cols][cols];

user_matrix(matrix);
matrix_outp(matrix);
printf(&quot;%d&quot;, is_tridiagonal(matrix));

}

void user_matrix(int matrix[cols][cols]) { for (int i = 0; i < cols; i++) { for (int j = 0; j < cols; j++) { printf("Enter matrix[%d][%d]: ", i + 1, j + 1); scanf_s("%d", &matrix[i][j]); } printf("\n"); } }

void matrix_outp(int matrix[cols][cols]) { for (int i = 0; i < cols; i++) { for (int j = 0; j < cols; j++) { printf("%6d", matrix[i][j]); } printf("\n"); } } // перебор элементов матрицы и проверка на 0 int is_tridiagonal(int matrix[cols][cols]) {

int schet = 0;

// выше главной и следующей после нее for (int i = 0; i < cols - 1; i++) { for (int j = i + 2; j < cols; j++) { if (matrix[i][j] != 0) schet++; }

} // ниже главной и следующей после нее for (int i = 1; i < cols; i++) { for (int j = 0; j < i - 1; j++) { if (matrix[i][j] != 0) schet++; } } // 3 главные по центру for (int i = 0; i < cols; i++) { for (int j = 0; j < cols; j++) { if (i == j || i == (j - 1) || i == (j + 1)) { if (matrix[i][j] == 0) { schet++; }

    }

}

}

if (schet == 0) printf("Trexdiagonal"); else printf("NETrexdiagonal");

}

qqueeez
  • 27

2 Answers2

0

Мы можем преобразовать матрицу в указать int* pointer = &matrix[0][0]; и работать как с одномерным массивом.

Если у нас матрица 5x5, N=5:

|1 1 0 0 0|
|1 1 1 0 0|
|0 1 1 1 0|
|0 0 1 1 1|
|0 0 0 1 1|

То одномерный массив:

|1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1|

Если мы разберём матрицы других размеров, то заметим закономерность:

2xA, 3xB, 3xA, 3xB, 3xA, 3xB, 3xA, 3xB, 2xA, где A - это 1, а B - это 0, и 2xA - это два раза A подряд и т.д.

Можем заметить, что A повторяется 3 раза, кроме начала и конца, так как три диагонали. А также 3 раза B, рассмотрев другие примеры получим -- N-2 раза подряд (по первой и последней строчке).

Таким образом, мы знаем откуда начинать -- с 3 элемента (2 индекс), сколько шагать -- проверить N-2 элемента, что равны 0, и пропустить 3 элемента (в сумме N+1), а также знаем, где остановиться -- это до предпоследнего элемента &matrix[N-1][N-2]!

Edit:

@DmitryK заметил, что я не правильно построил условие, которое верно только для N=5, и то, что я сразу не передаю указатель на матрицу в функцию.

Я изменил свой код. Теперь функция сразу принимает указатель на матрицу, но и не использует больше других указателей. Я проверил для 4,5,6 квадратных матриц, что "3xA" начинаются с индекса size и потом увеличиваются на size+1.

Идея заключается в том, как теперь мы ходим по развернутому массиву, ++i просто ходим на одну ячейку, и если условие выполняется, дополнительно перепрыгиваем три диагонали (3xA) i += 3.

(++i + 1)%(size+1)?:(i += 3) -- в (++i + 1) я инкрементирую i, чтобы перейти на следующую ячейку. Потом я увеличиваю на 1, чтобы индекс стал возможно кратен size+1. Потом идет трюк с ?:, т.е. если номер ячейки, увеличенной на 1, кратен size+1, то остаток от деления будет 0. Ноль в условиях - это ложь, поэтому будет выполняться в тернарном операторе часть после :, а если будет другое число, отличное от нуля, т.е. истина, то будет выполняться часть после ? и до :, но там ничего нет, поэтому там и ничего не произойдет.

В теле цикла мы просто берём элемент и проверяем не ноль ли он.

Да, код не использует указатель в том смысле, в котором Вы хотели. За это я прошу прощения.

Код:

#include <stdio.h>
#include <stdbool.h>

#define N 5

/* Old answer, works only for N = 5 bool is_tridiagonal(int size, int matrix[size][size]) { for (int* pointer = &matrix[0][2]; pointer < &matrix[size-1][size-2]; pointer += size + 1) if (pointer[0] || pointer[1] || pointer[2]) return false; return true; } */

bool is_tridiagonal(int size, int* matrix) { for (int i = 2; i < size*size-2; (++i + 1)%(size + 1) ?: (i += 3)) if (matrix[i]) return false; return true; }

int main(void) { int matrix[N][N] = { {1, 2, 0, 0, 0}, {3, 4, 5, 0, 0}, {0, 6, 7, 8, 0}, {0, 0, 9, 10, 11}, {0, 0, 0, 12, 13}, }; printf("%d", is_tridiagonal(N, (int*) matrix)); return 0; }

P.S. Поправьте меня, если моя логика неправильная!

Be3y4uu_K0T
  • 871
  • 4
  • 10
  • Компилятор выдает ошибку в строке где bool.. "недопустимый параметр" "выражение не определяется константой", в нижнем printf "передано недостаточно аргументов для строки формата" "is_tridiagonal: функция - шаблон не может преобразовать параметр 2 из типа "int[5][5]"". Будет ли работать данный код если матрица будет задана с клавиатуры или с помощью rand? – qqueeez May 29 '22 at 12:56
  • Попробовал собрать в онлайн компиляторе,выдает 1 даже при условии что в матрице все 0 – qqueeez May 29 '22 at 13:10
  • 1
    @qqueeez, перейдите к представлению квадратной матрицы в виде одномерного массива int a[] размером n * n. В таком случае i,j-ий (i -- индекс строки) элемент матрицы может быть представлен как a[i * n + j] / У вас наверное винда и M$ компайлер и он не понимает код в ответе) – avp May 29 '22 at 13:11
  • @qqueeez Ругается на bool? Вы импортировали #include <stdbool.h>? Можете заменить bool на int, false на 0 и true на 1. Вы наверное, что кроме %d ещё добавили в printf, но не передали в неё. Сейчас добавлю расширенный код. И да, код будет работать с любым способом записи матрицы. – Be3y4uu_K0T May 29 '22 at 13:14
  • Ничего не менял,ctrl + c, ctrl + v, дело в том что код пишу в Visual Studio 2019, возможно из-за этого такие ошибки выдает) – qqueeez May 29 '22 at 13:18
  • @qqueeez Очень странно.. Я использую clang с такими настройками clang main.c -o main -arch arm64 -O3 -Wall -std=c18 под свой MacBook на M1. – Be3y4uu_K0T May 29 '22 at 13:22
  • Увидел вашу вторую версию кода ,столько ошибок посыпалось при попытке запуска,может посоветуете какую-нибудь более "адекватную" среду разработки на С? – qqueeez May 29 '22 at 13:23
  • 42 ошибки и 4 предупреждения – qqueeez May 29 '22 at 13:23
  • @qqueeez Даже не знаю.. Я использую Visual Studio Code и компилятор clang из командной строки (встроенной в VSCode). – Be3y4uu_K0T May 29 '22 at 13:26
  • В основном подчеркивает как ошибку int matrix[size][size] в параметрах всех функций – qqueeez May 29 '22 at 13:27
  • @qqueeez Вы можете заменить параметры всех функций с (int size, int matrix[size][size]) на (int matrix[N][N]). НО не забудьте заменить везде size на N в коде. – Be3y4uu_K0T May 29 '22 at 13:28
  • @qqueeez Если мой ответ помог Вам, то прошу отметить его как решение https://ru.stackoverflow.com/help/someone-answers. – Be3y4uu_K0T May 29 '22 at 13:41
  • Да,спасибо огромное,вы очень помогли – qqueeez May 29 '22 at 13:44
  • А зачем передавать в функцию копию матрицы, и только внутри переводить в указатель? Нужно в функцию передавать указатель is_tridiagonal(int size, int* p) и ссылаться на элементы как написал @avp. Вычисления идут с абсолютными числами, а надо вычислять относительно N. Иначе если изменить значение N - алгоритм ломается if (*pointer != 0 || *(pointer + 1) != 0. – DmitryK May 30 '22 at 12:11
  • 1
    Работа с двумерным массивом как с одномерным, строго говоря, вызывает неопределённое поведение. – wololo May 30 '22 at 17:59
  • @DmitryK Хмм, я не до конца понимаю Вашу идею, как цикл for будет знать, где остановиться? А также, если я всё правильно помню, "Хотя параметр num объявляется как целочисленный массив из десяти элементов, С автоматически преобразует его к целочисленному указателю, поскольку не существует параметра, который мог бы на самом деле принять весь массив. " (ссылка: http://www.c-cpp.ru/books/peredacha-massivov-v-funkcii) Прошу, добавьте свой ответ! – Be3y4uu_K0T May 31 '22 at 08:34
  • @Be3y4uu_K0T В смысле как узнать? Вы же передаете не только массив, но и его размер is_tridiagonal(N, matrix). – DmitryK May 31 '22 at 08:49
  • @DmitryK Так ранее Вы говорили is_tridiagonal(int size, int* p) p - указатель, как Вы хотите проверять вышел ли указатель за границы?.. (Ещё раз Вас прошу, сделайте своё решение и предложите его) – Be3y4uu_K0T May 31 '22 at 08:58
0

Код с передачей указателя, место копирования матрицы:

bool is_tridiagonal(int size, int* matrix)
 {
    for (int* pointer = matrix + 2; pointer < (matrix + size*size-3); pointer += size + 1)
    {   // вот здесь должен быть цикл до size-2 - иначе при размере матрицы отличным от 5 - будет неправильная работа
        // сами же в описании написали - "B повторяется N-2 раза"
        if (*pointer != 0 || *(pointer + 1) != 0 || *(pointer + 2) != 0) // вот это работает только для N==5
            return false;
    }
    return true;
}

Кроме того, вы проверяете только что не в диагоналях находятся нули. Но не проверяете, что в диагоналях нули отсутствуют.
И ещё момент, сравнение в условии цикла - нужно посчитать с чем сравнивать, чтобы не было ошибки, т.к. там идет приращение нестандартное pointer += size + 1
А ещё нужна проверка что размер матрицы больше 2

DmitryK
  • 4,566
  • Я понял теперь о чем Вы говорили! Я исправлю свой ответ. Ещё мой компилятор (clang) ругается на int* matrix - warning: incompatible pointer types passing 'int [5][5]' to parameter of type 'int *' [-Wincompatible-pointer-types], я подставил Вашу функцию в свой код. Возможно надо добавить мне (int*) matrix – Be3y4uu_K0T May 31 '22 at 09:43
  • Да, это предупреждение. Разные компиляторы разное выдают. Т.к. при работе с указателями больше вероятность допустить ошибку. Вызов нужно сделать is_tridiagonal(N, &matrix[0][0]) или is_tridiagonal(N, matrix[0]). – DmitryK May 31 '22 at 10:02
  • Я изменил свое решение. С "B повторяется N-2 раза". – Be3y4uu_K0T May 31 '22 at 12:14
  • @Be3y4uu_K0T Чтобы было на указателях, замените matrix[i] на *(matrix + i). Кстати предыдущее решение было тоже ничего, только надо было прямое сравнение заменить на цикл. – DmitryK May 31 '22 at 12:39