【问题标题】:How do I rotate 2D matrix properly?如何正确旋转二维矩阵?
【发布时间】:2021-08-07 19:09:57
【问题描述】:

我得到一个方阵大小n 和矩阵包含的字符。

例如:

3
one
two
row

我必须将矩阵旋转 45 度。

              | | |o| | |
  |o|n|e|     | |t| |n| |         
  |t|w|o| ->  |r| |w| |e|
  |r|o|w|     | |o| |o| |
              | | |w| | |

我明白了

               | | |o| | |
   |o|n|e|     | |t| |n| |         
   |t|w|o| ->  |r| |w| |e|
   |r|o|w|     | |o|w|o| |
               | | | | | |

这是由于四舍五入造成的。

我写了代码。我的解决方案非常有限,我仅代表它来展示想法和我的错误。

我不明白的主要事情是如何将字符存储在数组中,以便在将浮点值舍入为整数值后它们具有正确的位置(坐标 x,y)。有正确的做法吗?

有什么建议或意见吗?

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cmath>

struct Coordinates
{
    int x, y;
};

int main()
{
    int n;
    std::ifstream fin("U3.txt");
    fin >> n;
    fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    std::vector<std::vector<char>> matrix;
    for (int i = 0; i < n; i++)
    {
        std::string temp_s;
        std::vector<char> temp_v(n);
        std::getline(fin, temp_s, '\n');
        for (int j = 0; j < n; j++)
        {
            temp_v[j] = temp_s[j];
        }
        matrix.push_back(temp_v);
    }
    fin.close();
    
    std::vector<Coordinates> cord(n * n); // Store coordinates after rotation
    int index = 0;
    for (int y = 0; y < n; y++)
    {
        for (int x = 0; x < n; x++)
        {
            // Multiplying two matrices
            /*
                [ cos(45) -sin(45) ] [ x ]
                [ sin(45)  con(45) ] [ y ]
                =
                [ sqrt(2)/2 -sqrt(2)/2 ] [ x ]
                [ sqrt(2)/2  sqrt(2)/2 ] [ y ]
                =
                [ sqrt(2)/2 * (x - y) ]
                [ sqrt(2)/2 * (x + y) ]
            */
            double new_x = (std::sqrt(2) / 2 * (x - y));
            double new_y = (std::sqrt(2) / 2 * (x + y));
            
            // Trying to round value to int because the index of array is int
            cord[index].x = (new_x >= 0.0 ? std::ceil(new_x) : std::floor(new_x));
            cord[index].y = (new_y >= 0.0 ? std::ceil(new_y) : std::floor(new_y));
            index++;
        }
    }
    /*
    
    Finding the min_x and min_y to know how much should I add to
    the new x and y coordinates to keep the coordinates positive,
    as I have to store them in array. 

    */
    int min_x = std::numeric_limits<int>::max();
    int min_y = std::numeric_limits<int>::max();

    for (int i = 0; i < n * n; i++)
    {
        if (min_x > cord[i].x) { min_x = cord[i].x; }
        if (min_y > cord[i].y) { min_y = cord[i].y; }
    }

    // If there are no negative coordinates then there is nothing to add
    // So I initialize min_x and min_y to 0
    if (min_x >= 0) { min_x = 0; }
    if (min_y >= 0) { min_y = 0; }

    std::vector<std::vector<char>> new_matrix(10, std::vector<char>(10, ' '));

    int row = 0, column = 0;
    for (int i = 0; i < cord.size(); i++)
    {
        new_matrix[cord[i].y + min_y * (-1)][cord[i].x + min_x * (-1)] = matrix[row][column];
        if ((i + 1) % n == 0) { row++; column = 0; continue; }
        column++;
    }

    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            std::cout << new_matrix[i][j];
        }
        std::cout << std::endl;
    }


    return 0;
}

【问题讨论】:

  • 我不明白的主要是如何将字符存储在数组中,以便它们有正确的位置 -- 我建议你先在纸上计划一下而不是写可能会让您目瞪口呆的 C++ 代码。一旦你在纸上解决了这个问题,然后你就可以编写代码了。然后要么代码遵循您的计划(因此有效),或者它有一个遵循您的计划的错误并且您修复了错误,或者您确定该计划是错误的并且您重新开始一个新计划。

标签: c++ matrix multidimensional-array


【解决方案1】:

嗯,据我了解,您使用旋转矩阵的算法将不起作用。你总是会遇到四舍五入的问题。并且将值正确放置在目标矩阵中的正确位置。

甚至不需要,因为二维的索引可以很简单地计算出来,不需要任何浮点运算。

为了找到更好的方法,让我们首先分析可能的数据:

如您所见。原始行的“下一个值”必须始终放置在右侧和下方。因此,对于 n = 3。打印 1,向右,向下,打印 2,向右,向下。以此类推。

如果一个新的源行开始,那么目标起始行和起始列是上面的值,但是左一下。然后再次与上面相同。对,下,对,下。以此类推。

所以,很简单。

那么,新矩阵的大小始终为 2*n-1,因为我们将始终在 2 个字符之间留有空格。

目的起始行当然也是0,目的起始列只是n-1。

所有索引当然都是从 0 开始的。

使用这种方法,无需转换。目标值可以立即放置在正确的坐标上。

解决方案很简单:

#include <string>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <iterator>

const std::string sourceFileName{ "U3.txt" };

int main() {

    // Open the source File and check, if it could be opened
    if (std::ifstream sourceFileStream{ sourceFileName }; sourceFileStream) {

        // Read the size of the matrix (Must be > 0). Eat trailing white spaces
        if (size_t matrixSize{}; (sourceFileStream >> matrixSize >> std::ws) and (matrixSize > 0)) {

            // Read the complete source file
            std::vector sourceRow(std::istream_iterator<std::string>(sourceFileStream), {});

            // Define a 2 dim vector for target. Size is 2*n-1
            std::vector<std::vector<char>> destination(matrixSize*2-1, std::vector<char>(matrixSize*2-1, ' '));

            // Set start indices for destination. Please note: XStart, or Column start is always matrixSize-1 
            size_t startOffsetIndexDestinationColumn{ matrixSize - 1 };
            size_t startOffsetIndexDestinationRow{};

            // Iterate over all source rows
            for (size_t row{}; (row < matrixSize) and (row < sourceRow.size()); ++row) {

                // Calculate offset for coordinates in destination table
                size_t offsetRow{ startOffsetIndexDestinationRow };
                size_t offsetColumn{ startOffsetIndexDestinationColumn - row};

                // Iterate over source columns in rows and assign value to calculated destination coordinates
                for (size_t column{}; (column < matrixSize) and (column < sourceRow[row].size()); ++column) {
                    // Assign value
                    destination[row + offsetRow++][column + offsetColumn] = sourceRow[row][column];
                }
            }
            // Show result to user. For each row in the destination vector
            for (const auto& row : destination) {

                // And for each column in this row
                for (const char c : row) std::cout << c;

                // Next row
                std::cout << '\n';
            }
        }
        else std::cerr << "\nError: Wrong dimension in source file ' " << sourceFileName << "'\n";
    }
    else std::cerr << "\nError: Could not open source file '" << sourceFileName << "'\n";
    return 0;
}

因此,选择合适的算法将为您省去很多麻烦。

【讨论】: